Skip to content

Teach IRGen to fulfill type parameters from class-constrained archetypes #12118

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
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
26 changes: 9 additions & 17 deletions lib/IRGen/Fulfillment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ static bool isLeafTypeMetadata(CanType type) {
case TypeKind::Tuple:
return cast<TupleType>(type)->getNumElements() == 0;

// Nominal types might have parents.
// Nominal types might have generic parents.
case TypeKind::Class:
case TypeKind::Enum:
case TypeKind::Protocol:
case TypeKind::Struct:
return !cast<NominalType>(type)->getParent();
return !cast<NominalType>(type)->getDecl()->isGenericContext();

// Bound generic types have type arguments.
case TypeKind::BoundGenericClass:
Expand Down Expand Up @@ -125,6 +125,13 @@ bool FulfillmentMap::searchTypeMetadata(IRGenModule &IGM, CanType type,
return hadFulfillment;
}

if (keys.isInterestingType(type)) {
if (auto superclassTy = keys.getSuperclassBound(type)) {
return searchNominalTypeMetadata(IGM, superclassTy, source,
std::move(path), keys);
}
}

// Inexact metadata will be a problem if we ever try to use this
// to remember that we already have the metadata for something.
if (isa<NominalType>(type) || isa<BoundGenericType>(type)) {
Expand Down Expand Up @@ -289,21 +296,6 @@ bool FulfillmentMap::addFulfillment(FulfillmentKey key,
}
}

bool FulfillmentMap::Everything::isInterestingType(CanType type) const {
return true;
}
bool FulfillmentMap::Everything::hasInterestingType(CanType type) const {
return true;
}
bool FulfillmentMap::Everything
::hasLimitedInterestingConformances(CanType type) const {
return false;
}
GenericSignature::ConformsToArray
FulfillmentMap::Everything::getInterestingConformances(CanType type) const{
return {};
}

void FulfillmentMap::dump() const {
auto &out = llvm::errs();
for (auto &entry : Fulfillments) {
Expand Down
13 changes: 3 additions & 10 deletions lib/IRGen/Fulfillment.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,10 @@ class FulfillmentMap {
virtual GenericSignature::ConformsToArray
getInterestingConformances(CanType type) const = 0;

virtual ~InterestingKeysCallback() = default;
};
/// Return the limited interesting conformances for an interesting type.
virtual CanType getSuperclassBound(CanType type) const = 0;

/// An implementation of InterestingKeysCallback that returns everything
/// fulfillable.
struct Everything : InterestingKeysCallback {
bool isInterestingType(CanType type) const override;
bool hasInterestingType(CanType type) const override;
bool hasLimitedInterestingConformances(CanType type) const override;
GenericSignature::ConformsToArray
getInterestingConformances(CanType type) const override;
virtual ~InterestingKeysCallback() = default;
};

FulfillmentMap() = default;
Expand Down
28 changes: 27 additions & 1 deletion lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ class PolymorphicConvention {
return Generics->getConformsTo(t, M);
}

CanType getSuperclassBound(Type t) {
if (auto superclassTy = Generics->getSuperclassBound(t, M))
return superclassTy->getCanonicalType();
return CanType();
}

public:
PolymorphicConvention(IRGenModule &IGM, CanSILFunctionType fnType);

Expand Down Expand Up @@ -291,6 +297,9 @@ bool PolymorphicConvention::considerType(CanType type, IsExact_t isExact,
getInterestingConformances(CanType type) const override {
return Self.getConformsTo(type);
}
CanType getSuperclassBound(CanType type) const override {
return Self.getSuperclassBound(type);
}
} callbacks(*this);
return Fulfillments.searchTypeMetadata(IGM, type, isExact, sourceIndex,
std::move(path), callbacks);
Expand Down Expand Up @@ -367,6 +376,15 @@ void PolymorphicConvention::considerParameter(SILParameterInfo param,
return;
}

if (isa<GenericTypeParamType>(type)) {
if (auto superclassTy = getSuperclassBound(type)) {
considerNewTypeSource(MetadataSource::Kind::ClassPointer,
paramIndex, superclassTy, IsInexact);
return;

}
}

// Thick metatypes are sources of metadata.
if (auto metatypeTy = dyn_cast<MetatypeType>(type)) {
if (metatypeTy->getRepresentation() != MetatypeRepresentation::Thick)
Expand Down Expand Up @@ -1284,6 +1302,11 @@ class AccessorConformanceInfo : public ConformanceInfo {
getInterestingConformances(CanType type) const override {
llvm_unreachable("no limits");
}
CanType getSuperclassBound(CanType type) const override {
if (auto superclassTy = cast<ArchetypeType>(type)->getSuperclass())
return superclassTy->getCanonicalType();
return CanType();
}
} callback;
Fulfillments->searchTypeMetadata(IGM, ConcreteType, IsExact,
/*sourceIndex*/ 0, MetadataPath(),
Expand Down Expand Up @@ -2023,7 +2046,10 @@ llvm::Value *MetadataPath::followComponent(IRGenFunction &IGF,
case Component::Kind::NominalTypeArgument:
case Component::Kind::NominalTypeArgumentConformance: {
assert(sourceKey.Kind == LocalTypeDataKind::forTypeMetadata());
auto *nominal = sourceKey.Type.getAnyNominal();
auto type = sourceKey.Type;
if (auto archetypeTy = dyn_cast<ArchetypeType>(type))
type = archetypeTy->getSuperclass()->getCanonicalType();
auto *nominal = type.getAnyNominal();
auto reqtIndex = component.getPrimaryIndex();

GenericTypeRequirements requirements(IGF.IGM, nominal);
Expand Down
24 changes: 23 additions & 1 deletion lib/IRGen/LocalTypeData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,34 @@ void LocalTypeDataCache::addAbstractForTypeMetadata(IRGenFunction &IGF,
CanType type,
IsExact_t isExact,
llvm::Value *metadata) {
struct Callback : FulfillmentMap::InterestingKeysCallback {
bool isInterestingType(CanType type) const override {
return true;
}
bool hasInterestingType(CanType type) const override {
return true;
}
bool hasLimitedInterestingConformances(CanType type) const override {
return false;
}
GenericSignature::ConformsToArray
getInterestingConformances(CanType type) const override {
llvm_unreachable("no limits");
}
CanType getSuperclassBound(CanType type) const override {
if (auto arch = dyn_cast<ArchetypeType>(type))
if (auto superclassTy = arch->getSuperclass())
return superclassTy->getCanonicalType();
return CanType();
}
} callbacks;

// Look for anything at all that's fulfilled by this. If we don't find
// anything, stop.
FulfillmentMap fulfillments;
if (!fulfillments.searchTypeMetadata(IGF.IGM, type, isExact,
/*source*/ 0, MetadataPath(),
FulfillmentMap::Everything())) {
callbacks)) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/SIL/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ SILFunctionType::getDefaultWitnessMethodProtocol(ModuleDecl &M) const {
assert(getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod);
auto selfTy = getSelfInstanceType();
if (auto paramTy = dyn_cast<GenericTypeParamType>(selfTy)) {
auto superclass = GenericSig->getSuperclassBound(paramTy, M);
if (superclass)
return nullptr;
assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0);
auto protos = GenericSig->getConformsTo(paramTy, M);
assert(protos.size() == 1);
Expand Down
41 changes: 37 additions & 4 deletions test/IRGen/class_bounded_generics.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s
// RUN: %target-swift-frontend -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s

// REQUIRES: CPU=x86_64
// XFAIL: linux
Expand All @@ -9,7 +9,7 @@ protocol ClassBound : class {
protocol ClassBound2 : class {
func classBoundMethod2()
}
protocol ClassBoundBinary : class, ClassBound {
protocol ClassBoundBinary : ClassBound {
func classBoundBinaryMethod(_ x: Self)
}
@objc protocol ObjCClassBound {
Expand Down Expand Up @@ -214,7 +214,7 @@ func objc_class_bounded_protocol_conversion_4
}

// CHECK-LABEL: define hidden swiftcc { i64, %objc_object*, i64 } @_T022class_bounded_generics0A28_generic_field_struct_fields{{[_0-9a-zA-Z]*}}F(i64, %objc_object*, i64, %swift.type* %T, i8** %T.ClassBound)
func class_generic_field_struct_fields<T : ClassBound>
func class_generic_field_struct_fields<T>
(_ x:ClassGenericFieldStruct<T>) -> (Int, T, Int) {
return (x.x, x.y, x.z)
}
Expand All @@ -226,7 +226,7 @@ func class_protocol_field_struct_fields
}

// CHECK-LABEL: define hidden swiftcc { i64, %objc_object*, i64 } @_T022class_bounded_generics0a15_generic_field_A7_fields{{[_0-9a-zA-Z]*}}F(%T22class_bounded_generics017ClassGenericFieldD0C*)
func class_generic_field_class_fields<T : ClassBound>
func class_generic_field_class_fields<T>
(_ x:ClassGenericFieldClass<T>) -> (Int, T, Int) {
return (x.x, x.y, x.z)
// CHECK: getelementptr inbounds %T22class_bounded_generics017ClassGenericFieldD0C, %T22class_bounded_generics017ClassGenericFieldD0C* %0, i32 0, i32 1
Expand Down Expand Up @@ -273,3 +273,36 @@ class M<T, S: A<T>> {
s = S.init()
}
}

// CHECK-LABEL: define hidden swiftcc void @_T022class_bounded_generics14takes_metatypeyxmlF(%swift.type*, %swift.type* %T)
func takes_metatype<T>(_: T.Type) {}

// CHECK-LABEL: define hidden swiftcc void @_T022class_bounded_generics023archetype_with_generic_A11_constraintyx1t_tAA1ACyq_GRbzr0_lF(%T22class_bounded_generics1AC.2*, %swift.type* %T)
// CHECK: [[ISA_ADDR:%.*]] = bitcast %T22class_bounded_generics1AC.2* %0 to %swift.type**
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
// CHECK-NEXT: call swiftcc void @_T022class_bounded_generics14takes_metatypeyxmlF(%swift.type* %T, %swift.type* %T)
// CHECK-NEXT: [[ISA_PTR:%.*]] = bitcast %swift.type* [[ISA]] to %swift.type**
// CHECK-NEXT: [[U_ADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[ISA_PTR]], i64 10
// CHECK-NEXT: [[U:%.*]] = load %swift.type*, %swift.type** [[U_ADDR]]
// CHECK-NEXT: call swiftcc void @_T022class_bounded_generics14takes_metatypeyxmlF(%swift.type* %U, %swift.type* %U)
// CHECK: ret void

func archetype_with_generic_class_constraint<T, U>(t: T) where T : A<U> {
takes_metatype(T.self)
takes_metatype(U.self)
}

// CHECK-LABEL: define hidden swiftcc void @_T022class_bounded_generics029calls_archetype_with_generic_A11_constraintyAA1ACyxG1a_tlF(%T22class_bounded_generics1AC*) #0 {
// CHECK: [[ISA_ADDR:%.*]] = getelementptr inbounds %T22class_bounded_generics1AC, %T22class_bounded_generics1AC* %0, i32 0, i32 0, i32 0
// CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]]
// CHECK: [[SELF:%.*]] = bitcast %T22class_bounded_generics1AC* %0 to %T22class_bounded_generics1AC.2*
// CHECK-NEXT: [[ISA_PTR:%.*]] = bitcast %swift.type* [[ISA]] to %swift.type**
// CHECK-NEXT: [[T_ADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[ISA_PTR]], i64 10
// CHECK-NEXT: [[T:%.*]] = load %swift.type*, %swift.type** [[T_ADDR]]
// CHECK-NEXT: [[A_OF_T:%.*]] = call %swift.type* @_T022class_bounded_generics1ACMa(%swift.type* [[T]])
// CHECK-NEXT: call swiftcc void @_T022class_bounded_generics023archetype_with_generic_A11_constraintyx1t_tAA1ACyq_GRbzr0_lF(%T22class_bounded_generics1AC.2* [[SELF]], %swift.type* [[A_OF_T]])
// CHECK: ret void

func calls_archetype_with_generic_class_constraint<T>(a: A<T>) {
archetype_with_generic_class_constraint(t: a)
}