Skip to content

Sema: Request the layout for generic parameters if there is a body #14411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7611,6 +7611,10 @@ namespace {
if (tc.coerceParameterListToType(params, closure, fnType))
return { false, nullptr };

// Require layout of dependent types that could be used to materialize
// metadata types/conformances during IRGen.
tc.requestRequiredNominalTypeLayoutForParameters(params);

// If this is a single-expression closure, convert the expression
// in the body to the result type of the closure.
if (closure->hasSingleExpressionBody()) {
Expand Down
19 changes: 19 additions & 0 deletions lib/Sema/TypeCheckPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,25 @@ static bool validateParameterType(ParamDecl *decl, DeclContext *DC,
return hadError;
}

/// Request nominal layout for any types that could be sources of typemetadata
/// or conformances.
void TypeChecker::requestRequiredNominalTypeLayoutForParameters(
ParameterList *PL) {
for (auto param : *PL) {
if (!param->hasType())
continue;

// Generic types are sources for typemetadata and conformances. If a
// parameter is of dependent type then the body of a function with said
// parameter could potentially require the generic type's layout to
// recover them.
if (auto *nominalDecl = dyn_cast_or_null<NominalTypeDecl>(
param->getType()->getAnyGeneric())) {
requestNominalLayout(nominalDecl);
}
}
}

/// Type check a parameter list.
bool TypeChecker::typeCheckParameterList(ParameterList *PL, DeclContext *DC,
TypeResolutionOptions options,
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,9 @@ bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
if (DebugTimeFunctionBodies || WarnLongFunctionBodies)
timer.emplace(AFD, DebugTimeFunctionBodies, WarnLongFunctionBodies);

for (auto paramList : AFD->getParameterLists())
requestRequiredNominalTypeLayoutForParameters(paramList);

if (typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc()))
return true;

Expand Down
6 changes: 5 additions & 1 deletion lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -1809,11 +1809,15 @@ class TypeChecker final : public LazyResolver {

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

/// Request nominal layout for any types that could be sources of typemetadata
/// or conformances.
void requestRequiredNominalTypeLayoutForParameters(ParameterList *PL);

/// Type check a parameter list.
bool typeCheckParameterList(ParameterList *PL, DeclContext *dc,
TypeResolutionOptions options,
GenericTypeResolver &resolver);

/// Coerce a pattern to the given type.
///
/// \param P The pattern, which may be modified by this coercion.
Expand Down
13 changes: 13 additions & 0 deletions test/multifile/Inputs/require-layout-generic-class.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
public class Base<T> {
var t: T
init(_ a: T) {
t = a
}
}

public class Sub<T> : Base<T> {
}

public func requestTypeThrough<T>(closure: (Sub<T>) -> (), arg: T) {
closure(Sub(arg))
}
29 changes: 29 additions & 0 deletions test/multifile/require-layout-generic-arg-closure.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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
// 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

// REQUIRES: CPU=x86_64

// The offset of the typemetadata in the class typemetadata must match.

// FILE1-LABEL: define internal swiftcc void @"$S4test12requestType21xyx_tlFyAA3SubCyxGcfU_"(%T4test3SubC*)
// FILE1: entry:
// FILE1: [[T1:%.*]] = bitcast %T4test3SubC* %0 to %swift.type**
// FILE1: [[TYPEMETADATA:%.*]] = load %swift.type*, %swift.type** [[T1]]
// FILE1: [[T2:%.*]] = bitcast %swift.type* [[TYPEMETADATA]] to %swift.type**
// This offset of T needs to be the same as the offset below.
// FILE1: [[T_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T2]], i64 16
// FILE1: [[T:%.*]] = load %swift.type*, %swift.type** [[T_PTR]]
// FILE1: call %swift.type* @"$S4test3SubCMa"(%swift.type* [[T]])

public func requestType2<T>(x: T) {
requestTypeThrough(closure: { x in print(x) }, arg: x)
}
// FILE2-LABEL: define private %swift.type* @create_generic_metadata_Sub(%swift.type_pattern*, i8**)
// FILE2: [[T_ADDR:%.*]] = bitcast i8** %1 to %swift.type**
// FILE2: [[T:%.*]] = load %swift.type*, %swift.type** %2
// FILE2: [[CLASSMETADATA:%.*]] = call %swift.type* @swift_allocateGenericClassMetadata
// FILE2: [[ADDR:%.*]] = bitcast %swift.type* [[CLASSMETADATA]] to i8**
// This offset of T here needs to be the same as the offset above.
// FILE2: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds i8*, i8** [[ADDR]], i64 16
// FILE2: [[T2:%.*]] = bitcast %swift.type* [[T]] to i8*
// FILE2: store i8* [[T2]], i8** [[T_IN_CLASSMETADATA]]
33 changes: 33 additions & 0 deletions test/multifile/require-layout-generic-arg-subscript.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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
// 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

// REQUIRES: CPU=x86_64

// The offset of the typemetadata in the class typemetadata must match.

// FILE1: define hidden swiftcc i64 @"$S4test12AccessorTestCySiAA3SubCyxGcluig"(%T4test3SubC*, %T4test12AccessorTestC* swiftself)
// FILE1: [[T1:%.*]] = bitcast %T4test3SubC* %0 to %swift.type**
// FILE1: [[TYPEMETADATA:%.*]] = load %swift.type*, %swift.type** [[T1]]
// FILE1: [[T2:%.*]] = bitcast %swift.type* [[TYPEMETADATA]] to %swift.type**
// This offset must match the offset below.
// FILE1: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T2]], i64 16
// FILE1: [[T:%.*]] = load %swift.type*, %swift.type** [[T_IN_CLASSMETADATA]]
// FILE1: call %swift.type* @swift_getMetatypeMetadata(%swift.type* [[T]])
public class AccessorTest {
subscript<T>(_ a: Sub<T>) -> Int {
get {
print(T.self)
return 1
}
}
}

// FILE2-LABEL: define private %swift.type* @create_generic_metadata_Sub(%swift.type_pattern*, i8**)
// FILE2: [[T_ADDR:%.*]] = bitcast i8** %1 to %swift.type**
// FILE2: [[T:%.*]] = load %swift.type*, %swift.type** %2
// FILE2: [[CLASSMETADATA:%.*]] = call %swift.type* @swift_allocateGenericClassMetadata
// FILE2: [[ADDR:%.*]] = bitcast %swift.type* [[CLASSMETADATA]] to i8**
// This offset must match the offset above.
// FILE2: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds i8*, i8** [[ADDR]], i64 16
// FILE2: [[T2:%.*]] = bitcast %swift.type* [[T]] to i8*
// FILE2: store i8* [[T2]], i8** [[T_IN_CLASSMETADATA]]
27 changes: 27 additions & 0 deletions test/multifile/require-layout-generic-arg.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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
// 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

// REQUIRES: CPU=x86_64

// The offset of the typemetadata in the class typemetadata must match.

// FILE1-LABEL: define{{.*}} swiftcc void @"$S4test11requestTypeyyAA3SubCyxGlF"(%T4test3SubC*)
// FILE1: [[T1:%.*]] = bitcast %T4test3SubC* %0 to %swift.type**
// FILE1: [[TYPEMETADATA:%.*]] = load %swift.type*, %swift.type** [[T1]]
// FILE1: [[T2:%.*]] = bitcast %swift.type* [[TYPEMETADATA]] to %swift.type**
// This offset must match the offset below.
// FILE1: [[T_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T2]], i64 16
// FILE1: [[T:%.*]] = load %swift.type*, %swift.type** [[T_PTR]]
public func requestType<T>(_ c: Sub<T>) {
print(T.self)
}

// FILE2-LABEL: define private %swift.type* @create_generic_metadata_Sub(%swift.type_pattern*, i8**)
// FILE2: [[T_ADDR:%.*]] = bitcast i8** %1 to %swift.type**
// FILE2: [[T:%.*]] = load %swift.type*, %swift.type** %2
// FILE2: [[CLASSMETADATA:%.*]] = call %swift.type* @swift_allocateGenericClassMetadata
// FILE2: [[ADDR:%.*]] = bitcast %swift.type* [[CLASSMETADATA]] to i8**
// This offset must match the offset above.
// FILE2: [[T_IN_CLASSMETADATA:%.*]] = getelementptr inbounds i8*, i8** [[ADDR]], i64 16
// FILE2: [[T2:%.*]] = bitcast %swift.type* [[T]] to i8*
// FILE2: store i8* [[T2]], i8** [[T_IN_CLASSMETADATA]]
12 changes: 12 additions & 0 deletions test/multifile/require-layout/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/../require-layout-generic-arg.swift %S/../Inputs/require-layout-generic-class.swift %s -o %t/test
// RUN: %target-run %t/test | %FileCheck %s

// REQUIRES: executable_test

func test() {
requestType(Sub(1))
}

// CHECK: Int
test()
12 changes: 12 additions & 0 deletions test/multifile/require-layout2/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/../require-layout-generic-arg-closure.swift %S/../Inputs/require-layout-generic-class.swift %s -o %t/test
// RUN: %target-run %t/test | %FileCheck %s

// REQUIRES: executable_test

func test() {
requestType2(x: 1)
}

// CHECK: test.Sub<Swift.Int>
test()
12 changes: 12 additions & 0 deletions test/multifile/require-layout3/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/../require-layout-generic-arg-subscript.swift %S/../Inputs/require-layout-generic-class.swift %s -o %t/test
// RUN: %target-run %t/test | %FileCheck %s

// REQUIRES: executable_test

func test() {
_ = AccessorTest()[Sub(1)]
}

// CHECK: Int
test()