Skip to content

Commit 8247525

Browse files
committed
[irgen] Force emission of objc class refs for non-foreign clang objc classes whose metadata we use.
Today in far more cases we are using mangled strings to look up metadata at runtime. If we do this for an objc class but for whatever reason we do not have any other references to the class, the static linker will fail to link in the relevant framework. The reason why this happens is that autolinking is treated by the static linker as a hint that a framework may be needed rather than as a "one must link against the framework". If there aren't any undefined symbols needed by the app from that framework, the linker just will ignore the hint. Of course this then causes the class lookup to fail at runtime when we use our mangled name to try to lookup the class. I included an Interpreter test as well as IRGen tests to make sure that we do not regress here in the future. NOTE: The test modifications here are due to my moving the ObjCClasses framework out of ./test/Interpreters/Inputs => test/Inputs since I am using it in the IRGen test along side the interpreter test. rdar://56136123
1 parent a0e7edd commit 8247525

20 files changed

+120
-24
lines changed

lib/IRGen/GenDecl.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,27 @@ void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type,
12591259
}
12601260
}
12611261

1262+
// Force emission of ObjC class refs used by type refs.
1263+
//
1264+
// Otherwise, autolinking can fail if we try to load the class decl from the
1265+
// name of the class.
1266+
if (auto *classDecl = dyn_cast<ClassDecl>(type)) {
1267+
// The logic behind this predicate is that:
1268+
//
1269+
// 1. We need to check if a class decl is foreign to exclude CF classes.
1270+
//
1271+
// 2. We want to check that the class decl is objc to exclude c++ classes in
1272+
// the future.
1273+
//
1274+
// 3. We check that we have a clang node since we want to ensure we have
1275+
// something coming from clang, rather than from swift which does not
1276+
// have this issue.
1277+
if (classDecl->hasClangNode() && classDecl->isObjC() && !classDecl->isForeign()) {
1278+
PrimaryIGM->getAddrOfObjCClassRef(classDecl);
1279+
return;
1280+
}
1281+
}
1282+
12621283
if (!hasLazyMetadata(type))
12631284
return;
12641285

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %empty-directory(%t)
2+
//
3+
// RUN: %target-clang -fobjc-arc %S/../Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o
4+
// RUN: mkdir -p %t/ObjCClasses.framework/Headers
5+
// RUN: cp %S/../Inputs/ObjCClasses/ObjCClasses.h %t/ObjCClasses.framework/Headers
6+
// RUN: mkdir -p %t/ObjCClasses.framework/Modules
7+
// RUN: cp %S/../Inputs/ObjCClasses/framework.module.map %t/ObjCClasses.framework/Modules/module.modulemap
8+
// RUN: %target-clang -dynamiclib -fobjc-arc -fmodules %t/ObjCClasses.o -o %t/ObjCClasses.framework/ObjCClasses
9+
// RUN: %target-codesign %t/ObjCClasses.framework/ObjCClasses
10+
11+
// RUN: %target-build-swift -emit-ir -F %t %s -o - | %FileCheck %s
12+
13+
// REQUIRES: objc_interop
14+
15+
// Make sure that we have our class ref here.
16+
// CHECK-DAG: @"OBJC_CLASS_REF_$_Animal"
17+
18+
// And make sure we have ObjCClasses in our autolinking info.
19+
//
20+
// CHECK-DAG: !{{[0-9][0-9]*}} = !{!"-framework", !"ObjCClasses"}
21+
22+
// Make sure that we emit an objc class ref to Animal so autolinking does not
23+
// fail.
24+
25+
import ObjCClasses
26+
27+
func main() {
28+
let a = Animal()
29+
print(a)
30+
}
31+
32+
main()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
// Module map used only if ObjCClasses are compiled into a framework, not using
3+
// -import-objc-header.
4+
5+
framework module ObjCClasses {
6+
header "ObjCClasses.h"
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
#import "ObjCStuff.h"
3+
4+
@implementation OJCCloud
5+
@end

test/Interpreter/SDK/mixed_mode_class_with_missing_properties.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// RUN: %empty-directory(%t)
22
// RUN: cp %s %t/main.swift
3+
// RUN: %target-clang -c -o %t/ObjCStuff.o -fobjc-arc -I %t -I %S/Inputs/mixed_mode %S/Inputs/mixed_mode/ObjCStuff.m -fmodules
34
// RUN: %target-build-swift -whole-module-optimization -emit-module-path %t/UsingObjCStuff.swiftmodule -c -o %t/UsingObjCStuff.o -module-name UsingObjCStuff -I %t -I %S/Inputs/mixed_mode -swift-version 5 -parse-as-library %S/Inputs/mixed_mode/UsingObjCStuff.swift
4-
// RUN: %target-build-swift -o %t/a.out.v4 -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 4 %t/main.swift %t/UsingObjCStuff.o
5-
// RUN: %target-build-swift -o %t/a.out.v5 -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 5 %t/main.swift %t/UsingObjCStuff.o
5+
// RUN: %target-build-swift -o %t/a.out.v4 -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 4 %t/main.swift %t/UsingObjCStuff.o %t/ObjCStuff.o
6+
// RUN: %target-build-swift -o %t/a.out.v5 -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 5 %t/main.swift %t/UsingObjCStuff.o %t/ObjCStuff.o
67
// RUN: %target-codesign %t/a.out.v4
78
// RUN: %target-codesign %t/a.out.v5
89
// RUN: %target-run %t/a.out.v4 | %FileCheck %s
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %empty-directory(%t)
2+
//
3+
// RUN: %target-clang -fobjc-arc %S/../Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o
4+
// RUN: mkdir -p %t/ObjCClasses.framework/Headers
5+
// RUN: cp %S/../Inputs/ObjCClasses/ObjCClasses.h %t/ObjCClasses.framework/Headers
6+
// RUN: mkdir -p %t/ObjCClasses.framework/Modules
7+
// RUN: cp %S/../Inputs/ObjCClasses/framework.module.map %t/ObjCClasses.framework/Modules/module.modulemap
8+
// RUN: %target-clang -dynamiclib -fobjc-arc -fmodules %t/ObjCClasses.o -o %t/ObjCClasses.framework/ObjCClasses
9+
// RUN: %target-codesign %t/ObjCClasses.framework/ObjCClasses
10+
11+
// RUN: %target-build-swift -F %t %s -o %t/main
12+
// RUN: %target-codesign %t/main
13+
// RUN: %target-run %t/main %t/ObjCClasses.framework
14+
15+
// REQUIRES: executable_test
16+
// REQUIRES: objc_interop
17+
18+
// Make sure that functionally if we refer to an objc class via autolinking such
19+
// that the class is not needed by the module directly, we still emit an objc
20+
// class ref to ensure that the static linker links against the autolinked
21+
// library.
22+
23+
import ObjCClasses
24+
25+
func main() {
26+
let a = Animal()
27+
print(a)
28+
}
29+
30+
main()

test/Interpreter/errors_imported.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
22
//
3-
// RUN: %target-clang -fobjc-arc %S/Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o
4-
// RUN: %target-build-swift -I %S/Inputs/ObjCClasses/ %t/ObjCClasses.o %s -o %t/a.out
3+
// RUN: %target-clang -fobjc-arc %S/../Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o
4+
// RUN: %target-build-swift -I %S/../Inputs/ObjCClasses/ %t/ObjCClasses.o %s -o %t/a.out
55
// RUN: %target-codesign %t/a.out
66
// RUN: %target-run %t/a.out
77

0 commit comments

Comments
 (0)