Skip to content

Commit 9cd3787

Browse files
authored
Merge pull request #80749 from kubamracek/clangimport-const-values
[ClangImporter] Import constant values for 'const' globals
2 parents c832696 + 0319aa4 commit 9cd3787

12 files changed

+380
-23
lines changed

lib/ClangImporter/ImportDecl.cpp

+58-7
Original file line numberDiff line numberDiff line change
@@ -4583,19 +4583,70 @@ namespace {
45834583
if (dc->isTypeContext())
45844584
isStatic = true;
45854585

4586-
// For now we don't import static constexpr
4586+
// For now we don't import static constexpr. TODO: Lift this restriction.
45874587
if (isStatic && decl->isConstexpr())
45884588
return nullptr;
45894589

45904590
auto introducer = Impl.shouldImportGlobalAsLet(decl->getType())
45914591
? VarDecl::Introducer::Let
45924592
: VarDecl::Introducer::Var;
4593-
auto result = Impl.createDeclWithClangNode<VarDecl>(
4594-
decl, importer::convertClangAccess(decl->getAccess()),
4595-
/*IsStatic*/ isStatic, introducer,
4596-
Impl.importSourceLoc(decl->getLocation()), name, dc);
4597-
result->setIsObjC(false);
4598-
result->setIsDynamic(false);
4593+
4594+
ValueDecl *result = nullptr;
4595+
4596+
bool initIsEvaluatable = false;
4597+
if (auto init = decl->getInit()) {
4598+
// Don't import values for partial specializations. TODO: Should we stop
4599+
// importing partially specialized variables completely?
4600+
bool partial = isa<clang::VarTemplatePartialSpecializationDecl>(decl);
4601+
4602+
// Don't import values when type-dependent or value-dependent.
4603+
bool typeDependent = decl->getType()->isDependentType();
4604+
bool valueDependent = init->isValueDependent();
4605+
4606+
initIsEvaluatable = !partial && !typeDependent && !valueDependent;
4607+
}
4608+
4609+
// If the variable is const (we're importing it as a let), and has an
4610+
// initializer, then ask Clang for its constant value and synthesize a
4611+
// getter with that value.
4612+
if (introducer == VarDecl::Introducer::Let && initIsEvaluatable) {
4613+
auto val = decl->evaluateValue();
4614+
// For now, only import integer and float constants. If in the future
4615+
// SwiftDeclSynthesizer::createConstant becomes able to import more
4616+
// types, we can lift this restriction.
4617+
if (val && (val->isFloat() || val->isInt())) {
4618+
auto type = Impl.importTypeIgnoreIUO(
4619+
decl->getType(), ImportTypeKind::Value,
4620+
ImportDiagnosticAdder(Impl, decl, decl->getLocation()),
4621+
isInSystemModule(dc), Bridgeability::None, ImportTypeAttrs());
4622+
4623+
// FIXME: Handle CGFloat too.
4624+
if (!type->isCGFloat()) {
4625+
auto convertKind = ConstantConvertKind::None;
4626+
// Request conversions on enums, and swift_wrapper((enum/struct))
4627+
// types
4628+
if (decl->getType()->isEnumeralType())
4629+
convertKind = ConstantConvertKind::Construction;
4630+
else if (findSwiftNewtype(decl, Impl.getClangSema(),
4631+
Impl.CurrentVersion))
4632+
convertKind = ConstantConvertKind::Construction;
4633+
4634+
result = synthesizer.createConstant(
4635+
name, dc, type, *val, convertKind, isStatic, decl,
4636+
importer::convertClangAccess(decl->getAccess()));
4637+
}
4638+
}
4639+
}
4640+
4641+
// Otherwise, import as an external declaration
4642+
if (!result) {
4643+
result = Impl.createDeclWithClangNode<VarDecl>(
4644+
decl, importer::convertClangAccess(decl->getAccess()),
4645+
/*IsStatic*/ isStatic, introducer,
4646+
Impl.importSourceLoc(decl->getLocation()), name, dc);
4647+
result->setIsObjC(false);
4648+
result->setIsDynamic(false);
4649+
}
45994650

46004651
// If imported as member, the member should be final.
46014652
if (dc->getSelfClassDecl())

test/ClangImporter/const_values.swift

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// RUN: %empty-directory(%t/src)
2+
// RUN: split-file %s %t/src
3+
4+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/src/main.swift \
5+
// RUN: -import-bridging-header %t/src/test.h \
6+
// RUN: -module-name main -I %t -emit-sil -serialize-diagnostics -serialize-diagnostics-path %t/test.diag | %FileCheck %s
7+
8+
// RUN: c-index-test -read-diagnostics %t/test.diag 2>&1 | %FileCheck --check-prefix CHECK-DIAG %t/src/test.h
9+
10+
//--- test.h
11+
#include <stdbool.h>
12+
13+
#define MACRO_INT 42
14+
15+
const int const_int = 42;
16+
static int static_int = 42;
17+
static const int static_const_int = 42;
18+
19+
static const bool static_const_bool = true;
20+
static const char static_const_char = 42;
21+
static const char static_const_char_with_long_literal = 42ull;
22+
static const char static_const_char_with_overflow_literal = 777ull; // CHECK-DIAG: [[@LINE]]:{{.*}}: warning: implicit conversion from 'unsigned long long' to 'char' changes value from 777 to 9
23+
static const long static_const_long = 42;
24+
static const float static_const_float = 42.0;
25+
static const double static_const_double = 42.0;
26+
27+
static const char static_const_char_array[4] = {1, 2, 3, 4};
28+
static const char *static_const_pointer = 0;
29+
30+
static const char static_const_char_referencing_other_const = 1 + static_const_char;
31+
32+
typedef enum MyEnum: char {
33+
MyEnumCase0 = 0, MyEnumCase1, MyEnumCase2
34+
} MyEnum;
35+
static const MyEnum static_const_enum = MyEnumCase1;
36+
37+
struct MyStruct {
38+
int field;
39+
};
40+
__attribute__((swift_name("MyStruct.static_const_int_as_a_member")))
41+
static const int static_const_int_as_a_member = 42;
42+
43+
typedef int ImportAsStruct __attribute__((swift_wrapper(struct)));
44+
static const ImportAsStruct ImportAsStructFoo = 123;
45+
typedef int ImportAsEnum __attribute__((swift_wrapper(enum)));
46+
static const ImportAsEnum ImportAsEnumFoo = 123;
47+
48+
//--- main.swift
49+
func foo() {
50+
print(MACRO_INT)
51+
52+
print(const_int)
53+
print(static_int)
54+
print(static_const_int)
55+
56+
print(static_const_bool)
57+
print(static_const_char)
58+
print(static_const_char_with_long_literal)
59+
print(static_const_char_with_overflow_literal)
60+
print(static_const_long)
61+
print(static_const_float)
62+
print(static_const_double)
63+
64+
print(static_const_char_array)
65+
print(static_const_pointer)
66+
67+
print(static_const_char_referencing_other_const)
68+
69+
print(static_const_enum)
70+
71+
print(MyStruct.static_const_int_as_a_member)
72+
73+
print(ImportAsStruct.foo)
74+
print(ImportAsEnum.foo)
75+
}
76+
77+
// Globals that don't get their value imported stay as public_external:
78+
// CHECK: sil_global public_external @static_int : $Int32
79+
// CHECK: sil_global public_external [let] @static_const_char_array : $(Int8, Int8, Int8, Int8)
80+
// CHECK: sil_global public_external @static_const_pointer : $Optional<UnsafePointer<Int8>>
81+
82+
// CHECK: sil shared [transparent] @$sSC9MACRO_INTs5Int32Vvg : $@convention(thin) () -> Int32 {
83+
// CHECK-NEXT: bb0:
84+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
85+
// CHECK-NEXT: %1 = struct $Int32 (%0)
86+
// CHECK-NEXT: return %1
87+
// CHECK-NEXT: }
88+
89+
// CHECK: sil shared [transparent] @$sSo9const_ints5Int32Vvg : $@convention(thin) () -> Int32 {
90+
// CHECK-NEXT: bb0:
91+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
92+
// CHECK-NEXT: %1 = struct $Int32 (%0)
93+
// CHECK-NEXT: return %1
94+
// CHECK-NEXT: }
95+
96+
// CHECK: sil shared [transparent] @$sSo16static_const_ints5Int32Vvg : $@convention(thin) () -> Int32 {
97+
// CHECK-NEXT: bb0:
98+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
99+
// CHECK-NEXT: %1 = struct $Int32 (%0)
100+
// CHECK-NEXT: return %1
101+
// CHECK-NEXT: }
102+
103+
// CHECK: sil shared [transparent] @$sSo17static_const_boolSbvg : $@convention(thin) () -> Bool {
104+
// CHECK-NEXT: bb0:
105+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
106+
// CHECK-NEXT: %1 = struct $Bool (%0)
107+
// CHECK-NEXT: return %1
108+
// CHECK-NEXT: }
109+
110+
// CHECK: sil shared [transparent] @$sSo17static_const_chars4Int8Vvg : $@convention(thin) () -> Int8 {
111+
// CHECK-NEXT: bb0:
112+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 42
113+
// CHECK-NEXT: %1 = struct $Int8 (%0)
114+
// CHECK-NEXT: return %1
115+
// CHECK-NEXT: }
116+
117+
// CHECK: sil shared [transparent] @$sSo35static_const_char_with_long_literals4Int8Vvg : $@convention(thin) () -> Int8 {
118+
// CHECK-NEXT: bb0:
119+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 42
120+
// CHECK-NEXT: %1 = struct $Int8 (%0)
121+
// CHECK-NEXT: return %1
122+
// CHECK-NEXT: }
123+
124+
// CHECK: sil shared [transparent] @$sSo39static_const_char_with_overflow_literals4Int8Vvg : $@convention(thin) () -> Int8 {
125+
// CHECK-NEXT: bb0:
126+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 9
127+
// CHECK-NEXT: %1 = struct $Int8 (%0)
128+
// CHECK-NEXT: return %1
129+
// CHECK-NEXT: }
130+
131+
// CHECK: sil shared [transparent] @$sSo17static_const_long{{.*}} : $@convention(thin) () -> {{.*}} {
132+
// CHECK-NEXT: bb0:
133+
// CHECK-NEXT: %0 = integer_literal {{.*}}, 42
134+
// CHECK-NEXT: %1 = struct {{.*}} (%0)
135+
// CHECK-NEXT: return %1
136+
// CHECK-NEXT: }
137+
138+
// CHECK: sil shared [transparent] @$sSo18static_const_floatSfvg : $@convention(thin) () -> Float {
139+
// CHECK-NEXT: bb0:
140+
// CHECK-NEXT: %0 = float_literal $Builtin.FPIEEE32, 0x42280000 // 42
141+
// CHECK-NEXT: %1 = struct $Float (%0)
142+
// CHECK-NEXT: return %1
143+
// CHECK-NEXT: }
144+
145+
// CHECK: sil shared [transparent] @$sSo19static_const_doubleSdvg : $@convention(thin) () -> Double {
146+
// CHECK-NEXT: bb0:
147+
// CHECK-NEXT: %0 = float_literal $Builtin.FPIEEE64, 0x4045000000000000 // 42
148+
// CHECK-NEXT: %1 = struct $Double (%0)
149+
// CHECK-NEXT: return %1
150+
// CHECK-NEXT: }
151+
152+
// CHECK: sil shared [transparent] @$sSo036static_const_char_referencing_other_B0s4Int8Vvg : $@convention(thin) () -> Int8 {
153+
// CHECK-NEXT: bb0:
154+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 43
155+
// CHECK-NEXT: %1 = struct $Int8 (%0)
156+
// CHECK-NEXT: return %1
157+
// CHECK-NEXT: }
158+
159+
// CHECK: sil shared [transparent] @$sSo17static_const_enumSo6MyEnumVvg : $@convention(thin) () -> MyEnum {
160+
// CHECK-NEXT: bb0:
161+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 1
162+
// CHECK-NEXT: %1 = struct $Int8 (%0)
163+
// CHECK-NEXT: %2 = struct $MyEnum (%1)
164+
// CHECK-NEXT: return %2
165+
// CHECK-NEXT: }
166+
167+
// CHECK: sil shared [transparent] @$sSo8MyStructV28static_const_int_as_a_members5Int32VvgZ : $@convention(method) (@thin MyStruct.Type) -> Int32 {
168+
// CHECK-NEXT: // %0 "self"
169+
// CHECK-NEXT: bb0(%0 : $@thin MyStruct.Type):
170+
// CHECK-NEXT: debug_value %0, let, name "self", argno 1
171+
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 42
172+
// CHECK-NEXT: %3 = struct $Int32 (%2)
173+
// CHECK-NEXT: return %3
174+
// CHECK-NEXT: }
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// RUN: %empty-directory(%t/src)
2+
// RUN: split-file %s %t/src
3+
4+
// RUN: %target-swiftxx-frontend %t/src/main.swift \
5+
// RUN: -import-bridging-header %t/src/test.h \
6+
// RUN: -module-name main -I %t -emit-sil | %FileCheck %s
7+
8+
//--- test.h
9+
10+
const int const_int = 42;
11+
namespace Namespace {
12+
const int namespaced_const_int = 42;
13+
}
14+
15+
static inline int foo() { return 42; }
16+
const int not_really_constant = foo();
17+
18+
constexpr int constexpr_func() { return 42; }
19+
constexpr int constexpr_int = constexpr_func();
20+
static constexpr int static_constexpr_int = constexpr_func();
21+
22+
class MyClass {
23+
public:
24+
const int class_const_int = 42; // member field, don't expect to import as a global, no CHECKs for this
25+
static const int class_static_const_int = 42;
26+
};
27+
28+
namespace OtherNamespace {
29+
constexpr int namespaced_constexpr_int = constexpr_func();
30+
static constexpr int namespaced_static_constexpr_int = constexpr_func();
31+
}
32+
class OtherClass {
33+
static constexpr int class_static_constexpr_int = 42;
34+
};
35+
36+
template <int N, int M>
37+
inline const int template_gcd = template_gcd<M, N % M>;
38+
39+
template <int N>
40+
inline const int template_gcd<N, 0> = N;
41+
42+
43+
//--- main.swift
44+
func foo() {
45+
print(const_int)
46+
print(Namespace.namespaced_const_int)
47+
print(not_really_constant)
48+
print(constexpr_int)
49+
print(static_constexpr_int)
50+
51+
// TODO: Non-top-level constexpr variables currently are not imported at all (would be imported as static members)
52+
//print(OtherNamespace.namespaced_constexpr_int)
53+
//print(OtherNamespace.namespaced_static_constexpr_int)
54+
//print(MyClass.class_static_constexpr_int)
55+
56+
print(MyClass().class_const_int)
57+
print(MyClass.class_static_const_int)
58+
59+
// TODO: This seems to be incorrectly imported, this test here is just to check that the compiler doesn't crash.
60+
print(template_gcd)
61+
}
62+
63+
// Only imported as external declarations:
64+
// CHECK: sil_global public_external [let] @not_really_constant : $Int32
65+
66+
// CHECK: sil shared [transparent] @$sSo9const_ints5Int32Vvg : $@convention(thin) () -> Int32 {
67+
// CHECK-NEXT: bb0:
68+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
69+
// CHECK-NEXT: %1 = struct $Int32 (%0)
70+
// CHECK-NEXT: return %1
71+
// CHECK-NEXT: }
72+
73+
// CHECK: sil shared [transparent] @$sSo9NamespaceO20namespaced_const_ints5Int32VvgZ : $@convention(method) (@thin Namespace.Type) -> Int32 {
74+
// CHECK-NEXT: // %0 "self"
75+
// CHECK-NEXT: bb0(%0 : $@thin Namespace.Type):
76+
// CHECK-NEXT: debug_value %0, let, name "self", argno 1
77+
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 42
78+
// CHECK-NEXT: %3 = struct $Int32 (%2)
79+
// CHECK-NEXT: return %3
80+
// CHECK-NEXT: }
81+
82+
// CHECK: sil shared [transparent] @$sSo13constexpr_ints5Int32Vvg : $@convention(thin) () -> Int32 {
83+
// CHECK-NEXT: bb0:
84+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
85+
// CHECK-NEXT: %1 = struct $Int32 (%0)
86+
// CHECK-NEXT: return %1
87+
// CHECK-NEXT: }
88+
89+
// CHECK: sil shared [transparent] @$sSo20static_constexpr_ints5Int32Vvg : $@convention(thin) () -> Int32 {
90+
// CHECK-NEXT: bb0:
91+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
92+
// CHECK-NEXT: %1 = struct $Int32 (%0)
93+
// CHECK-NEXT: return %1
94+
// CHECK-NEXT: }
95+
96+
// CHECK: sil shared [transparent] @$sSo7MyClassV22class_static_const_ints5Int32VvgZ : $@convention(method) (@thin MyClass.Type) -> Int32 {
97+
// CHECK-NEXT: // %0 "self"
98+
// CHECK-NEXT: bb0(%0 : $@thin MyClass.Type):
99+
// CHECK-NEXT: debug_value %0, let, name "self", argno 1
100+
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 42
101+
// CHECK-NEXT: %3 = struct $Int32 (%2)
102+
// CHECK-NEXT: return %3
103+
// CHECK-NEXT: }
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %empty-directory(%t/src)
2+
// RUN: split-file %s %t/src
3+
4+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/src/main.swift \
5+
// RUN: -import-bridging-header %t/src/test.h \
6+
// RUN: -module-name main -I %t -emit-sil -serialize-diagnostics -serialize-diagnostics-path %t/test.diag
7+
8+
// RUN: c-index-test -read-diagnostics %t/test.diag 2>&1 | %FileCheck --check-prefix CHECK-DIAG %t/src/test.h
9+
10+
//--- test.h
11+
static char other = 42;
12+
static const char static_const_char_that_is_not_const = 1 + other;
13+
// CHECK-DIAG: [[@LINE-1]]:{{.*}}: error: initializer element is not a compile-time constant
14+
// CHECK-DIAG: error: failed to import bridging header
15+
16+
//--- main.swift
17+
func foo() {
18+
print(static_const_char_that_is_not_const)
19+
}

test/Interop/Cxx/class/access/access-inversion-module-interface.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// CHECK: public init()
1010
// CHECK: private init()
1111
// CHECK: public func privateRecMethod()
12-
// CHECK: public static let PRIVATE_REC_CONST: Bool
12+
// CHECK: public static var PRIVATE_REC_CONST: Bool { get }
1313
// CHECK: }
1414

1515
// CHECK: private struct PrivateEnum : Hashable, Equatable, RawRepresentable {
@@ -26,7 +26,7 @@
2626
// CHECK: case privateEnumClassMember
2727
// CHECK: }
2828

29-
// CHECK: private static let PRIVATE_CONST: Bool
29+
// CHECK: private static var PRIVATE_CONST: Bool { get }
3030

3131
// CHECK: private static var privateAliasVal: Leaky.PrivateAlias
3232
// CHECK: private static var privateRecVal: Leaky.PrivateRec

0 commit comments

Comments
 (0)