Skip to content
65 changes: 58 additions & 7 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4583,19 +4583,70 @@ namespace {
if (dc->isTypeContext())
isStatic = true;

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

auto introducer = Impl.shouldImportGlobalAsLet(decl->getType())
? VarDecl::Introducer::Let
: VarDecl::Introducer::Var;
auto result = Impl.createDeclWithClangNode<VarDecl>(
decl, importer::convertClangAccess(decl->getAccess()),
/*IsStatic*/ isStatic, introducer,
Impl.importSourceLoc(decl->getLocation()), name, dc);
result->setIsObjC(false);
result->setIsDynamic(false);

ValueDecl *result = nullptr;

bool initIsEvaluatable = false;
if (auto init = decl->getInit()) {
// Don't import values for partial specializations. TODO: Should we stop
// importing partially specialized variables completely?
bool partial = isa<clang::VarTemplatePartialSpecializationDecl>(decl);

// Don't import values when type-dependent or value-dependent.
bool typeDependent = decl->getType()->isDependentType();
bool valueDependent = init->isValueDependent();

initIsEvaluatable = !partial && !typeDependent && !valueDependent;
}

// If the variable is const (we're importing it as a let), and has an
// initializer, then ask Clang for its constant value and synthesize a
// getter with that value.
if (introducer == VarDecl::Introducer::Let && initIsEvaluatable) {
auto val = decl->evaluateValue();
// For now, only import integer and float constants. If in the future
// SwiftDeclSynthesizer::createConstant becomes able to import more
// types, we can lift this restriction.
if (val && (val->isFloat() || val->isInt())) {
auto type = Impl.importTypeIgnoreIUO(
decl->getType(), ImportTypeKind::Value,
ImportDiagnosticAdder(Impl, decl, decl->getLocation()),
isInSystemModule(dc), Bridgeability::None, ImportTypeAttrs());

// FIXME: Handle CGFloat too.
if (!type->isCGFloat()) {
auto convertKind = ConstantConvertKind::None;
// Request conversions on enums, and swift_wrapper((enum/struct))
// types
if (decl->getType()->isEnumeralType())
convertKind = ConstantConvertKind::Construction;
else if (findSwiftNewtype(decl, Impl.getClangSema(),
Impl.CurrentVersion))
convertKind = ConstantConvertKind::Construction;

result = synthesizer.createConstant(
name, dc, type, *val, convertKind, isStatic, decl,
importer::convertClangAccess(decl->getAccess()));
}
}
}

// Otherwise, import as an external declaration
if (!result) {
result = Impl.createDeclWithClangNode<VarDecl>(
decl, importer::convertClangAccess(decl->getAccess()),
/*IsStatic*/ isStatic, introducer,
Impl.importSourceLoc(decl->getLocation()), name, dc);
result->setIsObjC(false);
result->setIsDynamic(false);
}

// If imported as member, the member should be final.
if (dc->getSelfClassDecl())
Expand Down
174 changes: 174 additions & 0 deletions test/ClangImporter/const_values.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// RUN: %empty-directory(%t/src)
// RUN: split-file %s %t/src

// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/src/main.swift \
// RUN: -import-bridging-header %t/src/test.h \
// RUN: -module-name main -I %t -emit-sil -serialize-diagnostics -serialize-diagnostics-path %t/test.diag | %FileCheck %s

// RUN: c-index-test -read-diagnostics %t/test.diag 2>&1 | %FileCheck --check-prefix CHECK-DIAG %t/src/test.h

//--- test.h
#include <stdbool.h>

#define MACRO_INT 42

const int const_int = 42;
static int static_int = 42;
static const int static_const_int = 42;

static const bool static_const_bool = true;
static const char static_const_char = 42;
static const char static_const_char_with_long_literal = 42ull;
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
static const long static_const_long = 42;
static const float static_const_float = 42.0;
static const double static_const_double = 42.0;

static const char static_const_char_array[4] = {1, 2, 3, 4};
static const char *static_const_pointer = 0;

static const char static_const_char_referencing_other_const = 1 + static_const_char;

typedef enum MyEnum: char {
MyEnumCase0 = 0, MyEnumCase1, MyEnumCase2
} MyEnum;
static const MyEnum static_const_enum = MyEnumCase1;

struct MyStruct {
int field;
};
__attribute__((swift_name("MyStruct.static_const_int_as_a_member")))
static const int static_const_int_as_a_member = 42;

typedef int ImportAsStruct __attribute__((swift_wrapper(struct)));
static const ImportAsStruct ImportAsStructFoo = 123;
typedef int ImportAsEnum __attribute__((swift_wrapper(enum)));
static const ImportAsEnum ImportAsEnumFoo = 123;

//--- main.swift
func foo() {
print(MACRO_INT)

print(const_int)
print(static_int)
print(static_const_int)

print(static_const_bool)
print(static_const_char)
print(static_const_char_with_long_literal)
print(static_const_char_with_overflow_literal)
print(static_const_long)
print(static_const_float)
print(static_const_double)

print(static_const_char_array)
print(static_const_pointer)

print(static_const_char_referencing_other_const)

print(static_const_enum)

print(MyStruct.static_const_int_as_a_member)

print(ImportAsStruct.foo)
print(ImportAsEnum.foo)
}

// Globals that don't get their value imported stay as public_external:
// CHECK: sil_global public_external @static_int : $Int32
// CHECK: sil_global public_external [let] @static_const_char_array : $(Int8, Int8, Int8, Int8)
// CHECK: sil_global public_external @static_const_pointer : $Optional<UnsafePointer<Int8>>

// CHECK: sil shared [transparent] @$sSC9MACRO_INTs5Int32Vvg : $@convention(thin) () -> Int32 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %1 = struct $Int32 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo9const_ints5Int32Vvg : $@convention(thin) () -> Int32 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %1 = struct $Int32 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo16static_const_ints5Int32Vvg : $@convention(thin) () -> Int32 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %1 = struct $Int32 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo17static_const_boolSbvg : $@convention(thin) () -> Bool {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: %1 = struct $Bool (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo17static_const_chars4Int8Vvg : $@convention(thin) () -> Int8 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 42
// CHECK-NEXT: %1 = struct $Int8 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo35static_const_char_with_long_literals4Int8Vvg : $@convention(thin) () -> Int8 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 42
// CHECK-NEXT: %1 = struct $Int8 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo39static_const_char_with_overflow_literals4Int8Vvg : $@convention(thin) () -> Int8 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 9
// CHECK-NEXT: %1 = struct $Int8 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo17static_const_long{{.*}} : $@convention(thin) () -> {{.*}} {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal {{.*}}, 42
// CHECK-NEXT: %1 = struct {{.*}} (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo18static_const_floatSfvg : $@convention(thin) () -> Float {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = float_literal $Builtin.FPIEEE32, 0x42280000 // 42
// CHECK-NEXT: %1 = struct $Float (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo19static_const_doubleSdvg : $@convention(thin) () -> Double {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = float_literal $Builtin.FPIEEE64, 0x4045000000000000 // 42
// CHECK-NEXT: %1 = struct $Double (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo036static_const_char_referencing_other_B0s4Int8Vvg : $@convention(thin) () -> Int8 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 43
// CHECK-NEXT: %1 = struct $Int8 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo17static_const_enumSo6MyEnumVvg : $@convention(thin) () -> MyEnum {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 1
// CHECK-NEXT: %1 = struct $Int8 (%0)
// CHECK-NEXT: %2 = struct $MyEnum (%1)
// CHECK-NEXT: return %2
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo8MyStructV28static_const_int_as_a_members5Int32VvgZ : $@convention(method) (@thin MyStruct.Type) -> Int32 {
// CHECK-NEXT: // %0 "self"
// CHECK-NEXT: bb0(%0 : $@thin MyStruct.Type):
// CHECK-NEXT: debug_value %0, let, name "self", argno 1
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %3 = struct $Int32 (%2)
// CHECK-NEXT: return %3
// CHECK-NEXT: }
103 changes: 103 additions & 0 deletions test/ClangImporter/const_values_cxx.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// RUN: %empty-directory(%t/src)
// RUN: split-file %s %t/src

// RUN: %target-swiftxx-frontend %t/src/main.swift \
// RUN: -import-bridging-header %t/src/test.h \
// RUN: -module-name main -I %t -emit-sil | %FileCheck %s

//--- test.h

const int const_int = 42;
namespace Namespace {
const int namespaced_const_int = 42;
}

static inline int foo() { return 42; }
const int not_really_constant = foo();

constexpr int constexpr_func() { return 42; }
constexpr int constexpr_int = constexpr_func();
static constexpr int static_constexpr_int = constexpr_func();

class MyClass {
public:
const int class_const_int = 42; // member field, don't expect to import as a global, no CHECKs for this
static const int class_static_const_int = 42;
};

namespace OtherNamespace {
constexpr int namespaced_constexpr_int = constexpr_func();
static constexpr int namespaced_static_constexpr_int = constexpr_func();
}
class OtherClass {
static constexpr int class_static_constexpr_int = 42;
};

template <int N, int M>
inline const int template_gcd = template_gcd<M, N % M>;

template <int N>
inline const int template_gcd<N, 0> = N;


//--- main.swift
func foo() {
print(const_int)
print(Namespace.namespaced_const_int)
print(not_really_constant)
print(constexpr_int)
print(static_constexpr_int)

// TODO: Non-top-level constexpr variables currently are not imported at all (would be imported as static members)
//print(OtherNamespace.namespaced_constexpr_int)
//print(OtherNamespace.namespaced_static_constexpr_int)
//print(MyClass.class_static_constexpr_int)

print(MyClass().class_const_int)
print(MyClass.class_static_const_int)

// TODO: This seems to be incorrectly imported, this test here is just to check that the compiler doesn't crash.
print(template_gcd)
}

// Only imported as external declarations:
// CHECK: sil_global public_external [let] @not_really_constant : $Int32

// CHECK: sil shared [transparent] @$sSo9const_ints5Int32Vvg : $@convention(thin) () -> Int32 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %1 = struct $Int32 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo9NamespaceO20namespaced_const_ints5Int32VvgZ : $@convention(method) (@thin Namespace.Type) -> Int32 {
// CHECK-NEXT: // %0 "self"
// CHECK-NEXT: bb0(%0 : $@thin Namespace.Type):
// CHECK-NEXT: debug_value %0, let, name "self", argno 1
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %3 = struct $Int32 (%2)
// CHECK-NEXT: return %3
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo13constexpr_ints5Int32Vvg : $@convention(thin) () -> Int32 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %1 = struct $Int32 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo20static_constexpr_ints5Int32Vvg : $@convention(thin) () -> Int32 {
// CHECK-NEXT: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %1 = struct $Int32 (%0)
// CHECK-NEXT: return %1
// CHECK-NEXT: }

// CHECK: sil shared [transparent] @$sSo7MyClassV22class_static_const_ints5Int32VvgZ : $@convention(method) (@thin MyClass.Type) -> Int32 {
// CHECK-NEXT: // %0 "self"
// CHECK-NEXT: bb0(%0 : $@thin MyClass.Type):
// CHECK-NEXT: debug_value %0, let, name "self", argno 1
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 42
// CHECK-NEXT: %3 = struct $Int32 (%2)
// CHECK-NEXT: return %3
// CHECK-NEXT: }
19 changes: 19 additions & 0 deletions test/ClangImporter/const_values_fail.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// RUN: %empty-directory(%t/src)
// RUN: split-file %s %t/src

// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/src/main.swift \
// RUN: -import-bridging-header %t/src/test.h \
// RUN: -module-name main -I %t -emit-sil -serialize-diagnostics -serialize-diagnostics-path %t/test.diag

// RUN: c-index-test -read-diagnostics %t/test.diag 2>&1 | %FileCheck --check-prefix CHECK-DIAG %t/src/test.h

//--- test.h
static char other = 42;
static const char static_const_char_that_is_not_const = 1 + other;
// CHECK-DIAG: [[@LINE-1]]:{{.*}}: error: initializer element is not a compile-time constant
// CHECK-DIAG: error: failed to import bridging header

//--- main.swift
func foo() {
print(static_const_char_that_is_not_const)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// CHECK: public init()
// CHECK: private init()
// CHECK: public func privateRecMethod()
// CHECK: public static let PRIVATE_REC_CONST: Bool
// CHECK: public static var PRIVATE_REC_CONST: Bool { get }
// CHECK: }

// CHECK: private struct PrivateEnum : Hashable, Equatable, RawRepresentable {
Expand All @@ -26,7 +26,7 @@
// CHECK: case privateEnumClassMember
// CHECK: }

// CHECK: private static let PRIVATE_CONST: Bool
// CHECK: private static var PRIVATE_CONST: Bool { get }

// CHECK: private static var privateAliasVal: Leaky.PrivateAlias
// CHECK: private static var privateRecVal: Leaky.PrivateRec
Expand Down
Loading