SE-0492: Add support for closures (with no captures) into @section expressions

This commit is contained in:
Kuba Mracek
2025-11-16 09:21:12 -08:00
parent 8748213d52
commit 8da5f3141e
4 changed files with 72 additions and 9 deletions

View File

@@ -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,

View File

@@ -46,6 +46,7 @@ enum IllegalConstErrorDiagnosis {
TypeExpression,
KeyPath,
Closure,
ClosureWithCaptures,
OpaqueDeclRef,
OpaqueFuncDeclRef,
NonConventionCFunc,
@@ -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;
@@ -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()) {
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)) {

View File

@@ -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
@@ -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"

View File

@@ -53,7 +53,7 @@ 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}}
@@ -61,11 +61,38 @@ func bar(x: Int) -> String { return "test" }
@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 }