From 204f6f2317033eb0d3ec91e8fca0f5a6b023d18e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 May 2017 22:35:58 -0700 Subject: [PATCH 1/4] Generalize TypeChecker::checkGenericArguments(). Make this routine suppress diagnostics when there is no source-location information (which allows it to be used as a query) and to distinguish the "we couldn't substitute into a requirement" failure mode from the "a requirement actually failed" failure mode. --- lib/Sema/TypeCheckGeneric.cpp | 42 +++++++++++++++++++--------------- lib/Sema/TypeCheckProtocol.cpp | 1 + lib/Sema/TypeCheckType.cpp | 1 + lib/Sema/TypeChecker.h | 7 ++++-- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 21a9721562993..ffecc97f4f83f 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -1246,11 +1246,12 @@ RequirementCheckResult TypeChecker::checkGenericArguments( LookupConformanceFn conformances, UnsatisfiedDependency *unsatisfiedDependency, ConformanceCheckOptions conformanceOptions, - GenericRequirementsCheckListener *listener) { + GenericRequirementsCheckListener *listener, + SubstOptions options) { bool valid = true; for (const auto &rawReq : genericSig->getRequirements()) { - auto req = rawReq.subst(substitutions, conformances); + auto req = rawReq.subst(substitutions, conformances, options); if (!req) { // Another requirement will fail later; just continue. valid = false; @@ -1286,6 +1287,7 @@ RequirementCheckResult TypeChecker::checkGenericArguments( switch (status) { case RequirementCheckResult::UnsatisfiedDependency: case RequirementCheckResult::Failure: + case RequirementCheckResult::SubstitutionFailure: // pass it on up. return status; case RequirementCheckResult::Success: @@ -1308,14 +1310,16 @@ RequirementCheckResult TypeChecker::checkGenericArguments( case RequirementKind::Superclass: // Superclass requirements. if (!isSubtypeOf(firstType, secondType, dc)) { - // FIXME: Poor source-location information. - diagnose(loc, diag::type_does_not_inherit, owner, firstType, - secondType); - - diagnose(noteLoc, diag::type_does_not_inherit_requirement, rawFirstType, - rawSecondType, - genericSig->gatherGenericParamBindingsText( - {rawFirstType, rawSecondType}, substitutions)); + if (loc.isValid()) { + // FIXME: Poor source-location information. + diagnose(loc, diag::type_does_not_inherit, owner, firstType, + secondType); + + diagnose(noteLoc, diag::type_does_not_inherit_requirement, + rawFirstType, rawSecondType, + genericSig->gatherGenericParamBindingsText( + {rawFirstType, rawSecondType}, substitutions)); + } return RequirementCheckResult::Failure; } @@ -1323,13 +1327,15 @@ RequirementCheckResult TypeChecker::checkGenericArguments( case RequirementKind::SameType: if (!firstType->isEqual(secondType)) { - // FIXME: Better location info for both diagnostics. - diagnose(loc, diag::types_not_equal, owner, firstType, secondType); - - diagnose(noteLoc, diag::types_not_equal_requirement, rawFirstType, - rawSecondType, - genericSig->gatherGenericParamBindingsText( - {rawFirstType, rawSecondType}, substitutions)); + if (loc.isValid()) { + // FIXME: Better location info for both diagnostics. + diagnose(loc, diag::types_not_equal, owner, firstType, secondType); + + diagnose(noteLoc, diag::types_not_equal_requirement, rawFirstType, + rawSecondType, + genericSig->gatherGenericParamBindingsText( + {rawFirstType, rawSecondType}, substitutions)); + } return RequirementCheckResult::Failure; } @@ -1339,5 +1345,5 @@ RequirementCheckResult TypeChecker::checkGenericArguments( if (valid) return RequirementCheckResult::Success; - return RequirementCheckResult::Failure; + return RequirementCheckResult::SubstitutionFailure; } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 2debb65376d1d..4cc93b6f3a0fa 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5453,6 +5453,7 @@ bool TypeChecker::useObjectiveCBridgeableConformancesOfArgs( case RequirementCheckResult::UnsatisfiedDependency: return true; case RequirementCheckResult::Failure: + case RequirementCheckResult::SubstitutionFailure: return false; case RequirementCheckResult::Success: { bool anyUnsatisfied = false; diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index a4288ee201b2a..374b280391734 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -764,6 +764,7 @@ Type TypeChecker::applyUnboundGenericArguments( case RequirementCheckResult::UnsatisfiedDependency: return Type(); case RequirementCheckResult::Failure: + case RequirementCheckResult::SubstitutionFailure: return ErrorType::get(Context); case RequirementCheckResult::Success: break; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index aa08242a42f80..01a421fbf5232 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -367,7 +367,9 @@ class GenericRequirementsCheckListener { }; /// The result of `checkGenericRequirement`. -enum class RequirementCheckResult { Success, Failure, UnsatisfiedDependency }; +enum class RequirementCheckResult { + Success, Failure, UnsatisfiedDependency, SubstitutionFailure +}; class ConformsToProtocolResult { Optional Data; @@ -1349,7 +1351,8 @@ class TypeChecker final : public LazyResolver { LookupConformanceFn conformances, UnsatisfiedDependency *unsatisfiedDependency, ConformanceCheckOptions conformanceOptions = ConformanceCheckFlags::Used, - GenericRequirementsCheckListener *listener = nullptr); + GenericRequirementsCheckListener *listener = nullptr, + SubstOptions options = None); /// Resolve the superclass of the given class. void resolveSuperclass(ClassDecl *classDecl) override; From dbb973aab40f1fe8d5725632d4d592ca9c75bfec Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 May 2017 22:38:53 -0700 Subject: [PATCH 2/4] [AST] Allow tentative type witnesses to be plumbed through SubstOptions. Extend SubstOptions, which controls how substitution is performed, to allow the caller to subst() to provide a callback function that may provide a type witness for a normal protocol conformance that is undergoing type witness inference. In effect, it's allowing us to provide tentative bindings for type witnesses so we can see the effects of substitution. --- include/swift/AST/ProtocolConformance.h | 18 ++++-- include/swift/AST/Type.h | 21 +++++- lib/AST/ProtocolConformance.cpp | 86 ++++++++++++++++++------- lib/AST/Type.cpp | 14 ++-- 4 files changed, 97 insertions(+), 42 deletions(-) diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index d0cada617afcc..2b5261583409b 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -145,13 +145,15 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// Retrieve the type witness for the given associated type. Type getTypeWitness(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const; + LazyResolver *resolver, + SubstOptions options = None) const; /// Retrieve the type witness and type decl (if one exists) /// for the given associated type. std::pair getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const; + LazyResolver *resolver, + SubstOptions options = None) const; /// Apply the given function object to each type witness within this /// protocol conformance. @@ -399,7 +401,8 @@ class NormalProtocolConformance : public ProtocolConformance, /// for the given associated type. std::pair getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const; + LazyResolver *resolver, + SubstOptions options = None) const; /// Determine whether the protocol conformance has a type witness for the /// given associated type. @@ -568,7 +571,8 @@ class SpecializedProtocolConformance : public ProtocolConformance, /// for the given associated type. std::pair getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const; + LazyResolver *resolver, + SubstOptions options = None) const; /// Given that the requirement signature of the protocol directly states /// that the given dependent type must conform to the given protocol, @@ -662,8 +666,10 @@ class InheritedProtocolConformance : public ProtocolConformance, /// for the given associated type. std::pair getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const { - return InheritedConformance->getTypeWitnessAndDecl(assocType, resolver); + LazyResolver *resolver, + SubstOptions options = None) const { + return InheritedConformance->getTypeWitnessAndDecl(assocType, resolver, + options); } /// Given that the requirement signature of the protocol directly states diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 8b84d2ff6e399..f64f04c6c2cd2 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -165,7 +165,26 @@ enum class SubstFlags { }; /// Options for performing substitutions into a type. -typedef OptionSet SubstOptions; +struct SubstOptions : public OptionSet { + typedef std::function + GetTentativeTypeWitness; + + /// Function that retrieves a tentative type witness for a protocol + /// conformance with the state \c CheckingTypeWitnesses. + GetTentativeTypeWitness getTentativeTypeWitness; + + SubstOptions(llvm::NoneType) : OptionSet(None) { } + + SubstOptions(SubstFlags flags) : OptionSet(flags) { } + + SubstOptions(OptionSet options) : OptionSet(options) { } + + SubstOptions(OptionSet options, + GetTentativeTypeWitness getTentativeTypeWitness) + : OptionSet(options), + getTentativeTypeWitness(std::move(getTentativeTypeWitness)) { } +}; inline SubstOptions operator|(SubstFlags lhs, SubstFlags rhs) { return SubstOptions(lhs) | rhs; diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 03bbe0a08382c..4dd8d70a1978c 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -215,13 +215,16 @@ ProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType, std::pair ProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const { - CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessAndDecl, (assocType, resolver)) + LazyResolver *resolver, + SubstOptions options) const { + CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessAndDecl, + (assocType, resolver, options)) } Type ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const { - return getTypeWitnessAndDecl(assocType, resolver).first; + LazyResolver *resolver, + SubstOptions options) const { + return getTypeWitnessAndDecl(assocType, resolver, options).first; } ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement, @@ -472,22 +475,39 @@ static bool resolveKnownTypeWitness(NormalProtocolConformance *conformance, std::pair NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, - LazyResolver *resolver) const { + LazyResolver *resolver, + SubstOptions options) const { if (Resolver) resolveLazyInfo(); + // Check whether we already have a type witness. auto known = TypeWitnesses.find(assocType); - if (known == TypeWitnesses.end()) { - PrettyStackTraceRequirement trace("resolving", this, assocType); - if (!resolveKnownTypeWitness(const_cast(this), - assocType)) { - assert(resolver && "Unable to resolve type witness"); - resolver->resolveTypeWitness(this, assocType); + if (known != TypeWitnesses.end()) + return known->second; + + // If this conformance is in a state where it is inferring type witnesses, + // check tentative witnesses. + if (getState() == ProtocolConformanceState::CheckingTypeWitnesses) { + // If there is a tentative-type-witness function, use it. + if (options.getTentativeTypeWitness) { + if (Type witnessType = options.getTentativeTypeWitness(this, assocType)) + return { witnessType, nullptr }; } - known = TypeWitnesses.find(assocType); - assert(known != TypeWitnesses.end() && "Didn't resolve witness?"); + + // Otherwise, we fail; this is the only case in which we can retturn a + // null type. + return { Type(), nullptr }; } + // Otherwise, resolve the type witness. + PrettyStackTraceRequirement trace("resolving", this, assocType); + if (!resolveKnownTypeWitness(const_cast(this), + assocType)) { + assert(resolver && "Unable to resolve type witness"); + resolver->resolveTypeWitness(this, assocType); + } + known = TypeWitnesses.find(assocType); + assert(known != TypeWitnesses.end() && "Didn't resolve witness?"); return known->second; } @@ -656,7 +676,8 @@ bool SpecializedProtocolConformance::hasTypeWitness( std::pair SpecializedProtocolConformance::getTypeWitnessAndDecl( AssociatedTypeDecl *assocType, - LazyResolver *resolver) const { + LazyResolver *resolver, + SubstOptions options) const { // If we've already created this type witness, return it. auto known = TypeWitnesses.find(assocType); if (known != TypeWitnesses.end()) { @@ -669,26 +690,41 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl( auto substitutionMap = genericSig->getSubstitutionMap(GenericSubstitutions); + // Local function to determine whether we will end up + auto normal = GenericConformance->getRootNormalConformance(); + auto isTentativeWitness = [&] { + if (normal->getState() != ProtocolConformanceState::CheckingTypeWitnesses) + return false; + + return !normal->hasTypeWitness(assocType, nullptr); + }; + auto genericWitnessAndDecl - = GenericConformance->getTypeWitnessAndDecl(assocType, resolver); + = GenericConformance->getTypeWitnessAndDecl(assocType, resolver, options); + + auto genericWitness = genericWitnessAndDecl.first; + if (!genericWitness) + return { Type(), nullptr }; - auto &genericWitness = genericWitnessAndDecl.first; auto *typeDecl = genericWitnessAndDecl.second; // Apply the substitution we computed above auto specializedType - = genericWitness.subst(substitutionMap); - if (!specializedType) - specializedType = ErrorType::get(genericWitness); + = genericWitness.subst(substitutionMap, options); + if (!specializedType) { + if (isTentativeWitness()) + return { Type(), nullptr }; - // If the type witness was unchanged, just copy it directly. - if (specializedType.getPointer() == genericWitness.getPointer()) { - TypeWitnesses[assocType] = genericWitnessAndDecl; - return TypeWitnesses[assocType]; + specializedType = ErrorType::get(genericWitness); } - TypeWitnesses[assocType] = std::make_pair(specializedType, typeDecl); - return TypeWitnesses[assocType]; + // If we aren't in a case where we used the tentative type witness + // information, cache the result. + auto specializedWitnessAndDecl = std::make_pair(specializedType, typeDecl); + if (!isTentativeWitness() && !specializedType->hasError()) + TypeWitnesses[assocType] = specializedWitnessAndDecl; + + return specializedWitnessAndDecl; } ProtocolConformanceRef diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 068aa680fc118..20c61c401111c 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2909,17 +2909,11 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, if (!conformance) return failed(); if (!conformance->isConcrete()) return failed(); - // If we have an unsatisfied type witness while we're checking the - // conformances we're supposed to skip this conformance's unsatisfied type - // witnesses, and we have an unsatisfied type witness, return - // "missing". - if (conformance->getConcrete()->getRootNormalConformance()->getState() - == ProtocolConformanceState::CheckingTypeWitnesses && - !conformance->getConcrete()->hasTypeWitness(assocType, nullptr)) - return failed(); - + // Retrieve the type witness. auto witness = - conformance->getConcrete()->getTypeWitness(assocType, resolver); + conformance->getConcrete()->getTypeWitness(assocType, resolver, options); + if (!witness) + return failed(); // This is a hacky feature allowing code completion to migrate to // using Type::subst() without changing output. From 3dbfa9789f91abe56e1403d7f428722d993140b7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 May 2017 22:51:15 -0700 Subject: [PATCH 3/4] [Associated type inference] Check solutions against full requirement signature When we have a potential assignment of associated types to type witnesses during associated type inference, check that set of type witnesses against the requirements in the requirement signature, so that we can reject any solutions that fail some of the protocol's requirements. This is most of rdar://problem/31830524 --- but gets thwarted by the inability of associated type inference to work across multiple protocols. --- lib/Sema/TypeCheckProtocol.cpp | 53 ++++++++++++++++--- test/Constraints/associated_types.swift | 6 +-- .../associated_type_where_clause.swift | 45 ++++++++++++++++ test/decl/protocol/conforms/failure.swift | 6 +-- ...ericenv-nullptr-too-much-circularity.swift | 2 +- 5 files changed, 97 insertions(+), 15 deletions(-) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/28737-genericenv-nullptr-too-much-circularity.swift (88%) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 4cc93b6f3a0fa..1ab81a136d274 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4012,6 +4012,7 @@ void ConformanceChecker::resolveTypeWitnesses() { typedef decltype(typeWitnesses)::ScopeTy TypeWitnessesScope; unsigned numTypeWitnesses = 0; SmallVector solutions; + SmallVector nonViableSolutions; // Information to use for diagnosing failures when we don't have // something more specific. @@ -4194,13 +4195,44 @@ void ConformanceChecker::resolveTypeWitnesses() { if (replaced.isNull()) return true; - if (checkTypeWitness(TC, DC, assocType, replaced)) - return true; - known->first = replaced; } } + // Check any same-type requirements in the protocol's requirement signature. + if (Proto->isRequirementSignatureComputed()) { + SubstOptions options(None); + options.getTentativeTypeWitness = + [&](const NormalProtocolConformance *conformance, + AssociatedTypeDecl *assocType) -> Type { + if (conformance != Conformance) return Type(0); + + return typeWitnesses.begin(assocType)->first; + }; + + auto substitutions = + SubstitutionMap::getProtocolSubstitutions( + Proto, Conformance->getType(), + ProtocolConformanceRef(Conformance)); + + auto requirementSig = Proto->getRequirementSignature(); + auto result = + TC.checkGenericArguments(DC, SourceLoc(), SourceLoc(), + Conformance->getType(), requirementSig, + QuerySubstitutionMap{substitutions}, + TypeChecker::LookUpConformance( + TC, Conformance->getDeclContext()), + nullptr, None, nullptr, options); + switch (result) { + case RequirementCheckResult::Failure: + case RequirementCheckResult::UnsatisfiedDependency: + return true; + + case RequirementCheckResult::Success: + case RequirementCheckResult::SubstitutionFailure: + return false; + } + } return false; }; @@ -4262,9 +4294,7 @@ void ConformanceChecker::resolveTypeWitnesses() { } /// Check the current set of type witnesses. - if (checkCurrentTypeWitnesses()) { - return; - } + bool invalid = checkCurrentTypeWitnesses(); // Determine whether there is already a solution with the same // bindings. @@ -4284,8 +4314,9 @@ void ConformanceChecker::resolveTypeWitnesses() { return; } - solutions.push_back(InferredTypeWitnessesSolution()); - auto &solution = solutions.back(); + auto &solutionList = invalid ? nonViableSolutions : solutions; + solutionList.push_back(InferredTypeWitnessesSolution()); + auto &solution = solutionList.back(); // Copy the type witnesses. for (auto assocType : unresolvedAssocTypes) { @@ -4479,6 +4510,12 @@ void ConformanceChecker::resolveTypeWitnesses() { } } + // If we have no solution, but we did find something that is nonviable, + // use the first nonviable one to improve error reporting. + if (solutions.empty() && !nonViableSolutions.empty()) { + solutions.push_back(std::move(nonViableSolutions.front())); + } + // If we found a single solution, take it. if (solutions.size() == 1) { // Record each of the deduced witnesses. diff --git a/test/Constraints/associated_types.swift b/test/Constraints/associated_types.swift index 6dee6f6a07a2f..fda25688dea0c 100644 --- a/test/Constraints/associated_types.swift +++ b/test/Constraints/associated_types.swift @@ -62,8 +62,8 @@ protocol XReqt {} protocol YReqt {} protocol SameTypedDefaultWithReqts { - associatedtype X: XReqt // expected-note{{}} - associatedtype Y: YReqt // expected-note{{}} + associatedtype X: XReqt + associatedtype Y: YReqt static var x: X { get } static var y: Y { get } } @@ -86,7 +86,7 @@ struct UsesSameTypedDefaultWithoutSatisfyingReqts: SameTypedDefaultWithReqts { } protocol SameTypedDefaultBaseWithReqts { - associatedtype X: XReqt // expected-note{{}} + associatedtype X: XReqt static var x: X { get } } protocol SameTypedDefaultDerivedWithReqts: SameTypedDefaultBaseWithReqts { diff --git a/test/Generics/associated_type_where_clause.swift b/test/Generics/associated_type_where_clause.swift index a94d6f079e244..5c936e15de444 100644 --- a/test/Generics/associated_type_where_clause.swift +++ b/test/Generics/associated_type_where_clause.swift @@ -157,3 +157,48 @@ protocol P4 { protocol P5: P3, P4 { associatedtype B where B == A? } + +// Associated type inference should account for where clauses. +protocol P6 { + associatedtype A +} + +struct X1 { } + +struct X2 { } + +struct Y1 : P6 { + typealias A = X1 +} + +struct Y2 : P6 { + typealias A = X2 +} + +protocol P7 { + associatedtype B: P6 // expected-note{{ambiguous inference of associated type 'B': 'Y1' vs. 'Y2'}} + associatedtype C: P6 where B.A == C.A + + func getB() -> B + func getC() -> C +} + +struct Z1 : P7 { + func getB() -> Y1 { return Y1() } + func getB() -> Y2 { return Y2() } + + func getC() -> Y1 { return Y1() } +} + +func testZ1(z1: Z1) { + let _: Z1.C = Y1() +} + + +struct Z2 : P7 { // expected-error{{type 'Z2' does not conform to protocol 'P7'}} + func getB() -> Y1 { return Y1() } // expected-note{{matching requirement 'getB()' to this declaration inferred associated type to 'Y1'}} + func getB() -> Y2 { return Y2() } // expected-note{{matching requirement 'getB()' to this declaration inferred associated type to 'Y2'}} + + func getC() -> Y1 { return Y1() } + func getC() -> Y2 { return Y2() } +} diff --git a/test/decl/protocol/conforms/failure.swift b/test/decl/protocol/conforms/failure.swift index e50db4925cb65..da58c1ecd9ad1 100644 --- a/test/decl/protocol/conforms/failure.swift +++ b/test/decl/protocol/conforms/failure.swift @@ -75,9 +75,9 @@ struct P5Conformer : P5 { // expected-error {{does not conform}} protocol P6Base { - associatedtype Foo // expected-note {{protocol requires nested type 'Foo'; do you want to add it?}} + associatedtype Foo func foo() - func bar() -> Foo + func bar() -> Foo // expected-note{{protocol requires function 'bar()' with type '() -> P6Conformer.Bar?'; do you want to add a stub?}} } extension P6Base { } @@ -85,7 +85,7 @@ protocol P6 : P6Base { associatedtype Bar // expected-note {{protocol requires nested type 'Bar'}} } extension P6 { - func bar() -> Bar? { return nil } + func bar() -> Bar? { return nil } // expected-note{{candidate has non-matching type ' () -> Self.Bar?' [with Foo = P6Conformer.Bar?]}} } struct P6Conformer : P6 { // expected-error 2 {{does not conform}} diff --git a/validation-test/compiler_crashers/28737-genericenv-nullptr-too-much-circularity.swift b/validation-test/compiler_crashers_fixed/28737-genericenv-nullptr-too-much-circularity.swift similarity index 88% rename from validation-test/compiler_crashers/28737-genericenv-nullptr-too-much-circularity.swift rename to validation-test/compiler_crashers_fixed/28737-genericenv-nullptr-too-much-circularity.swift index e5f6419f82dbd..804d8aae79c88 100644 --- a/validation-test/compiler_crashers/28737-genericenv-nullptr-too-much-circularity.swift +++ b/validation-test/compiler_crashers_fixed/28737-genericenv-nullptr-too-much-circularity.swift @@ -6,5 +6,5 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // REQUIRES: asserts -// RUN: not --crash %target-swift-frontend %s -emit-ir +// RUN: not %target-swift-frontend %s -emit-ir protocol A:a{typealias e}class a:A{typealias e:S From ca493117566a7d7f7c0d894e13e7c9e064ae2034 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 4 May 2017 00:42:10 -0700 Subject: [PATCH 4/4] [WIP] [Stdlib] Eliminate SubSequence from _Indexable. This is intended to fix ABI FIXMEs #178 and #180, as well as rdar://problem/31830524, given that rdar://25214066 is mostly fixed, but we still run into associated type inference issues. Sigh. --- stdlib/public/SDK/Dispatch/Data.swift | 1 + stdlib/public/core/Collection.swift | 14 ---------- .../core/CollectionAlgorithms.swift.gyb | 6 +---- stdlib/public/core/MutableCollection.swift | 24 ----------------- stdlib/public/core/Range.swift.gyb | 4 +-- stdlib/public/core/StringCharacterView.swift | 2 ++ .../stdlib/CollectionType.swift.gyb | 26 +++++++++++++++++++ 7 files changed, 32 insertions(+), 45 deletions(-) diff --git a/stdlib/public/SDK/Dispatch/Data.swift b/stdlib/public/SDK/Dispatch/Data.swift index b419d7f0f10b8..c788816e59f6c 100644 --- a/stdlib/public/SDK/Dispatch/Data.swift +++ b/stdlib/public/SDK/Dispatch/Data.swift @@ -299,6 +299,7 @@ public struct DispatchData : RandomAccessCollection, _ObjectiveCBridgeable { } public struct DispatchDataIterator : IteratorProtocol, Sequence { + public typealias Element = UInt8 /// Create an iterator over the given DispatchData public init(_data: DispatchData) { diff --git a/stdlib/public/core/Collection.swift b/stdlib/public/core/Collection.swift index 4d86d9c65d008..e72c48ac03d1f 100644 --- a/stdlib/public/core/Collection.swift +++ b/stdlib/public/core/Collection.swift @@ -88,20 +88,6 @@ public protocol _IndexableBase { /// /// - Complexity: O(1) subscript(position: Index) -> _Element { get } - - // WORKAROUND: rdar://25214066 - // FIXME(ABI)#178 (Type checker) - /// A sequence that represents a contiguous subrange of the collection's - /// elements. - associatedtype SubSequence - - /// Accesses the subsequence bounded by the given range. - /// - /// - Parameter bounds: A range of the collection's indices. The upper and - /// lower bounds of the range must be valid indices of the collection. - /// - /// - Complexity: O(1) - subscript(bounds: Range) -> SubSequence { get } /// Performs a range check in O(1), or a no-op when a range check is not /// implementable in O(1). diff --git a/stdlib/public/core/CollectionAlgorithms.swift.gyb b/stdlib/public/core/CollectionAlgorithms.swift.gyb index a9077db2c6656..795a651b621aa 100644 --- a/stdlib/public/core/CollectionAlgorithms.swift.gyb +++ b/stdlib/public/core/CollectionAlgorithms.swift.gyb @@ -475,7 +475,7 @@ ${orderingExplanation} } } -% for Self in '_Indexable', '_MutableIndexable': +% for Self in 'Collection', 'MutableCollection': %{ subscriptCommentPre = """\ @@ -511,8 +511,6 @@ subscriptCommentPost = """\ /// - Parameter bounds: A range of the collection's indices. The bounds of /// the range must be valid indices of the collection.""" }% -// FIXME(ABI)#180 (Type checker) -// WORKAROUND rdar://25214066 - should be on Collection extension ${Self} { ${subscriptCommentPre} ${subscriptCommentMid} @@ -540,8 +538,6 @@ ${subscriptCommentPost} } } -// FIXME(ABI)#180 (Type checker) -// WORKAROUND rdar://25214066 - should be on Collection extension ${Self} where Index : Strideable, Index.Stride : SignedInteger { ${subscriptCommentPre} ${subscriptCommentMid} diff --git a/stdlib/public/core/MutableCollection.swift b/stdlib/public/core/MutableCollection.swift index a1ae475bfe99a..bee79e54a0eaa 100644 --- a/stdlib/public/core/MutableCollection.swift +++ b/stdlib/public/core/MutableCollection.swift @@ -68,30 +68,6 @@ public protocol _MutableIndexable : _Indexable { /// must be a valid index of the collection that is not equal to the /// `endIndex` property. subscript(position: Index) -> _Element { get set } - - /// Accesses a contiguous subrange of the collection's elements. - /// - /// The accessed slice uses the same indices for the same elements as the - /// original collection. Always use the slice's `startIndex` property - /// instead of assuming that its indices start at a particular value. - /// - /// This example demonstrates getting a slice of an array of strings, finding - /// the index of one of the strings in the slice, and then using that index - /// in the original array. - /// - /// let streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"] - /// let streetsSlice = streets[2 ..< streets.endIndex] - /// print(streetsSlice) - /// // Prints "["Channing", "Douglas", "Evarts"]" - /// - /// let index = streetsSlice.index(of: "Evarts") // 4 - /// streets[index!] = "Eustace" - /// print(streets[index!]) - /// // Prints "Eustace" - /// - /// - Parameter bounds: A range of the collection's indices. The bounds of - /// the range must be valid indices of the collection. - subscript(bounds: Range) -> SubSequence { get set } /// Performs a range check in O(1), or a no-op when a range check is not /// implementable in O(1). diff --git a/stdlib/public/core/Range.swift.gyb b/stdlib/public/core/Range.swift.gyb index 5c732de830960..e8b6764be50b3 100644 --- a/stdlib/public/core/Range.swift.gyb +++ b/stdlib/public/core/Range.swift.gyb @@ -913,13 +913,13 @@ extension Strideable where Stride: SignedInteger { } } -extension _Indexable { +extension Collection { @_inlineable public subscript(r: R) -> SubSequence where R.Bound == Index { return self[r.relative(to: self)] } } -extension _MutableIndexable { +extension MutableCollection { @_inlineable public subscript(r: R) -> SubSequence where R.Bound == Index { get { diff --git a/stdlib/public/core/StringCharacterView.swift b/stdlib/public/core/StringCharacterView.swift index 8b78ad5402a2c..b08f882ca7c72 100644 --- a/stdlib/public/core/StringCharacterView.swift +++ b/stdlib/public/core/StringCharacterView.swift @@ -527,6 +527,8 @@ extension String.CharacterView : RangeReplaceableCollection { // Algorithms extension String.CharacterView { + public typealias SubSequence = String.CharacterView + /// Accesses the characters in the given range. /// /// The example below uses this subscript to access the characters up to, but diff --git a/validation-test/stdlib/CollectionType.swift.gyb b/validation-test/stdlib/CollectionType.swift.gyb index 652274227329c..ddfa51c1beecd 100644 --- a/validation-test/stdlib/CollectionType.swift.gyb +++ b/validation-test/stdlib/CollectionType.swift.gyb @@ -854,5 +854,31 @@ CollectionTypeTests.test("AssociatedTypes/${Collection}") { } % end +// rdar://problem/31830524 - associated type inference failure with +// SubSequence.SubSequence == SubSequence requirement +struct SillyIntCollection : Collection { + public var startIndex: Int { return 0 } + public var endIndex: Int { return 100 } + public subscript (position: Int) -> Int { + get { + return position + } + set { } + } + + // Note: [Int] is not the SubSequence type here. + public subscript (range: Range) -> [Int] { + get { return [] } + set { } + } + + public func index(after i: Int) -> Int {return i + 1} + public func index(before i: Int) -> Int {return i - 1} +} + +func checkSillyIntCollectionType(x: SillyIntCollection.SubSequence) { + let _: Slice = x +} + runAllTests()