Skip to content

Commit ae496a7

Browse files
committed
[4.1] Sema: Request the layout for generic parameters if there is a body
Cherry-picked: "Merge pull request #14411 from aschwaighofer/sema_require_layout_generic_params" Generic parameters can become sources of metadata types and conformances. To access them the layout needs to be available to IRGen. rdar://37086144 SR-6879 * Explanation: With swift-4.1-branch we made requiring the layout of nominal types more lazy. Too lazy, in the case of parameters that are nominal and dependent because such parameters can be used to recover the bound generic type variables (and their conformances) by IRGen which requires their layout. * Scope: Introduced with swift-4.1-branch (see above). Causes runtime crash because type metadata / conformances are loaded from wrong offsets. * Testing: Swift CI tests added. The project from the bug report executes successfully after patch. * Risk: Low. We add a requirement to typecheck the nominal layout (which we did pre swift-4.1 lazyness. * Reviewed: Doug G
1 parent a95faf7 commit ae496a7

File tree

11 files changed

+169
-1
lines changed

11 files changed

+169
-1
lines changed

lib/Sema/CSApply.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7441,6 +7441,10 @@ namespace {
74417441
if (tc.coerceParameterListToType(params, closure, fnType))
74427442
return { false, nullptr };
74437443

7444+
// Require layout of dependent types that could be used to materialize
7445+
// metadata types/conformances during IRGen.
7446+
tc.requestRequiredNominalTypeLayoutForParameters(params);
7447+
74447448
// If this is a single-expression closure, convert the expression
74457449
// in the body to the result type of the closure.
74467450
if (closure->hasSingleExpressionBody()) {

lib/Sema/TypeCheckPattern.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,25 @@ static bool validateParameterType(ParamDecl *decl, DeclContext *DC,
755755
return hadError;
756756
}
757757

758+
/// Request nominal layout for any types that could be sources of typemetadata
759+
/// or conformances.
760+
void TypeChecker::requestRequiredNominalTypeLayoutForParameters(
761+
ParameterList *PL) {
762+
for (auto param : *PL) {
763+
if (!param->hasType())
764+
continue;
765+
766+
// Generic types are sources for typemetadata and conformances. If a
767+
// parameter is of dependent type then the body of a function with said
768+
// parameter could potentially require the generic type's layout to
769+
// recover them.
770+
if (auto *nominalDecl = dyn_cast_or_null<NominalTypeDecl>(
771+
param->getType()->getAnyGeneric())) {
772+
requestNominalLayout(nominalDecl);
773+
}
774+
}
775+
}
776+
758777
/// Type check a parameter list.
759778
bool TypeChecker::typeCheckParameterList(ParameterList *PL, DeclContext *DC,
760779
TypeResolutionOptions options,

lib/Sema/TypeCheckStmt.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,9 @@ bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
13611361
if (DebugTimeFunctionBodies || WarnLongFunctionBodies)
13621362
timer.emplace(AFD, DebugTimeFunctionBodies, WarnLongFunctionBodies);
13631363

1364+
for (auto paramList : AFD->getParameterLists())
1365+
requestRequiredNominalTypeLayoutForParameters(paramList);
1366+
13641367
if (typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc()))
13651368
return true;
13661369

lib/Sema/TypeChecker.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1824,11 +1824,15 @@ class TypeChecker final : public LazyResolver {
18241824

18251825
bool typeCheckCatchPattern(CatchStmt *S, DeclContext *dc);
18261826

1827+
/// Request nominal layout for any types that could be sources of typemetadata
1828+
/// or conformances.
1829+
void requestRequiredNominalTypeLayoutForParameters(ParameterList *PL);
1830+
18271831
/// Type check a parameter list.
18281832
bool typeCheckParameterList(ParameterList *PL, DeclContext *dc,
18291833
TypeResolutionOptions options,
18301834
GenericTypeResolver &resolver);
1831-
1835+
18321836
/// Coerce a pattern to the given type.
18331837
///
18341838
/// \param P The pattern, which may be modified by this coercion.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
public class Base<T> {
2+
var t: T
3+
init(_ a: T) {
4+
t = a
5+
}
6+
}
7+
8+
public class Sub<T> : Base<T> {
9+
}
10+
11+
public func requestTypeThrough<T>(closure: (Sub<T>) -> (), arg: T) {
12+
closure(Sub(arg))
13+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-swift-frontend -module-name test -emit-ir -verify -primary-file %s %S/Inputs/require-layout-generic-class.swift | %FileCheck --check-prefix=FILE1 %s
2+
// RUN: %target-swift-frontend -module-name test -emit-ir -verify %s -primary-file %S/Inputs/require-layout-generic-class.swift | %FileCheck --check-prefix=FILE2 %s
3+
4+
// REQUIRES: CPU=x86_64
5+
6+
// The offset of the typemetadata in the class typemetadata must match.
7+
8+
// FILE1-LABEL: define internal swiftcc void @{{.*}}S4test12requestType{{.*}}(%T4test3SubC*)
9+
// FILE1: entry:
10+
// FILE1: [[T1:%.*]] = bitcast %T4test3SubC* %0 to %swift.type**
11+
// FILE1: [[TYPEMETADATA:%.*]] = load %swift.type*, %swift.type** [[T1]]
12+
// FILE1: [[T2:%.*]] = bitcast %swift.type* [[TYPEMETADATA]] to %swift.type**
13+
// This offset of T needs to be the same as the offset below.
14+
// FILE1: [[T_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T2]], i64 16
15+
// FILE1: [[T:%.*]] = load %swift.type*, %swift.type** [[T_PTR]]
16+
// FILE1: call %swift.type* @{{.*}}S4test3SubCMa{{.*}}(%swift.type* [[T]])
17+
18+
public func requestType2<T>(x: T) {
19+
requestTypeThrough(closure: { x in print(x) }, arg: x)
20+
}
21+
// FILE2-LABEL: define private %swift.type* @create_generic_metadata_Sub(%swift.type_pattern*, i8**)
22+
// FILE2: [[T_ADDR:%.*]] = bitcast i8** %1 to %swift.type**
23+
// FILE2: [[T:%.*]] = load %swift.type*, %swift.type** %2
24+
// FILE2: [[CLASSMETADATA:%.*]] = call %swift.type* @swift_allocateGenericClassMetadata
25+
// FILE2: [[ADDR:%.*]] = bitcast %swift.type* [[CLASSMETADATA]] to i8**
26+
// This offset of T here needs to be the same as the offset above.
27+
// FILE2: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds i8*, i8** [[ADDR]], i64 16
28+
// FILE2: [[T2:%.*]] = bitcast %swift.type* [[T]] to i8*
29+
// FILE2: store i8* [[T2]], i8** [[T_IN_CLASSMETADATA]]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %target-swift-frontend -module-name test -emit-ir -verify -primary-file %s %S/Inputs/require-layout-generic-class.swift | %FileCheck --check-prefix=FILE1 %s
2+
// RUN: %target-swift-frontend -module-name test -emit-ir -verify %s -primary-file %S/Inputs/require-layout-generic-class.swift | %FileCheck --check-prefix=FILE2 %s
3+
4+
// REQUIRES: CPU=x86_64
5+
6+
// The offset of the typemetadata in the class typemetadata must match.
7+
8+
// FILE1: define hidden swiftcc i64 @{{.*}}S4test12AccessorTestCySiAA3Sub{{.*}}(%T4test3SubC*, %T4test12AccessorTestC* swiftself)
9+
// FILE1: [[T1:%.*]] = bitcast %T4test3SubC* %0 to %swift.type**
10+
// FILE1: [[TYPEMETADATA:%.*]] = load %swift.type*, %swift.type** [[T1]]
11+
// FILE1: [[T2:%.*]] = bitcast %swift.type* [[TYPEMETADATA]] to %swift.type**
12+
// This offset must match the offset below.
13+
// FILE1: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T2]], i64 16
14+
// FILE1: [[T:%.*]] = load %swift.type*, %swift.type** [[T_IN_CLASSMETADATA]]
15+
// FILE1: call %swift.type* @swift_getMetatypeMetadata(%swift.type* [[T]])
16+
public class AccessorTest {
17+
subscript<T>(_ a: Sub<T>) -> Int {
18+
get {
19+
print(T.self)
20+
return 1
21+
}
22+
}
23+
}
24+
25+
// FILE2-LABEL: define private %swift.type* @create_generic_metadata_Sub(%swift.type_pattern*, i8**)
26+
// FILE2: [[T_ADDR:%.*]] = bitcast i8** %1 to %swift.type**
27+
// FILE2: [[T:%.*]] = load %swift.type*, %swift.type** %2
28+
// FILE2: [[CLASSMETADATA:%.*]] = call %swift.type* @swift_allocateGenericClassMetadata
29+
// FILE2: [[ADDR:%.*]] = bitcast %swift.type* [[CLASSMETADATA]] to i8**
30+
// This offset must match the offset above.
31+
// FILE2: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds i8*, i8** [[ADDR]], i64 16
32+
// FILE2: [[T2:%.*]] = bitcast %swift.type* [[T]] to i8*
33+
// FILE2: store i8* [[T2]], i8** [[T_IN_CLASSMETADATA]]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-swift-frontend -module-name test -emit-ir -verify -primary-file %s %S/Inputs/require-layout-generic-class.swift | %FileCheck --check-prefix=FILE1 %s
2+
// RUN: %target-swift-frontend -module-name test -emit-ir -verify %s -primary-file %S/Inputs/require-layout-generic-class.swift | %FileCheck --check-prefix=FILE2 %s
3+
4+
// REQUIRES: CPU=x86_64
5+
6+
// The offset of the typemetadata in the class typemetadata must match.
7+
8+
// FILE1-LABEL: define{{.*}} swiftcc void @{{.*}}S4test11requestTypeyyAA3Sub{{.*}}(%T4test3SubC*)
9+
// FILE1: [[T1:%.*]] = bitcast %T4test3SubC* %0 to %swift.type**
10+
// FILE1: [[TYPEMETADATA:%.*]] = load %swift.type*, %swift.type** [[T1]]
11+
// FILE1: [[T2:%.*]] = bitcast %swift.type* [[TYPEMETADATA]] to %swift.type**
12+
// This offset must match the offset below.
13+
// FILE1: [[T_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T2]], i64 16
14+
// FILE1: [[T:%.*]] = load %swift.type*, %swift.type** [[T_PTR]]
15+
public func requestType<T>(_ c: Sub<T>) {
16+
print(T.self)
17+
}
18+
19+
// FILE2-LABEL: define private %swift.type* @create_generic_metadata_Sub(%swift.type_pattern*, i8**)
20+
// FILE2: [[T_ADDR:%.*]] = bitcast i8** %1 to %swift.type**
21+
// FILE2: [[T:%.*]] = load %swift.type*, %swift.type** %2
22+
// FILE2: [[CLASSMETADATA:%.*]] = call %swift.type* @swift_allocateGenericClassMetadata
23+
// FILE2: [[ADDR:%.*]] = bitcast %swift.type* [[CLASSMETADATA]] to i8**
24+
// This offset must match the offset above.
25+
// FILE2: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds i8*, i8** [[ADDR]], i64 16
26+
// FILE2: [[T2:%.*]] = bitcast %swift.type* [[T]] to i8*
27+
// FILE2: store i8* [[T2]], i8** [[T_IN_CLASSMETADATA]]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %S/../require-layout-generic-arg.swift %S/../Inputs/require-layout-generic-class.swift %s -o %t/test
3+
// RUN: %target-run %t/test | %FileCheck %s
4+
5+
// REQUIRES: executable_test
6+
7+
func test() {
8+
requestType(Sub(1))
9+
}
10+
11+
// CHECK: Int
12+
test()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %S/../require-layout-generic-arg-closure.swift %S/../Inputs/require-layout-generic-class.swift %s -o %t/test
3+
// RUN: %target-run %t/test | %FileCheck %s
4+
5+
// REQUIRES: executable_test
6+
7+
func test() {
8+
requestType2(x: 1)
9+
}
10+
11+
// CHECK: test.Sub<Swift.Int>
12+
test()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %S/../require-layout-generic-arg-subscript.swift %S/../Inputs/require-layout-generic-class.swift %s -o %t/test
3+
// RUN: %target-run %t/test | %FileCheck %s
4+
5+
// REQUIRES: executable_test
6+
7+
func test() {
8+
_ = AccessorTest()[Sub(1)]
9+
}
10+
11+
// CHECK: Int
12+
test()

0 commit comments

Comments
 (0)