[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:
Doug Gregor
2016-11-02 16:32:40 -07:00
parent 245f995b5d
commit f168e7270c
17 changed files with 226 additions and 323 deletions

View File

@@ -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())) {