From 270be443b2a30e2e9c2a29423893a9bef21fd0ad Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 12:53:34 -0400 Subject: [PATCH 01/10] ASTMangler: Support for pack conformances The names of the private witness table accessor thunks we generate for an opaque return type mangle the concrete conformance of the underlying type. If a conformance requirement of the opaque return type was witnessed by a conditional conformance of a variadic generic type, we would crash because of an unimplemented case in the mangler. Fixes rdar://problem/125668798. --- docs/ABI/Mangling.rst | 3 + include/swift/AST/ASTMangler.h | 3 + include/swift/Demangling/DemangleNodes.def | 1 + include/swift/Demangling/Demangler.h | 1 + lib/AST/ASTMangler.cpp | 64 +++++++++++++++++++--- lib/Demangling/Demangler.cpp | 9 +++ lib/Demangling/NodePrinter.cpp | 25 ++++++++- lib/Demangling/OldRemangler.cpp | 6 ++ lib/Demangling/Remangler.cpp | 10 ++++ test/Demangle/Inputs/manglings.txt | 1 + test/IRGen/variadic_generic_opaque.swift | 39 +++++++++++-- 11 files changed, 150 insertions(+), 12 deletions(-) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 3d68b3e4601ea..630cfe4e890a1 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -966,6 +966,7 @@ Property behaviors are implemented using private protocol conformances. any-protocol-conformance ::= concrete-protocol-conformance any-protocol-conformance ::= dependent-protocol-conformance + any-protocol-conformance ::= pack-protocol-conformance any-protocol-conformance-list ::= any-protocol-conformance '_' any-protocol-conformance-list any-protocol-conformance-list ::= empty-list @@ -980,6 +981,8 @@ Property behaviors are implemented using private protocol conformances. dependent-associated-conformance ::= type protocol dependent-protocol-conformance ::= dependent-protocol-conformance opaque-type 'HO' + pack-protocol-conformance ::= any-protocol-conformance-list 'HX' + A compact representation used to represent mangled protocol conformance witness arguments at runtime. The ``module`` is only specified for conformances that are "retroactive", meaning that the context in which the conformance is defined diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 9bb1df4feb6f1..ee1d3059b2914 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -703,6 +703,9 @@ class ASTMangler : public Mangler { void appendConcreteProtocolConformance( const ProtocolConformance *conformance, GenericSignature sig); + void appendPackProtocolConformance( + const PackConformance *conformance, + GenericSignature sig); void appendDependentProtocolConformance(const ConformancePath &path, GenericSignature sig); void appendOpParamForLayoutConstraint(LayoutConstraint Layout); diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 1f726f6f1e22c..afdd3bdb76318 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -51,6 +51,7 @@ NODE(ClangType) CONTEXT_NODE(Class) NODE(ClassMetadataBaseOffset) NODE(ConcreteProtocolConformance) +NODE(PackProtocolConformance) NODE(ConformanceAttachedMacroExpansion) CONTEXT_NODE(Constructor) NODE(CoroutineContinuationPrototype) diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index 5ab3e4c983a60..67aee24cd2d90 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -587,6 +587,7 @@ class Demangler : public NodeFactory { NodePointer demangleRetroactiveProtocolConformanceRef(); NodePointer popAnyProtocolConformance(); NodePointer demangleConcreteProtocolConformance(); + NodePointer demanglePackProtocolConformance(); NodePointer popDependentProtocolConformance(); NodePointer demangleDependentProtocolConformanceRoot(); NodePointer demangleDependentProtocolConformanceInherited(); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 4df445740977c..5ec7ae62540c3 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -28,6 +28,7 @@ #include "swift/AST/MacroDiscriminatorContext.h" #include "swift/AST/Module.h" #include "swift/AST/Ownership.h" +#include "swift/AST/PackConformance.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/ProtocolConformance.h" @@ -1874,8 +1875,22 @@ static bool isRetroactiveConformance(const RootProtocolConformance *root) { /// Determine whether the given protocol conformance contains a retroactive /// protocol conformance anywhere in it. static bool containsRetroactiveConformance( - const ProtocolConformance *conformance, + ProtocolConformanceRef conformanceRef, ModuleDecl *module) { + if (!conformanceRef.isPack() && !conformanceRef.isConcrete()) + return false; + + if (conformanceRef.isPack()) { + for (auto patternConf : conformanceRef.getPack()->getPatternConformances()) { + if (containsRetroactiveConformance(patternConf, module)) + return true; + } + + return false; + } + + auto *conformance = conformanceRef.getConcrete(); + // If the root conformance is retroactive, it's retroactive. const RootProtocolConformance *rootConformance = conformance->getRootConformance(); @@ -1897,8 +1912,7 @@ static bool containsRetroactiveConformance( // for indexing purposes. continue; } - if (conformance.isConcrete() && - containsRetroactiveConformance(conformance.getConcrete(), module)) { + if (containsRetroactiveConformance(conformance, module)) { return true; } } @@ -1924,14 +1938,18 @@ void ASTMangler::appendRetroactiveConformances(SubstitutionMap subMap, }; // Ignore abstract conformances. - if (!conformance.isConcrete()) + if (!conformance.isConcrete() && !conformance.isPack()) continue; // Skip non-retroactive conformances. - if (!containsRetroactiveConformance(conformance.getConcrete(), fromModule)) + if (!containsRetroactiveConformance(conformance, fromModule)) continue; - appendConcreteProtocolConformance(conformance.getConcrete(), sig); + if (conformance.isConcrete()) + appendConcreteProtocolConformance(conformance.getConcrete(), sig); + else + appendPackProtocolConformance(conformance.getPack(), sig); + appendOperator("g", Index(numProtocolRequirements)); } } @@ -4143,8 +4161,14 @@ void ASTMangler::appendAnyProtocolConformance( appendDependentProtocolConformance(conformancePath, opaqueSignature); appendType(conformingType, genericSig); appendOperator("HO"); - } else { + } else if (conformance.isConcrete()) { appendConcreteProtocolConformance(conformance.getConcrete(), genericSig); + } else if (conformance.isPack()) { + appendPackProtocolConformance(conformance.getPack(), genericSig); + } else { + llvm::errs() << "Bad conformance in mangler: "; + conformance.dump(llvm::errs()); + abort(); } } @@ -4199,6 +4223,32 @@ void ASTMangler::appendConcreteProtocolConformance( appendOperator("HC"); } +void ASTMangler::appendPackProtocolConformance( + const PackConformance *conformance, + GenericSignature sig) { + auto conformingType = conformance->getType(); + auto patternConformances = conformance->getPatternConformances(); + assert(conformingType->getNumElements() == patternConformances.size()); + + if (conformingType->getNumElements() == 0) { + appendOperator("y"); + } else { + bool firstField = true; + for (unsigned i = 0, e = conformingType->getNumElements(); i < e; ++i) { + auto type = conformingType->getElementType(i); + auto conf = patternConformances[i]; + + if (auto *expansionTy = type->getAs()) + type = expansionTy->getPatternType(); + + appendAnyProtocolConformance(sig, type->getCanonicalType(), conf); + appendListSeparator(firstField); + } + } + + appendOperator("HX"); +} + void ASTMangler::appendOpParamForLayoutConstraint(LayoutConstraint layout) { assert(layout); switch (layout->getKind()) { diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index e542e67495f57..ddb03dec23c19 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -996,6 +996,7 @@ NodePointer Demangler::demangleOperator() { case 'p': return createWithChild( Node::Kind::ProtocolConformanceRefInProtocolModule, popProtocol()); + case 'X': return demanglePackProtocolConformance(); // Runtime records (type/protocol/conformance/function) case 'c': @@ -1847,6 +1848,7 @@ NodePointer Demangler::popAnyProtocolConformance() { return popNode([](Node::Kind kind) { switch (kind) { case Node::Kind::ConcreteProtocolConformance: + case Node::Kind::PackProtocolConformance: case Node::Kind::DependentProtocolConformanceRoot: case Node::Kind::DependentProtocolConformanceInherited: case Node::Kind::DependentProtocolConformanceAssociated: @@ -1884,6 +1886,13 @@ NodePointer Demangler::demangleConcreteProtocolConformance() { type, conformanceRef, conditionalConformanceList); } +NodePointer Demangler::demanglePackProtocolConformance() { + NodePointer patternConformanceList = popAnyProtocolConformanceList(); + + return createWithChild(Node::Kind::PackProtocolConformance, + patternConformanceList); +} + NodePointer Demangler::popDependentProtocolConformance() { return popNode([](Node::Kind kind) { switch (kind) { diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 9c2687b13edd7..39c4d40353068 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -596,6 +596,7 @@ class NodePrinter { case Node::Kind::AnonymousContext: case Node::Kind::AnyProtocolConformanceList: case Node::Kind::ConcreteProtocolConformance: + case Node::Kind::PackProtocolConformance: case Node::Kind::DependentAssociatedConformance: case Node::Kind::DependentProtocolConformanceAssociated: case Node::Kind::DependentProtocolConformanceInherited: @@ -3106,12 +3107,31 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, printChildren(Node, depth); return nullptr; case Node::Kind::AnyProtocolConformanceList: - printChildren(Node, depth); + if (Node->getNumChildren() > 0) { + Printer << "("; + for (unsigned i = 0; i < Node->getNumChildren(); ++i) { + if (i > 0) + Printer << ", "; + print(Node->getChild(i), depth + 1); + } + Printer << ")"; + } return nullptr; case Node::Kind::ConcreteProtocolConformance: Printer << "concrete protocol conformance "; if (Node->hasIndex()) Printer << "#" << Node->getIndex() << " "; + print(Node->getChild(0), depth + 1); + Printer << " to "; + print(Node->getChild(1), depth + 1); + if (Node->getNumChildren() > 2 && + Node->getChild(2)->getNumChildren() > 0) { + Printer << " with conditional requirements: "; + print(Node->getChild(2), depth + 1); + } + return nullptr; + case Node::Kind::PackProtocolConformance: + Printer << "pack protocol conformance "; printChildren(Node, depth); return nullptr; case Node::Kind::DependentAssociatedConformance: @@ -3122,18 +3142,21 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, Printer << "dependent associated protocol conformance "; printOptionalIndex(Node->getChild(2)); print(Node->getChild(0), depth + 1); + Printer << " to "; print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::DependentProtocolConformanceInherited: Printer << "dependent inherited protocol conformance "; printOptionalIndex(Node->getChild(2)); print(Node->getChild(0), depth + 1); + Printer << " to "; print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::DependentProtocolConformanceRoot: Printer << "dependent root protocol conformance "; printOptionalIndex(Node->getChild(2)); print(Node->getChild(0), depth + 1); + Printer << " to "; print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::ProtocolConformanceRefInTypeModule: diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index f0a028a39a7de..52fc469254c34 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -517,6 +517,12 @@ ManglingError Remangler::mangleConcreteProtocolConformance(Node *node, return MANGLING_ERROR(ManglingError::UnsupportedNodeKind, node); } +ManglingError Remangler::manglePackProtocolConformance(Node *node, + unsigned depth) { + // Pack conformances aren't in the old mangling + return MANGLING_ERROR(ManglingError::UnsupportedNodeKind, node); +} + ManglingError Remangler::mangleAnyProtocolConformanceList(Node *node, unsigned depth) { // Conformance lists aren't in the old mangling diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 7879bb7c6705d..6fee8cdc04db5 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -2614,6 +2614,14 @@ ManglingError Remangler::mangleConcreteProtocolConformance(Node *node, return ManglingError::Success; } +ManglingError Remangler::manglePackProtocolConformance(Node *node, + unsigned depth) { + RETURN_IF_ERROR( + mangleAnyProtocolConformanceList(node->getChild(0), depth + 1)); + Buffer << "HX"; + return ManglingError::Success; +} + ManglingError Remangler::mangleDependentProtocolConformanceRoot(Node *node, unsigned depth) { RETURN_IF_ERROR(mangleType(node->getChild(0), depth + 1)); @@ -2663,6 +2671,8 @@ ManglingError Remangler::mangleAnyProtocolConformance(Node *node, switch (node->getKind()) { case Node::Kind::ConcreteProtocolConformance: return mangleConcreteProtocolConformance(node, depth + 1); + case Node::Kind::PackProtocolConformance: + return manglePackProtocolConformance(node, depth + 1); case Node::Kind::DependentProtocolConformanceRoot: return mangleDependentProtocolConformanceRoot(node, depth + 1); case Node::Kind::DependentProtocolConformanceInherited: diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 868cf921ab19c..6f213b51c1950 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -472,3 +472,4 @@ $sSRyxG15Synchronization19AtomicRepresentableABRi_zrlMc ---> protocol conformanc $sSRyxG15Synchronization19AtomicRepresentableABRi0_zrlMc ---> protocol conformance descriptor for < where A: ~Swift.Escapable> Swift.UnsafeBufferPointer : Synchronization.AtomicRepresentable in Synchronization $sSRyxG15Synchronization19AtomicRepresentableABRi1_zrlMc ---> protocol conformance descriptor for < where A: ~Swift.> Swift.UnsafeBufferPointer : Synchronization.AtomicRepresentable in Synchronization +$s23variadic_generic_opaque2G2VyAA2S1V_AA2S2VQPGAA1PHPAeA1QHPyHC_AgaJHPyHCHX_HC ---> concrete protocol conformance variadic_generic_opaque.G2 to protocol conformance ref (type's module) variadic_generic_opaque.P with conditional requirements: (pack protocol conformance (concrete protocol conformance variadic_generic_opaque.S1 to protocol conformance ref (type's module) variadic_generic_opaque.Q, concrete protocol conformance variadic_generic_opaque.S2 to protocol conformance ref (type's module) variadic_generic_opaque.Q)) diff --git a/test/IRGen/variadic_generic_opaque.swift b/test/IRGen/variadic_generic_opaque.swift index 218afccda5b66..e679867248c32 100644 --- a/test/IRGen/variadic_generic_opaque.swift +++ b/test/IRGen/variadic_generic_opaque.swift @@ -1,11 +1,42 @@ -// RUN: %target-swift-frontend -emit-ir %s -disable-availability-checking - -// FIXME: Add more tests +// RUN: %target-swift-frontend -emit-ir %s -disable-availability-checking | %FileCheck %s public protocol P {} public struct G: P {} -public func returnsG(_ t: repeat each T) -> some P { +public func concreteG(_ t: repeat each T) -> some P { + return G() +} + +public func abstractG(_ t: T) -> some P { + return G() +} + +public func variadicG(_ t: repeat each T) -> some P { return G() } + +// Opaque return type is witnessed by a conditional conformance +protocol Q {} + +struct S1: Q {} +struct S2: Q {} + +struct G2 {} +extension G2: P where repeat each T: Q {} + +func concreteG2() -> some P { + G2() +} + +func abstractG2(_: T) -> some P { + G2() +} + +func variadicG2(_: repeat each T) -> some P { + G2() +} + +// CHECK: define private ptr @"get_witness_table 23variadic_generic_opaque2G2VyAA2S1V_AA2S2VQPGAA1PHPAeA1QHPyHC_AgaJHPyHCHX_HC" +// CHECK: define private ptr @"get_witness_table 23variadic_generic_opaque1QRzlAA2G2VyAA2S1V_xQPGAA1PHPAfaBHPyHC_xAaBHD1_HX_HC" +// CHECK: define private ptr @"get_witness_table Rvz23variadic_generic_opaque1QRzlAA2G2VyxxQp_QPGAA1PHPxAaBHD1__HX_HC" \ No newline at end of file From 50794eec904b1115079ae8b9bdd4430f417e8fe6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 12:59:45 -0400 Subject: [PATCH 02/10] ASTMangler: Remove an unused parameter --- include/swift/AST/ASTMangler.h | 3 +-- lib/AST/ASTMangler.cpp | 26 ++++++++++---------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index ee1d3059b2914..0e3ea7ca6862e 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -443,8 +443,7 @@ class ASTMangler : public Mangler { /// Append any retroactive conformances. void appendRetroactiveConformances(Type type, GenericSignature sig); void appendRetroactiveConformances(SubstitutionMap subMap, - GenericSignature sig, - ModuleDecl *fromModule); + GenericSignature sig); void appendImplFunctionType(SILFunctionType *fn, GenericSignature sig, const ValueDecl *forDecl = nullptr); void appendOpaqueTypeArchetype(ArchetypeType *archetype, diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 5ec7ae62540c3..b9cf4649df933 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1875,14 +1875,13 @@ static bool isRetroactiveConformance(const RootProtocolConformance *root) { /// Determine whether the given protocol conformance contains a retroactive /// protocol conformance anywhere in it. static bool containsRetroactiveConformance( - ProtocolConformanceRef conformanceRef, - ModuleDecl *module) { + ProtocolConformanceRef conformanceRef) { if (!conformanceRef.isPack() && !conformanceRef.isConcrete()) return false; if (conformanceRef.isPack()) { for (auto patternConf : conformanceRef.getPack()->getPatternConformances()) { - if (containsRetroactiveConformance(patternConf, module)) + if (containsRetroactiveConformance(patternConf)) return true; } @@ -1912,7 +1911,7 @@ static bool containsRetroactiveConformance( // for indexing purposes. continue; } - if (containsRetroactiveConformance(conformance, module)) { + if (containsRetroactiveConformance(conformance)) { return true; } } @@ -1921,8 +1920,7 @@ static bool containsRetroactiveConformance( } void ASTMangler::appendRetroactiveConformances(SubstitutionMap subMap, - GenericSignature sig, - ModuleDecl *fromModule) { + GenericSignature sig) { if (subMap.empty()) return; unsigned numProtocolRequirements = 0; @@ -1942,7 +1940,7 @@ void ASTMangler::appendRetroactiveConformances(SubstitutionMap subMap, continue; // Skip non-retroactive conformances. - if (!containsRetroactiveConformance(conformance, fromModule)) + if (!containsRetroactiveConformance(conformance)) continue; if (conformance.isConcrete()) @@ -1972,7 +1970,7 @@ void ASTMangler::appendRetroactiveConformances(Type type, GenericSignature sig) subMap = type->getContextSubstitutionMap(module, nominal); } - appendRetroactiveConformances(subMap, sig, module); + appendRetroactiveConformances(subMap, sig); } void ASTMangler::appendSymbolicExtendedExistentialType( @@ -1995,11 +1993,7 @@ void ASTMangler::appendSymbolicExtendedExistentialType( for (auto argType : genInfo.Generalization.getReplacementTypes()) appendType(argType, sig, forDecl); - // What module should be used here? The existential isn't anchored - // to any given module; we should just treat conformances as - // retroactive if they're "objectively" retroactive. - appendRetroactiveConformances(genInfo.Generalization, sig, - /*from module*/ nullptr); + appendRetroactiveConformances(genInfo.Generalization, sig); } appendOperator("Xj"); @@ -2190,7 +2184,7 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn, } if (auto subs = fn->getInvocationSubstitutions()) { appendFlatGenericArgs(subs, sig, forDecl); - appendRetroactiveConformances(subs, sig, Mod); + appendRetroactiveConformances(subs, sig); } if (auto subs = fn->getPatternSubstitutions()) { appendGenericSignature(subs.getGenericSignature()); @@ -2199,7 +2193,7 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn, ? fn->getInvocationGenericSignature() : outerGenericSig; appendFlatGenericArgs(subs, sig, forDecl); - appendRetroactiveConformances(subs, sig, Mod); + appendRetroactiveConformances(subs, sig); } OpArgs.push_back('_'); @@ -2235,7 +2229,7 @@ void ASTMangler::appendOpaqueTypeArchetype(ArchetypeType *archetype, appendOpaqueDeclName(opaqueDecl); bool isFirstArgList = true; appendBoundGenericArgs(opaqueDecl, sig, subs, isFirstArgList, forDecl); - appendRetroactiveConformances(subs, sig, opaqueDecl->getParentModule()); + appendRetroactiveConformances(subs, sig); appendOperator("Qo", Index(genericParam->getIndex())); } else { From 1b798d03f55efd8ba73f5d990ec99da1f586c22f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 13:10:39 -0400 Subject: [PATCH 03/10] ASTMangler: Consolidate duplicated code for conditional requirements --- lib/AST/ASTMangler.cpp | 88 +++++++++++------------- test/IRGen/variadic_generic_opaque.swift | 13 +++- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index b9cf4649df933..c75557168fc1b 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1872,6 +1872,31 @@ static bool isRetroactiveConformance(const RootProtocolConformance *root) { return conformance->isRetroactive(); } +template +static bool forEachConditionalConformance(const ProtocolConformance *conformance, + Fn fn) { + auto *rootConformance = conformance->getRootConformance(); + + auto subMap = conformance->getSubstitutionMap(); + for (auto requirement : rootConformance->getConditionalRequirements()) { + if (requirement.getKind() != RequirementKind::Conformance) + continue; + ProtocolDecl *proto = requirement.getProtocolDecl(); + auto conformance = subMap.lookupConformance( + requirement.getFirstType()->getCanonicalType(), proto); + if (conformance.isInvalid()) { + // This should only happen when mangling invalid ASTs, but that happens + // for indexing purposes. + continue; + } + + if (fn(requirement.getFirstType().subst(subMap), conformance)) + return true; + } + + return false; +} + /// Determine whether the given protocol conformance contains a retroactive /// protocol conformance anywhere in it. static bool containsRetroactiveConformance( @@ -1899,24 +1924,10 @@ static bool containsRetroactiveConformance( // If the conformance is conditional and any of the substitutions used to // satisfy the conditions are retroactive, it's retroactive. - auto subMap = conformance->getSubstitutionMap(); - for (auto requirement : rootConformance->getConditionalRequirements()) { - if (requirement.getKind() != RequirementKind::Conformance) - continue; - ProtocolDecl *proto = requirement.getProtocolDecl(); - auto conformance = subMap.lookupConformance( - requirement.getFirstType()->getCanonicalType(), proto); - if (conformance.isInvalid()) { - // This should only happen when mangling invalid ASTs, but that happens - // for indexing purposes. - continue; - } - if (containsRetroactiveConformance(conformance)) { - return true; - } - } - - return false; + return forEachConditionalConformance(conformance, + [&](Type substType, ProtocolConformanceRef substConf) -> bool { + return containsRetroactiveConformance(substConf); + }); } void ASTMangler::appendRetroactiveConformances(SubstitutionMap subMap, @@ -4169,48 +4180,27 @@ void ASTMangler::appendAnyProtocolConformance( void ASTMangler::appendConcreteProtocolConformance( const ProtocolConformance *conformance, GenericSignature sig) { - auto module = conformance->getDeclContext()->getParentModule(); - // Conforming type. Type conformingType = conformance->getType(); if (conformingType->hasArchetype()) conformingType = conformingType->mapTypeOutOfContext(); - appendType(conformingType->getCanonicalType(), sig); + appendType(conformingType->getReducedType(sig), sig); // Protocol conformance reference. appendProtocolConformanceRef(conformance->getRootConformance()); // Conditional conformance requirements. bool firstRequirement = true; - for (const auto &conditionalReq : conformance->getConditionalRequirements()) { - switch (conditionalReq.getKind()) { - case RequirementKind::SameShape: - llvm_unreachable("Same-shape requirement not supported here"); - case RequirementKind::Layout: - case RequirementKind::SameType: - case RequirementKind::Superclass: - continue; - - case RequirementKind::Conformance: { - auto type = conditionalReq.getFirstType(); - if (type->hasArchetype()) - type = type->mapTypeOutOfContext(); - CanType canType = type->getReducedType(sig); - auto proto = conditionalReq.getProtocolDecl(); - - ProtocolConformanceRef conformance; - - if (canType->isTypeParameter() || canType->is()){ - conformance = ProtocolConformanceRef(proto); - } else { - conformance = module->lookupConformance(canType, proto); - } - appendAnyProtocolConformance(sig, canType, conformance); + forEachConditionalConformance(conformance, + [&](Type substType, ProtocolConformanceRef substConf) -> bool { + if (substType->hasArchetype()) + substType = substType->mapTypeOutOfContext(); + CanType canType = substType->getReducedType(sig); + appendAnyProtocolConformance(sig, canType, substConf); appendListSeparator(firstRequirement); - break; - } - } - } + return false; + }); + if (firstRequirement) appendOperator("y"); diff --git a/test/IRGen/variadic_generic_opaque.swift b/test/IRGen/variadic_generic_opaque.swift index e679867248c32..f5f757a726ffa 100644 --- a/test/IRGen/variadic_generic_opaque.swift +++ b/test/IRGen/variadic_generic_opaque.swift @@ -39,4 +39,15 @@ func variadicG2(_: repeat each T) -> some P { // CHECK: define private ptr @"get_witness_table 23variadic_generic_opaque2G2VyAA2S1V_AA2S2VQPGAA1PHPAeA1QHPyHC_AgaJHPyHCHX_HC" // CHECK: define private ptr @"get_witness_table 23variadic_generic_opaque1QRzlAA2G2VyAA2S1V_xQPGAA1PHPAfaBHPyHC_xAaBHD1_HX_HC" -// CHECK: define private ptr @"get_witness_table Rvz23variadic_generic_opaque1QRzlAA2G2VyxxQp_QPGAA1PHPxAaBHD1__HX_HC" \ No newline at end of file +// CHECK: define private ptr @"get_witness_table Rvz23variadic_generic_opaque1QRzlAA2G2VyxxQp_QPGAA1PHPxAaBHD1__HX_HC" + +// Conditional same-shape requirement +public struct Outer { + public struct Inner {} +} + +extension Outer.Inner: P where (repeat (each T, each U)): Any {} + +func concreteOuterInner() -> some P { + Outer.Inner() +} \ No newline at end of file From 3630ce71305996be702e00ba54c333b698a5ce2c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 14:51:59 -0400 Subject: [PATCH 04/10] AST: Fix swift::substOpaqueTypesWithUnderlyingTypes() to pass PreservePackExpansionLevel Fixes rdar://problem/123645784. --- lib/AST/Type.cpp | 6 +++--- .../variadic_generic_opaque_multifile_other.swift | 11 +++++++++++ test/SILGen/variadic_generic_opaque_multifile.swift | 6 ++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 test/SILGen/Inputs/variadic_generic_opaque_multifile_other.swift create mode 100644 test/SILGen/variadic_generic_opaque_multifile.swift diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 8a7d3e4284040..efa2ba074059e 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -5364,10 +5364,10 @@ CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, ReplaceOpaqueTypesWithUnderlyingTypes replacer( context.getContext(), context.getResilienceExpansion(), context.isWholeModuleContext()); - SubstOptions flags = SubstFlags::SubstituteOpaqueArchetypes; + SubstOptions flags = (SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); if (allowLoweredTypes) - flags = - SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes; + flags |= SubstFlags::AllowLoweredTypes; return ty.subst(replacer, replacer, flags)->getCanonicalType(); } diff --git a/test/SILGen/Inputs/variadic_generic_opaque_multifile_other.swift b/test/SILGen/Inputs/variadic_generic_opaque_multifile_other.swift new file mode 100644 index 0000000000000..433554b9e47b2 --- /dev/null +++ b/test/SILGen/Inputs/variadic_generic_opaque_multifile_other.swift @@ -0,0 +1,11 @@ +public protocol P { + func f() +} + +public struct G: P { + public func f() {} +} + +public func callee(_: repeat each T) -> some P { + G() +} diff --git a/test/SILGen/variadic_generic_opaque_multifile.swift b/test/SILGen/variadic_generic_opaque_multifile.swift new file mode 100644 index 0000000000000..434cd7fba4e2d --- /dev/null +++ b/test/SILGen/variadic_generic_opaque_multifile.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/variadic_generic_opaque_multifile_other.swift -disable-availability-checking +// RUN: %target-swift-frontend -emit-silgen %s %S/Inputs/variadic_generic_opaque_multifile_other.swift -disable-availability-checking + +public func caller() { + callee(1, 2, 3).f() +} From 082246094b16bcd6d040cfbbe7157e5ed67d220c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 14:56:13 -0400 Subject: [PATCH 05/10] AST: Pass SubstFlags::PreservePackExpansionLevel in a few more places --- lib/APIDigester/ModuleAnalyzerNodes.cpp | 4 +++- lib/AST/TypeSubstitution.cpp | 7 +++++-- lib/SIL/IR/SILFunctionType.cpp | 6 ++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index bb7fb0fce582e..029d5e8fa3576 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -1633,7 +1633,9 @@ SwiftDeclCollector::constructTypeNode(Type T, TypeInitInfo Info) { ReplaceOpaqueTypesWithUnderlyingTypes replacer( /*inContext=*/nullptr, ResilienceExpansion::Maximal, /*isWholeModuleContext=*/false); - T = T.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes) + T = T.subst(replacer, replacer, + SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel) ->getCanonicalType(); } } diff --git a/lib/AST/TypeSubstitution.cpp b/lib/AST/TypeSubstitution.cpp index 9f1f2d4a9b5ad..ac01fbdee1fde 100644 --- a/lib/AST/TypeSubstitution.cpp +++ b/lib/AST/TypeSubstitution.cpp @@ -1016,7 +1016,9 @@ static Type substOpaqueTypesWithUnderlyingTypesRec( llvm::DenseSet &decls) { ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion, isWholeModuleContext, decls); - return ty.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); + return ty.subst(replacer, replacer, + SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); } /// Checks that \p dc has access to \p ty for the purposes of an opaque @@ -1169,7 +1171,8 @@ static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypesRec( ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion, isWholeModuleContext, decls); return ref.subst(origType, replacer, replacer, - SubstFlags::SubstituteOpaqueArchetypes); + SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); } ProtocolConformanceRef swift::substOpaqueTypesWithUnderlyingTypes( diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 462b53f21fa43..bc565776b2c4f 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -4800,14 +4800,16 @@ static bool areABICompatibleParamsOrReturns(SILType a, SILType b, if (aa.getASTType()->hasOpaqueArchetype()) opaqueTypesSubstituted = aa.subst(inFunction->getModule(), replacer, replacer, CanGenericSignature(), - SubstFlags::SubstituteOpaqueArchetypes); + SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); auto opaqueTypesSubstituted2 = bb; if (bb.getASTType()->hasOpaqueArchetype()) opaqueTypesSubstituted2 = bb.subst(inFunction->getModule(), replacer, replacer, CanGenericSignature(), - SubstFlags::SubstituteOpaqueArchetypes); + SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); if (opaqueTypesSubstituted == opaqueTypesSubstituted2) continue; } From cbb2374d76d8d5d49516ace2f46ec124094066ca Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 14:56:25 -0400 Subject: [PATCH 06/10] AST: Small cleanup for transformWithPosition() --- lib/AST/Type.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index efa2ba074059e..e40c36ccc33ef 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4455,11 +4455,8 @@ case TypeKind::Id: auto sig = opaque->getDecl()->getGenericSignature(); auto newSubMap = SubstitutionMap::get(sig, - [&](SubstitutableType *t) -> Type { - auto index = sig->getGenericParamOrdinal(cast(t)); - return newSubs[index]; - }, - LookUpConformanceInModule(opaque->getDecl()->getModuleContext())); + QueryReplacementTypeArray{sig, newSubs}, + LookUpConformanceInModule(opaque->getDecl()->getModuleContext())); return OpaqueTypeArchetypeType::get(opaque->getDecl(), opaque->getInterfaceType(), newSubMap); From cbf4dce06893646024b2065ee0b4bbc5280ee592 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 14:56:40 -0400 Subject: [PATCH 07/10] IRGen: Clean up substOpaqueTypesWithUnderlyingTypes() wrappers --- lib/IRGen/MetadataRequest.cpp | 39 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 768e85f4458f1..cd059c8dfaebf 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -528,13 +528,10 @@ CanType IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type) { // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - getSwiftModule(), ResilienceExpansion::Maximal, - getSILModule().isWholeModule()); - auto underlyingTy = - type.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes) - ->getCanonicalType(); - return underlyingTy; + auto context = TypeExpansionContext::maximal(getSwiftModule(), + getSILModule().isWholeModule()); + return swift::substOpaqueTypesWithUnderlyingTypes(type, context, + /*allowLoweredTypes=*/false); } return type; @@ -545,13 +542,12 @@ SILType IRGenModule::substOpaqueTypesWithUnderlyingTypes( // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type.getASTType()->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - getSwiftModule(), ResilienceExpansion::Maximal, - getSILModule().isWholeModule()); - auto underlyingTy = - type.subst(getSILModule(), replacer, replacer, genericSig, - SubstFlags::SubstituteOpaqueArchetypes); - return underlyingTy; + auto context = TypeExpansionContext::maximal(getSwiftModule(), + getSILModule().isWholeModule()); + return SILType::getPrimitiveType( + swift::substOpaqueTypesWithUnderlyingTypes(type.getASTType(), context, + /*allowLoweredTypes=*/true), + type.getCategory()); } return type; @@ -563,15 +559,12 @@ IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type, // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - getSwiftModule(), ResilienceExpansion::Maximal, - getSILModule().isWholeModule()); - auto substConformance = conformance.subst( - type, replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); - auto underlyingTy = - type.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes) - ->getCanonicalType(); - return std::make_pair(underlyingTy, substConformance); + auto context = TypeExpansionContext::maximal(getSwiftModule(), + getSILModule().isWholeModule()); + return std::make_pair( + swift::substOpaqueTypesWithUnderlyingTypes(type, context, + /*allowLoweredTypes=*/false), + swift::substOpaqueTypesWithUnderlyingTypes(conformance, type, context)); } return std::make_pair(type, conformance); From 153da375214df83c35219ff793f10bea7940850e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 15:59:25 -0400 Subject: [PATCH 08/10] IRGen: Use getMaximalTypeExpansionContext() --- lib/IRGen/MetadataRequest.cpp | 9 +++------ .../opaque_associated_type_requirements.swift | 16 ++++------------ 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index cd059c8dfaebf..013c9baf623ce 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -528,8 +528,7 @@ CanType IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type) { // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - auto context = TypeExpansionContext::maximal(getSwiftModule(), - getSILModule().isWholeModule()); + auto context = getMaximalTypeExpansionContext(); return swift::substOpaqueTypesWithUnderlyingTypes(type, context, /*allowLoweredTypes=*/false); } @@ -542,8 +541,7 @@ SILType IRGenModule::substOpaqueTypesWithUnderlyingTypes( // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type.getASTType()->hasOpaqueArchetype()) { - auto context = TypeExpansionContext::maximal(getSwiftModule(), - getSILModule().isWholeModule()); + auto context = getMaximalTypeExpansionContext(); return SILType::getPrimitiveType( swift::substOpaqueTypesWithUnderlyingTypes(type.getASTType(), context, /*allowLoweredTypes=*/true), @@ -559,8 +557,7 @@ IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type, // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - auto context = TypeExpansionContext::maximal(getSwiftModule(), - getSILModule().isWholeModule()); + auto context = getMaximalTypeExpansionContext(); return std::make_pair( swift::substOpaqueTypesWithUnderlyingTypes(type, context, /*allowLoweredTypes=*/false), diff --git a/test/Reflection/opaque_associated_type_requirements.swift b/test/Reflection/opaque_associated_type_requirements.swift index c3a577141c31a..05de98ebaa7f6 100644 --- a/test/Reflection/opaque_associated_type_requirements.swift +++ b/test/Reflection/opaque_associated_type_requirements.swift @@ -10,20 +10,12 @@ // RUN: %target-swift-reflection-dump %t/AssociatedTypeRequirements | %FileCheck %s +// This test is useless and was just relying on a bug where we didn't substitute away +// known opaque return types in non-WMO mode. + // CHECK: ASSOCIATED TYPES: // CHECK: - AssociatedTypeRequirements.Foo : AssociatedTypeRequirements.myProto -// CHECK-NEXT: typealias PerformReturn = opaque type symbolic reference -// CHECK-NEXT: opaque type symbolic reference -// CHECK-NEXT: (bound_generic_struct AssociatedTypeRequirements.Bar - -// CHECK: conformance requirements: -// CHECK-NEXT: AssociatedTypeRequirements.protoA -// CHECK-NEXT: AssociatedTypeRequirements.protoB -// CHECK-NEXT: testModB.testModBProtocol - -// CHECK: same-type requirements: -// CHECK-NEXT: A.AssociatedTypeRequirements.protoB.K = Sf (Swift.Float) -// CHECK-NEXT: A.AssociatedTypeRequirements.protoA.T = 8testModB0aB7BStructV (testModB.testModBStruct) +// CHECK-NEXT: typealias PerformReturn = AssociatedTypeRequirements.Bar import testModB From 072d12a952f1a90aadf9e371224879468cf43505 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 4 Apr 2024 16:01:13 -0400 Subject: [PATCH 09/10] AST: Move substOpaqueTypesWithUnderlyingTypes() to TypeSubstitution.cpp --- lib/AST/Type.cpp | 17 ----------------- lib/AST/TypeSubstitution.cpp | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index e40c36ccc33ef..fa2c1d539d57e 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -5351,23 +5351,6 @@ Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened, return opened; } -CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, - TypeExpansionContext context, - bool allowLoweredTypes) { - if (!context.shouldLookThroughOpaqueTypeArchetypes() || - !ty->hasOpaqueArchetype()) - return ty; - - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - context.getContext(), context.getResilienceExpansion(), - context.isWholeModuleContext()); - SubstOptions flags = (SubstFlags::SubstituteOpaqueArchetypes | - SubstFlags::PreservePackExpansionLevel); - if (allowLoweredTypes) - flags |= SubstFlags::AllowLoweredTypes; - return ty.subst(replacer, replacer, flags)->getCanonicalType(); -} - AnyFunctionType *AnyFunctionType::getWithoutDifferentiability() const { SmallVector newParams; for (auto ¶m : getParams()) { diff --git a/lib/AST/TypeSubstitution.cpp b/lib/AST/TypeSubstitution.cpp index ac01fbdee1fde..f4524600c55e5 100644 --- a/lib/AST/TypeSubstitution.cpp +++ b/lib/AST/TypeSubstitution.cpp @@ -1164,6 +1164,23 @@ operator()(SubstitutableType *maybeOpaqueType) const { return substTy; } +CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, + TypeExpansionContext context, + bool allowLoweredTypes) { + if (!context.shouldLookThroughOpaqueTypeArchetypes() || + !ty->hasOpaqueArchetype()) + return ty; + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + SubstOptions flags = (SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); + if (allowLoweredTypes) + flags |= SubstFlags::AllowLoweredTypes; + return ty.subst(replacer, replacer, flags)->getCanonicalType(); +} + static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypesRec( ProtocolConformanceRef ref, Type origType, const DeclContext *inContext, ResilienceExpansion contextExpansion, bool isWholeModuleContext, From 6d3229a420bca33ab3dcc8f7dffd008668f55393 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 3 Jan 2024 12:40:11 -0500 Subject: [PATCH 10/10] Sema: Update tuple conformance test case to use pack iteration Unfortunately SILGen still blows up when emitting witness thunks here. --- test/Generics/tuple-conformances.swift | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/test/Generics/tuple-conformances.swift b/test/Generics/tuple-conformances.swift index cffc8a70aec15..b963769724f5d 100644 --- a/test/Generics/tuple-conformances.swift +++ b/test/Generics/tuple-conformances.swift @@ -96,18 +96,30 @@ extension Tuple: Equatable where repeat each Element: Equatable { // FIXME: Hack @_disfavoredOverload public static func ==(lhs: Self, rhs: Self) -> Bool { - var result = true - func update(lhs: E, rhs: E) { - result = result && (lhs == rhs) + for (l, r) in repeat (each lhs, each rhs) { + if l != r { return false } } - - repeat update(lhs: each lhs, rhs: each rhs) - return result + return true } } extension Tuple: Hashable where repeat each Element: Hashable { public func hash(into hasher: inout Hasher) { - repeat (each self).hash(into: &hasher) + for elt in repeat each self { + elt.hash(into: &hasher) + } } } + +extension Tuple: Comparable where repeat each Element: Comparable { + // FIXME: Hack + @_disfavoredOverload + public static func <(lhs: Self, rhs: Self) -> Bool { + for (l, r) in repeat (each lhs, each rhs) { + if l > r { return false } + if l < r { return true } + } + return false + } +} +