Skip to content

Commit 77dd9b2

Browse files
committed
Split exact-subclass and bindable-to-subclass queries.
In many places, we're interested in whether a type with archetypes *might be* a superclass of another type with the right bindings, particularly in the optimizer. Provide a separate Type::isBindableToSuperclassOf method that performs this check. Use it in the devirtualizer to fix rdar://problem/24993618. Using it might unblock other places where the optimizer is conservative, but we can fix those separately.
1 parent 9b00e35 commit 77dd9b2

20 files changed

+390
-32
lines changed

include/swift/AST/Types.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,14 +623,34 @@ class alignas(1 << TypeAlignInBits) TypeBase {
623623
/// superclass.
624624
Type getSuperclass(LazyResolver *resolver);
625625

626-
/// \brief True if this type is a superclass of another type.
626+
/// \brief True if this type is the exact superclass of another type.
627627
///
628628
/// \param ty The potential subclass.
629629
/// \param resolver The resolver for lazy type checking, or null if the
630630
/// AST is already type-checked.
631631
///
632632
/// \returns True if this type is \c ty or a superclass of \c ty.
633-
bool isSuperclassOf(Type ty, LazyResolver *resolver);
633+
///
634+
/// If this type is a bound generic class \c Foo<T>, the method only
635+
/// returns true if the generic parameters of \c ty exactly match the
636+
/// superclass of \c ty. For instance, if \c ty is a
637+
/// class DerivedClass: Base<Int>, then \c Base<T> (where T is an archetype)
638+
/// will return false. `isBindableToSuperclassOf` should be used
639+
/// for queries that care whether a generic class type can be substituted into
640+
/// a type's subclass.
641+
bool isExactSuperclassOf(Type ty, LazyResolver *resolver);
642+
643+
/// \brief True if this type is the superclass of another type, or a generic
644+
/// type that could be bound to the superclass.
645+
///
646+
/// \param ty The potential subclass.
647+
/// \param resolver The resolver for lazy type checking, or null if the
648+
/// AST is already type-checked.
649+
///
650+
/// \returns True if this type is \c ty, a superclass of \c ty, or an
651+
/// archetype-parameterized type that can be bound to a superclass
652+
/// of \c ty.
653+
bool isBindableToSuperclassOf(Type ty, LazyResolver *resolver);
634654

635655
/// \brief Determines whether this type is permitted as a method override
636656
/// of the \p other.

include/swift/SIL/SILType.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,19 @@ class SILType {
418418
return SILType::getPrimitiveObjectType(superclass->getCanonicalType());
419419
}
420420

421-
/// Return true if Ty is a subtype of this SILType, or null otherwise.
422-
bool isSuperclassOf(SILType Ty) const {
423-
return getSwiftRValueType()->isSuperclassOf(Ty.getSwiftRValueType(),
424-
nullptr);
421+
/// Return true if Ty is a subtype of this exact SILType, or false otherwise.
422+
bool isExactSuperclassOf(SILType Ty) const {
423+
return getSwiftRValueType()->isExactSuperclassOf(Ty.getSwiftRValueType(),
424+
nullptr);
425+
}
426+
427+
/// Return true if Ty is a subtype of this SILType, or if this SILType
428+
/// contains archetypes that can be found to form a supertype of Ty, or false
429+
/// otherwise.
430+
bool isBindableToSuperclassOf(SILType Ty) const {
431+
return getSwiftRValueType()->isBindableToSuperclassOf(
432+
Ty.getSwiftRValueType(),
433+
nullptr);
425434
}
426435

427436
/// Transform the function type SILType by replacing all of its interface

include/swift/SIL/TypeSubstCloner.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ class TypeSubstCloner : public SILClonerWithScopes<ImplClass> {
208208
CanType Ty = Conformance.getConcrete()->getType()->getCanonicalType();
209209

210210
if (Ty != newLookupType) {
211-
assert(Ty->isSuperclassOf(newLookupType, nullptr) &&
211+
assert(Ty->isExactSuperclassOf(newLookupType, nullptr) &&
212212
"Should only create upcasts for sub class.");
213213

214214
// We use the super class as the new look up type.

lib/AST/ASTVerifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2467,7 +2467,7 @@ struct ASTNodeBase {};
24672467

24682468
// If the destination is a class, walk the supertypes of the source.
24692469
if (destTy->getClassOrBoundGenericClass()) {
2470-
if (!destTy->isSuperclassOf(srcTy, nullptr)) {
2470+
if (!destTy->isBindableToSuperclassOf(srcTy, nullptr)) {
24712471
srcTy.print(Out);
24722472
Out << " is not a superclass of ";
24732473
destTy.print(Out);

lib/AST/ArchetypeBuilder.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -979,12 +979,33 @@ bool ArchetypeBuilder::addSuperclassRequirement(PotentialArchetype *T,
979979

980980
// If T already has a superclass, make sure it's related.
981981
if (T->Superclass) {
982-
if (T->Superclass->isSuperclassOf(Superclass, nullptr)) {
982+
// TODO: In principle, this could be isBindableToSuperclassOf instead of
983+
// isExactSubclassOf. If you had:
984+
//
985+
// class Foo<T>
986+
// class Bar: Foo<Int>
987+
//
988+
// func foo<T, U where U: Foo<T>, U: Bar>(...) { ... }
989+
//
990+
// then the second constraint should be allowed, constraining U to Bar
991+
// and secondarily imposing a T == Int constraint.
992+
if (T->Superclass->isExactSuperclassOf(Superclass, nullptr)) {
983993
T->Superclass = Superclass;
984994

985995
// We've strengthened the bound, so update superclass conformances.
986996
updateSuperclassConformances();
987-
} else if (!Superclass->isSuperclassOf(T->Superclass, nullptr)) {
997+
// TODO: Similar to the above, a more general isBindableToSuperclassOf
998+
// base class constraint could potentially introduce secondary constraints.
999+
// If you had:
1000+
//
1001+
// class Foo<T>
1002+
// class Bar: Foo<Int>
1003+
//
1004+
// func foo<T, U where U: Bar, U: Foo<T>>(...) { ... }
1005+
//
1006+
// then the second `U: Foo<T>` constraint introduces a `T == Int`
1007+
// constraint.
1008+
} else if (!Superclass->isExactSuperclassOf(T->Superclass, nullptr)) {
9881009
Diags.diagnose(Source.getLoc(),
9891010
diag::requires_superclass_conflict, T->getName(),
9901011
T->Superclass, Superclass)

lib/AST/ProtocolConformance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ ProtocolConformance::getInheritedConformance(ProtocolDecl *protocol) const {
521521
subMap, conformanceMap);
522522
}
523523
assert((getType()->isEqual(foundInherited->getType()) ||
524-
foundInherited->getType()->isSuperclassOf(getType(), nullptr))
524+
foundInherited->getType()->isExactSuperclassOf(getType(), nullptr))
525525
&& "inherited conformance does not match type");
526526
return foundInherited;
527527
}

lib/AST/Type.cpp

Lines changed: 185 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,7 +1652,7 @@ Type TypeBase::getSuperclass(LazyResolver *resolver) {
16521652
return superclassTy.subst(module, substitutions, None);
16531653
}
16541654

1655-
bool TypeBase::isSuperclassOf(Type ty, LazyResolver *resolver) {
1655+
bool TypeBase::isExactSuperclassOf(Type ty, LazyResolver *resolver) {
16561656
// For there to be a superclass relationship, we must be a superclass, and
16571657
// the potential subtype must be a class or superclass-bounded archetype.
16581658
if (!getClassOrBoundGenericClass() || !ty->mayHaveSuperclass())
@@ -1667,6 +1667,189 @@ bool TypeBase::isSuperclassOf(Type ty, LazyResolver *resolver) {
16671667
return false;
16681668
}
16691669

1670+
/// Returns true if type `a` has archetypes that can be bound to form `b`.
1671+
static bool isBindableTo(Type a, Type b, LazyResolver *resolver) {
1672+
class IsBindableVisitor : public TypeVisitor<IsBindableVisitor, bool, CanType>
1673+
{
1674+
llvm::DenseMap<ArchetypeType *, CanType> Bindings;
1675+
LazyResolver *Resolver;
1676+
public:
1677+
IsBindableVisitor(LazyResolver *Resolver)
1678+
: Resolver(Resolver) {}
1679+
1680+
bool visitArchetypeType(ArchetypeType *orig, CanType subst) {
1681+
// If we already bound this archetype, make sure the new binding candidate
1682+
// is the same type.
1683+
auto bound = Bindings.find(orig);
1684+
if (bound != Bindings.end()) {
1685+
return bound->second->isEqual(subst);
1686+
}
1687+
// Check that the archetype isn't constrained in a way that makes the
1688+
// binding impossible.
1689+
// For instance, if the archetype is class-constrained, and the binding
1690+
// is not a class, it can never be bound.
1691+
if (orig->requiresClass() && !subst->mayHaveSuperclass())
1692+
return false;
1693+
1694+
// TODO: If the archetype has a superclass constraint, check that the
1695+
// substitution is a subclass.
1696+
1697+
// TODO: For private types or protocols, we might be able to definitively
1698+
// deny bindings.
1699+
1700+
// Otherwise, there may be an external retroactive conformance that
1701+
// allows the binding.
1702+
1703+
// Remember the binding, and succeed.
1704+
Bindings.insert({orig, subst});
1705+
return true;
1706+
}
1707+
1708+
bool visitType(TypeBase *orig, CanType subst) {
1709+
llvm_unreachable("not a valid canonical type substitution");
1710+
}
1711+
1712+
bool visitNominalType(NominalType *nom, CanType subst) {
1713+
if (auto substNom = dyn_cast<NominalType>(subst)) {
1714+
if (nom->getDecl() != substNom->getDecl())
1715+
return false;
1716+
1717+
if (nom->getDecl()->isInvalid())
1718+
return false;
1719+
1720+
// Same decl should always either have or not have a parent.
1721+
assert((bool)nom->getParent() == (bool)substNom->getParent());
1722+
1723+
if (nom->getParent())
1724+
return visit(nom->getParent()->getCanonicalType(),
1725+
substNom->getParent()->getCanonicalType());
1726+
return true;
1727+
}
1728+
return false;
1729+
}
1730+
1731+
bool visitAnyMetatypeType(AnyMetatypeType *meta, CanType subst) {
1732+
if (auto substMeta = dyn_cast<AnyMetatypeType>(subst)) {
1733+
if (substMeta->getKind() != meta->getKind())
1734+
return false;
1735+
return visit(meta->getInstanceType()->getCanonicalType(),
1736+
substMeta->getInstanceType()->getCanonicalType());
1737+
}
1738+
return false;
1739+
}
1740+
1741+
bool visitTupleType(TupleType *tuple, CanType subst) {
1742+
if (auto substTuple = dyn_cast<TupleType>(subst)) {
1743+
// Tuple elements must match.
1744+
if (tuple->getNumElements() != substTuple->getNumElements())
1745+
return false;
1746+
// TODO: Label reordering?
1747+
for (unsigned i : indices(tuple->getElements())) {
1748+
auto elt = tuple->getElements()[i],
1749+
substElt = substTuple->getElements()[i];
1750+
if (elt.getName() != substElt.getName())
1751+
return false;
1752+
if (!visit(elt.getType(), substElt.getType()->getCanonicalType()))
1753+
return false;
1754+
}
1755+
return true;
1756+
}
1757+
return false;
1758+
}
1759+
1760+
bool visitDependentMemberType(DependentMemberType *dt, CanType subst) {
1761+
llvm_unreachable("can't visit dependent types");
1762+
}
1763+
bool visitGenericTypeParamType(GenericTypeParamType *dt, CanType subst) {
1764+
llvm_unreachable("can't visit dependent types");
1765+
}
1766+
1767+
bool visitFunctionType(FunctionType *func, CanType subst) {
1768+
if (auto substFunc = dyn_cast<FunctionType>(subst)) {
1769+
if (func->getExtInfo() != substFunc->getExtInfo())
1770+
return false;
1771+
1772+
if (!visit(func->getInput()->getCanonicalType(),
1773+
substFunc->getInput()->getCanonicalType()))
1774+
return false;
1775+
1776+
return visit(func->getResult()->getCanonicalType(),
1777+
substFunc->getResult()->getCanonicalType());
1778+
}
1779+
return false;
1780+
}
1781+
1782+
bool visitBoundGenericType(BoundGenericType *bgt, CanType subst) {
1783+
if (auto substBGT = dyn_cast<BoundGenericType>(subst)) {
1784+
if (bgt->getDecl() != substBGT->getDecl())
1785+
return false;
1786+
1787+
if (bgt->getDecl()->isInvalid())
1788+
return false;
1789+
1790+
auto origSubs = bgt->getSubstitutions(bgt->getDecl()->getParentModule(),
1791+
Resolver);
1792+
auto substSubs = substBGT->getSubstitutions(
1793+
bgt->getDecl()->getParentModule(),
1794+
Resolver);
1795+
assert(origSubs.size() == substSubs.size());
1796+
for (unsigned subi : indices(origSubs)) {
1797+
if (!visit(origSubs[subi].getReplacement()->getCanonicalType(),
1798+
substSubs[subi].getReplacement()->getCanonicalType()))
1799+
return false;
1800+
assert(origSubs[subi].getConformances().size()
1801+
== substSubs[subi].getConformances().size());
1802+
for (unsigned conformancei :
1803+
indices(origSubs[subi].getConformances())) {
1804+
if (origSubs[subi].getConformances()[conformancei]
1805+
!= substSubs[subi].getConformances()[conformancei])
1806+
return false;
1807+
}
1808+
}
1809+
1810+
// Same decl should always either have or not have a parent.
1811+
assert((bool)bgt->getParent() == (bool)substBGT->getParent());
1812+
if (bgt->getParent())
1813+
return visit(bgt->getParent()->getCanonicalType(),
1814+
substBGT->getParent()->getCanonicalType());
1815+
return true;
1816+
}
1817+
return false;
1818+
}
1819+
};
1820+
1821+
return IsBindableVisitor(resolver).visit(a->getCanonicalType(),
1822+
b->getCanonicalType());
1823+
}
1824+
1825+
bool TypeBase::isBindableToSuperclassOf(Type ty, LazyResolver *resolver) {
1826+
// Do an exact match if no archetypes are involved.
1827+
if (!hasArchetype())
1828+
return isExactSuperclassOf(ty, resolver);
1829+
1830+
// For there to be a superclass relationship,
1831+
// the potential subtype must be a class or superclass-bounded archetype.
1832+
if (!ty->mayHaveSuperclass())
1833+
return false;
1834+
1835+
// If the type is itself an archetype, we could always potentially bind it
1836+
// to the superclass (via external retroactive conformance, even if the
1837+
// type isn't statically known to conform).
1838+
//
1839+
// We could theoretically reject cases where the set of conformances is known
1840+
// (say the protocol or classes are private or internal).
1841+
if (is<ArchetypeType>())
1842+
return true;
1843+
1844+
do {
1845+
if (isBindableTo(this, ty, resolver))
1846+
return true;
1847+
if (ty->getAnyNominal() && ty->getAnyNominal()->isInvalid())
1848+
return false;
1849+
} while ((ty = ty->getSuperclass(resolver)));
1850+
return false;
1851+
}
1852+
16701853
static bool isBridgeableObjectType(CanType type) {
16711854
// Metatypes aren't always trivially bridgeable unless they've been
16721855
// SIL-lowered to have an @objc representation.
@@ -1829,7 +2012,7 @@ static bool canOverride(CanType t1, CanType t2,
18292012
}
18302013

18312014
// Class-to-class.
1832-
return t2->isSuperclassOf(t1, resolver);
2015+
return t2->isExactSuperclassOf(t1, resolver);
18332016
}
18342017

18352018
bool TypeBase::canOverride(Type other, bool allowUnsafeParameterOverride,

lib/ClangImporter/ImportType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2413,7 +2413,7 @@ bool ClangImporter::Implementation::matchesNSObjectBound(Type type) {
24132413
return false;
24142414

24152415
// Class type or existential that inherits from NSObject.
2416-
if (NSObjectType->isSuperclassOf(type, getTypeResolver()))
2416+
if (NSObjectType->isExactSuperclassOf(type, getTypeResolver()))
24172417
return true;
24182418

24192419
// Struct or enum type must have been bridged.

lib/SIL/DynamicCasts.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,11 @@ swift::classifyDynamicCast(Module *M,
369369
auto targetClass = target.getClassOrBoundGenericClass();
370370
if (sourceClass) {
371371
if (targetClass) {
372-
if (target->isSuperclassOf(source, nullptr))
372+
if (target->isExactSuperclassOf(source, nullptr))
373373
return DynamicCastFeasibility::WillSucceed;
374-
if (source->isSuperclassOf(target, nullptr))
374+
if (target->isBindableToSuperclassOf(source, nullptr))
375+
return DynamicCastFeasibility::MaySucceed;
376+
if (source->isBindableToSuperclassOf(target, nullptr))
375377
return DynamicCastFeasibility::MaySucceed;
376378

377379
// FIXME: bridged types, e.g. CF <-> NS (but not for metatypes).
@@ -896,7 +898,7 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M,
896898
// If we statically know the target is an NSError subclass, then the cast
897899
// can go through the scalar path (and it's trivially true so can be
898900
// killed).
899-
return targetType->isSuperclassOf(objectType, nullptr);
901+
return targetType->isExactSuperclassOf(objectType, nullptr);
900902
}
901903

902904
// Three supported cases:

lib/SIL/SILVerifier.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,7 +2077,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
20772077
require(toCanTy.getClassOrBoundGenericClass(),
20782078
"downcast must convert to a class type");
20792079
require(SILType::getPrimitiveObjectType(fromCanTy).
2080-
isSuperclassOf(SILType::getPrimitiveObjectType(toCanTy)),
2080+
isBindableToSuperclassOf(SILType::getPrimitiveObjectType(toCanTy)),
20812081
"downcast must convert to a subclass");
20822082
}
20832083
}
@@ -2231,7 +2231,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
22312231
->getInstanceType());
22322232
require(instTy->getClassOrBoundGenericClass(),
22332233
"upcast must convert a class metatype to a class metatype");
2234-
require(instTy->isSuperclassOf(opInstTy, nullptr),
2234+
require(instTy->isExactSuperclassOf(opInstTy, nullptr),
22352235
"upcast must cast to a superclass or an existential metatype");
22362236
return;
22372237
}
@@ -2257,7 +2257,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
22572257

22582258
require(ToTy.getClassOrBoundGenericClass(),
22592259
"upcast must convert a class instance to a class type");
2260-
require(ToTy.isSuperclassOf(FromTy),
2260+
require(ToTy.isExactSuperclassOf(FromTy),
22612261
"upcast must cast to a superclass");
22622262
}
22632263

0 commit comments

Comments
 (0)