Skip to content

Commit 1d1ecf3

Browse files
committed
Sema: Variadic parameters are always @escaping and cannot be @autoclosure
A variadic parameter of function type must be @escaping -- we cannot reason about an array of non-escaping closures, so this was a safety hole. Also, attempting to define an @autoclosure variadic did not produce a diagnostic, but would fail later on if you actually tried to do anything with it. Let's ban this completely. Both changes are source breaking, but impact is limited to code that was already only marginally valid.
1 parent d99e1b0 commit 1d1ecf3

File tree

10 files changed

+114
-73
lines changed

10 files changed

+114
-73
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -906,10 +906,6 @@ ERROR(attribute_requires_operator_identifier,none,
906906
ERROR(attribute_requires_single_argument,none,
907907
"'%0' requires a function with one argument", (StringRef))
908908

909-
ERROR(inout_cant_be_variadic,none,
910-
"inout arguments cannot be variadic", ())
911-
ERROR(inout_only_parameter,none,
912-
"'inout' is only valid in parameter lists", ())
913909
ERROR(var_parameter_not_allowed,none,
914910
"parameters may not have the 'var' specifier", ())
915911

@@ -1831,10 +1827,12 @@ ERROR(property_behavior_invalid_parameter,none,
18311827
// Type Check Attributes
18321828
//------------------------------------------------------------------------------
18331829

1834-
ERROR(attr_only_only_one_decl_kind,none,
1830+
ERROR(attr_only_one_decl_kind,none,
18351831
"%0 may only be used on '%1' declarations", (DeclAttribute,StringRef))
1836-
ERROR(attr_only_only_on_parameters,none,
1832+
ERROR(attr_only_on_parameters,none,
18371833
"%0 may only be used on parameters", (StringRef))
1834+
ERROR(attr_not_on_variadic_parameters,none,
1835+
"%0 may not be used on variadic parameters", (StringRef))
18381836

18391837

18401838
ERROR(override_final,none,

lib/Sema/TypeCheckAttr.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ void TypeChecker::checkDeclAttributesEarly(Decl *D) {
681681
}
682682

683683
if (!OnlyKind.empty())
684-
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_only_one_decl_kind,
684+
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind,
685685
attr, OnlyKind);
686686
else if (attr->isDeclModifier())
687687
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
@@ -1577,7 +1577,7 @@ void TypeChecker::checkTypeModifyingDeclAttributes(VarDecl *var) {
15771577
checkAutoClosureAttr(pd, attr);
15781578
else {
15791579
AttributeEarlyChecker Checker(*this, var);
1580-
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_only_one_decl_kind,
1580+
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind,
15811581
attr, "parameter");
15821582
}
15831583
}
@@ -1586,7 +1586,7 @@ void TypeChecker::checkTypeModifyingDeclAttributes(VarDecl *var) {
15861586
checkNoEscapeAttr(pd, attr);
15871587
else {
15881588
AttributeEarlyChecker Checker(*this, var);
1589-
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_only_one_decl_kind,
1589+
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind,
15901590
attr, "parameter");
15911591
}
15921592
}

lib/Sema/TypeCheckPattern.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -739,21 +739,21 @@ static bool validateParameterType(ParamDecl *decl, DeclContext *DC,
739739
TypeChecker &TC) {
740740
if (auto ty = decl->getTypeLoc().getType())
741741
return ty->is<ErrorType>();
742-
742+
743+
// If the element is a variadic parameter, resolve the parameter type as if
744+
// it were in non-parameter position, since we want functions to be
745+
// @escaping in this case.
746+
auto elementOptions = (options |
747+
(decl->isVariadic() ? TR_VariadicFunctionInput
748+
: TR_FunctionInput));
743749
bool hadError = TC.validateType(decl->getTypeLoc(), DC,
744-
options|TR_FunctionInput, resolver);
750+
elementOptions, resolver);
745751

746752
Type Ty = decl->getTypeLoc().getType();
747753
if (decl->isVariadic() && !hadError) {
748-
// If isn't legal to declare something both inout and variadic.
749-
if (Ty->is<InOutType>()) {
750-
TC.diagnose(decl->getStartLoc(), diag::inout_cant_be_variadic);
754+
Ty = TC.getArraySliceType(decl->getStartLoc(), Ty);
755+
if (Ty.isNull()) {
751756
hadError = true;
752-
} else {
753-
Ty = TC.getArraySliceType(decl->getStartLoc(), Ty);
754-
if (Ty.isNull()) {
755-
hadError = true;
756-
}
757757
}
758758
decl->getTypeLoc().setType(Ty);
759759
}

lib/Sema/TypeCheckType.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
17791779
bool isFunctionParam =
17801780
options.contains(TR_FunctionInput) ||
17811781
options.contains(TR_ImmediateFunctionInput);
1782+
bool isVariadicFunctionParam =
1783+
options.contains(TR_VariadicFunctionInput);
17821784

17831785
// The type we're working with, in case we want to build it differently
17841786
// based on the attributes we see.
@@ -1959,7 +1961,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
19591961
// @autoclosure is only valid on parameters.
19601962
if (!isFunctionParam && attrs.has(TAK_autoclosure)) {
19611963
TC.diagnose(attrs.getLoc(TAK_autoclosure),
1962-
diag::attr_only_only_on_parameters, "@autoclosure");
1964+
isVariadicFunctionParam
1965+
? diag::attr_not_on_variadic_parameters
1966+
: diag::attr_only_on_parameters, "@autoclosure");
19631967
attrs.clearAttribute(TAK_autoclosure);
19641968
}
19651969

@@ -2390,7 +2394,11 @@ Type TypeResolver::resolveInOutType(InOutTypeRepr *repr,
23902394
// inout is only valid for function parameters.
23912395
if (!(options & TR_FunctionInput) &&
23922396
!(options & TR_ImmediateFunctionInput)) {
2393-
TC.diagnose(repr->getInOutLoc(), diag::inout_only_parameter);
2397+
TC.diagnose(repr->getInOutLoc(),
2398+
(options & TR_VariadicFunctionInput)
2399+
? diag::attr_not_on_variadic_parameters
2400+
: diag::attr_only_on_parameters,
2401+
"'inout'");
23942402
repr->setInvalid();
23952403
return ErrorType::get(Context);
23962404
}
@@ -2510,38 +2518,58 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
25102518
if (options & TR_ImmediateFunctionInput)
25112519
elementOptions |= TR_FunctionInput;
25122520
}
2513-
2521+
2522+
bool complained = false;
2523+
2524+
// Variadic tuples are not permitted.
2525+
if (repr->hasEllipsis() &&
2526+
!(options & TR_ImmediateFunctionInput)) {
2527+
TC.diagnose(repr->getEllipsisLoc(), diag::tuple_ellipsis);
2528+
repr->removeEllipsis();
2529+
complained = true;
2530+
}
2531+
25142532
for (auto tyR : repr->getElements()) {
25152533
NamedTypeRepr *namedTyR = dyn_cast<NamedTypeRepr>(tyR);
2516-
if (namedTyR && !(options & TR_ImmediateFunctionInput)) {
2517-
Type ty = resolveType(namedTyR->getTypeRepr(), elementOptions);
2518-
if (!ty || ty->is<ErrorType>()) return ty;
2534+
Type ty;
2535+
Identifier name;
2536+
bool variadic = false;
25192537

2520-
elements.push_back(TupleTypeElt(ty, namedTyR->getName()));
2521-
} else {
2538+
// If the element has a label, stash the label and get the underlying type.
2539+
if (namedTyR) {
25222540
// FIXME: Preserve and serialize parameter names in function types, maybe
25232541
// with a new sugar type.
2524-
Type ty = resolveType(namedTyR ? namedTyR->getTypeRepr() : tyR,
2525-
elementOptions);
2526-
if (!ty || ty->is<ErrorType>()) return ty;
2542+
if (!(options & TR_ImmediateFunctionInput))
2543+
name = namedTyR->getName();
25272544

2528-
elements.push_back(TupleTypeElt(ty));
2545+
tyR = namedTyR->getTypeRepr();
25292546
}
2530-
}
25312547

2532-
// Tuple representations are limited outside of function inputs.
2533-
if (!(options & TR_ImmediateFunctionInput)) {
2534-
bool complained = false;
2548+
// If the element is a variadic parameter, resolve the parameter type as if
2549+
// it were in non-parameter position, since we want functions to be
2550+
// @escaping in this case.
2551+
auto thisElementOptions = elementOptions;
2552+
if (repr->hasEllipsis() &&
2553+
elements.size() == repr->getEllipsisIndex()) {
2554+
thisElementOptions = withoutContext(elementOptions);
2555+
thisElementOptions |= TR_VariadicFunctionInput;
2556+
variadic = true;
2557+
}
2558+
2559+
ty = resolveType(tyR, thisElementOptions);
2560+
if (!ty || ty->is<ErrorType>()) return ty;
2561+
2562+
// If the element is a variadic parameter, the underlying type is actually
2563+
// an ArraySlice of the element type.
2564+
if (variadic)
2565+
ty = TC.getArraySliceType(repr->getEllipsisLoc(), ty);
25352566

2536-
// Variadic tuples are not permitted.
2537-
if (repr->hasEllipsis()) {
2538-
TC.diagnose(repr->getEllipsisLoc(), diag::tuple_ellipsis);
2539-
repr->removeEllipsis();
2540-
complained = true;
2541-
}
2567+
elements.push_back(TupleTypeElt(ty, name, variadic));
2568+
}
25422569

2543-
// Single-element labeled tuples are not permitted outside of declarations
2544-
// or SIL, either.
2570+
// Single-element labeled tuples are not permitted outside of declarations
2571+
// or SIL, either.
2572+
if (!(options & TR_ImmediateFunctionInput)) {
25452573
if (elements.size() == 1 && elements[0].hasName()
25462574
&& !(options & TR_SILType)
25472575
&& !(options & TR_EnumCase)) {
@@ -2557,14 +2585,6 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
25572585
}
25582586
}
25592587

2560-
if (repr->hasEllipsis()) {
2561-
auto &element = elements[repr->getEllipsisIndex()];
2562-
Type baseTy = element.getType();
2563-
Type fullTy = TC.getArraySliceType(repr->getEllipsisLoc(), baseTy);
2564-
Identifier name = element.getName();
2565-
element = TupleTypeElt(fullTy, name, true);
2566-
}
2567-
25682588
return TupleType::get(elements, Context);
25692589
}
25702590

lib/Sema/TypeChecker.h

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -327,58 +327,61 @@ enum TypeResolutionFlags : unsigned {
327327
/// Whether this is the immediate input type to a function type,
328328
TR_ImmediateFunctionInput = 0x40,
329329

330+
/// Whether this is a variadic function input.
331+
TR_VariadicFunctionInput = 0x100,
332+
330333
/// Whether we are in the result type of a function body that is
331334
/// known to produce dynamic Self.
332-
TR_DynamicSelfResult = 0x100,
335+
TR_DynamicSelfResult = 0x200,
333336

334337
/// Whether this is a resolution based on a non-inferred type pattern.
335-
TR_FromNonInferredPattern = 0x200,
338+
TR_FromNonInferredPattern = 0x400,
336339

337340
/// Whether we are the variable type in a for/in statement.
338-
TR_EnumerationVariable = 0x400,
341+
TR_EnumerationVariable = 0x800,
339342

340343
/// Whether we are looking only in the generic signature of the context
341344
/// we're searching, rather than the entire context.
342-
TR_GenericSignature = 0x800,
345+
TR_GenericSignature = 0x1000,
343346

344347
/// Whether this type is the referent of a global type alias.
345-
TR_GlobalTypeAlias = 0x1000,
348+
TR_GlobalTypeAlias = 0x2000,
346349

347350
/// Whether this type is the value carried in an enum case.
348-
TR_EnumCase = 0x2000,
351+
TR_EnumCase = 0x4000,
349352

350353
/// Whether this type is being used in an expression or local declaration.
351354
///
352355
/// This affects what sort of dependencies are recorded when resolving the
353356
/// type.
354-
TR_InExpression = 0x4000,
357+
TR_InExpression = 0x8000,
355358

356359
/// Whether this type resolution is guaranteed not to affect downstream files.
357-
TR_KnownNonCascadingDependency = 0x8000,
360+
TR_KnownNonCascadingDependency = 0x10000,
358361

359362
/// Whether we should allow references to unavailable types.
360-
TR_AllowUnavailable = 0x10000,
363+
TR_AllowUnavailable = 0x20000,
361364

362365
/// Whether this is the payload subpattern of an enum pattern.
363-
TR_EnumPatternPayload = 0x20000,
366+
TR_EnumPatternPayload = 0x40000,
364367

365368
/// Whether we are binding an extension declaration, which limits
366369
/// the lookup.
367-
TR_ExtensionBinding = 0x40000,
370+
TR_ExtensionBinding = 0x80000,
368371

369372
/// Whether we are in the inheritance clause of a nominal type declaration
370373
/// or extension.
371-
TR_InheritanceClause = 0x80000,
374+
TR_InheritanceClause = 0x100000,
372375

373376
/// Whether we should resolve only the structure of the resulting
374377
/// type rather than its complete semantic properties.
375-
TR_ResolveStructure = 0x100000,
378+
TR_ResolveStructure = 0x200000,
376379

377380
/// Whether this is the type of an editor placeholder.
378-
TR_EditorPlaceholder = 0x200000,
381+
TR_EditorPlaceholder = 0x400000,
379382

380383
/// Whether we are in a type argument for an optional
381-
TR_ImmediateOptionalTypeArgument = 0x400000,
384+
TR_ImmediateOptionalTypeArgument = 0x800000,
382385
};
383386

384387
/// Option set describing how type resolution should work.
@@ -394,6 +397,7 @@ static inline TypeResolutionOptions
394397
withoutContext(TypeResolutionOptions options) {
395398
options -= TR_ImmediateFunctionInput;
396399
options -= TR_FunctionInput;
400+
options -= TR_VariadicFunctionInput;
397401
options -= TR_EnumCase;
398402
options -= TR_ImmediateOptionalTypeArgument;
399403
return options;

test/attr/attr_autoclosure.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,7 @@ func callAutoclosureWithNoEscape_3(_ fn: @autoclosure () -> Int) {
156156
takesAutoclosure(fn()) // ok
157157
}
158158

159+
// expected-error @+1 {{@autoclosure may not be used on variadic parameters}}
160+
func variadicAutoclosure(_ fn: @autoclosure () -> ()...) {
161+
for _ in fn {}
162+
}

test/attr/attr_escaping.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,18 @@ func testModuloOptionalness() {
127127
deepOptionalClosure = fn // expected-error{{assigning non-escaping parameter 'fn' to an @escaping closure}}
128128
}
129129
}
130+
131+
// Check that functions in vararg position are @escaping
132+
func takesEscapingFunction(fn: @escaping () -> ()) {}
133+
func takesArrayOfFunctions(array: [() -> ()]) {}
134+
135+
func takesVarargsOfFunctions(fns: () -> ()...) {
136+
takesArrayOfFunctions(array: fns)
137+
for fn in fns {
138+
takesEscapingFunction(fn: fn)
139+
}
140+
}
141+
142+
func takesNoEscapeFunction(fn: () -> ()) { // expected-note {{parameter 'fn' is implicitly non-escaping}}
143+
takesVarargsOfFunctions(fns: fn) // expected-error {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
144+
}

test/attr/attr_inout.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ func f(x : inout Int) { } // okay
44

55
func h(_ : inout Int) -> (inout Int) -> (inout Int) -> Int { }
66

7-
func ff(x: (inout Int, inout Float)) { } // expected-error {{'inout' is only valid in parameter lists}}
7+
func ff(x: (inout Int, inout Float)) { } // expected-error {{'inout' may only be used on parameters}}
88

99
enum inout_carrier {
10-
case carry(inout Int) // expected-error {{'inout' is only valid in parameter lists}}
10+
case carry(inout Int) // expected-error {{'inout' may only be used on parameters}}
1111
}
1212

1313
func deprecated(inout x: Int) {} // expected-error {{'inout' before a parameter name is not allowed, place it before the parameter type instead}}

test/decl/func/vararg.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ f3({ print($0) })
1919
func f4(_ a: Int..., b: Int) { }
2020

2121
// rdar://16008564
22-
func inoutVariadic(_ i: inout Int...) { // expected-error {{inout arguments cannot be variadic}}
22+
func inoutVariadic(_ i: inout Int...) { // expected-error {{'inout' may not be used on variadic parameters}}
2323
}
2424

2525
// rdar://19722429

test/type/types.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ var h10 : Int?.Type?.Type
4444

4545
var i = Int?(42)
4646

47-
var bad_io : (Int) -> (inout Int, Int) // expected-error {{'inout' is only valid in parameter lists}}
47+
var bad_io : (Int) -> (inout Int, Int) // expected-error {{'inout' may only be used on parameters}}
4848

49-
func bad_io2(_ a: (inout Int, Int)) {} // expected-error {{'inout' is only valid in parameter lists}}
49+
func bad_io2(_ a: (inout Int, Int)) {} // expected-error {{'inout' may only be used on parameters}}
5050

5151
// <rdar://problem/15588967> Array type sugar default construction syntax doesn't work
5252
func test_array_construct<T>(_: T) {
@@ -148,12 +148,12 @@ let dictWithTuple = [String: (age:Int, count:Int)]()
148148
let bb2 = [Int!](repeating: nil, count: 2)
149149

150150
// <rdar://problem/21560309> inout allowed on function return type
151-
func r21560309<U>(_ body: (_: inout Int) -> inout U) {} // expected-error {{'inout' is only valid in parameter lists}}
151+
func r21560309<U>(_ body: (_: inout Int) -> inout U) {} // expected-error {{'inout' may only be used on parameters}}
152152
r21560309 { x in x }
153153

154154
// <rdar://problem/21949448> Accepts-invalid: 'inout' shouldn't be allowed on stored properties
155155
class r21949448 {
156-
var myArray: inout [Int] = [] // expected-error {{'inout' is only valid in parameter lists}}
156+
var myArray: inout [Int] = [] // expected-error {{'inout' may only be used on parameters}}
157157
}
158158

159159
// SE-0066 - Standardize function type argument syntax to require parentheses

0 commit comments

Comments
 (0)