159159#include " swift/AST/TypeMatcher.h"
160160#include " swift/AST/TypeRepr.h"
161161#include " swift/Basic/Assertions.h"
162+ #include " swift/Basic/Defer.h"
162163#include " llvm/ADT/SmallVector.h"
163164#include " llvm/ADT/SetVector.h"
164165#include " Diagnostics.h"
@@ -802,15 +803,122 @@ void swift::rewriting::applyInverses(
802803 ASTContext &ctx,
803804 ArrayRef<Type> gps,
804805 ArrayRef<InverseRequirement> inverseList,
806+ ArrayRef<StructuralRequirement> explicitRequirements,
805807 SmallVectorImpl<StructuralRequirement> &result,
806808 SmallVectorImpl<RequirementError> &errors) {
807809
808- // No inverses to even validate.
809- if (inverseList.empty ())
810+ // Are there even any inverses or same-type requirements to validate?
811+ if (inverseList.empty () && explicitRequirements. empty ()) {
810812 return ;
813+ }
811814
812815 const bool allowInverseOnAssocType =
813816 ctx.LangOpts .hasFeature (Feature::SuppressedAssociatedTypes);
817+
818+ llvm::DenseMap<CanType, CanType> representativeGPs;
819+
820+ // Start with an identity mapping.
821+ for (auto gp : gps) {
822+ auto canGP = gp->getCanonicalType ();
823+ representativeGPs.insert ({canGP, canGP});
824+ }
825+ bool hadSameTypeConstraintInScope = false ;
826+
827+ // Return the in-scope generic parameter that represents the equivalence class
828+ // for `gp`, or return null if the parameter is constrained out of scope.
829+ auto representativeGPFor = [&](CanType gp) -> CanType {
830+ while (true ) {
831+ auto found = representativeGPs.find (gp);
832+ if (found == representativeGPs.end ()) {
833+ return CanType ();
834+ }
835+ if (found->second == CanType ()) {
836+ return CanType ();
837+ }
838+
839+ if (found->second == gp) {
840+ return gp;
841+ }
842+
843+ gp = found->second ;
844+ }
845+ };
846+
847+ // Look for same-type constraints that equate multiple generic parameters
848+ // within the scope so we can treat the equivalence class as a unit.
849+ for (auto &explicitReqt : explicitRequirements) {
850+ if (explicitReqt.req .getKind () != RequirementKind::SameType) {
851+ continue ;
852+ }
853+
854+ // If one end of the same-type requirement is in scope, and the other is
855+ // a concrete type or out-of-scope generic parameter, then the other
856+ // parameter is also effectively out of scope.
857+ auto firstTy = explicitReqt.req .getFirstType ()->getCanonicalType ();
858+ auto secondTy = explicitReqt.req .getSecondType ()->getCanonicalType ();
859+ if (!representativeGPs.count (firstTy)
860+ && !representativeGPs.count (secondTy)) {
861+ // Same type constraint doesn't involve any in-scope generic parameters.
862+ continue ;
863+ }
864+
865+ CanType typeInScope;
866+ CanType typeOutOfScope;
867+
868+ if (representativeGPs.count (firstTy)
869+ && !representativeGPs.count (secondTy)){
870+ // First type is constrained out of scope.
871+ typeInScope = firstTy;
872+ typeOutOfScope = secondTy;
873+ } else if (!representativeGPs.count (firstTy)
874+ && representativeGPs.count (secondTy)) {
875+ // Second type is constrained out of scope.
876+ typeInScope = secondTy;
877+ typeOutOfScope = firstTy;
878+ } else {
879+ // Otherwise, both ends of the same-type constraint are in scope.
880+ // Fold the lexicographically-greater parameter with the lesser.
881+ auto firstGP = cast<GenericTypeParamType>(firstTy);
882+ auto secondGP = cast<GenericTypeParamType>(secondTy);
883+
884+ if (firstGP == secondGP) {
885+ // `T == T` has no effect.
886+ continue ;
887+ }
888+
889+ if (firstGP->getDepth () > secondGP->getDepth ()
890+ || (firstGP->getDepth () == secondGP->getDepth ()
891+ && firstGP->getIndex () > secondGP->getIndex ())) {
892+ std::swap (firstGP, secondGP);
893+ }
894+
895+ hadSameTypeConstraintInScope = true ;
896+ representativeGPs.insert_or_assign (secondGP, representativeGPFor (firstGP));
897+ continue ;
898+ }
899+
900+ // If the out-of-scope type is another type parameter or associated type,
901+ // then ignore this same-type constraint and allow defaulting to continue.
902+ //
903+ // It would probably have been more principled to suppress any defaulting
904+ // in this case, but this behavior shipped in Swift 6.0 and 6.1, so we
905+ // need to maintain source compatibility.
906+ if (typeOutOfScope->isTypeParameter ()) {
907+ continue ;
908+ }
909+
910+ // If the out-of-scope type contains errors, then similarly, ignore the
911+ // same type constraint. Any additional diagnostics arising from the type
912+ // parameter being left ~Copyable or ~Escapable might be misleading if the
913+ // corrected code is attempting to refer to a Copyable or Escapable type.
914+ if (typeOutOfScope->hasError ()) {
915+ continue ;
916+ }
917+
918+ representativeGPs.insert_or_assign (representativeGPFor (typeInScope),
919+ CanType ());
920+ hadSameTypeConstraintInScope = true ;
921+ }
814922
815923 // Summarize the inverses and diagnose ones that are incorrect.
816924 llvm::DenseMap<CanType, InvertibleProtocolSet> inverses;
@@ -837,11 +945,6 @@ void swift::rewriting::applyInverses(
837945 continue ;
838946 }
839947
840- // WARNING: possible quadratic behavior, but should be OK in practice.
841- auto notInScope = llvm::none_of (gps, [=](Type t) {
842- return t->getCanonicalType () == canSubject;
843- });
844-
845948 // If the inverse is on a subject that wasn't permitted by our caller, then
846949 // remove and diagnose as an error. This can happen when an inner context
847950 // has a constraint on some outer generic parameter, e.g.,
@@ -850,27 +953,40 @@ void swift::rewriting::applyInverses(
850953 // func f() where Self: ~Copyable
851954 // }
852955 //
853- if (notInScope ) {
956+ if (representativeGPs. find (canSubject) == representativeGPs. end () ) {
854957 errors.push_back (
855958 RequirementError::forInvalidInverseOuterSubject (inverse));
856959 continue ;
857960 }
858961
859- auto state = inverses.getOrInsertDefault (canSubject);
962+ auto representativeSubject = representativeGPFor (canSubject);
963+
964+ // If the subject is in scope, but same-type constrained to a type out of
965+ // scope, then allow inverses to be stated even though they are redundant.
966+ // This is because older versions of Swift not only accepted but required
967+ // `extension Foo where T == NonCopyableType, T: ~Copyable {}` to be
968+ // written, so we need to continue to accept that formulation for source
969+ // compatibility.
970+ if (!representativeSubject) {
971+ continue ;
972+ }
973+
974+ auto state = inverses.getOrInsertDefault (representativeSubject);
860975
861976 // Check if this inverse has already been seen.
862977 auto inverseKind = inverse.getKind ();
863978 if (state.contains (inverseKind))
864979 continue ;
865980
866981 state.insert (inverseKind);
867- inverses[canSubject ] = state;
982+ inverses[representativeSubject ] = state;
868983 }
869984
870- // Fast-path: if there are no valid inverses, then there are no requirements
871- // to be removed.
872- if (inverses.empty ())
985+ // Fast-path: if there are no valid inverses or same-type constraints, then
986+ // there are no requirements to be removed.
987+ if (inverses.empty () && !hadSameTypeConstraintInScope) {
873988 return ;
989+ }
874990
875991 // Scan the structural requirements and cancel out any inferred requirements
876992 // based on the inverses we saw.
@@ -882,18 +998,30 @@ void swift::rewriting::applyInverses(
882998
883999 // Only consider requirements involving an invertible protocol.
8841000 auto proto = req.getProtocolDecl ()->getInvertibleProtocolKind ();
885- if (!proto)
1001+ if (!proto) {
8861002 return false ;
1003+ }
8871004
8881005 // See if this subject is in-scope.
8891006 auto subject = req.getFirstType ()->getCanonicalType ();
890- auto result = inverses .find (subject);
891- if (result == inverses .end ())
1007+ auto representative = representativeGPs .find (subject);
1008+ if (representative == representativeGPs .end ()) {
8921009 return false ;
1010+ }
1011+
1012+ // If this type is same-type constrained into another equivalence class,
1013+ // then it doesn't need its own defaulted requirements.
1014+ if (representative->second != subject) {
1015+ return true ;
1016+ }
8931017
8941018 // We now have found the inferred constraint 'Subject : Proto'.
8951019 // So, remove it if we have recorded a 'Subject : ~Proto'.
896- auto recordedInverses = result->getSecond ();
1020+ auto foundInverses = inverses.find (subject);
1021+ if (foundInverses == inverses.end ()) {
1022+ return false ;
1023+ }
1024+ auto recordedInverses = foundInverses->getSecond ();
8971025 return recordedInverses.contains (*proto);
8981026 }), result.end ());
8991027}
@@ -1002,7 +1130,8 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
10021130
10031131 SmallVector<StructuralRequirement, 2 > defaults;
10041132 InverseRequirement::expandDefaults (ctx, needsDefaultRequirements, defaults);
1005- applyInverses (ctx, needsDefaultRequirements, inverses, defaults, errors);
1133+ applyInverses (ctx, needsDefaultRequirements, inverses, result,
1134+ defaults, errors);
10061135 result.append (defaults);
10071136
10081137 diagnoseRequirementErrors (ctx, errors,
@@ -1076,7 +1205,8 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
10761205 && !proto->isSpecificProtocol (KnownProtocolKind::Sendable))
10771206 InverseRequirement::expandDefaults (ctx, needsDefaultRequirements, defaults);
10781207
1079- applyInverses (ctx, needsDefaultRequirements, inverses, defaults, errors);
1208+ applyInverses (ctx, needsDefaultRequirements, inverses, result,
1209+ defaults, errors);
10801210 result.append (defaults);
10811211
10821212 diagnoseRequirementErrors (ctx, errors,
0 commit comments