From 8da5f3141e2e0bf2c9a8b754c0338b2f1af27c35 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Sun, 16 Nov 2025 09:21:12 -0800 Subject: [PATCH] SE-0492: Add support for closures (with no captures) into @section expressions --- include/swift/AST/DiagnosticsSema.def | 2 ++ lib/Sema/LegalConstExprVerifier.cpp | 20 +++++++++++-- test/ConstValues/SectionIR.swift | 20 +++++++++++++ test/ConstValues/SectionSyntactic.swift | 39 +++++++++++++++++++++---- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 784bd8f3e67a1..f63485114f19c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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, diff --git a/lib/Sema/LegalConstExprVerifier.cpp b/lib/Sema/LegalConstExprVerifier.cpp index c61e694a415a1..51c9a4ade5055 100644 --- a/lib/Sema/LegalConstExprVerifier.cpp +++ b/lib/Sema/LegalConstExprVerifier.cpp @@ -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(expr)) return std::make_pair(expr, KeyPath); - // Closure expressions are not supported in constant expressions - if (isa(expr)) - return std::make_pair(expr, Closure); + // Closures are allowed if they have no captures + if (auto closureExpr = dyn_cast(expr)) { + TypeChecker::computeCaptures(const_cast(closureExpr)); + if (!closureExpr->getCaptureInfo().isTrivial()) { + return std::make_pair(expr, ClosureWithCaptures); + } + continue; + } + + // No auto-closures + if (isa(expr)) { + return std::make_pair(expr, Default); + } // Function conversions are allowed if the conversion is to '@convention(c)' if (auto functionConvExpr = dyn_cast(expr)) { diff --git a/test/ConstValues/SectionIR.swift b/test/ConstValues/SectionIR.swift index fa928f370d342..b9c194c67cf24 100644 --- a/test/ConstValues/SectionIR.swift +++ b/test/ConstValues/SectionIR.swift @@ -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" diff --git a/test/ConstValues/SectionSyntactic.swift b/test/ConstValues/SectionSyntactic.swift index 044897a8ba399..cd5fe579b1f88 100644 --- a/test/ConstValues/SectionSyntactic.swift +++ b/test/ConstValues/SectionSyntactic.swift @@ -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 }