Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,8 @@ ERROR(const_unsupported_type_expr,none,
"type expressions not supported in a constant expression", ())
ERROR(const_unsupported_closure,none,
"closures not supported in a constant expression", ())
ERROR(const_unsupported_closure_with_captures,none,
"closures with captures not supported in a constant expression", ())
ERROR(const_unsupported_keypath,none,
"keypaths not supported in a constant expression", ())
ERROR(const_opaque_decl_ref,none,
Expand Down
20 changes: 17 additions & 3 deletions lib/Sema/LegalConstExprVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ enum IllegalConstErrorDiagnosis {
TypeExpression,
KeyPath,
Closure,
ClosureWithCaptures,
OpaqueDeclRef,
OpaqueFuncDeclRef,
NonConventionCFunc,
Expand Down Expand Up @@ -82,6 +83,9 @@ static void diagnoseError(const Expr *errorExpr,
case Closure:
diags.diagnose(errorLoc, diag::const_unsupported_closure);
break;
case ClosureWithCaptures:
diags.diagnose(errorLoc, diag::const_unsupported_closure_with_captures);
break;
case OpaqueDeclRef:
diags.diagnose(errorLoc, diag::const_opaque_decl_ref);
break;
Expand Down Expand Up @@ -222,9 +226,19 @@ checkSupportedWithSectionAttribute(const Expr *expr,
if (isa<KeyPathExpr>(expr))
return std::make_pair(expr, KeyPath);

// Closure expressions are not supported in constant expressions
if (isa<AbstractClosureExpr>(expr))
return std::make_pair(expr, Closure);
// Closures are allowed if they have no captures
if (auto closureExpr = dyn_cast<ClosureExpr>(expr)) {
TypeChecker::computeCaptures(const_cast<ClosureExpr *>(closureExpr));
if (!closureExpr->getCaptureInfo().isTrivial()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that came out nicer than expected.

return std::make_pair(expr, ClosureWithCaptures);
}
continue;
}

// No auto-closures
if (isa<AbstractClosureExpr>(expr)) {
return std::make_pair(expr, Default);
}

// Function conversions are allowed if the conversion is to '@convention(c)'
if (auto functionConvExpr = dyn_cast<FunctionConversionExpr>(expr)) {
Expand Down
20 changes: 20 additions & 0 deletions test/ConstValues/SectionIR.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ func bar(x: Int) -> String { return "test" }
@section("mysection") let funcRef1 = foo // ok
@section("mysection") let funcRef2 = bar // ok

// closures
@section("mysection") let closure1 = { }
@section("mysection") let closure2 = { return 42 }
@section("mysection") let closure3 = { (x: Int) in return x + 1 }
@section("mysection") let closure4: () -> Void = { }
@section("mysection") let closure5: (Int) -> Int = { x in x * 2 }
@section("mysection") let closure6: @convention(c) (Int) -> Int = { x in x * 2 }
struct W {
@section("mysection") static let closure7: @convention(c) (Int) -> Int = { x in x * 2 }
}

// metatypes - TODO
//@section("mysection") let metatype1 = Int.self

Expand Down Expand Up @@ -56,6 +67,15 @@ func bar(x: Int) -> String { return "test" }
// CHECK: @"$s9SectionIR12boolLiteral2Sbvp" = {{.*}}constant %TSb zeroinitializer, section "mysection"
// CHECK: @"$s9SectionIR8funcRef1Siycvp" = {{.*}}constant %swift.function { ptr @"$s9SectionIR3fooSiyF{{.*}}", ptr null }, section "mysection"
// CHECK: @"$s9SectionIR8funcRef2ySSSicvp" = {{.*}}constant %swift.function { ptr @"$s9SectionIR3bar1xSSSi_tF{{.*}}", ptr null }, section "mysection"

// CHECK: @"$s9SectionIR8closure1yycvp" = {{.*}}constant %swift.function { {{.*}} }, section "mysection"
// CHECK: @"$s9SectionIR8closure2Siycvp" = {{.*}}constant %swift.function { {{.*}} }, section "mysection"
// CHECK: @"$s9SectionIR8closure3yS2icvp" = {{.*}}constant %swift.function { {{.*}} }, section "mysection"
// CHECK: @"$s9SectionIR8closure4yycvp" = {{.*}}constant %swift.function { {{.*}} }, section "mysection"
// CHECK: @"$s9SectionIR8closure5yS2icvp" = {{.*}}constant %swift.function { {{.*}} }, section "mysection"
// CHECK: @"$s9SectionIR8closure6yS2iXCvp" = {{.*}}constant ptr @"$s9SectionIR8closure6yS2iXCvpfiS2icfU_To", section "mysection"
// CHECK: @"$s9SectionIR1WV8closure7yS2iXCvpZ" = {{.*}}constant ptr @"$s9SectionIR1WV8closure7yS2iXCvpZfiS2icfU_To", section "mysection"

// CHECK: @"$s9SectionIR6tuple1Si_S2iSdSbtvp" = {{.*}}constant <{ %TSi, %TSi, %TSi, {{.*}} }> <{ %TSi <{ {{i64|i32}} 1 }>, %TSi <{ {{i64|i32}} 2 }>, %TSi <{ {{i64|i32}} 3 }>, {{.*}} }>, section "mysection"
// CHECK: @"$s9SectionIR6tuple2Si_SfSbtvp" = {{.*}}constant <{ %TSi, %TSf, %TSb }> <{ %TSi <{ {{i64|i32}} 42 }>, %TSf <{ float 0x40091EB860000000 }>, %TSb zeroinitializer }>, section "mysection"
// CHECK: @"$s9SectionIR6tuple3Siyc_SSSictvp" = {{.*}}constant <{ %swift.function, %swift.function }> <{ %swift.function { ptr @"$s9SectionIR3fooSiyF{{.*}}", ptr null }, %swift.function { ptr @"$s9SectionIR3bar1xSSSi_tF{{.*}}", ptr null } }>, section "mysection"
Expand Down
39 changes: 33 additions & 6 deletions test/ConstValues/SectionSyntactic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,46 @@ func bar(x: Int) -> String { return "test" }
@section("mysection") let invalidFuncRef1 = foo()
// expected-error@-1{{not supported in a constant expression}}
@section("mysection") let invalidFuncRef2 = Bool.self.random
// expected-error@-1{{closures not supported in a constant expression}}
// expected-error@-1{{not supported in a constant expression}}
@section("mysection") let invalidFuncRef3 = (Bool.self as Bool.Type).random
// expected-error@-1{{not supported in a constant expression}}

// generic function references (should be rejected)
@section("mysection") let invalidGenericFunc = [Int].randomElement
// expected-error@-1{{not supported in a constant expression}}

// closures (should be rejected)
@section("mysection") let invalidClosure1 = { }
// expected-error@-1{{closures not supported in a constant expression}}
@section("mysection") let invalidClosure2 = { return 42 }
// expected-error@-1{{closures not supported in a constant expression}}
// closures
@section("mysection") let closure1 = { } // ok
@section("mysection") let closure2 = { return 42 } // ok
@section("mysection") let closure3 = { (x: Int) in return x + 1 } // ok
@section("mysection") let closure4: () -> Void = { } // ok
@section("mysection") let closure5: (Int) -> Int = { x in x * 2 } // ok
@section("mysection") let closure6: @convention(c) (Int) -> Int = { x in x * 2 } // ok
struct W {
@section("mysection") static let closure7: @convention(c) (Int) -> Int = { x in x * 2 } // ok
}

let capturedVar = 10
class TestClass {}
var capturedMutableVar = TestClass()

// closures with captures (should be rejected)
@section("mysection") let invalidClosure1 = { capturedVar }
// expected-error@-1{{closures with captures not supported in a constant expression}}
@section("mysection") let invalidClosure2 = { return capturedVar + 1 }
// expected-error@-1{{closures with captures not supported in a constant expression}}
@section("mysection") let invalidClosure3 = { [capturedVar] in return capturedVar }
// expected-error@-1{{not supported in a constant expression}}
@section("mysection") let invalidClosure4 = { [weak capturedMutableVar] in return capturedMutableVar }
// expected-error@-1{{not supported in a constant expression}}
@section("mysection") let invalidClosure5 = { [unowned capturedMutableVar] in return capturedMutableVar }
// expected-error@-1{{not supported in a constant expression}}
@section("mysection") let invalidClosure6 = { [capturedVar, capturedMutableVar] in return 42 }
// expected-error@-1{{not supported in a constant expression}}
@section("mysection") let invalidClosure7 = { [renamed = capturedVar] in return renamed * 2 }
// expected-error@-1{{not supported in a constant expression}}
@section("mysection") let invalidClosure8 = { [computed = capturedVar + 5] in return computed }
// expected-error@-1{{not supported in a constant expression}}

struct S { }
enum E { case a }
Expand Down