159
159
#include " swift/AST/TypeMatcher.h"
160
160
#include " swift/AST/TypeRepr.h"
161
161
#include " swift/Basic/Assertions.h"
162
+ #include " swift/Basic/Defer.h"
162
163
#include " llvm/ADT/SmallVector.h"
163
164
#include " llvm/ADT/SetVector.h"
164
165
#include " Diagnostics.h"
@@ -802,15 +803,122 @@ void swift::rewriting::applyInverses(
802
803
ASTContext &ctx,
803
804
ArrayRef<Type> gps,
804
805
ArrayRef<InverseRequirement> inverseList,
806
+ ArrayRef<StructuralRequirement> explicitRequirements,
805
807
SmallVectorImpl<StructuralRequirement> &result,
806
808
SmallVectorImpl<RequirementError> &errors) {
807
809
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 ()) {
810
812
return ;
813
+ }
811
814
812
815
const bool allowInverseOnAssocType =
813
816
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
+ }
814
922
815
923
// Summarize the inverses and diagnose ones that are incorrect.
816
924
llvm::DenseMap<CanType, InvertibleProtocolSet> inverses;
@@ -837,11 +945,6 @@ void swift::rewriting::applyInverses(
837
945
continue ;
838
946
}
839
947
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
-
845
948
// If the inverse is on a subject that wasn't permitted by our caller, then
846
949
// remove and diagnose as an error. This can happen when an inner context
847
950
// has a constraint on some outer generic parameter, e.g.,
@@ -850,27 +953,40 @@ void swift::rewriting::applyInverses(
850
953
// func f() where Self: ~Copyable
851
954
// }
852
955
//
853
- if (notInScope ) {
956
+ if (representativeGPs. find (canSubject) == representativeGPs. end () ) {
854
957
errors.push_back (
855
958
RequirementError::forInvalidInverseOuterSubject (inverse));
856
959
continue ;
857
960
}
858
961
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);
860
975
861
976
// Check if this inverse has already been seen.
862
977
auto inverseKind = inverse.getKind ();
863
978
if (state.contains (inverseKind))
864
979
continue ;
865
980
866
981
state.insert (inverseKind);
867
- inverses[canSubject ] = state;
982
+ inverses[representativeSubject ] = state;
868
983
}
869
984
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) {
873
988
return ;
989
+ }
874
990
875
991
// Scan the structural requirements and cancel out any inferred requirements
876
992
// based on the inverses we saw.
@@ -882,18 +998,30 @@ void swift::rewriting::applyInverses(
882
998
883
999
// Only consider requirements involving an invertible protocol.
884
1000
auto proto = req.getProtocolDecl ()->getInvertibleProtocolKind ();
885
- if (!proto)
1001
+ if (!proto) {
886
1002
return false ;
1003
+ }
887
1004
888
1005
// See if this subject is in-scope.
889
1006
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 ()) {
892
1009
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
+ }
893
1017
894
1018
// We now have found the inferred constraint 'Subject : Proto'.
895
1019
// 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 ();
897
1025
return recordedInverses.contains (*proto);
898
1026
}), result.end ());
899
1027
}
@@ -1002,7 +1130,8 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
1002
1130
1003
1131
SmallVector<StructuralRequirement, 2 > defaults;
1004
1132
InverseRequirement::expandDefaults (ctx, needsDefaultRequirements, defaults);
1005
- applyInverses (ctx, needsDefaultRequirements, inverses, defaults, errors);
1133
+ applyInverses (ctx, needsDefaultRequirements, inverses, result,
1134
+ defaults, errors);
1006
1135
result.append (defaults);
1007
1136
1008
1137
diagnoseRequirementErrors (ctx, errors,
@@ -1076,7 +1205,8 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
1076
1205
&& !proto->isSpecificProtocol (KnownProtocolKind::Sendable))
1077
1206
InverseRequirement::expandDefaults (ctx, needsDefaultRequirements, defaults);
1078
1207
1079
- applyInverses (ctx, needsDefaultRequirements, inverses, defaults, errors);
1208
+ applyInverses (ctx, needsDefaultRequirements, inverses, result,
1209
+ defaults, errors);
1080
1210
result.append (defaults);
1081
1211
1082
1212
diagnoseRequirementErrors (ctx, errors,
0 commit comments