mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Type checker] Use DependentMemberType instead of type variables for nested types.
In the constraint solver, we've traditionally modeled nested type via a "type member" constraint of the form $T1 = $T0.NameOfTypeMember and treated $T1 as a type variable. While the solver did generally try to avoid attempting bindings for $T1 (it would wait until $T0 was bound, which solves the constraint), on occasion we would get weird behavior because the solver did try to bind the type variable. With this commit, model nested types via DependentMemberType, the same way we handle (e.g.) the nested type of a generic type parameter. This solution maintains more information (e.g., we know specifically which associated type we're referring to), fits in better with the type system (we know how to deal with dependent members throughout the type checker, AST, and so on), and is easier to reason able. This change is a performance optimization for the type checker for a few reasons. First, it reduces the number of type variables we need to deal with significantly (we create half as many type variables while type checking the standard library), and the solver scales poorly with the number of type variables because it visits all of the as-yet-unbound type variables at each solving step. Second, it eliminates a number of redundant by-name lookups in cases where we already know which associated type we want. Overall, this change provides a 25% speedup when type-checking the standard library.
This commit is contained in:
@@ -348,102 +348,28 @@ ConstraintLocator *ConstraintSystem::getConstraintLocator(
|
||||
return getConstraintLocator(anchor, path, builder.getSummaryFlags());
|
||||
}
|
||||
|
||||
TypeVariableType *
|
||||
ConstraintSystem::getMemberType(TypeVariableType *baseTypeVar,
|
||||
AssociatedTypeDecl *assocType,
|
||||
ConstraintLocatorBuilder locator,
|
||||
unsigned options) {
|
||||
return CG.getMemberType(baseTypeVar, assocType->getName(), [&]() {
|
||||
// FIXME: Premature associated type -> identifier mapping. We should
|
||||
// retain the associated type throughout.
|
||||
auto loc = getConstraintLocator(locator);
|
||||
auto memberTypeVar = createTypeVariable(loc, options);
|
||||
addTypeMemberConstraint(baseTypeVar, assocType->getName(),
|
||||
memberTypeVar, loc);
|
||||
return memberTypeVar;
|
||||
});
|
||||
Type ConstraintSystem::getMemberType(TypeVariableType *baseTypeVar,
|
||||
AssociatedTypeDecl *assocType,
|
||||
ConstraintLocatorBuilder locator,
|
||||
unsigned options) {
|
||||
return DependentMemberType::get(baseTypeVar, assocType);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Function object that retrieves a type variable corresponding to the
|
||||
/// given dependent type.
|
||||
class GetTypeVariable {
|
||||
ConstraintSystem &CS;
|
||||
ConstraintGraph &CG;
|
||||
ConstraintLocatorBuilder &Locator;
|
||||
|
||||
public:
|
||||
GetTypeVariable(ConstraintSystem &cs,
|
||||
ConstraintLocatorBuilder &locator)
|
||||
: CS(cs), CG(CS.getConstraintGraph()), Locator(locator) {}
|
||||
|
||||
TypeVariableType *operator()(Type base, AssociatedTypeDecl *member) {
|
||||
// FIXME: Premature associated type -> identifier mapping. We should
|
||||
// retain the associated type throughout.
|
||||
auto baseTypeVar = base->castTo<TypeVariableType>();
|
||||
return CG.getMemberType(baseTypeVar, member->getName(),
|
||||
[&]() -> TypeVariableType* {
|
||||
auto implArchetype = baseTypeVar->getImpl().getArchetype();
|
||||
if (!implArchetype) {
|
||||
// If the base type variable doesn't have an associated archetype,
|
||||
// just form the member constraint.
|
||||
// FIXME: Additional requirements?
|
||||
auto locator = CS.getConstraintLocator(
|
||||
Locator.withPathElement(member));
|
||||
auto memberTypeVar = CS.createTypeVariable(locator,
|
||||
TVO_PrefersSubtypeBinding);
|
||||
CS.addTypeMemberConstraint(baseTypeVar, member->getName(),
|
||||
memberTypeVar, locator);
|
||||
return memberTypeVar;
|
||||
}
|
||||
|
||||
|
||||
ArchetypeType::NestedType nestedType;
|
||||
ArchetypeType* archetype = nullptr;
|
||||
|
||||
if (implArchetype->hasNestedType(member->getName())) {
|
||||
nestedType = implArchetype->getNestedType(member->getName());
|
||||
archetype = nestedType.getValue()->getAs<ArchetypeType>();
|
||||
}
|
||||
|
||||
ConstraintLocator *locator;
|
||||
if (archetype) {
|
||||
locator = CS.getConstraintLocator(
|
||||
Locator.withPathElement(LocatorPathElt(archetype)));
|
||||
} else {
|
||||
// FIXME: Occurs when the nested type is a concrete type,
|
||||
// in which case it's quite silly to create a type variable at all.
|
||||
locator = CS.getConstraintLocator(Locator.withPathElement(member));
|
||||
}
|
||||
|
||||
auto memberTypeVar = CS.createTypeVariable(locator,
|
||||
TVO_PrefersSubtypeBinding);
|
||||
|
||||
// Bind the member's type variable as a type member of the base.
|
||||
CS.addTypeMemberConstraint(baseTypeVar, member->getName(),
|
||||
memberTypeVar, locator);
|
||||
|
||||
return memberTypeVar;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/// Function object that replaces all occurrences of archetypes and
|
||||
/// dependent types with type variables.
|
||||
class ReplaceDependentTypes {
|
||||
ConstraintSystem &cs;
|
||||
ConstraintLocatorBuilder &locator;
|
||||
llvm::DenseMap<CanType, TypeVariableType *> &replacements;
|
||||
GetTypeVariable &getTypeVariable;
|
||||
llvm::DenseMap<CanType, Type> dependentMemberReplacements;
|
||||
|
||||
public:
|
||||
ReplaceDependentTypes(
|
||||
ConstraintSystem &cs,
|
||||
ConstraintLocatorBuilder &locator,
|
||||
llvm::DenseMap<CanType, TypeVariableType *> &replacements,
|
||||
GetTypeVariable &getTypeVariable)
|
||||
: cs(cs), locator(locator), replacements(replacements),
|
||||
getTypeVariable(getTypeVariable) { }
|
||||
llvm::DenseMap<CanType, TypeVariableType *> &replacements)
|
||||
: cs(cs), locator(locator), replacements(replacements) { }
|
||||
|
||||
Type operator()(Type type) {
|
||||
// Swift only supports rank-1 polymorphism.
|
||||
@@ -469,15 +395,19 @@ namespace {
|
||||
// member of its base type.
|
||||
if (auto dependentMember = type->getAs<DependentMemberType>()) {
|
||||
// Check whether we've already dealt with this dependent member.
|
||||
auto known = replacements.find(dependentMember->getCanonicalType());
|
||||
if (known != replacements.end())
|
||||
auto known = dependentMemberReplacements.find(
|
||||
dependentMember->getCanonicalType());
|
||||
if (known != dependentMemberReplacements.end())
|
||||
return known->second;
|
||||
|
||||
// Replace archetypes in the base type.
|
||||
// FIXME: Tracking the dependent members seems unnecessary.
|
||||
if (auto base =
|
||||
((*this)(dependentMember->getBase()))->getAs<TypeVariableType>()) {
|
||||
auto result = getTypeVariable(base, dependentMember->getAssocType());
|
||||
replacements[dependentMember->getCanonicalType()] = result;
|
||||
auto result =
|
||||
DependentMemberType::get(base, dependentMember->getAssocType());
|
||||
dependentMemberReplacements[dependentMember->getCanonicalType()] =
|
||||
result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -538,10 +468,7 @@ Type ConstraintSystem::openType(
|
||||
Type startingType,
|
||||
ConstraintLocatorBuilder locator,
|
||||
llvm::DenseMap<CanType, TypeVariableType *> &replacements) {
|
||||
GetTypeVariable getTypeVariable{*this, locator};
|
||||
|
||||
ReplaceDependentTypes replaceDependentTypes(*this, locator, replacements,
|
||||
getTypeVariable);
|
||||
ReplaceDependentTypes replaceDependentTypes(*this, locator, replacements);
|
||||
return startingType.transform(replaceDependentTypes);
|
||||
}
|
||||
|
||||
@@ -675,6 +602,7 @@ Type ConstraintSystem::getFixedTypeRecursive(Type type,
|
||||
TypeMatchOptions &flags,
|
||||
bool wantRValue,
|
||||
bool retainParens) {
|
||||
|
||||
if (wantRValue)
|
||||
type = type->getRValueType();
|
||||
|
||||
@@ -686,17 +614,39 @@ Type ConstraintSystem::getFixedTypeRecursive(Type type,
|
||||
}
|
||||
}
|
||||
|
||||
while (auto typeVar = type->getAs<TypeVariableType>()) {
|
||||
if (auto fixed = getFixedType(typeVar)) {
|
||||
if (wantRValue)
|
||||
fixed = fixed->getRValueType();
|
||||
while (true) {
|
||||
if (auto depMemType = type->getAs<DependentMemberType>()) {
|
||||
if (!depMemType->getBase()->isTypeVariableOrMember()) return type;
|
||||
|
||||
type = fixed;
|
||||
// FIXME: Perform a more limited simplification?
|
||||
Type newType = simplifyType(type);
|
||||
if (newType.getPointer() == type.getPointer()) return type;
|
||||
|
||||
if (wantRValue)
|
||||
newType = newType->getRValueType();
|
||||
|
||||
type = newType;
|
||||
|
||||
// Once we've simplified a dependent member type, we need to generate a
|
||||
// new constraint.
|
||||
flags |= TMF_GenerateConstraints;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto typeVar = type->getAs<TypeVariableType>()) {
|
||||
if (auto fixed = getFixedType(typeVar)) {
|
||||
if (wantRValue)
|
||||
fixed = fixed->getRValueType();
|
||||
|
||||
type = fixed;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -983,9 +933,7 @@ void ConstraintSystem::openGeneric(
|
||||
replacements[gp->getCanonicalType()] = typeVar;
|
||||
}
|
||||
|
||||
GetTypeVariable getTypeVariable{*this, locator};
|
||||
ReplaceDependentTypes replaceDependentTypes(*this, locator, replacements,
|
||||
getTypeVariable);
|
||||
ReplaceDependentTypes replaceDependentTypes(*this, locator, replacements);
|
||||
|
||||
// Remember that any new constraints generated by opening this generic are
|
||||
// due to the opening.
|
||||
@@ -1569,6 +1517,51 @@ Type ConstraintSystem::simplifyType(Type type) {
|
||||
return tvt;
|
||||
}
|
||||
|
||||
// If this is a dependent member type for which we end up simplifying
|
||||
// the base to a non-type-variable, perform lookup.
|
||||
if (auto depMemTy = type->getAs<DependentMemberType>()) {
|
||||
// Simplify the base.
|
||||
Type newBase = simplifyType(depMemTy->getBase());
|
||||
if (!newBase) return type;
|
||||
|
||||
// If nothing changed, we're done.
|
||||
if (newBase.getPointer() == depMemTy->getBase().getPointer())
|
||||
return type;
|
||||
|
||||
Type lookupBaseType = newBase;
|
||||
|
||||
// Look through an inout type.
|
||||
if (auto inout = lookupBaseType->getAs<InOutType>())
|
||||
lookupBaseType = inout->getObjectType();
|
||||
|
||||
// Look through a metatype.
|
||||
if (auto metatype = lookupBaseType->getAs<AnyMetatypeType>())
|
||||
lookupBaseType = metatype->getInstanceType();
|
||||
|
||||
// If the new base is still something we can't handle, just build a
|
||||
// new dependent member type.
|
||||
if (lookupBaseType->is<TypeVariableType>() ||
|
||||
lookupBaseType->is<UnresolvedType>()) {
|
||||
if (auto assocType = depMemTy->getAssocType())
|
||||
return DependentMemberType::get(newBase, assocType);
|
||||
else
|
||||
return DependentMemberType::get(newBase, assocType);
|
||||
}
|
||||
|
||||
// Dependent member types should only be created for associated types.
|
||||
auto assocType = depMemTy->getAssocType();
|
||||
assert(depMemTy->getAssocType());
|
||||
|
||||
auto result = lookupBaseType->getTypeOfMember(
|
||||
DC->getParentModule(), assocType, &TC,
|
||||
assocType->getDeclaredInterfaceType());
|
||||
|
||||
// FIXME: Record failure somehow?
|
||||
if (!result) return type;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// If this is a FunctionType and we inferred new function attributes, apply
|
||||
// them.
|
||||
if (auto ft = dyn_cast<FunctionType>(type.getPointer())) {
|
||||
@@ -1589,6 +1582,7 @@ Type ConstraintSystem::simplifyType(Type type) {
|
||||
}
|
||||
|
||||
Type Solution::simplifyType(TypeChecker &tc, Type type) const {
|
||||
// FIXME: Nearly identical to ConstraintSystem::simplifyType().
|
||||
return type.transform([&](Type type) -> Type {
|
||||
if (auto tvt = dyn_cast<TypeVariableType>(type.getPointer())) {
|
||||
auto known = typeBindings.find(tvt);
|
||||
@@ -1596,6 +1590,36 @@ Type Solution::simplifyType(TypeChecker &tc, Type type) const {
|
||||
return known->second;
|
||||
}
|
||||
|
||||
// If this is a dependent member type for which we end up simplifying
|
||||
// the base to a non-type-variable, perform lookup.
|
||||
if (auto depMemTy = type->getAs<DependentMemberType>()) {
|
||||
// Simplify the base.
|
||||
Type newBase = simplifyType(tc, depMemTy->getBase());
|
||||
if (!newBase) return type;
|
||||
|
||||
// If nothing changed, we're done.
|
||||
if (newBase.getPointer() == depMemTy->getBase().getPointer())
|
||||
return type;
|
||||
|
||||
Type lookupBaseType = newBase;
|
||||
|
||||
// Look through an inout type.
|
||||
if (auto inout = lookupBaseType->getAs<InOutType>())
|
||||
lookupBaseType = inout->getObjectType();
|
||||
|
||||
// Look through a metatype.
|
||||
if (auto metatype = lookupBaseType->getAs<AnyMetatypeType>())
|
||||
lookupBaseType = metatype->getInstanceType();
|
||||
|
||||
// Dependent member types should only be created for associated types.
|
||||
auto assocType = depMemTy->getAssocType();
|
||||
assert(depMemTy->getAssocType());
|
||||
|
||||
return lookupBaseType->getTypeOfMember(
|
||||
getConstraintSystem().DC->getParentModule(), assocType, &tc,
|
||||
assocType->getDeclaredInterfaceType());
|
||||
}
|
||||
|
||||
// If this is a FunctionType and we inferred new function attributes, apply
|
||||
// them.
|
||||
if (auto ft = dyn_cast<FunctionType>(type.getPointer())) {
|
||||
|
||||
Reference in New Issue
Block a user