From 5f81c1b7933428362b2fde3d099a9beba649c5c5 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Sat, 25 Oct 2025 10:48:44 -0700 Subject: [PATCH 1/3] SE-0492: Handle top-level `@section`-annotated globals Currently, normal globals are represented as a PatternBindingDecl and a VarDecl in the AST, directly under the SourceFile: ``` // var variable_name = 42, compiled with -parse-as-library (source_file ... (pattern_binding_decl ... (pattern_entry ... (pattern_named ... "variable_name") ... (var_decl "variable_name" ... ``` Top-level globals are represented more like local variables, under a TopLevelCodeDecl. Note that the VarDecl is still at the file scope. In SILGen, this case has some special handling to use the a storage of a global variable, and to avoid cleanups (see `emitInitializationForVarDecl`). Effectively, this means the globals are initialized inside the `main` function. ``` // var variable_name = 42, compiled without -parse-as-library (source_file ... (top_level_code_decl ... (brace_stmt ... (pattern_binding_decl ... (pattern_named ... "variable_name") ... (var_decl "variable_name" ... top_level_global ``` SE-0492 needs top-level globals that have a `@section` annotation to behave like a normal global -- initialization must happen statically, and not in `main`. This PR changes the parsing of those globals to match normal globals, without the TopLevelCodeDecl wrapper. SILGen and IRGen then handles them correctly. --- lib/Parse/ParseDecl.cpp | 9 ++++-- test/ConstValues/SectionTopLevel.swift | 44 +++++++++++--------------- test/ConstValues/TopLevel.swift | 2 +- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index dd926e32b4337..9bb93bf28c108 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -8749,13 +8749,16 @@ Parser::parseDeclVar(ParseDeclOptions Flags, SourceLoc VarLoc = newBindingContext.getIntroducer() ? consumeToken() : Tok.getLoc(); + bool IsConst = Attributes.hasAttribute() || + Attributes.hasAttribute(); + // If this is a var in the top-level of script/repl source file, wrap the // PatternBindingDecl in a TopLevelCodeDecl, since it represents executable // code. The VarDecl and any accessor decls (for computed properties) go in - // CurDeclContext. - // + // CurDeclContext. @const/@section globals are not top-level, per SE-0492. TopLevelCodeDecl *topLevelDecl = nullptr; - if (allowTopLevelCode() && CurDeclContext->isModuleScopeContext()) { + if (allowTopLevelCode() && CurDeclContext->isModuleScopeContext() && + !IsConst) { // The body of topLevelDecl will get set later. topLevelDecl = new (Context) TopLevelCodeDecl(CurDeclContext); } diff --git a/test/ConstValues/SectionTopLevel.swift b/test/ConstValues/SectionTopLevel.swift index 014a1a9079e29..cd693916cf045 100644 --- a/test/ConstValues/SectionTopLevel.swift +++ b/test/ConstValues/SectionTopLevel.swift @@ -1,9 +1,6 @@ // Constant globals using @section // RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s -// TODO -// XFAIL: * - // integer literals @section("mysection") let intLiteral1 = 42 // ok @section("mysection") let intLiteral2: Int8 = 127 // ok @@ -31,30 +28,27 @@ func bar(x: Int) -> String { return "test" } @section("mysection") let funcRef1 = foo // ok @section("mysection") let funcRef2 = bar // ok -// metatypes - TODO -//@section("mysection") let metatype1 = Int.self - // tuples @section("mysection") let tuple1 = (1, 2, 3, 2.718, true) // ok @section("mysection") let tuple2: (Int, Float, Bool) = (42, 3.14, false) // ok @section("mysection") let tuple3 = (foo, bar) // ok (function references in tuple) -// CHECK: @"$s9SectionIR11intLiteral1Sivp" = {{.*}}constant %TSi <{ i64 42 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral2s4Int8Vvp" = {{.*}}constant %Ts4Int8V <{ i8 127 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral3s5Int16Vvp" = {{.*}}constant %Ts5Int16V <{ i16 32767 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral4s5Int32Vvp" = {{.*}}constant %Ts5Int32V <{ i32 2147483647 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral5s5Int64Vvp" = {{.*}}constant %Ts5Int64V <{ i64 9223372036854775807 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral6Suvp" = {{.*}}constant %TSu <{ i64 42 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral7s5UInt8Vvp" = {{.*}}constant %Ts5UInt8V <{ i8 -1 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral8s6UInt16Vvp" = {{.*}}constant %Ts6UInt16V <{ i16 -1 }>, section "mysection" -// CHECK: @"$s9SectionIR11intLiteral9s6UInt32Vvp" = {{.*}}constant %Ts6UInt32V <{ i32 -1 }>, section "mysection" -// CHECK: @"$s9SectionIR12intLiteral10s6UInt64Vvp" = {{.*}}constant %Ts6UInt64V <{ i64 -1 }>, section "mysection" -// CHECK: @"$s9SectionIR13floatLiteral1Sfvp" = {{.*}}constant %TSf <{ float 0x40091EB860000000 }>, section "mysection" -// CHECK: @"$s9SectionIR13floatLiteral2Sdvp" = {{.*}}constant %TSd <{ double 2.718000e+00 }>, section "mysection" -// CHECK: @"$s9SectionIR12boolLiteral1Sbvp" = {{.*}}constant %TSb <{ i1 true }>, section "mysection" -// 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: @"$s9SectionIR6tuple1Si_S2iSdSbtvp" = {{.*}}constant <{ %TSi, %TSi, %TSi, %TSd, %TSb }> <{ %TSi <{ i64 1 }>, %TSi <{ i64 2 }>, %TSi <{ i64 3 }>, %TSd <{ double 2.718000e+00 }>, %TSb <{ i1 true }> }>, section "mysection" -// CHECK: @"$s9SectionIR6tuple2Si_SfSbtvp" = {{.*}}constant <{ %TSi, %TSf, %TSb }> <{ %TSi <{ i64 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" +// CHECK: @"$s15SectionTopLevel11intLiteral1Sivp" = {{.*}}constant %TSi <{ i64 42 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral2s4Int8Vvp" = {{.*}}constant %Ts4Int8V <{ i8 127 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral3s5Int16Vvp" = {{.*}}constant %Ts5Int16V <{ i16 32767 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral4s5Int32Vvp" = {{.*}}constant %Ts5Int32V <{ i32 2147483647 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral5s5Int64Vvp" = {{.*}}constant %Ts5Int64V <{ i64 9223372036854775807 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral6Suvp" = {{.*}}constant %TSu <{ i64 42 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral7s5UInt8Vvp" = {{.*}}constant %Ts5UInt8V <{ i8 -1 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral8s6UInt16Vvp" = {{.*}}constant %Ts6UInt16V <{ i16 -1 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel11intLiteral9s6UInt32Vvp" = {{.*}}constant %Ts6UInt32V <{ i32 -1 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel12intLiteral10s6UInt64Vvp" = {{.*}}constant %Ts6UInt64V <{ i64 -1 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel13floatLiteral1Sfvp" = {{.*}}constant %TSf <{ float 0x40091EB860000000 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel13floatLiteral2Sdvp" = {{.*}}constant %TSd <{ double 2.718000e+00 }>, section "mysection" +// CHECK: @"$s15SectionTopLevel12boolLiteral1Sbvp" = {{.*}}constant %TSb <{ i1 true }>, section "mysection" +// CHECK: @"$s15SectionTopLevel12boolLiteral2Sbvp" = {{.*}}constant %TSb zeroinitializer, section "mysection" +// CHECK: @"$s15SectionTopLevel8funcRef1Siycvp" = {{.*}}constant %swift.function { ptr @"$s15SectionTopLevel3fooSiyF", ptr null }, section "mysection" +// CHECK: @"$s15SectionTopLevel8funcRef2ySSSicvp" = {{.*}}constant %swift.function { ptr @"$s15SectionTopLevel3bar1xSSSi_tF", ptr null }, section "mysection" +// CHECK: @"$s15SectionTopLevel6tuple1Si_S2iSdSbtvp" = {{.*}}constant <{ %TSi, %TSi, %TSi, %TSd, %TSb }> <{ %TSi <{ i64 1 }>, %TSi <{ i64 2 }>, %TSi <{ i64 3 }>, %TSd <{ double 2.718000e+00 }>, %TSb <{ i1 true }> }>, section "mysection" +// CHECK: @"$s15SectionTopLevel6tuple2Si_SfSbtvp" = {{.*}}constant <{ %TSi, %TSf, %TSb }> <{ %TSi <{ i64 42 }>, %TSf <{ float 0x40091EB860000000 }>, %TSb zeroinitializer }>, section "mysection" +// CHECK: @"$s15SectionTopLevel6tuple3Siyc_SSSictvp" = {{.*}}constant <{ %swift.function, %swift.function }> <{ %swift.function { ptr @"$s15SectionTopLevel3fooSiyF", ptr null }, %swift.function { ptr @"$s15SectionTopLevel3bar1xSSSi_tF", ptr null } }>, section "mysection" diff --git a/test/ConstValues/TopLevel.swift b/test/ConstValues/TopLevel.swift index 86711c9e718e4..6e9b699f67af8 100644 --- a/test/ConstValues/TopLevel.swift +++ b/test/ConstValues/TopLevel.swift @@ -1,7 +1,7 @@ // Constant globals should "work" even in top-level code mode. // REQUIRES: swift_feature_CompileTimeValues // REQUIRES: swift_feature_CompileTimeValuesPreview -// REQUIRES: rdar146954355 + // RUN: %target-swift-frontend -emit-ir -primary-file %s -enable-experimental-feature CompileTimeValues -enable-experimental-feature CompileTimeValuesPreview // RUN: %target-swift-frontend -emit-ir -primary-file %s -parse-as-library -enable-experimental-feature CompileTimeValues -enable-experimental-feature CompileTimeValuesPreview From ee623f8d5995a7e345e69d9c3ddddf775cee2329 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Mon, 3 Nov 2025 13:46:24 -0800 Subject: [PATCH 2/3] Add @section/@const handling of globals to ASTGen too --- include/swift/AST/ASTBridging.h | 4 ++++ lib/AST/Bridging/DeclAttributeBridging.cpp | 5 +++++ lib/ASTGen/Sources/ASTGen/Decls.swift | 6 +++++- test/ConstValues/SectionTopLevel.swift | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index c29df7b5d3d65..004b1d768d9b9 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -777,6 +777,10 @@ SWIFT_NAME("BridgedDeclAttributes.add(self:_:)") void BridgedDeclAttributes_add(BridgedDeclAttributes *_Nonnull attrs, BridgedDeclAttribute add); +SWIFT_NAME("BridgedDeclAttributes.hasAttribute(self:_:)") +bool BridgedDeclAttributes_hasAttribute( + const BridgedDeclAttributes *_Nonnull attrs, swift::DeclAttrKind kind); + SWIFT_NAME("BridgedDeclAttribute.createSimple(_:kind:atLoc:nameLoc:)") BridgedDeclAttribute BridgedDeclAttribute_createSimple( BridgedASTContext cContext, swift::DeclAttrKind kind, diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index 4ed7146f16bd5..3af8c1e5a8327 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -98,6 +98,11 @@ void BridgedDeclAttributes_add(BridgedDeclAttributes *cAttrs, *cAttrs = attrs; } +bool BridgedDeclAttributes_hasAttribute( + const BridgedDeclAttributes *_Nonnull attrs, swift::DeclAttrKind kind) { + return attrs->unbridged().getAttribute(kind) != nullptr; +} + static AvailableAttr::Kind unbridge(BridgedAvailableAttrKind value) { switch (value) { case BridgedAvailableAttrKindDefault: diff --git a/lib/ASTGen/Sources/ASTGen/Decls.swift b/lib/ASTGen/Sources/ASTGen/Decls.swift index 1509ff7e040d6..598bd4a05e39d 100644 --- a/lib/ASTGen/Sources/ASTGen/Decls.swift +++ b/lib/ASTGen/Sources/ASTGen/Decls.swift @@ -610,8 +610,12 @@ extension ASTGenVisitor { // TODO: Diagnostics fatalError("invalid pattern binding introducer") } + + // @const/@section globals are not top-level, per SE-0492. + let isConst = attrs.attributes.hasAttribute(.Section) || attrs.attributes.hasAttribute(.ConstVal) + let topLevelDecl: BridgedTopLevelCodeDecl? - if self.declContext.isModuleScopeContext, self.declContext.parentSourceFile.isScriptMode { + if self.declContext.isModuleScopeContext, self.declContext.parentSourceFile.isScriptMode, !isConst { topLevelDecl = BridgedTopLevelCodeDecl.create(self.ctx, declContext: self.declContext) } else { topLevelDecl = nil diff --git a/test/ConstValues/SectionTopLevel.swift b/test/ConstValues/SectionTopLevel.swift index cd693916cf045..2d29a868736ed 100644 --- a/test/ConstValues/SectionTopLevel.swift +++ b/test/ConstValues/SectionTopLevel.swift @@ -1,5 +1,6 @@ // Constant globals using @section // RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-ir %s -enable-experimental-feature ParserASTGen | %FileCheck %s // integer literals @section("mysection") let intLiteral1 = 42 // ok From eaa3f8a5b0c5855576163882332fc64e1df04c4b Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Mon, 3 Nov 2025 21:11:05 -0800 Subject: [PATCH 3/3] Annotate SectionTopLevel.swift with REQUIRES: swift_feature_ParserASTGen --- test/ConstValues/SectionTopLevel.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ConstValues/SectionTopLevel.swift b/test/ConstValues/SectionTopLevel.swift index 2d29a868736ed..78a1090dbd11f 100644 --- a/test/ConstValues/SectionTopLevel.swift +++ b/test/ConstValues/SectionTopLevel.swift @@ -2,6 +2,8 @@ // RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s // RUN: %target-swift-frontend -emit-ir %s -enable-experimental-feature ParserASTGen | %FileCheck %s +// REQUIRES: swift_feature_ParserASTGen + // integer literals @section("mysection") let intLiteral1 = 42 // ok @section("mysection") let intLiteral2: Int8 = 127 // ok