diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index 1a7bcb250cf..37da79a97ab 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -620,7 +620,8 @@ public: /// generic signature builder no longer has valid state. GenericSignature computeGenericSignature( bool allowConcreteGenericParams = false, - bool allowBuilderToMove = true) &&; + bool buildingRequirementSignature = false, + bool rebuildingWithoutRedundantConformances = false) &&; /// Compute the requirement signature for the given protocol. static GenericSignature computeRequirementSignature(ProtocolDecl *proto); @@ -645,6 +646,8 @@ public: private: void computeRedundantRequirements(); + bool hasExplicitConformancesImpliedByConcrete() const; + /// Describes the relationship between a given constraint and /// the canonical constraint of the equivalence class. enum class ConstraintRelation { diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index a8633b469d7..344d4fb6e0c 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -124,6 +124,8 @@ STATISTIC(NumRewriteRhsSimplifiedToLhs, "# of rewrite rule right-hand sides simplified to lhs (and removed)"); STATISTIC(NumRewriteRulesRedundant, "# of rewrite rules that are redundant (and removed)"); +STATISTIC(NumSignaturesRebuiltWithoutRedundantRequirements, + "# of generic signatures which had a concretized conformance requirement"); namespace { @@ -4645,7 +4647,7 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement( unresolvedHandling); } - // If the resolved subject is a type, there may be things we can infer (if it + // If the resolved subject is concrete, there may be things we can infer (if it // conditionally conforms to the protocol), and we can probably perform // diagnostics here. if (auto subjectType = resolvedSubject.getAsConcreteType()) { @@ -8172,9 +8174,60 @@ static void checkGenericSignature(CanGenericSignature canSig, } #endif +bool GenericSignatureBuilder::hasExplicitConformancesImpliedByConcrete() const { + for (auto pair : Impl->RedundantRequirements) { + if (pair.first.getKind() != RequirementKind::Conformance) + continue; + + for (auto impliedByReq : pair.second) { + if (impliedByReq.getKind() == RequirementKind::Superclass) + return true; + + if (impliedByReq.getKind() == RequirementKind::SameType) + return true; + } + } + + return false; +} + +static Type stripBoundDependentMemberTypes(Type t) { + if (auto *depMemTy = t->getAs()) { + return DependentMemberType::get( + stripBoundDependentMemberTypes(depMemTy->getBase()), + depMemTy->getName()); + } + + return t; +} + +static Requirement stripBoundDependentMemberTypes(Requirement req) { + auto subjectType = stripBoundDependentMemberTypes(req.getFirstType()); + + switch (req.getKind()) { + case RequirementKind::Conformance: + return Requirement(RequirementKind::Conformance, subjectType, + req.getSecondType()); + + case RequirementKind::Superclass: + case RequirementKind::SameType: + return Requirement(req.getKind(), subjectType, + req.getSecondType().transform([](Type t) { + return stripBoundDependentMemberTypes(t); + })); + + case RequirementKind::Layout: + return Requirement(RequirementKind::Conformance, subjectType, + req.getLayoutConstraint()); + } + + llvm_unreachable("Bad requirement kind"); +} + GenericSignature GenericSignatureBuilder::computeGenericSignature( bool allowConcreteGenericParams, - bool allowBuilderToMove) && { + bool buildingRequirementSignature, + bool rebuildingWithoutRedundantConformances) && { // Finalize the builder, producing any necessary diagnostics. finalize(getGenericParams(), allowConcreteGenericParams); @@ -8185,6 +8238,43 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature( // Form the generic signature. auto sig = GenericSignature::get(getGenericParams(), requirements); + // If any of our explicit conformance requirements were implied by + // superclass or concrete same-type requirements, we have to build the + // signature again, since dropping the redundant conformance requirements + // changes the canonical type computation. + // + // However, if we already diagnosed an error, don't do this, because + // we might end up emitting duplicate diagnostics. + // + // Also, don't do this when building a requirement signature. + if (!buildingRequirementSignature && + !Impl->HadAnyError && + hasExplicitConformancesImpliedByConcrete()) { + NumSignaturesRebuiltWithoutRedundantRequirements++; + + if (rebuildingWithoutRedundantConformances) { + llvm::errs() << "Rebuilt signature still has " + << "redundant conformance requirements: "; + llvm::errs() << sig << "\n"; + abort(); + } + + GenericSignatureBuilder newBuilder(Context); + + for (auto param : sig->getGenericParams()) + newBuilder.addGenericParameter(param); + + for (auto &req : sig->getRequirements()) { + newBuilder.addRequirement(stripBoundDependentMemberTypes(req), + FloatingRequirementSource::forAbstract(), nullptr); + } + + return std::move(newBuilder).computeGenericSignature( + allowConcreteGenericParams, + buildingRequirementSignature, + /*rebuildingWithoutRedundantConformances=*/true); + } + #ifndef NDEBUG if (!Impl->HadAnyError) { checkGenericSignature(sig.getCanonicalSignature(), *this); @@ -8196,7 +8286,9 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature( // will produce the same thing. // // We cannot do this when there were errors. - if (allowBuilderToMove && !Impl->HadAnyError) { + // + // Also, we cannot do this when building a requirement signature. + if (!buildingRequirementSignature && !Impl->HadAnyError) { // Register this generic signature builder as the canonical builder for the // given signature. Context.registerGenericSignatureBuilder(sig, std::move(*this)); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 020aa61eb5c..b0d48afea7a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -939,7 +939,7 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, auto reqSignature = std::move(builder).computeGenericSignature( /*allowConcreteGenericParams=*/false, - /*allowBuilderToMove=*/false); + /*buildingRequirementSignature=*/true); return reqSignature->getRequirements(); } diff --git a/test/Generics/rdar65263302.swift b/test/Generics/rdar65263302.swift new file mode 100644 index 00000000000..caafdd610c6 --- /dev/null +++ b/test/Generics/rdar65263302.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -verify -emit-ir %s + +public protocol P { + associatedtype Element +} + +public class C: P { + public typealias Element = O.Element +} + +// CHECK: Generic signature: , O : P, E : P, O.Element == E.Element> +public func toe1(_: T, _: O, _: E, _: T.Element) + where T : P, // expected-warning {{redundant conformance constraint 'T': 'P'}} + O : P, + O.Element == T.Element, + T : C {} // expected-note {{conformance constraint 'T': 'P' implied here}} + +// CHECK: Generic signature: , O : P, E : P, O.Element == E.Element> +public func toe2(_: T, _: O, _: E, _: T.Element) + where O : P, + O.Element == T.Element, + T : C {} + diff --git a/test/Generics/rdar75171977.swift b/test/Generics/rdar75171977.swift new file mode 100644 index 00000000000..52b7e877701 --- /dev/null +++ b/test/Generics/rdar75171977.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -verify -emit-ir %s + +public protocol P1 {} + +public protocol P2 { + associatedtype A : P1 + associatedtype B : P2 + func f() +} + +public extension P2 where B.A == A { + func f() {} +} + +public class C: P2 { + public typealias A = B.A +}