mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
Merge pull request #5626 from DougGregor/dependent-type-variables
[Type checker] Use DependentMemberType instead of type variables for nested types.
This commit is contained in:
@@ -121,11 +121,6 @@ enum class KnownFoundationEntity {
|
||||
/// entity name.
|
||||
Optional<KnownFoundationEntity> getKnownFoundationEntity(StringRef name);
|
||||
|
||||
/// Callback function used when referring to a type member of a given
|
||||
/// type variable.
|
||||
typedef std::function<Type(TypeVariableType *, AssociatedTypeDecl *)>
|
||||
GetTypeVariableMemberCallback;
|
||||
|
||||
/// \brief Introduces a new constraint checker arena, whose lifetime is
|
||||
/// tied to the lifetime of this RAII object.
|
||||
class ConstraintCheckerArenaRAII {
|
||||
@@ -142,8 +137,7 @@ public:
|
||||
/// \param allocator The allocator used for allocating any data that
|
||||
/// goes into the constraint checker arena.
|
||||
ConstraintCheckerArenaRAII(ASTContext &self,
|
||||
llvm::BumpPtrAllocator &allocator,
|
||||
GetTypeVariableMemberCallback getTypeMember);
|
||||
llvm::BumpPtrAllocator &allocator);
|
||||
|
||||
ConstraintCheckerArenaRAII(const ConstraintCheckerArenaRAII &) = delete;
|
||||
ConstraintCheckerArenaRAII(ConstraintCheckerArenaRAII &&) = delete;
|
||||
@@ -519,13 +513,6 @@ public:
|
||||
const CanType TheIEEE128Type; /// 128-bit IEEE floating point
|
||||
const CanType ThePPC128Type; /// 128-bit PowerPC 2xDouble
|
||||
|
||||
/// Retrieve a type member of the given base type variable.
|
||||
///
|
||||
/// Note that this routine is only usable when a constraint system
|
||||
/// is active.
|
||||
Type getTypeVariableMemberType(TypeVariableType *baseTypeVar,
|
||||
AssociatedTypeDecl *assocType);
|
||||
|
||||
/// Adds a search path to SearchPathOpts, unless it is already present.
|
||||
///
|
||||
/// Does any proper bookkeeping to keep all module loaders up to date as well.
|
||||
|
||||
+13
-17
@@ -330,12 +330,8 @@ struct ASTContext::Implementation {
|
||||
/// The allocator used for all allocations within this arena.
|
||||
llvm::BumpPtrAllocator &Allocator;
|
||||
|
||||
/// Callback used to get a type member of a type variable.
|
||||
GetTypeVariableMemberCallback GetTypeMember;
|
||||
|
||||
ConstraintSolverArena(llvm::BumpPtrAllocator &allocator,
|
||||
GetTypeVariableMemberCallback &&getTypeMember)
|
||||
: Allocator(allocator), GetTypeMember(std::move(getTypeMember)) { }
|
||||
ConstraintSolverArena(llvm::BumpPtrAllocator &allocator)
|
||||
: Allocator(allocator) { }
|
||||
|
||||
ConstraintSolverArena(const ConstraintSolverArena &) = delete;
|
||||
ConstraintSolverArena(ConstraintSolverArena &&) = delete;
|
||||
@@ -369,14 +365,11 @@ ASTContext::Implementation::~Implementation() {
|
||||
}
|
||||
|
||||
ConstraintCheckerArenaRAII::
|
||||
ConstraintCheckerArenaRAII(ASTContext &self, llvm::BumpPtrAllocator &allocator,
|
||||
GetTypeVariableMemberCallback getTypeMember)
|
||||
ConstraintCheckerArenaRAII(ASTContext &self, llvm::BumpPtrAllocator &allocator)
|
||||
: Self(self), Data(self.Impl.CurrentConstraintSolverArena.release())
|
||||
{
|
||||
Self.Impl.CurrentConstraintSolverArena.reset(
|
||||
new ASTContext::Implementation::ConstraintSolverArena(
|
||||
allocator,
|
||||
std::move(getTypeMember)));
|
||||
new ASTContext::Implementation::ConstraintSolverArena(allocator));
|
||||
}
|
||||
|
||||
ConstraintCheckerArenaRAII::~ConstraintCheckerArenaRAII() {
|
||||
@@ -1164,12 +1157,6 @@ void ASTContext::setSubstitutions(TypeBase* type,
|
||||
boundGenericSubstitutions[{type, gpContext}] = Subs;
|
||||
}
|
||||
|
||||
Type ASTContext::getTypeVariableMemberType(TypeVariableType *baseTypeVar,
|
||||
AssociatedTypeDecl *assocType) {
|
||||
auto &arena = *Impl.CurrentConstraintSolverArena;
|
||||
return arena.GetTypeMember(baseTypeVar, assocType);
|
||||
}
|
||||
|
||||
void ASTContext::addSearchPath(StringRef searchPath, bool isFramework) {
|
||||
OptionSet<SearchPathKind> &loaded = Impl.SearchPathsSet[searchPath];
|
||||
auto kind = isFramework ? SearchPathKind::Framework : SearchPathKind::Import;
|
||||
@@ -1386,6 +1373,15 @@ ASTContext::getBehaviorConformance(Type conformingType,
|
||||
auto conformance = new (*this, AllocationArena::Permanent)
|
||||
NormalProtocolConformance(conformingType, conformingInterfaceType,
|
||||
protocol, loc, storage, state);
|
||||
|
||||
if (auto nominal = conformingInterfaceType->getAnyNominal()) {
|
||||
// Note: this is an egregious hack. The conformances need to be associated
|
||||
// with the actual storage declarations.
|
||||
SmallVector<ProtocolConformance *, 2> conformances;
|
||||
if (!nominal->lookupConformance(nominal->getModuleContext(), protocol,
|
||||
conformances))
|
||||
nominal->registerProtocolConformance(conformance);
|
||||
}
|
||||
return conformance;
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -586,28 +586,24 @@ TypeBase::gatherAllSubstitutions(Module *module,
|
||||
// The type itself contains substitutions up to the innermost
|
||||
// non-type context.
|
||||
CanType parent(canon);
|
||||
auto *parentDC = gpContext;
|
||||
ArrayRef<GenericTypeParamType *> genericParams =
|
||||
genericSig->getGenericParams();
|
||||
unsigned lastGenericIndex = genericParams.size();
|
||||
while (parent) {
|
||||
if (auto boundGeneric = dyn_cast<BoundGenericType>(parent)) {
|
||||
auto genericSig = parentDC->getGenericSignatureOfContext();
|
||||
unsigned index = 0;
|
||||
|
||||
assert(boundGeneric->getGenericArgs().size() ==
|
||||
genericSig->getInnermostGenericParams().size());
|
||||
|
||||
unsigned index = lastGenericIndex - boundGeneric->getGenericArgs().size();
|
||||
for (Type arg : boundGeneric->getGenericArgs()) {
|
||||
auto paramTy = genericSig->getInnermostGenericParams()[index++];
|
||||
auto paramTy = genericParams[index++];
|
||||
substitutions[paramTy->getCanonicalType().getPointer()] = arg;
|
||||
}
|
||||
lastGenericIndex -= boundGeneric->getGenericArgs().size();
|
||||
|
||||
parent = CanType(boundGeneric->getParent());
|
||||
parentDC = parentDC->getParent();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto nominal = dyn_cast<NominalType>(parent)) {
|
||||
parent = CanType(nominal->getParent());
|
||||
parentDC = parentDC->getParent();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -616,6 +612,9 @@ TypeBase::gatherAllSubstitutions(Module *module,
|
||||
|
||||
// Add forwarding substitutions from the outer context if we have
|
||||
// a type nested inside a generic function.
|
||||
auto *parentDC = gpContext;
|
||||
while (parentDC->isTypeContext())
|
||||
parentDC = parentDC->getParent();
|
||||
if (auto *outerEnv = parentDC->getGenericEnvironmentOfContext())
|
||||
for (auto pair : outerEnv->getInterfaceToArchetypeMap()) {
|
||||
auto result = substitutions.insert(pair);
|
||||
|
||||
+11
-8
@@ -521,7 +521,13 @@ public:
|
||||
Type visitParenType(ParenType *pt) {
|
||||
return ParenType::get(pt->getASTContext(), visit(pt->getUnderlyingType()));
|
||||
}
|
||||
|
||||
|
||||
Type visitSubstitutedType(SubstitutedType *st) {
|
||||
return SubstitutedType::get(st->getOriginal(),
|
||||
visit(st->getReplacementType()),
|
||||
st->getASTContext());
|
||||
}
|
||||
|
||||
Type visitType(TypeBase *t) {
|
||||
// Other types should not structurally contain lvalues.
|
||||
assert(!t->isLValueType()
|
||||
@@ -2774,13 +2780,10 @@ static Type getMemberForBaseType(ConformanceSource conformances,
|
||||
return getDependentMemberType(ErrorType::get(substBase));
|
||||
}
|
||||
|
||||
// If the parent is a type variable, retrieve its member type
|
||||
// variable.
|
||||
if (auto typeVarParent = substBase->getAs<TypeVariableType>()) {
|
||||
assert(assocType && "Missing associated type");
|
||||
return substBase->getASTContext().getTypeVariableMemberType(typeVarParent,
|
||||
assocType);
|
||||
}
|
||||
// If the parent is a type variable or a member rooted in a type variable,
|
||||
// we're done.
|
||||
if (substBase->isTypeVariableOrMember())
|
||||
return getDependentMemberType(substBase);
|
||||
|
||||
// Retrieve the member type with the given name.
|
||||
|
||||
|
||||
+6
-20
@@ -1690,17 +1690,11 @@ namespace {
|
||||
locator);
|
||||
|
||||
// Its subexpression should be convertible to a tuple (T.Element...).
|
||||
// FIXME: We should really go through the conformance above to extract
|
||||
// the element type, rather than just looking for the element type.
|
||||
// FIXME: Member constraint is still weird here.
|
||||
ConstraintLocatorBuilder builder(locator);
|
||||
auto arrayElementTy = CS.getMemberType(arrayTy, elementAssocTy,
|
||||
builder.withPathElement(
|
||||
ConstraintLocator::Member),
|
||||
/*options=*/0);
|
||||
Type arrayElementTy = DependentMemberType::get(arrayTy, elementAssocTy);
|
||||
|
||||
// Introduce conversions from each element to the element type of the
|
||||
// array.
|
||||
ConstraintLocatorBuilder builder(locator);
|
||||
unsigned index = 0;
|
||||
for (auto element : expr->getElements()) {
|
||||
CS.addConstraint(ConstraintKind::Conversion,
|
||||
@@ -1758,18 +1752,10 @@ namespace {
|
||||
|
||||
// Its subexpression should be convertible to a tuple ((T.Key,T.Value)...).
|
||||
ConstraintLocatorBuilder locatorBuilder(locator);
|
||||
auto dictionaryKeyTy = CS.getMemberType(dictionaryTy,
|
||||
keyAssocTy,
|
||||
locatorBuilder.withPathElement(
|
||||
ConstraintLocator::Member),
|
||||
/*options=*/0);
|
||||
/// FIXME: ArrayElementType is a total hack here.
|
||||
auto dictionaryValueTy = CS.getMemberType(dictionaryTy,
|
||||
valueAssocTy,
|
||||
locatorBuilder.withPathElement(
|
||||
ConstraintLocator::ArrayElementType),
|
||||
/*options=*/0);
|
||||
|
||||
auto dictionaryKeyTy = DependentMemberType::get(dictionaryTy,
|
||||
keyAssocTy);
|
||||
auto dictionaryValueTy = DependentMemberType::get(dictionaryTy,
|
||||
valueAssocTy);
|
||||
TupleTypeElt tupleElts[2] = { TupleTypeElt(dictionaryKeyTy),
|
||||
TupleTypeElt(dictionaryValueTy) };
|
||||
Type elementTy = TupleType::get(tupleElts, C);
|
||||
|
||||
+15
-6
@@ -1438,8 +1438,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
|
||||
}
|
||||
}
|
||||
|
||||
bool isTypeVarOrMember1 = desugar1->isTypeVariableOrMember();
|
||||
bool isTypeVarOrMember2 = desugar2->isTypeVariableOrMember();
|
||||
|
||||
llvm::SmallVector<RestrictionOrFix, 4> conversionsOrFixes;
|
||||
bool concrete = !typeVar1 && !typeVar2;
|
||||
bool concrete = !isTypeVarOrMember1 && !isTypeVarOrMember2;
|
||||
|
||||
// If this is an argument conversion, handle it directly. The rules are
|
||||
// different from normal conversions.
|
||||
@@ -1482,9 +1485,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
|
||||
return SolutionKind::Error;
|
||||
|
||||
case TypeKind::GenericTypeParam:
|
||||
case TypeKind::DependentMember:
|
||||
llvm_unreachable("unmapped dependent type in type checker");
|
||||
|
||||
case TypeKind::DependentMember:
|
||||
// Nothing we can solve.
|
||||
return formUnsolvedResult();
|
||||
|
||||
case TypeKind::TypeVariable:
|
||||
case TypeKind::Archetype:
|
||||
// Nothing to do here; handle type variables and archetypes below.
|
||||
@@ -2077,7 +2083,7 @@ commit_to_conversions:
|
||||
|
||||
// If we should attempt fixes, add those to the list. They'll only be visited
|
||||
// if there are no other possible solutions.
|
||||
if (shouldAttemptFixes() && !typeVar1 && !typeVar2 &&
|
||||
if (shouldAttemptFixes() && !isTypeVarOrMember1 && !isTypeVarOrMember2 &&
|
||||
!flags.contains(TMF_ApplyingFix) && kind >= ConstraintKind::Conversion) {
|
||||
Type objectType1 = type1->getRValueObjectType();
|
||||
|
||||
@@ -2136,8 +2142,10 @@ commit_to_conversions:
|
||||
}
|
||||
|
||||
if (conversionsOrFixes.empty()) {
|
||||
// If one of the types is a type variable, we leave this unsolved.
|
||||
if (typeVar1 || typeVar2) return formUnsolvedResult();
|
||||
// If one of the types is a type variable or member thereof, we leave this
|
||||
// unsolved.
|
||||
if (isTypeVarOrMember1 || isTypeVarOrMember2)
|
||||
return formUnsolvedResult();
|
||||
|
||||
return SolutionKind::Error;
|
||||
}
|
||||
@@ -2200,6 +2208,7 @@ ConstraintSystem::SolutionKind
|
||||
ConstraintSystem::simplifyConstructionConstraint(
|
||||
Type valueType, FunctionType *fnType, TypeMatchOptions flags,
|
||||
FunctionRefKind functionRefKind, ConstraintLocator *locator) {
|
||||
|
||||
// Desugar the value type.
|
||||
auto desugarValueType = valueType->getDesugaredType();
|
||||
|
||||
@@ -2223,10 +2232,10 @@ ConstraintSystem::simplifyConstructionConstraint(
|
||||
|
||||
case TypeKind::GenericFunction:
|
||||
case TypeKind::GenericTypeParam:
|
||||
case TypeKind::DependentMember:
|
||||
llvm_unreachable("unmapped dependent type");
|
||||
|
||||
case TypeKind::TypeVariable:
|
||||
case TypeKind::DependentMember:
|
||||
return SolutionKind::Unsolved;
|
||||
|
||||
case TypeKind::Tuple: {
|
||||
|
||||
+140
-14
@@ -15,6 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "ConstraintSystem.h"
|
||||
#include "ConstraintGraph.h"
|
||||
#include "swift/AST/TypeWalker.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
@@ -30,6 +31,7 @@ using namespace constraints;
|
||||
#define JOIN(X,Y) JOIN2(X,Y)
|
||||
#define JOIN2(X,Y) X##Y
|
||||
STATISTIC(NumSolutionAttempts, "# of solution attempts");
|
||||
STATISTIC(TotalNumTypeVariables, "# of type variables created");
|
||||
|
||||
#define CS_STATISTIC(Name, Description) \
|
||||
STATISTIC(JOIN2(Overall,Name), Description);
|
||||
@@ -42,6 +44,16 @@ STATISTIC(NumSolutionAttempts, "# of solution attempts");
|
||||
#include "ConstraintSolverStats.def"
|
||||
STATISTIC(LargestSolutionAttemptNumber, "# of the largest solution attempt");
|
||||
|
||||
TypeVariableType *ConstraintSystem::createTypeVariable(
|
||||
ConstraintLocator *locator,
|
||||
unsigned options) {
|
||||
++TotalNumTypeVariables;
|
||||
auto tv = TypeVariableType::getNew(TC.Context, assignTypeVariableID(),
|
||||
locator, options);
|
||||
addTypeVariable(tv);
|
||||
return tv;
|
||||
}
|
||||
|
||||
/// \brief Check whether the given type can be used as a binding for the given
|
||||
/// type variable.
|
||||
///
|
||||
@@ -633,6 +645,48 @@ namespace {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dump(TypeVariableType *typeVar, llvm::raw_ostream &out,
|
||||
unsigned indent) const {
|
||||
out.indent(indent);
|
||||
out << "(";
|
||||
if (typeVar)
|
||||
out << "$T" << typeVar->getImpl().getID();
|
||||
if (FullyBound)
|
||||
out << " fully_bound";
|
||||
if (SubtypeOfExistentialType)
|
||||
out << " subtype_of_existential";
|
||||
if (LiteralBinding != LiteralBindingKind::None)
|
||||
out << " literal=" << static_cast<int>(LiteralBinding);
|
||||
if (InvolvesTypeVariables)
|
||||
out << " involves_type_vars";
|
||||
if (NumDefaultableBindings > 0)
|
||||
out << " defaultable_bindings=" << NumDefaultableBindings;
|
||||
out << " bindings=";
|
||||
interleave(Bindings, [&](const PotentialBinding &binding) {
|
||||
auto type = binding.BindingType;
|
||||
auto &ctx = type->getASTContext();
|
||||
llvm::SaveAndRestore<bool>
|
||||
debugConstraints(ctx.LangOpts.DebugConstraintSolver, true);
|
||||
switch (binding.Kind) {
|
||||
case AllowedBindingKind::Exact:
|
||||
break;
|
||||
|
||||
case AllowedBindingKind::Subtypes:
|
||||
out << "(subtypes of) ";
|
||||
break;
|
||||
|
||||
case AllowedBindingKind::Supertypes:
|
||||
out << "(supertypes of) ";
|
||||
break;
|
||||
}
|
||||
if (binding.DefaultedProtocol)
|
||||
out << "(default from " << (*binding.DefaultedProtocol)->getName()
|
||||
<< ") ";
|
||||
out << type.getString();
|
||||
}, [&]() { out << " "; });
|
||||
out << ")\n";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -674,6 +728,37 @@ static bool shouldBindToValueType(Constraint *constraint)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the set of type variables that are inferrable from the given type.
|
||||
///
|
||||
/// \param type The type to search.
|
||||
/// \param typeVars Collects the type variables that are inferrable from the
|
||||
/// given type. This set is not cleared, so that multiple types can be explored
|
||||
/// and introduce their results into the same set.
|
||||
static void findInferrableTypeVars(
|
||||
Type type,
|
||||
SmallPtrSetImpl<TypeVariableType *> &typeVars) {
|
||||
type = type->getCanonicalType();
|
||||
if (!type->hasTypeVariable()) return;
|
||||
|
||||
class Walker : public TypeWalker {
|
||||
SmallPtrSetImpl<TypeVariableType *> &typeVars;
|
||||
public:
|
||||
explicit Walker(SmallPtrSetImpl<TypeVariableType *> &typeVars)
|
||||
: typeVars(typeVars) { }
|
||||
|
||||
virtual Action walkToTypePre(Type ty) override {
|
||||
if (ty->is<DependentMemberType>())
|
||||
return Action::SkipChildren;
|
||||
|
||||
if (auto typeVar = ty->getAs<TypeVariableType>())
|
||||
typeVars.insert(typeVar);
|
||||
return Action::Continue;
|
||||
}
|
||||
};
|
||||
|
||||
type.walk(Walker(typeVars));
|
||||
}
|
||||
|
||||
/// \brief Retrieve the set of potential type bindings for the given
|
||||
/// representative type variable, along with flags indicating whether
|
||||
/// those types should be opened.
|
||||
@@ -727,6 +812,8 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
|
||||
SmallVector<Constraint *, 2> defaultableConstraints;
|
||||
bool addOptionalSupertypeBindings = false;
|
||||
auto &tc = cs.getTypeChecker();
|
||||
bool hasNonDependentMemberRelationalConstraints = false;
|
||||
bool hasDependentMemberRelationalConstraints = false;
|
||||
for (auto constraint : constraints) {
|
||||
// Only visit each constraint once.
|
||||
if (!visitedConstraints.insert(constraint).second)
|
||||
@@ -761,8 +848,10 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
|
||||
case ConstraintKind::Defaultable:
|
||||
// Do these in a separate pass.
|
||||
if (cs.getFixedTypeRecursive(constraint->getFirstType(), true)
|
||||
->getAs<TypeVariableType>() == typeVar)
|
||||
->getAs<TypeVariableType>() == typeVar) {
|
||||
defaultableConstraints.push_back(constraint);
|
||||
hasNonDependentMemberRelationalConstraints = true;
|
||||
}
|
||||
continue;
|
||||
|
||||
case ConstraintKind::Disjunction:
|
||||
@@ -795,6 +884,7 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
|
||||
|
||||
// Note that we have a literal constraint with this protocol.
|
||||
literalProtocols.insert(constraint->getProtocol());
|
||||
hasNonDependentMemberRelationalConstraints = true;
|
||||
|
||||
// Handle unspecialized types directly.
|
||||
if (!defaultType->isUnspecializedGeneric()) {
|
||||
@@ -838,15 +928,23 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
|
||||
|
||||
case ConstraintKind::ApplicableFunction:
|
||||
case ConstraintKind::BindOverload: {
|
||||
if (result.FullyBound && result.InvolvesTypeVariables) continue;
|
||||
|
||||
// If this variable is in the left-hand side, it is fully bound.
|
||||
// FIXME: Can we avoid simplification here by walking the graph? Is it
|
||||
// worthwhile?
|
||||
if (ConstraintSystem::typeVarOccursInType(
|
||||
typeVar,
|
||||
cs.simplifyType(constraint->getFirstType()),
|
||||
&result.InvolvesTypeVariables)) {
|
||||
SmallPtrSet<TypeVariableType *, 4> typeVars;
|
||||
findInferrableTypeVars(cs.simplifyType(constraint->getFirstType()),
|
||||
typeVars);
|
||||
if (typeVars.count(typeVar))
|
||||
result.FullyBound = true;
|
||||
}
|
||||
|
||||
if (result.InvolvesTypeVariables) continue;
|
||||
|
||||
// If this and another type variable occur, this result involves
|
||||
// type variables.
|
||||
findInferrableTypeVars(cs.simplifyType(constraint->getSecondType()),
|
||||
typeVars);
|
||||
if (typeVars.size() > 1 && typeVars.count(typeVar))
|
||||
result.InvolvesTypeVariables = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -904,15 +1002,29 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
|
||||
kind = AllowedBindingKind::Supertypes;
|
||||
} else {
|
||||
// Can't infer anything.
|
||||
if (!result.InvolvesTypeVariables)
|
||||
ConstraintSystem::typeVarOccursInType(typeVar, first,
|
||||
&result.InvolvesTypeVariables);
|
||||
if (!result.InvolvesTypeVariables)
|
||||
ConstraintSystem::typeVarOccursInType(typeVar, second,
|
||||
&result.InvolvesTypeVariables);
|
||||
if (result.InvolvesTypeVariables) continue;
|
||||
|
||||
// Check whether both this type and another type variable are
|
||||
// inferrable.
|
||||
SmallPtrSet<TypeVariableType *, 4> typeVars;
|
||||
findInferrableTypeVars(first, typeVars);
|
||||
findInferrableTypeVars(second, typeVars);
|
||||
if (typeVars.size() > 1 && typeVars.count(typeVar))
|
||||
result.InvolvesTypeVariables = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the type we'd be binding to is a dependent member, don't try to
|
||||
// resolve this type variable yet.
|
||||
if (type->is<DependentMemberType>()) {
|
||||
if (!ConstraintSystem::typeVarOccursInType(
|
||||
typeVar, type, &result.InvolvesTypeVariables)) {
|
||||
hasDependentMemberRelationalConstraints = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
hasNonDependentMemberRelationalConstraints = true;
|
||||
|
||||
// Check whether we can perform this binding.
|
||||
// FIXME: this has a super-inefficient extraneous simplifyType() in it.
|
||||
bool isNilLiteral = false;
|
||||
@@ -1107,6 +1219,15 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
|
||||
}
|
||||
}
|
||||
|
||||
// If there were both dependent-member and non-dependent-member relational
|
||||
// constraints, consider this "fully bound"; we don't want to touch it.
|
||||
if (hasDependentMemberRelationalConstraints) {
|
||||
if (hasNonDependentMemberRelationalConstraints)
|
||||
result.FullyBound = true;
|
||||
else
|
||||
result.Bindings.clear();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2157,6 +2278,11 @@ bool ConstraintSystem::solveSimplified(
|
||||
if (!bindings)
|
||||
continue;
|
||||
|
||||
if (TC.getLangOpts().DebugConstraintSolver) {
|
||||
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
||||
bindings.dump(typeVar, log, solverState->depth * 2);
|
||||
}
|
||||
|
||||
// If these are the first bindings, or they are better than what
|
||||
// we saw before, use them instead.
|
||||
if (!bestTypeVar || bindings < bestBindings) {
|
||||
|
||||
@@ -215,19 +215,6 @@ void ConstraintGraphNode::removeFixedBinding(TypeVariableType *typeVar) {
|
||||
});
|
||||
}
|
||||
|
||||
TypeVariableType *
|
||||
ConstraintGraphNode::getMemberType(Identifier name,
|
||||
std::function<TypeVariableType *()> create) {
|
||||
auto known = MemberTypeIndex.find(name);
|
||||
if (known != MemberTypeIndex.end())
|
||||
return MemberTypes[known->second].second;
|
||||
|
||||
auto memberType = create();
|
||||
MemberTypeIndex.insert({name, MemberTypes.size()});
|
||||
MemberTypes.push_back({name, memberType});
|
||||
return memberType;
|
||||
}
|
||||
|
||||
#pragma mark Graph scope management
|
||||
ConstraintGraphScope::ConstraintGraphScope(ConstraintGraph &CG)
|
||||
: CG(CG), ParentScope(CG.ActiveScope), NumChanges(CG.Changes.size())
|
||||
@@ -292,16 +279,6 @@ ConstraintGraph::Change::boundTypeVariable(TypeVariableType *typeVar,
|
||||
return result;
|
||||
}
|
||||
|
||||
ConstraintGraph::Change
|
||||
ConstraintGraph::Change::addedMemberType(TypeVariableType *typeVar,
|
||||
Identifier name) {
|
||||
Change result;
|
||||
result.Kind = ChangeKind::AddedMemberType;
|
||||
result.MemberType.TypeVar = typeVar;
|
||||
result.MemberType.Name = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConstraintGraph::Change::undo(ConstraintGraph &cg) {
|
||||
/// Temporarily change the active scope to null, so we don't record
|
||||
/// any changes made while performing the undo operation.
|
||||
@@ -332,27 +309,6 @@ void ConstraintGraph::Change::undo(ConstraintGraph &cg) {
|
||||
case ChangeKind::BoundTypeVariable:
|
||||
cg.unbindTypeVariable(Binding.TypeVar, Binding.FixedType);
|
||||
break;
|
||||
|
||||
case ChangeKind::AddedMemberType: {
|
||||
auto &node = cg[MemberType.TypeVar];
|
||||
|
||||
// Erase the member type entry from the
|
||||
auto known = node.MemberTypeIndex.find(MemberType.Name);
|
||||
assert(known != node.MemberTypeIndex.end() && "Constraint graph corrupted");
|
||||
unsigned index = known->second;
|
||||
node.MemberTypeIndex.erase(known);
|
||||
|
||||
// If this was not the last member type, swap it with the last
|
||||
// member type.
|
||||
if (index != node.MemberTypes.size()-1) {
|
||||
node.MemberTypes[index] = node.MemberTypes.back();
|
||||
node.MemberTypeIndex[node.MemberTypes[index].first] = index;
|
||||
}
|
||||
|
||||
// Pop off the last member type.
|
||||
node.MemberTypes.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,21 +380,6 @@ void ConstraintGraph::removeConstraint(Constraint *constraint) {
|
||||
Changes.push_back(Change::removedConstraint(constraint));
|
||||
}
|
||||
|
||||
TypeVariableType *ConstraintGraph::getMemberType(
|
||||
TypeVariableType *typeVar,
|
||||
Identifier name,
|
||||
std::function<TypeVariableType *()> create) {
|
||||
auto repTypeVar = CS.getRepresentative(typeVar);
|
||||
auto &node = (*this)[repTypeVar];
|
||||
|
||||
return node.getMemberType(name, [&]() {
|
||||
auto memberTypeVar = create();
|
||||
if (ActiveScope)
|
||||
Changes.push_back(Change::addedMemberType(repTypeVar, name));
|
||||
return memberTypeVar;
|
||||
});
|
||||
}
|
||||
|
||||
void ConstraintGraph::mergeNodes(TypeVariableType *typeVar1,
|
||||
TypeVariableType *typeVar2) {
|
||||
assert(CS.getRepresentative(typeVar1) == CS.getRepresentative(typeVar2) &&
|
||||
@@ -462,44 +403,6 @@ void ConstraintGraph::mergeNodes(TypeVariableType *typeVar1,
|
||||
// Merge equivalence class from the non-representative type variable.
|
||||
auto &nonRepNode = (*this)[typeVarNonRep];
|
||||
repNode.addToEquivalenceClass(nonRepNode.getEquivalenceClassUnsafe());
|
||||
|
||||
// Merge member types.
|
||||
for (auto newEquivTypeVar : nonRepNode.getEquivalenceClassUnsafe()) {
|
||||
auto &newEquivNode = newEquivTypeVar == typeVarNonRep
|
||||
? nonRepNode
|
||||
: (*this)[newEquivTypeVar];
|
||||
for (auto memberType : newEquivNode.MemberTypes) {
|
||||
auto repKnown = repNode.MemberTypeIndex.find(memberType.first);
|
||||
if (repKnown == repNode.MemberTypeIndex.end()) {
|
||||
// We haven't seen this member type before. Add it.
|
||||
repNode.MemberTypeIndex.insert({memberType.first,
|
||||
repNode.MemberTypes.size()});
|
||||
repNode.MemberTypes.push_back(memberType);
|
||||
if (ActiveScope)
|
||||
Changes.push_back(Change::addedMemberType(typeVarRep,
|
||||
memberType.first));
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have seen this member before. If the type variables are
|
||||
// the same, do nothing. This is a fast-patch check.
|
||||
auto repMemberTypeVar = repNode.MemberTypes[repKnown->second].second;
|
||||
if (repMemberTypeVar == memberType.second)
|
||||
continue;
|
||||
|
||||
// Find the representatives for the member type variables.
|
||||
repMemberTypeVar = CS.getRepresentative(repMemberTypeVar);
|
||||
auto otherMemberTypeVar = CS.getRepresentative(memberType.second);
|
||||
|
||||
// If the representatives are equivalent, do nothing.
|
||||
if (repMemberTypeVar == otherMemberTypeVar)
|
||||
continue;
|
||||
|
||||
// We have two different type variables representing the same
|
||||
// member type; merge them.
|
||||
CS.mergeEquivalenceClasses(repMemberTypeVar, otherMemberTypeVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConstraintGraph::bindTypeVariable(TypeVariableType *typeVar, Type fixed) {
|
||||
@@ -513,6 +416,8 @@ void ConstraintGraph::bindTypeVariable(TypeVariableType *typeVar, Type fixed) {
|
||||
auto &node = (*this)[typeVar];
|
||||
for (auto otherTypeVar : typeVars) {
|
||||
if (knownTypeVars.insert(otherTypeVar).second) {
|
||||
if (typeVar == otherTypeVar) continue;
|
||||
|
||||
(*this)[otherTypeVar].addFixedBinding(typeVar);
|
||||
node.addFixedBinding(otherTypeVar);
|
||||
}
|
||||
@@ -925,19 +830,6 @@ void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) {
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
// Print member types.
|
||||
if (!MemberTypes.empty()) {
|
||||
out.indent(indent + 2);
|
||||
out << "Member types:\n";
|
||||
for (auto memberType : MemberTypes) {
|
||||
out.indent(indent + 4);
|
||||
out << memberType.first.str() << " -> ";
|
||||
memberType.second->print(out);
|
||||
out << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ConstraintGraphNode::dump() {
|
||||
@@ -1030,11 +922,6 @@ static void printValue(llvm::raw_ostream &os, unsigned value) {
|
||||
os << value;
|
||||
}
|
||||
|
||||
/// Print an identifier value.
|
||||
static void printValue(llvm::raw_ostream &os, Identifier value) {
|
||||
os << value.str();
|
||||
}
|
||||
|
||||
void ConstraintGraphNode::verify(ConstraintGraph &cg) {
|
||||
#define require(condition, complaint) _require(condition, complaint, cg, this)
|
||||
#define requireWithContext(condition, complaint, context) \
|
||||
@@ -1071,14 +958,6 @@ void ConstraintGraphNode::verify(ConstraintGraph &cg) {
|
||||
"adjacency information has higher degree than # of constraints");
|
||||
}
|
||||
|
||||
// Verify that the member types haven't gotten out of sync.
|
||||
for (auto index : MemberTypeIndex) {
|
||||
require(index.second < MemberTypes.size(),
|
||||
"member type index out-of-range");
|
||||
requireSameValue(index.first, MemberTypes[index.second].first,
|
||||
"member type index map provides wrong index into vector");
|
||||
}
|
||||
|
||||
// Based on the constraints we have, build up a representation of what
|
||||
// we expect the adjacencies to look like.
|
||||
llvm::DenseMap<TypeVariableType *, unsigned> expectedAdjacencies;
|
||||
|
||||
@@ -122,16 +122,6 @@ private:
|
||||
/// Remove a type variable from the fixed-binding relationship.
|
||||
void removeFixedBinding(TypeVariableType *typeVar);
|
||||
|
||||
/// Retrieves the member type of this type variable that corresponds
|
||||
/// to the given name.
|
||||
///
|
||||
/// \param name The name of the type member.
|
||||
///
|
||||
/// \param create If the member does not already exist, this
|
||||
/// callback will be invoked to create it.
|
||||
TypeVariableType *getMemberType(Identifier name,
|
||||
std::function<TypeVariableType *()> create);
|
||||
|
||||
/// The type variable this node represents.
|
||||
TypeVariableType *TypeVar;
|
||||
|
||||
@@ -158,14 +148,6 @@ private:
|
||||
/// are representatives of their equivalence classes.
|
||||
mutable SmallVector<TypeVariableType *, 2> EquivalenceClass;
|
||||
|
||||
/// A set of (name, type variable) pairs representing the member types of
|
||||
/// the given type variable.
|
||||
llvm::SmallVector<std::pair<Identifier, TypeVariableType *>, 2> MemberTypes;
|
||||
|
||||
/// A mapping from the names of members of this type variable to an
|
||||
/// index into the \c MemberTypes vector.
|
||||
llvm::SmallDenseMap<Identifier, unsigned> MemberTypeIndex;
|
||||
|
||||
/// Print this graph node.
|
||||
void print(llvm::raw_ostream &out, unsigned indent);
|
||||
|
||||
@@ -217,22 +199,6 @@ public:
|
||||
/// Remove a constraint from the graph.
|
||||
void removeConstraint(Constraint *constraint);
|
||||
|
||||
/// Retrieves the member type of a type variable that corresponds to
|
||||
/// the given name.
|
||||
///
|
||||
/// \param typeVar The type variable.
|
||||
///
|
||||
/// \param name The name of the type member.
|
||||
///
|
||||
/// \param create If the member does not already exist, this
|
||||
/// callback will be invoked to create it.
|
||||
///
|
||||
/// \returns the type variable representing the named member of the
|
||||
/// given type variable.
|
||||
TypeVariableType *getMemberType(TypeVariableType *typeVar,
|
||||
Identifier name,
|
||||
std::function<TypeVariableType *()> create);
|
||||
|
||||
/// Merge the two nodes for the two given type variables.
|
||||
///
|
||||
/// The type variables must actually have been merged already; this
|
||||
@@ -335,8 +301,6 @@ private:
|
||||
ExtendedEquivalenceClass,
|
||||
/// Added a fixed binding for a type variable.
|
||||
BoundTypeVariable,
|
||||
/// Added a member type.
|
||||
AddedMemberType
|
||||
};
|
||||
|
||||
/// A change made to the constraint graph.
|
||||
@@ -366,14 +330,6 @@ private:
|
||||
/// The fixed type to which the type variable was bound.
|
||||
TypeBase *FixedType;
|
||||
} Binding;
|
||||
|
||||
struct {
|
||||
/// The type variable whose member type was created.
|
||||
TypeVariableType *TypeVar;
|
||||
|
||||
/// The name of the member.
|
||||
Identifier Name;
|
||||
} MemberType;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -395,9 +351,6 @@ private:
|
||||
/// Create a change that bound a type variable to a fixed type.
|
||||
static Change boundTypeVariable(TypeVariableType *typeVar, Type fixed);
|
||||
|
||||
/// Create a change that added a member type with the given name.
|
||||
static Change addedMemberType(TypeVariableType *typeVar, Identifier name);
|
||||
|
||||
/// Undo this change, reverting the constraint graph to the state it
|
||||
/// had prior to this change.
|
||||
///
|
||||
|
||||
+118
-106
@@ -26,12 +26,7 @@ using namespace constraints;
|
||||
ConstraintSystem::ConstraintSystem(TypeChecker &tc, DeclContext *dc,
|
||||
ConstraintSystemOptions options)
|
||||
: TC(tc), DC(dc), Options(options),
|
||||
Arena(tc.Context, Allocator,
|
||||
[&](TypeVariableType *baseTypeVar, AssociatedTypeDecl *assocType) {
|
||||
return getMemberType(baseTypeVar, assocType,
|
||||
ConstraintLocatorBuilder(nullptr),
|
||||
/*options=*/0);
|
||||
}),
|
||||
Arena(tc.Context, Allocator),
|
||||
CG(*new ConstraintGraph(*this))
|
||||
{
|
||||
assert(DC && "context required");
|
||||
@@ -348,102 +343,21 @@ 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;
|
||||
});
|
||||
}
|
||||
|
||||
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 +383,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 +456,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 +590,7 @@ Type ConstraintSystem::getFixedTypeRecursive(Type type,
|
||||
TypeMatchOptions &flags,
|
||||
bool wantRValue,
|
||||
bool retainParens) {
|
||||
|
||||
if (wantRValue)
|
||||
type = type->getRValueType();
|
||||
|
||||
@@ -686,17 +602,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 +921,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 +1505,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 +1570,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 +1578,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())) {
|
||||
|
||||
@@ -1203,12 +1203,7 @@ public:
|
||||
|
||||
/// \brief Create a new type variable.
|
||||
TypeVariableType *createTypeVariable(ConstraintLocator *locator,
|
||||
unsigned options) {
|
||||
auto tv = TypeVariableType::getNew(TC.Context, assignTypeVariableID(),
|
||||
locator, options);
|
||||
addTypeVariable(tv);
|
||||
return tv;
|
||||
}
|
||||
unsigned options);
|
||||
|
||||
/// Retrieve the set of active type variables.
|
||||
ArrayRef<TypeVariableType *> getTypeVariables() const {
|
||||
@@ -1477,24 +1472,6 @@ public:
|
||||
InactiveConstraints.erase(constraint);
|
||||
}
|
||||
|
||||
/// Retrieve the type that corresponds to the given member of the
|
||||
/// given base type, which may be a newly-created type variable.
|
||||
///
|
||||
/// \param baseTypeVar The base type variable whose member is being queried.
|
||||
///
|
||||
/// \param assocType The associated type we're referencing.
|
||||
///
|
||||
/// \param locator The location used to describe this member access.
|
||||
///
|
||||
/// \param options Options to be supplied to type variable creation if
|
||||
/// a new type is created.
|
||||
///
|
||||
/// \returns the type variable representing the member type.
|
||||
TypeVariableType *getMemberType(TypeVariableType *baseTypeVar,
|
||||
AssociatedTypeDecl *assocType,
|
||||
ConstraintLocatorBuilder locator,
|
||||
unsigned options);
|
||||
|
||||
/// Retrieve the list of inactive constraints.
|
||||
ConstraintList &getConstraints() { return InactiveConstraints; }
|
||||
|
||||
|
||||
@@ -2023,28 +2023,39 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) {
|
||||
//
|
||||
// We will diagnose it later.
|
||||
if (!sequenceType->isExistentialType()) {
|
||||
auto member = cs.TC.lookupMemberType(cs.DC,
|
||||
sequenceType,
|
||||
tc.Context.Id_Iterator,
|
||||
lookupOptions);
|
||||
|
||||
if (member) {
|
||||
iteratorType = member.front().second;
|
||||
ASTContext &ctx = tc.Context;
|
||||
auto iteratorAssocType =
|
||||
cast<AssociatedTypeDecl>(
|
||||
sequenceProto->lookupDirect(ctx.Id_Iterator).front());
|
||||
|
||||
member = cs.TC.lookupMemberType(cs.DC,
|
||||
iteratorType,
|
||||
tc.Context.Id_Element,
|
||||
lookupOptions);
|
||||
iteratorType = sequenceType->getTypeOfMember(
|
||||
cs.DC->getParentModule(),
|
||||
iteratorAssocType,
|
||||
&tc,
|
||||
iteratorAssocType->getDeclaredInterfaceType());
|
||||
|
||||
if (member)
|
||||
elementType = member.front().second;
|
||||
if (iteratorType) {
|
||||
auto iteratorProto =
|
||||
tc.getProtocol(Stmt->getForLoc(),
|
||||
KnownProtocolKind::IteratorProtocol);
|
||||
if (!iteratorProto)
|
||||
return true;
|
||||
|
||||
auto elementAssocType =
|
||||
cast<AssociatedTypeDecl>(
|
||||
iteratorProto->lookupDirect(ctx.Id_Element).front());
|
||||
|
||||
elementType = iteratorType->getTypeOfMember(
|
||||
cs.DC->getParentModule(),
|
||||
elementAssocType,
|
||||
&tc,
|
||||
elementAssocType->getDeclaredInterfaceType());
|
||||
}
|
||||
}
|
||||
|
||||
// If the type lookup failed, just add some constraints we can
|
||||
// try to solve later.
|
||||
if (elementType.isNull()) {
|
||||
|
||||
// Determine the iterator type of the sequence.
|
||||
iteratorType = cs.createTypeVariable(Locator, /*options=*/0);
|
||||
cs.addTypeMemberConstraint(SequenceType, tc.Context.Id_Iterator,
|
||||
|
||||
@@ -158,3 +158,17 @@ func joinWithNil(s: String) {
|
||||
let a4 = [nil, "hello"]
|
||||
let _: Int = a4 // expected-error{{value of type '[String?]'}}
|
||||
}
|
||||
|
||||
struct OptionSetLike : ExpressibleByArrayLiteral {
|
||||
typealias Element = OptionSetLike
|
||||
init() { }
|
||||
|
||||
init(arrayLiteral elements: OptionSetLike...) { }
|
||||
|
||||
static let option: OptionSetLike = OptionSetLike()
|
||||
}
|
||||
|
||||
func testOptionSetLike(b: Bool) {
|
||||
let _: OptionSetLike = [ b ? [] : OptionSetLike.option, OptionSetLike.option]
|
||||
let _: OptionSetLike = [ b ? [] : .option, .option]
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ func rdar21078316() {
|
||||
|
||||
// <rdar://problem/20978044> QoI: Poor diagnostic when using an incorrect tuple element in a closure
|
||||
var numbers = [1, 2, 3]
|
||||
zip(numbers, numbers).filter { $0.2 > 1 } // expected-error {{value of tuple type '(Int, Int)' has no member '2'}}
|
||||
zip(numbers, numbers).filter { $0.2 > 1 } // expected-error {{contextual closure type '(Int, Int) -> Bool' expects 2 arguments, but 1 was used in closure body}}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ func <(lhs: IntWrapper, rhs: IntWrapper) -> Bool {
|
||||
prefix func ***(lhs: IntWrapper) {}
|
||||
|
||||
// This is provided as an operator but not implemented here.
|
||||
prefix operator ^^^ {}
|
||||
prefix operator ^^^
|
||||
|
||||
// CHECK-DAG: "ClassFromOtherFile"
|
||||
class Subclass : ClassFromOtherFile {}
|
||||
@@ -126,9 +126,9 @@ protocol ExpressibleByExtraFloatLiteral
|
||||
private protocol ExpressibleByExtraCharLiteral : ExpressibleByUnicodeScalarLiteral {
|
||||
}
|
||||
|
||||
prefix operator ~~~ {}
|
||||
prefix operator ~~~
|
||||
protocol ThreeTilde {
|
||||
prefix func ~~~(lhs: Self)
|
||||
prefix static func ~~~(lhs: Self)
|
||||
}
|
||||
|
||||
private struct ThreeTildeTypeImpl : ThreeTilde {
|
||||
@@ -141,7 +141,7 @@ func overloadedOnProto<T: ThreeTilde>(_: T) {}
|
||||
private prefix func ~~~(_: ThreeTildeTypeImpl) {}
|
||||
|
||||
// CHECK-DAG: - "~~~~"
|
||||
prefix operator ~~~~ {}
|
||||
prefix operator ~~~~
|
||||
protocol FourTilde {
|
||||
prefix static func ~~~~(arg: Self)
|
||||
}
|
||||
@@ -405,13 +405,9 @@ struct Sentinel2 {}
|
||||
// CHECK-DAG: - ["Ps25ExpressibleByFloatLiteral", ""]
|
||||
// CHECK-DAG: - !private ["Ps33ExpressibleByUnicodeScalarLiteral", ""]
|
||||
// CHECK-DAG: - !private ["Ps10Strideable", "Stride"]
|
||||
// CHECK-DAG: - !private ["Sa", "Element"]
|
||||
// CHECK-DAG: - !private ["Sa", "reduce"]
|
||||
// CHECK-DAG: - !private ["Sb", "_getBuiltinLogicValue"]
|
||||
// CHECK-DAG: - ["Sb", "InnerToBool"]
|
||||
// CHECK-DAG: - !private ["Vs10Dictionary", "Key"]
|
||||
// CHECK-DAG: - !private ["Vs10Dictionary", "Value"]
|
||||
// CHECK-DAG: - !private ["V4main17OtherFileIntArray", "Iterator"]
|
||||
// CHECK-DAG: - !private ["V4main17OtherFileIntArray", "deinit"]
|
||||
// CHECK-DAG: - !private ["V4main18OtherFileOuterType", "InnerType"]
|
||||
// CHECK-DAG: - !private ["VV4main18OtherFileOuterType9InnerType", "init"]
|
||||
@@ -419,9 +415,7 @@ struct Sentinel2 {}
|
||||
// CHECK-DAG: - !private ["VV4main26OtherFileSecretTypeWrapper10SecretType", "constant"]
|
||||
// CHECK-DAG: - !private ["V4main25OtherFileProtoImplementor", "deinit"]
|
||||
// CHECK-DAG: - !private ["V4main26OtherFileProtoImplementor2", "deinit"]
|
||||
// CHECK-DAG: - !private ["Vs13EmptyIterator", "Element"]
|
||||
// CHECK-DAG: - !private ["Vs13EmptyIterator", "init"]
|
||||
// CHECK-DAG: - !private ["Vs16IndexingIterator", "Element"]
|
||||
// CHECK-DAG: - ["O4main13OtherFileEnum", "Value"]
|
||||
// CHECK-DAG: - !private ["V4main20OtherFileEnumWrapper", "Enum"]
|
||||
|
||||
@@ -451,7 +445,6 @@ struct Sentinel2 {}
|
||||
// CHECK-DAG: !private "Ps10Strideable"
|
||||
// CHECK-DAG: !private "Sa"
|
||||
// CHECK-DAG: - "Sb"
|
||||
// CHECK-DAG: !private "Vs10Dictionary"
|
||||
// CHECK-DAG: !private "V4main17OtherFileIntArray"
|
||||
// CHECK-DAG: !private "V4main18OtherFileOuterType"
|
||||
// CHECK-DAG: !private "VV4main18OtherFileOuterType9InnerType"
|
||||
@@ -459,7 +452,6 @@ struct Sentinel2 {}
|
||||
// CHECK-DAG: !private "V4main25OtherFileProtoImplementor"
|
||||
// CHECK-DAG: !private "V4main26OtherFileProtoImplementor2"
|
||||
// CHECK-DAG: !private "Vs13EmptyIterator"
|
||||
// CHECK-DAG: !private "Vs16IndexingIterator"
|
||||
// CHECK-DAG: - "O4main13OtherFileEnum"
|
||||
// CHECK-DAG: !private "V4main20OtherFileEnumWrapper"
|
||||
// CHECK-DAG: !private "V4main20OtherFileEnumWrapper"
|
||||
|
||||
@@ -221,7 +221,7 @@ func testP4(_ s4a: S4a, s4b: S4b, s4c: S4c, s4d: S4d) {
|
||||
s4c.extP4Int() // okay
|
||||
var b1 = s4d.extP4a() // okay, "Bool" version
|
||||
b1 = true // checks type above
|
||||
s4d.extP4Int() // expected-error{{'Int' is not convertible to 'S4d.AssocP4' (aka 'Bool')}}
|
||||
s4d.extP4Int() // expected-error{{'Bool' is not convertible to 'Int'}}
|
||||
_ = b1
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ struct WrongIsEqual : IsEqualComparable { // expected-error{{type 'WrongIsEqual'
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
func existentialSequence(_ e: Sequence) { // expected-error{{has Self or associated type requirements}}
|
||||
var x = e.makeIterator() // expected-error{{'Sequence' is not convertible to '<<error type>>'}}
|
||||
var x = e.makeIterator() // expected-error{{'Sequence' is not convertible to 'Sequence.Iterator'}}
|
||||
x.next()
|
||||
x.nonexistent()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// RUN: %target-swift-frontend %s -parse -verify
|
||||
|
||||
var d = [String:String]()
|
||||
_ = "\(d.map{ [$0 : $0] })" // expected-error {{type of expression is ambiguous without more context}}
|
||||
_ = "\(d.map{ [$0 : $0] })" // expected-error {{contextual closure type specifies '(key: String, value: String)', but 1 was used in closure body, try adding extra parentheses around the single tuple argument}}
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@
|
||||
// See http://swift.org/LICENSE.txt for license information
|
||||
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
|
||||
// RUN: not --crash %target-swift-frontend %s -parse
|
||||
// RUN: not %target-swift-frontend %s -parse
|
||||
func f<T{{if true as[T.h
|
||||
+1
-2
@@ -5,6 +5,5 @@
|
||||
// See http://swift.org/LICENSE.txt for license information
|
||||
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
|
||||
// RUN: not --crash %target-swift-frontend %s -parse
|
||||
// REQUIRES: asserts
|
||||
// RUN: not %target-swift-frontend %s -parse
|
||||
func a<T{if let a<T:T.h{
|
||||
@@ -65,9 +65,8 @@ struct GoodIndexable : Indexable {
|
||||
}
|
||||
|
||||
|
||||
// expected-warning@+3 {{'Indexable' is deprecated: it will be removed in Swift 4.0. Please use 'Collection' instead}}
|
||||
// expected-error@+2 {{type 'BadIndexable1' does not conform to protocol '_IndexableBase'}}
|
||||
// expected-error@+1 {{type 'BadIndexable1' does not conform to protocol '_Indexable'}}
|
||||
// expected-warning@+2 {{'Indexable' is deprecated: it will be removed in Swift 4.0. Please use 'Collection' instead}}
|
||||
// expected-error@+1 {{type 'BadIndexable1' does not conform to protocol '_IndexableBase'}}
|
||||
struct BadIndexable1 : Indexable {
|
||||
func index(after i: Int) -> Int { return i + 1 }
|
||||
var startIndex: Int { return 0 }
|
||||
|
||||
Reference in New Issue
Block a user