diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 297fb90ec37f9..b968976571d26 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -98,6 +98,61 @@ CheckTypeWitnessResult checkTypeWitness(Type type, AssociatedTypeDecl *assocType, NormalProtocolConformance *Conf); +/// Describes the means of inferring an abstract type witness. +enum class AbstractTypeWitnessKind : uint8_t { + /// The type witness was inferred via a same-type-to-concrete constraint + /// in a protocol requirement signature. + Fixed, + + /// The type witness was inferred via a defaulted associated type. + Default, + + /// The type witness was inferred to a generic parameter of the + /// conforming type. + GenericParam, +}; + +/// A type witness inferred without the aid of a specific potential +/// value witness. +class AbstractTypeWitness { + AbstractTypeWitnessKind Kind; + AssociatedTypeDecl *AssocType; + Type TheType; + + /// When this is a default type witness, the declaration responsible for it. + /// May not necessarilly match \c AssocType. + AssociatedTypeDecl *DefaultedAssocType; + + AbstractTypeWitness(AbstractTypeWitnessKind Kind, + AssociatedTypeDecl *AssocType, Type TheType, + AssociatedTypeDecl *DefaultedAssocType) + : Kind(Kind), AssocType(AssocType), TheType(TheType), + DefaultedAssocType(DefaultedAssocType) { + assert(AssocType && TheType); + } + +public: + static AbstractTypeWitness forFixed(AssociatedTypeDecl *assocType, Type type); + + static AbstractTypeWitness forDefault(AssociatedTypeDecl *assocType, + Type type, + AssociatedTypeDecl *defaultedAssocType); + + static AbstractTypeWitness forGenericParam(AssociatedTypeDecl *assocType, + Type type); + +public: + AbstractTypeWitnessKind getKind() const { return Kind; } + + AssociatedTypeDecl *getAssocType() const { return AssocType; } + + Type getType() const { return TheType; } + + AssociatedTypeDecl *getDefaultedAssocType() const { + return DefaultedAssocType; + } +}; + /// The set of associated types that have been inferred by matching /// the given value witness to its corresponding requirement. struct InferredAssociatedTypesByWitness { @@ -818,17 +873,17 @@ class AssociatedTypeInference { /// Compute the default type witness from an associated type default, /// if there is one. - Type computeDefaultTypeWitness(AssociatedTypeDecl *assocType); + Optional + computeDefaultTypeWitness(AssociatedTypeDecl *assocType); /// Compute the "derived" type witness for an associated type that is /// known to the compiler. std::pair computeDerivedTypeWitness(AssociatedTypeDecl *assocType); - /// Compute a type witness without using a specific potential witness, - /// e.g., using a fixed type (from a refined protocol), default type - /// on an associated type, or deriving the type. - Type computeAbstractTypeWitness(AssociatedTypeDecl *assocType); + /// Compute a type witness without using a specific potential witness. + Optional + computeAbstractTypeWitness(AssociatedTypeDecl *assocType); /// Substitute the current type witnesses into the given interface type. Type substCurrentTypeWitnesses(Type type); @@ -847,6 +902,15 @@ class AssociatedTypeInference { /// requirements of the given constrained extension. bool checkConstrainedExtension(ExtensionDecl *ext); + /// Validate the current tentative solution represented by \p typeWitnesses + /// and attempt to resolve abstract type witnesses for associated types that + /// could not be inferred otherwise. + /// + /// \returns \c nullptr, or the associated type that failed. + AssociatedTypeDecl * + completeSolution(ArrayRef unresolvedAssocTypes, + unsigned reqDepth); + /// Top-level operation to find solutions for the given unresolved /// associated types. void findSolutions( diff --git a/lib/Sema/TypeCheckProtocolInference.cpp b/lib/Sema/TypeCheckProtocolInference.cpp index 8bcad441e957e..823befcbacd9c 100644 --- a/lib/Sema/TypeCheckProtocolInference.cpp +++ b/lib/Sema/TypeCheckProtocolInference.cpp @@ -43,6 +43,25 @@ STATISTIC(NumDuplicateSolutionStates, using namespace swift; +AbstractTypeWitness AbstractTypeWitness::forFixed(AssociatedTypeDecl *assocType, + Type type) { + return AbstractTypeWitness(AbstractTypeWitnessKind::Fixed, assocType, type, + nullptr); +} + +AbstractTypeWitness +AbstractTypeWitness::forDefault(AssociatedTypeDecl *assocType, Type type, + AssociatedTypeDecl *defaultedAssocType) { + return AbstractTypeWitness(AbstractTypeWitnessKind::Default, assocType, type, + defaultedAssocType); +} + +AbstractTypeWitness +AbstractTypeWitness::forGenericParam(AssociatedTypeDecl *assocType, Type type) { + return AbstractTypeWitness(AbstractTypeWitnessKind::GenericParam, assocType, + type, nullptr); +} + void InferredAssociatedTypesByWitness::dump() const { dump(llvm::errs(), 0); } @@ -811,65 +830,24 @@ Type AssociatedTypeInference::computeFixedTypeWitness( return resultType; } -Type AssociatedTypeInference::computeDefaultTypeWitness( - AssociatedTypeDecl *assocType) { +Optional +AssociatedTypeInference::computeDefaultTypeWitness( + AssociatedTypeDecl *assocType) { // Go find a default definition. - auto defaultedAssocType = findDefaultedAssociatedType(assocType); - if (!defaultedAssocType) return Type(); - - // If we don't have a default definition, we're done. - auto selfType = proto->getSelfInterfaceType(); - - // Create a set of type substitutions for all known associated type. - // FIXME: Base this on dependent types rather than archetypes? - TypeSubstitutionMap substitutions; - substitutions[proto->mapTypeIntoContext(selfType) - ->castTo()] = dc->mapTypeIntoContext(adoptee); - for (auto assocType : proto->getAssociatedTypeMembers()) { - auto archetype = proto->mapTypeIntoContext( - assocType->getDeclaredInterfaceType()) - ->getAs(); - if (!archetype) - continue; - if (conformance->hasTypeWitness(assocType)) { - substitutions[archetype] = - dc->mapTypeIntoContext(conformance->getTypeWitness(assocType)); - } else { - auto known = typeWitnesses.begin(assocType); - if (known != typeWitnesses.end()) - substitutions[archetype] = known->first; - else - substitutions[archetype] = ErrorType::get(archetype); - } - } - - Type defaultType = defaultedAssocType->getDefaultDefinitionType(); + auto *const defaultedAssocType = findDefaultedAssociatedType(assocType); + if (!defaultedAssocType) + return None; + const Type defaultType = defaultedAssocType->getDefaultDefinitionType(); // FIXME: Circularity if (!defaultType) - return Type(); - - // Map it into our protocol's context. - defaultType = proto->mapTypeIntoContext(defaultType); - defaultType = defaultType.subst( - QueryTypeSubstitutionMap{substitutions}, - LookUpConformanceInModule(dc->getParentModule())); + return None; if (defaultType->hasError()) - return Type(); - - if (auto failed = checkTypeWitness(defaultType, assocType, conformance)) { - // Record the failure, if we haven't seen one already. - if (!failedDefaultedAssocType && !failed.isError()) { - failedDefaultedAssocType = defaultedAssocType; - failedDefaultedWitness = defaultType; - failedDefaultedResult = failed; - } - - return Type(); - } + return None; - return defaultType; + return AbstractTypeWitness::forDefault(assocType, defaultType, + defaultedAssocType); } std::pair @@ -900,27 +878,27 @@ AssociatedTypeInference::computeDerivedTypeWitness( return result; } -Type +Optional AssociatedTypeInference::computeAbstractTypeWitness( - AssociatedTypeDecl *assocType) { + AssociatedTypeDecl *assocType) { // We don't have a type witness for this associated type, so go // looking for more options. if (Type concreteType = computeFixedTypeWitness(assocType)) - return concreteType; + return AbstractTypeWitness::forFixed(assocType, concreteType); // If we can form a default type, do so. - if (Type defaultType = computeDefaultTypeWitness(assocType)) - return defaultType; + if (auto typeWitness = computeDefaultTypeWitness(assocType)) + return typeWitness; // If there is a generic parameter of the named type, use that. if (auto genericSig = dc->getGenericSignatureOfContext()) { for (auto gp : genericSig->getInnermostGenericParams()) { if (gp->getName() == assocType->getName()) - return dc->mapTypeIntoContext(gp); + return AbstractTypeWitness::forGenericParam(assocType, gp); } } - return Type(); + return None; } Type AssociatedTypeInference::substCurrentTypeWitnesses(Type type) { @@ -1064,7 +1042,8 @@ AssociatedTypeInference::getSubstOptionsWithCurrentTypeWitnesses() { if (auto *aliasTy = dyn_cast(type.getPointer())) type = aliasTy->getSinglyDesugaredType(); - return type->mapTypeOutOfContext().getPointer(); + return type->hasArchetype() ? type->mapTypeOutOfContext().getPointer() + : type.getPointer(); }; return options; } @@ -1149,6 +1128,81 @@ bool AssociatedTypeInference::checkConstrainedExtension(ExtensionDecl *ext) { llvm_unreachable("unhandled result"); } +AssociatedTypeDecl *AssociatedTypeInference::completeSolution( + ArrayRef unresolvedAssocTypes, unsigned reqDepth) { + // Examine the solution for errors and attempt to compute abstract type + // witnesses for associated types that are still lacking an entry. + llvm::SmallVector abstractTypeWitnesses; + for (auto *const assocType : unresolvedAssocTypes) { + const auto typeWitness = typeWitnesses.begin(assocType); + if (typeWitness != typeWitnesses.end()) { + // The solution contains an error. + if (typeWitness->first->hasError()) { + return assocType; + } + + continue; + } + + // Try to compute the type without the aid of a specific potential witness. + if (const auto &typeWitness = computeAbstractTypeWitness(assocType)) { + // Record the type witness immediately to make it available + // for substitutions into other tentative type witnesses. + typeWitnesses.insert(assocType, {typeWitness->getType(), reqDepth}); + + abstractTypeWitnesses.push_back(std::move(typeWitness.getValue())); + continue; + } + + // The solution is incomplete. + return assocType; + } + + // Check each abstract type witness we computed against the generic + // requirements on the corresponding associated type. + for (const auto &witness : abstractTypeWitnesses) { + Type type = witness.getType(); + if (type->hasTypeParameter()) { + if (witness.getKind() != AbstractTypeWitnessKind::GenericParam) { + // Replace type parameters with other known or tentative type witnesses. + type = type.subst( + [&](SubstitutableType *type) { + if (type->isEqual(proto->getSelfInterfaceType())) + return adoptee; + + return Type(); + }, + LookUpConformanceInModule(dc->getParentModule()), + getSubstOptionsWithCurrentTypeWitnesses()); + + // If the substitution produced an error, we're done. + if (type->hasError()) + return witness.getAssocType(); + } + type = dc->mapTypeIntoContext(type); + } + + if (const auto &failed = + checkTypeWitness(type, witness.getAssocType(), conformance)) { + // We failed to satisfy a requirement. If this is a default type + // witness failure and we haven't seen one already, write it down. + if (witness.getKind() == AbstractTypeWitnessKind::Default && + !failedDefaultedAssocType && !failed.isError()) { + failedDefaultedAssocType = witness.getDefaultedAssocType(); + failedDefaultedWitness = type; + failedDefaultedResult = std::move(failed); + } + + return witness.getAssocType(); + } + + // Update the solution entry. + typeWitnesses.insert(witness.getAssocType(), {type, reqDepth}); + } + + return nullptr; +} + void AssociatedTypeInference::findSolutions( ArrayRef unresolvedAssocTypes, SmallVectorImpl &solutions) { @@ -1173,39 +1227,14 @@ void AssociatedTypeInference::findSolutionsRec( // Introduce a hash table scope; we may add type witnesses here. TypeWitnessesScope typeWitnessesScope(typeWitnesses); - // Check for completeness of the solution - for (auto assocType : unresolvedAssocTypes) { - // Local function to record a missing associated type. - auto recordMissing = [&] { - if (!missingTypeWitness) - missingTypeWitness = assocType; - }; - - auto typeWitness = typeWitnesses.begin(assocType); - if (typeWitness != typeWitnesses.end()) { - // The solution contains an error. - if (typeWitness->first->hasError()) { - recordMissing(); - return; - } - - continue; - } - - // Try to compute the type without the aid of a specific potential - // witness. - if (Type type = computeAbstractTypeWitness(assocType)) { - if (type->hasError()) { - recordMissing(); - return; - } - - typeWitnesses.insert(assocType, {type, reqDepth}); - continue; - } + // Validate and complete the solution. + if (auto *const assocType = + completeSolution(unresolvedAssocTypes, reqDepth)) { + // The solution is decisively incomplete; record the associated type + // we failed on and bail out. + if (!missingTypeWitness) + missingTypeWitness = assocType; - // The solution is incomplete. - recordMissing(); return; } diff --git a/test/decl/protocol/req/associated_type_inference.swift b/test/decl/protocol/req/associated_type_inference.swift index 97c9d1136787c..0de5e8ec1b6bc 100644 --- a/test/decl/protocol/req/associated_type_inference.swift +++ b/test/decl/protocol/req/associated_type_inference.swift @@ -607,3 +607,90 @@ protocol SR_13172_P2 { enum SR_13172_E2: SR_13172_P2 { case bar // Okay } + +/** References to type parameters in type witnesses. */ + +// Circular reference through a fixed type witness. +protocol P35a { + associatedtype A = Array // expected-note {{protocol requires nested type 'A'}} + associatedtype B // expected-note {{protocol requires nested type 'B'}} +} +protocol P35b: P35a where B == A {} +// expected-error@+2 {{type 'S35' does not conform to protocol 'P35a'}} +// expected-error@+1 {{type 'S35' does not conform to protocol 'P35b'}} +struct S35: P35b {} + +// Circular reference through a value witness. +protocol P36a { + associatedtype A // expected-note {{protocol requires nested type 'A'}} + + func foo(arg: A) +} +protocol P36b: P36a { + associatedtype B = (Self) -> A // expected-note {{protocol requires nested type 'B'}} +} +// expected-error@+2 {{type 'S36' does not conform to protocol 'P36a'}} +// expected-error@+1 {{type 'S36' does not conform to protocol 'P36b'}} +struct S36: P36b { + func foo(arg: Array) {} +} + +// Test that we can resolve abstract type witnesses that reference +// other abstract type witnesses. +protocol P37 { + associatedtype A = Array + associatedtype B: Equatable = Never +} +struct S37: P37 {} + +protocol P38a { + associatedtype A = Never + associatedtype B: Equatable +} +protocol P38b: P38a where B == Array {} +struct S38: P38b {} + +protocol P39 where A: Sequence { + associatedtype A = Array + associatedtype B +} +struct S39: P39 {} + +// Test that we can handle an analogous complex case involving all kinds of +// type witness resolution. +protocol P40a { + associatedtype A + associatedtype B: P40a + + func foo(arg: A) +} +protocol P40b: P40a { + associatedtype C = (A, B.A, D.D, E) -> Self + associatedtype D: P40b + associatedtype E: Equatable +} +protocol P40c: P40b where D == S40 {} +struct S40: P40c { + func foo(arg: Never) {} + + typealias B = Self +} + +// Self is not treated as a fixed type witness. +protocol FIXME_P1a { + associatedtype A // expected-note {{protocol requires nested type 'A'}} +} +protocol FIXME_P1b: FIXME_P1a where A == Self {} +// expected-error@+2 {{type 'FIXME_S1' does not conform to protocol 'FIXME_P1a'}} +// expected-error@+1 {{type 'FIXME_S1' does not conform to protocol 'FIXME_P1b'}} +struct FIXME_S1: FIXME_P1b {} + +// Fails to find the fixed type witness B == FIXME_S2. +protocol FIXME_P2a { + associatedtype A: Equatable = Never // expected-note {{protocol requires nested type 'A'}} + associatedtype B: FIXME_P2a // expected-note {{protocol requires nested type 'B'}} +} +protocol FIXME_P2b: FIXME_P2a where B == FIXME_S2 {} +// expected-error@+2 {{type 'FIXME_S2' does not conform to protocol 'FIXME_P2a'}} +// expected-error@+1 {{type 'FIXME_S2' does not conform to protocol 'FIXME_P2b'}} +struct FIXME_S2: FIXME_P2b {}