@@ -123,9 +123,16 @@ operator()(InFlightSubstitution &IFS, Type dependentType,
123123InFlightSubstitution::InFlightSubstitution (TypeSubstitutionFn substType,
124124 LookupConformanceFn lookupConformance,
125125 SubstOptions options)
126- : Options(options),
127- BaselineSubstType(substType),
128- BaselineLookupConformance(lookupConformance) {
126+ : BaselineSubstType(substType),
127+ BaselineLookupConformance(lookupConformance),
128+ Options(options) {
129+
130+ LimitReached = 0 ;
131+
132+ // FIXME: Make these configurable
133+ RemainingCount = 1000 ;
134+ RemainingDepth = 20 ;
135+
129136 // FIXME: Don't substitute type parameters if one of the special flags is set.
130137 Props |= RecursiveTypeProperties::HasTypeParameter;
131138
@@ -233,6 +240,14 @@ Type InFlightSubstitution::projectLaneFromPackType(Type substType,
233240 }
234241}
235242
243+ bool InFlightSubstitution::checkLimits () {
244+ if (RemainingCount == 0 || RemainingDepth == 0 ) {
245+ LimitReached = true ;
246+ return true ;
247+ }
248+ return false ;
249+ }
250+
236251Type InFlightSubstitution::substType (SubstitutableType *origType,
237252 unsigned level) {
238253 auto substType = BaselineSubstType (origType);
@@ -244,6 +259,20 @@ Type InFlightSubstitution::substType(SubstitutableType *origType,
244259 else
245260 substType = substType->increasePackElementLevel (level);
246261
262+ // Opaque type replacement must iterate until fixed point.
263+ if (shouldSubstituteOpaqueArchetypes () &&
264+ substType->hasOpaqueArchetype () &&
265+ !substType->isEqual (origType)) {
266+ if (checkLimits ()) {
267+ return ErrorType::get (substType);
268+ }
269+
270+ --RemainingCount;
271+ --RemainingDepth;
272+ substType = substType.subst (*this );
273+ ++RemainingDepth;
274+ }
275+
247276 return substType;
248277}
249278
@@ -271,6 +300,19 @@ InFlightSubstitution::lookupConformance(Type dependentType,
271300 substConfRef.getPack (), level);
272301 }
273302
303+ // Opaque type replacement must iterate until fixed point.
304+ if (shouldSubstituteOpaqueArchetypes () && substConfRef &&
305+ substConfRef.getType ()->hasOpaqueArchetype () &&
306+ !substConfRef.getType ()->isEqual (dependentType)) {
307+ if (checkLimits ()) {
308+ return ProtocolConformanceRef::forInvalid ();
309+ }
310+ --RemainingCount;
311+ --RemainingDepth;
312+ substConfRef = substConfRef.subst (*this );
313+ ++RemainingDepth;
314+ }
315+
274316 return substConfRef;
275317}
276318
@@ -906,17 +948,6 @@ ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
906948 return OpaqueSubstitutionKind::SubstituteNonResilientModule;
907949}
908950
909- static Type substOpaqueTypesWithUnderlyingTypesRec (
910- Type ty, const DeclContext *inContext, ResilienceExpansion contextExpansion,
911- bool isWholeModuleContext,
912- llvm::DenseSet<ReplaceOpaqueTypesWithUnderlyingTypes::SeenDecl> &decls) {
913- ReplaceOpaqueTypesWithUnderlyingTypes replacer (inContext, contextExpansion,
914- isWholeModuleContext, decls);
915- return ty.subst (replacer, replacer,
916- SubstFlags::SubstituteOpaqueArchetypes |
917- SubstFlags::PreservePackExpansionLevel);
918- }
919-
920951// / Checks that \p dc has access to \p ty for the purposes of an opaque
921952// / substitution described by \p kind.
922953// /
@@ -973,13 +1004,6 @@ static bool canSubstituteTypeInto(Type ty, const DeclContext *dc,
9731004 llvm_unreachable (" invalid substitution kind" );
9741005}
9751006
976- ReplaceOpaqueTypesWithUnderlyingTypes::ReplaceOpaqueTypesWithUnderlyingTypes (
977- const DeclContext *inContext, ResilienceExpansion contextExpansion,
978- bool isWholeModuleContext, llvm::DenseSet<SeenDecl> &seen)
979- : contextExpansion(contextExpansion),
980- inContextAndIsWholeModule(inContext, isWholeModuleContext),
981- seenDecls(&seen) {}
982-
9831007Type ReplaceOpaqueTypesWithUnderlyingTypes::
9841008operator ()(SubstitutableType *maybeOpaqueType) const {
9851009 auto *archetype = dyn_cast<OpaqueTypeArchetypeType>(maybeOpaqueType);
@@ -1027,36 +1051,7 @@ operator()(SubstitutableType *maybeOpaqueType) const {
10271051 // for its type arguments. We perform this substitution after checking for
10281052 // visibility, since we do not want the result of the visibility check to
10291053 // depend on the substitutions previously applied.
1030- auto substTy = partialSubstTy.subst (outerSubs);
1031-
1032- // If the type changed, but still contains opaque types, recur.
1033- if (!substTy->isEqual (maybeOpaqueType) && substTy->hasOpaqueArchetype ()) {
1034- SeenDecl seenKey (decl, outerSubs);
1035- if (auto *alreadySeen = this ->seenDecls ) {
1036- // Detect substitution loops. If we find one, just bounce the original
1037- // type back to the caller. This substitution will fail at runtime
1038- // instead.
1039- if (!alreadySeen->insert (seenKey).second ) {
1040- return maybeOpaqueType;
1041- }
1042-
1043- auto res = ::substOpaqueTypesWithUnderlyingTypesRec (
1044- substTy, inContext, contextExpansion, isContextWholeModule,
1045- *alreadySeen);
1046- alreadySeen->erase (seenKey);
1047- return res;
1048- } else {
1049- // We're the top of the stack for the recursion check. Allocate a set of
1050- // opaque result type decls we've already seen for the rest of the check.
1051- llvm::DenseSet<SeenDecl> seenDecls;
1052- seenDecls.insert (seenKey);
1053- return ::substOpaqueTypesWithUnderlyingTypesRec (
1054- substTy, inContext, contextExpansion, isContextWholeModule,
1055- seenDecls);
1056- }
1057- }
1058-
1059- return substTy;
1054+ return partialSubstTy.subst (outerSubs);
10601055}
10611056
10621057Type swift::substOpaqueTypesWithUnderlyingTypes (Type ty,
@@ -1068,9 +1063,25 @@ Type swift::substOpaqueTypesWithUnderlyingTypes(Type ty,
10681063 ReplaceOpaqueTypesWithUnderlyingTypes replacer (
10691064 context.getContext (), context.getResilienceExpansion (),
10701065 context.isWholeModuleContext ());
1071- SubstOptions flags = (SubstFlags::SubstituteOpaqueArchetypes |
1072- SubstFlags::PreservePackExpansionLevel);
1073- return ty.subst (replacer, replacer, flags);
1066+ InFlightSubstitution IFS (replacer, replacer,
1067+ SubstFlags::SubstituteOpaqueArchetypes |
1068+ SubstFlags::PreservePackExpansionLevel);
1069+
1070+ auto substTy = ty.subst (IFS);
1071+
1072+ // FIXME: This should be a diagnostic at the source location of the innermost
1073+ // request or something.
1074+ if (IFS.wasLimitReached ()) {
1075+ ABORT ([&](auto &out) {
1076+ out << " Possible non-terminating type substitution detected\n\n " ;
1077+ out << " Original type:\n " ;
1078+ ty.dump (out);
1079+ out << " Substituted type:\n " ;
1080+ substTy.dump (out);
1081+ });
1082+ }
1083+
1084+ return substTy;
10741085}
10751086
10761087CanType
@@ -1080,24 +1091,29 @@ swift::substOpaqueTypesWithUnderlyingTypes(CanType ty,
10801091 ->getCanonicalType ();
10811092}
10821093
1083- static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypesRec (
1084- ProtocolConformanceRef ref, const DeclContext *inContext,
1085- ResilienceExpansion contextExpansion, bool isWholeModuleContext,
1086- llvm::DenseSet<ReplaceOpaqueTypesWithUnderlyingTypes::SeenDecl> &decls) {
1087- ReplaceOpaqueTypesWithUnderlyingTypes replacer (inContext, contextExpansion,
1088- isWholeModuleContext, decls);
1089- return ref.subst (replacer, replacer,
1090- SubstFlags::SubstituteOpaqueArchetypes |
1091- SubstFlags::PreservePackExpansionLevel);
1092- }
1093-
10941094ProtocolConformanceRef swift::substOpaqueTypesWithUnderlyingTypes (
10951095 ProtocolConformanceRef ref, TypeExpansionContext context) {
10961096 ReplaceOpaqueTypesWithUnderlyingTypes replacer (
10971097 context.getContext (), context.getResilienceExpansion (),
10981098 context.isWholeModuleContext ());
1099- return ref.subst (replacer, replacer,
1100- SubstFlags::SubstituteOpaqueArchetypes);
1099+ InFlightSubstitution IFS (replacer, replacer,
1100+ SubstFlags::SubstituteOpaqueArchetypes);
1101+
1102+ auto substRef = ref.subst (IFS);
1103+
1104+ // FIXME: This should be a diagnostic at the source location of the innermost
1105+ // request or something.
1106+ if (IFS.wasLimitReached ()) {
1107+ ABORT ([&](auto &out) {
1108+ out << " Possible non-terminating conformance substitution detected\n\n " ;
1109+ out << " Original conformance:\n " ;
1110+ ref.dump (out);
1111+ out << " \n Substituted conformance:\n " ;
1112+ substRef.dump (out);
1113+ });
1114+ }
1115+
1116+ return substRef;
11011117}
11021118
11031119ProtocolConformanceRef ReplaceOpaqueTypesWithUnderlyingTypes::
@@ -1151,36 +1167,7 @@ operator()(InFlightSubstitution &IFS, Type maybeOpaqueType,
11511167 auto partialSubstRef =
11521168 subs->lookupConformance (archetype->getInterfaceType ()->getCanonicalType (),
11531169 protocol);
1154- auto substRef = partialSubstRef.subst (outerSubs);
1155-
1156- // If the type still contains opaque types, recur.
1157- if (substRef.getType ()->hasOpaqueArchetype ()) {
1158- SeenDecl seenKey (decl, outerSubs);
1159-
1160- if (auto *alreadySeen = this ->seenDecls ) {
1161- // Detect substitution loops. If we find one, just bounce the original
1162- // type back to the caller. This substitution will fail at runtime
1163- // instead.
1164- if (!alreadySeen->insert (seenKey).second ) {
1165- return lookupConformance (maybeOpaqueType.subst (IFS), protocol);
1166- }
1167-
1168- auto res = ::substOpaqueTypesWithUnderlyingTypesRec (
1169- substRef, inContext, contextExpansion, isContextWholeModule,
1170- *alreadySeen);
1171- alreadySeen->erase (seenKey);
1172- return res;
1173- } else {
1174- // We're the top of the stack for the recursion check. Allocate a set of
1175- // opaque result type decls we've already seen for the rest of the check.
1176- llvm::DenseSet<SeenDecl> seenDecls;
1177- seenDecls.insert (seenKey);
1178- return ::substOpaqueTypesWithUnderlyingTypesRec (
1179- substRef, inContext, contextExpansion, isContextWholeModule,
1180- seenDecls);
1181- }
1182- }
1183- return substRef;
1170+ return partialSubstRef.subst (outerSubs);
11841171}
11851172
11861173Type ReplaceExistentialArchetypesWithConcreteTypes::getInterfaceType (
0 commit comments