3434// decompositions can be found for all "derived" conformance rules, producing
3535// a minimal set of generating conformances.
3636//
37+ // There are two small complications to handle implementation details of
38+ // Swift generics:
39+ //
40+ // 1) Inherited witness tables must be derivable by following other protocol
41+ // refinement requirements only, without looking at non-Self associated
42+ // types. This is expressed by saying that the generating conformance
43+ // equations for a protocol refinement can only be written in terms of
44+ // other protocol refinements; conformance paths involving non-Self
45+ // associated types are not considered.
46+ //
47+ // 2) The subject type of each conformance requirement must be derivable at
48+ // runtime as well, so for each generating conformance, it must be
49+ // possible to write down a conformance path for the parent type without
50+ // using any generating conformance recursively in the parent path of
51+ // itself.
52+ //
53+ // The generating conformances finds fewer conformance requirements to be
54+ // redundant than homotopy reduction, which is why homotopy reduction only
55+ // deletes non-protocol conformance requirements.
56+ //
3757// ===----------------------------------------------------------------------===//
3858
3959#include " swift/AST/Decl.h"
@@ -283,23 +303,48 @@ void RewriteSystem::computeCandidateConformancePaths(
283303 llvm::dbgs () << " \n " ;
284304 }
285305
306+ // Two conformance rules in empty context (T.[P] => T) and (T'.[P] => T)
307+ // are interchangeable, and contribute a trivial pair of conformance
308+ // equations expressing that each one can be written in terms of the
309+ // other:
310+ //
311+ // (T.[P] => T) := (T'.[P])
312+ // (T'.[P] => T') := (T.[P])
313+ for (unsigned candidateRuleID : notInContext) {
314+ for (unsigned otherRuleID : notInContext) {
315+ if (otherRuleID == candidateRuleID)
316+ continue ;
317+
318+ SmallVector<unsigned , 2 > path;
319+ path.push_back (otherRuleID);
320+ conformancePaths[candidateRuleID].push_back (path);
321+ }
322+ }
323+
286324 // Suppose a 3-cell contains a conformance rule (T.[P] => T) in an empty
287- // context, and a conformance rule (V.[P] => V) with a possibly non-empty
288- // left context U and empty right context.
325+ // context, and a conformance rule (V.[P] => V) with a non-empty left
326+ // context U.
327+ //
328+ // The 3-cell looks something like this:
329+ //
330+ // ... ⊗ (T.[P] => T) ⊗ ... ⊗ U.(V => V.[P]) ⊗ ...
331+ // ^ ^
332+ // | |
333+ // + basepoint ========================= basepoint +
289334 //
290335 // We can decompose U into a product of conformance rules:
291336 //
292337 // (V1.[P1] => V1)...(Vn.[Pn] => Vn),
293338 //
339+ // Note that (V1)...(Vn) is canonically equivalent to U.
340+ //
294341 // Now, we can record a candidate decomposition of (T.[P] => T) as a
295342 // product of conformance rules:
296343 //
297344 // (T.[P] => T) := (V1.[P1] => V1)...(Vn.[Pn] => Vn).(V.[P] => V)
298345 //
299- // Now if U is empty, this becomes the trivial candidate:
300- //
301- // (T.[P] => T) := (V.[P] => V)
302- SmallVector<SmallVector<unsigned , 2 >, 2 > candidatePaths;
346+ // Again, note that (V1)...(Vn).V is canonically equivalent to U.V,
347+ // and therefore T.
303348 for (auto pair : inContext) {
304349 // We have a term U, and a rule V.[P] => V.
305350 SmallVector<unsigned , 2 > conformancePath;
@@ -313,26 +358,10 @@ void RewriteSystem::computeCandidateConformancePaths(
313358 decomposeTermIntoConformanceRuleLeftHandSides (term, pair.second ,
314359 conformancePath);
315360
316- candidatePaths.push_back (conformancePath);
317- }
318-
319- for (unsigned candidateRuleID : notInContext) {
320- // If multiple conformance rules appear in an empty context, each one
321- // can be replaced with any other conformance rule.
322- for (unsigned otherRuleID : notInContext) {
323- if (otherRuleID == candidateRuleID)
324- continue ;
325-
326- SmallVector<unsigned , 2 > path;
327- path.push_back (otherRuleID);
328- conformancePaths[candidateRuleID].push_back (path);
329- }
330-
331- // If conformance rules appear in non-empty context, they define a
332- // conformance access path for each conformance rule in empty context.
333- for (const auto &path : candidatePaths) {
334- conformancePaths[candidateRuleID].push_back (path);
335- }
361+ // This decomposition defines a conformance access path for each
362+ // conformance rule we saw in empty context.
363+ for (unsigned otherRuleID : notInContext)
364+ conformancePaths[otherRuleID].push_back (conformancePath);
336365 }
337366 }
338367 }
@@ -492,6 +521,48 @@ void RewriteSystem::verifyGeneratingConformanceEquations(
492521#endif
493522}
494523
524+ static const ProtocolDecl *getParentConformanceForTerm (Term lhs) {
525+ // The last element is a protocol symbol, because this is the left hand side
526+ // of a conformance rule.
527+ assert (lhs.back ().getKind () == Symbol::Kind::Protocol);
528+
529+ // The second to last symbol is either an associated type, protocol or generic
530+ // parameter symbol.
531+ assert (lhs.size () >= 2 );
532+
533+ auto parentSymbol = lhs[lhs.size () - 2 ];
534+
535+ switch (parentSymbol.getKind ()) {
536+ case Symbol::Kind::AssociatedType: {
537+ // In a conformance rule of the form [P:T].[Q] => [P:T], the parent type is
538+ // trivial.
539+ if (lhs.size () == 2 )
540+ return nullptr ;
541+
542+ // If we have a rule of the form X.[P:Y].[Q] => X.[P:Y] wih non-empty X,
543+ // then the parent type is X.[P].
544+ const auto protos = parentSymbol.getProtocols ();
545+ assert (protos.size () == 1 );
546+
547+ return protos[0 ];
548+ }
549+
550+ case Symbol::Kind::GenericParam:
551+ case Symbol::Kind::Protocol:
552+ // The parent type is trivial (either a generic parameter, or the protocol
553+ // 'Self' type).
554+ return nullptr ;
555+
556+ case Symbol::Kind::Name:
557+ case Symbol::Kind::Layout:
558+ case Symbol::Kind::Superclass:
559+ case Symbol::Kind::ConcreteType:
560+ break ;
561+ }
562+
563+ llvm_unreachable (" Bad symbol kind" );
564+ }
565+
495566// / Computes a minimal set of generating conformances, assuming that homotopy
496567// / reduction has already eliminated all redundant rewrite rules that are not
497568// / conformance rules.
@@ -536,49 +607,21 @@ void RewriteSystem::computeGeneratingConformances(
536607
537608 auto lhs = rule.getLHS ();
538609
539- auto parentSymbol = lhs[lhs.size () - 2 ];
540-
541- // The last element is a protocol symbol, because this is a conformance rule.
542- // The second to last symbol is either an associated type, protocol or generic
543- // parameter symbol.
544- switch (parentSymbol.getKind ()) {
545- case Symbol::Kind::AssociatedType: {
546- // If we have a rule of the form X.[P:Y].[Q] => X.[P:Y] wih non-empty X,
547- // then the parent type is X.[P].
548- if (lhs.size () == 2 )
549- continue ;
550-
610+ // Record a parent path if the subject type itself requires a non-trivial
611+ // conformance path to derive.
612+ if (auto *parentProto = getParentConformanceForTerm (lhs)) {
551613 MutableTerm mutTerm (lhs.begin (), lhs.end () - 2 );
552614 assert (!mutTerm.empty ());
553615
554- const auto protos = parentSymbol.getProtocols ();
555- assert (protos.size () == 1 );
556-
557616 bool simplified = simplify (mutTerm);
558617 assert (!simplified || rule.isSimplified ());
559618 (void ) simplified;
560619
561- mutTerm.add (Symbol::forProtocol (protos[ 0 ] , Context));
620+ mutTerm.add (Symbol::forProtocol (parentProto , Context));
562621
563622 // Get a conformance path for X.[P] and record it.
564623 decomposeTermIntoConformanceRuleLeftHandSides (mutTerm, parentPaths[ruleID]);
565- continue ;
566624 }
567-
568- case Symbol::Kind::GenericParam:
569- case Symbol::Kind::Protocol:
570- // Don't record a parent path, since the parent type is trivial (either a
571- // generic parameter, or the protocol 'Self' type).
572- continue ;
573-
574- case Symbol::Kind::Name:
575- case Symbol::Kind::Layout:
576- case Symbol::Kind::Superclass:
577- case Symbol::Kind::ConcreteType:
578- break ;
579- }
580-
581- llvm_unreachable (" Bad symbol kind" );
582625 }
583626
584627 computeCandidateConformancePaths (conformancePaths);
0 commit comments