From f21a306ae55d1f616d00dd8645d7d0ca9472b95c Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 23 May 2020 11:07:38 -0400 Subject: [PATCH 01/15] [AST] Introduce BuiltinProtocolConformance --- include/swift/AST/ASTContext.h | 6 ++ include/swift/AST/ProtocolConformance.h | 114 +++++++++++++++++++++- include/swift/SIL/SILCloner.h | 5 +- lib/AST/ASTContext.cpp | 24 +++++ lib/AST/ASTDumper.cpp | 6 +- lib/AST/ASTMangler.cpp | 27 +++-- lib/AST/ASTPrinter.cpp | 8 +- lib/AST/ProtocolConformance.cpp | 52 +++++++++- lib/SIL/IR/SILModule.cpp | 7 +- lib/SILGen/SILGenLazyConformance.cpp | 4 +- lib/Sema/CSSimplify.cpp | 3 +- lib/Serialization/DeclTypeRecordNodes.def | 4 +- lib/Serialization/Deserialization.cpp | 30 +++++- lib/Serialization/ModuleFormat.h | 12 ++- lib/Serialization/Serialization.cpp | 16 ++- lib/Serialization/SerializeSIL.cpp | 3 +- 16 files changed, 294 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index c9f3fbe06e187..b41dcfbc5fee1 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -103,6 +103,7 @@ namespace swift { class InheritedProtocolConformance; class SelfProtocolConformance; class SpecializedProtocolConformance; + class BuiltinProtocolConformance; enum class ProtocolConformanceState; class Pattern; enum PointerTypeKind : unsigned; @@ -948,6 +949,11 @@ class ASTContext final { SelfProtocolConformance * getSelfConformance(ProtocolDecl *protocol); + /// Produce the builtin conformance for some structural type to some protocol. + BuiltinProtocolConformance * + getBuiltinConformance(Type type, ProtocolDecl *protocol, + ArrayRef conformances); + /// A callback used to produce a diagnostic for an ill-formed protocol /// conformance that was type-checked before we're actually walking the /// conformance itself, along with a bit indicating whether this diagnostic diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 1f61cdf438175..31fda612c717f 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -66,7 +66,10 @@ enum class ProtocolConformanceKind { Specialized, /// Conformance of a generic class type projected through one of its /// superclass's conformances. - Inherited + Inherited, + /// Builtin conformances are special conformaces that the runtime handles + /// and isn't implemented directly in Swift. + Builtin }; /// Describes the state of a protocol conformance, which may be complete, @@ -329,7 +332,9 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// - the type is directly declared to conform to the protocol (a /// normal conformance) or /// - the protocol's existential type is known to conform to itself (a -/// self-conformance). +/// self-conformance) or +/// - the type's conformance is declared within the runtime (a builtin +/// conformance). class RootProtocolConformance : public ProtocolConformance { protected: RootProtocolConformance(ProtocolConformanceKind kind, Type conformingType) @@ -380,7 +385,8 @@ class RootProtocolConformance : public ProtocolConformance { static bool classof(const ProtocolConformance *conformance) { return conformance->getKind() == ProtocolConformanceKind::Normal || - conformance->getKind() == ProtocolConformanceKind::Self; + conformance->getKind() == ProtocolConformanceKind::Self || + conformance->getKind() == ProtocolConformanceKind::Builtin; } }; @@ -1014,6 +1020,106 @@ class InheritedProtocolConformance : public ProtocolConformance, } }; +/// A builtin conformance appears when a special non-nominal type has a runtime +/// declared conformance. E.g. the runtime implements Equatable for tuples. +class BuiltinProtocolConformance final : public RootProtocolConformance, + private llvm::TrailingObjects { + friend ASTContext; + friend TrailingObjects; + + ProtocolDecl *protocol = nullptr; + size_t numConformances; + + mutable Optional> conditionalConformances = None; + + BuiltinProtocolConformance(Type conformingType, ProtocolDecl *protocol, + ArrayRef conformances); + + size_t numTrailingObjects(OverloadToken) const { + return numConformances; + } + +public: + /// Get the protocol being conformed to. + ProtocolDecl *getProtocol() const { + return protocol; + } + + /// Get the trailing conformances that this builtin conformance needs. + MutableArrayRef getConformances() { + return {getTrailingObjects(), numConformances}; + } + + /// Get the trailing conformances that this builtin conformance needs. + ArrayRef getConformances() const { + return {getTrailingObjects(), numConformances}; + } + + /// Get any requirements that must be satisfied for this conformance to apply. + Optional> + getConditionalRequirementsIfAvailable() const { + return ArrayRef(); + } + + /// Get any requirements that must be satisfied for this conformance to apply. + ArrayRef getConditionalRequirements() const; + + /// Get the declaration context that contains the nominal type declaration. + DeclContext *getDeclContext() const { + return getProtocol(); + } + + /// Retrieve the state of this conformance. + ProtocolConformanceState getState() const { + return ProtocolConformanceState::Complete; + } + + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const { + return ConformanceEntryKind::Synthesized; + } + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const { + return nullptr; + } + + bool hasTypeWitness(AssociatedTypeDecl *assocType) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Retrieve the type witness and type decl (if one exists) + /// for the given associated type. + TypeWitnessAndDecl + getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, + SubstOptions options=None) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Given that the requirement signature of the protocol directly states + /// that the given dependent type must conform to the given protocol, + /// return its associated conformance. + ProtocolConformanceRef + getAssociatedConformance(Type assocType, ProtocolDecl *protocol) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Retrieve the witness corresponding to the given value requirement. + ConcreteDeclRef getWitnessDeclRef(ValueDecl *requirement) const { + return ConcreteDeclRef(requirement); + } + + /// Determine whether the witness for the given requirement + /// is either the default definition or was otherwise deduced. + bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + static bool classof(const ProtocolConformance *conformance) { + return conformance->getKind() == ProtocolConformanceKind::Builtin; + } +}; + inline bool ProtocolConformance::isInvalid() const { return getRootConformance()->isInvalid(); } diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 8e0a0110561d7..c51d0b7831019 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -2093,7 +2093,8 @@ SILCloner::visitWitnessMethodInst(WitnessMethodInst *Inst) { if (conformance.isConcrete()) { CanType Ty = conformance.getConcrete()->getType()->getCanonicalType(); - if (Ty != newLookupType) { + if (Ty != newLookupType && + !isa(conformance.getConcrete())) { assert( (Ty->isExactSuperclassOf(newLookupType) || getBuilder().getModule().Types.getLoweredRValueType( diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 7cf42afa2d904..212e923c1f136 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -406,6 +406,10 @@ struct ASTContext::Implementation { /// The set of inherited protocol conformances. llvm::FoldingSet InheritedConformances; + /// The set of builtin protocol conformances. + llvm::DenseMap, + BuiltinProtocolConformance *> BuiltinConformances; + /// The set of substitution maps (uniqued by their storage). llvm::FoldingSet SubstitutionMaps; @@ -2014,6 +2018,24 @@ ASTContext::getSelfConformance(ProtocolDecl *protocol) { return entry; } +/// Produce the builtin conformance for some non-nominal to some protocol. +BuiltinProtocolConformance * +ASTContext::getBuiltinConformance(Type type, ProtocolDecl *protocol, + ArrayRef conformances) { + auto key = std::make_pair(type, protocol); + auto &builtinConformances = + getImpl().getArena(AllocationArena::Permanent).BuiltinConformances; + auto &entry = builtinConformances[key]; + if (!entry) { + auto size = BuiltinProtocolConformance:: + totalSizeToAlloc(conformances.size()); + auto mem = this->Allocate(size, alignof(BuiltinProtocolConformance), + AllocationArena::Permanent); + entry = new (mem) BuiltinProtocolConformance(type, protocol, conformances); + } + return entry; +} + /// If one of the ancestor conformances already has a matching type, use /// that instead. static ProtocolConformance *collapseSpecializedConformance( @@ -2030,6 +2052,7 @@ static ProtocolConformance *collapseSpecializedConformance( case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Inherited: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: // If the conformance matches, return it. if (conformance->getType()->isEqual(type)) { for (auto subConformance : substitutions.getConformances()) @@ -2252,6 +2275,7 @@ size_t ASTContext::Implementation::Arena::getTotalMemory() const { // NormalConformances ? // SpecializedConformances ? // InheritedConformances ? + // BuiltinConformances ? } void AbstractFunctionDecl::setForeignErrorConvention( diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 7cc169599a95c..9228cfb175652 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -3310,6 +3310,10 @@ static void dumpProtocolConformanceRec( visited); break; } + + case ProtocolConformanceKind::Builtin: { + printCommon("builtin"); + } } PrintWithColorRAII(out, ParenthesisColor) << ')'; diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 490385afbe1a1..cc9d60109fec2 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -200,9 +200,11 @@ std::string ASTMangler::mangleWitnessTable(const RootProtocolConformance *C) { if (isa(C)) { appendProtocolConformance(C); appendOperator("WP"); - } else { + } else if (isa(C)) { appendProtocolName(cast(C)->getProtocol()); appendOperator("WS"); + } else { + llvm_unreachable("mangling unknown conformance kind"); } return finalize(); } @@ -226,7 +228,11 @@ std::string ASTMangler::mangleWitnessThunk( } if (Conformance) { - appendOperator(isa(Conformance) ? "TS" : "TW"); + if (isa(Conformance)) { + appendOperator("TS"); + } else { + appendOperator("TW"); + } } return finalize(); } @@ -1462,7 +1468,8 @@ void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) { static bool conformanceHasIdentity(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); + assert(isa(root) || + isa(root)); return true; } @@ -1483,8 +1490,9 @@ static bool conformanceHasIdentity(const RootProtocolConformance *root) { static bool isRetroactiveConformance(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); - return false; // self-conformances are never retroactive. + assert(isa(root) || + isa(root)); + return false; // self-conformances are never retroactive. nor are builtin. } return conformance->isRetroactive(); @@ -2910,6 +2918,10 @@ ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) { appendModule(Mod, DC->getAsDecl()->getAlternateModuleName()); } + // If this is a non-nominal type, we're done. + if (!conformingType->getAnyNominal()) + return; + contextSig = conformingType->getAnyNominal()->getGenericSignatureOfContext(); @@ -2934,6 +2946,9 @@ void ASTMangler::appendProtocolConformanceRef( assert(DC->getAsDecl()); appendModule(conformance->getDeclContext()->getParentModule(), DC->getAsDecl()->getAlternateModuleName()); + // Builtin conformances are always from the Swift module. + } else if (isa(conformance)) { + appendOperator("HP"); } else if (conformance->getDeclContext()->getParentModule() == conformance->getType()->getAnyNominal()->getParentModule()) { appendOperator("HP"); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 5ebaa4d44d22d..3764a7677dc99 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -5035,6 +5035,12 @@ void ProtocolConformance::printName(llvm::raw_ostream &os, os << ")"; break; } + case ProtocolConformanceKind::Builtin: { + auto builtin = cast(this); + os << builtin->getProtocol()->getName() + << " type " << builtin->getType(); + break; + } } } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 62b4791e29c88..f890f5bdbc9ea 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -202,6 +202,11 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Inherited: \ return cast(this)->Method Args; \ + case ProtocolConformanceKind::Builtin: \ + static_assert(&ProtocolConformance::Method != \ + &BuiltinProtocolConformance::Method, \ + "Must override BuiltinProtocolConformance::" #Method); \ + return cast(this)->Method Args; \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -213,6 +218,7 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ case ProtocolConformanceKind::Inherited: \ + case ProtocolConformanceKind::Builtin: \ llvm_unreachable("not a root conformance"); \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -275,6 +281,8 @@ ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement) const { case ProtocolConformanceKind::Specialized: return cast(this) ->getGenericConformance()->getWitnessDecl(requirement); + case ProtocolConformanceKind::Builtin: + return requirement; } llvm_unreachable("unhandled kind"); } @@ -296,6 +304,7 @@ GenericEnvironment *ProtocolConformance::getGenericEnvironment() const { return getDeclContext()->getGenericEnvironmentOfContext(); case ProtocolConformanceKind::Specialized: + case ProtocolConformanceKind::Builtin: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open // type variables. @@ -317,6 +326,7 @@ GenericSignature ProtocolConformance::getGenericSignature() const { return getDeclContext()->getGenericSignatureOfContext(); case ProtocolConformanceKind::Specialized: + case ProtocolConformanceKind::Builtin: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open // type variables. @@ -334,6 +344,7 @@ SubstitutionMap ProtocolConformance::getSubstitutions(ModuleDecl *M) const { switch (parent->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: parent = @@ -1110,6 +1121,7 @@ ProtocolConformance::getRootConformance() const { switch (C->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: return cast(C); case ProtocolConformanceKind::Inherited: C = cast(C) @@ -1159,6 +1171,7 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, subMap); } case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: return const_cast(this); case ProtocolConformanceKind::Inherited: { // Substitute the base. @@ -1418,7 +1431,8 @@ bool ProtocolConformance::isCanonical() const { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { return true; } case ProtocolConformanceKind::Inherited: { @@ -1447,7 +1461,8 @@ ProtocolConformance *ProtocolConformance::getCanonicalConformance() { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { // Root conformances are always canonical by construction. return this; } @@ -1489,6 +1504,37 @@ ProtocolConformanceRef::getCanonicalConformanceRef() const { return ProtocolConformanceRef(getConcrete()->getCanonicalConformance()); } +BuiltinProtocolConformance::BuiltinProtocolConformance( + Type conformingType, ProtocolDecl *protocol, + ArrayRef conformances) : + RootProtocolConformance(ProtocolConformanceKind::Builtin, conformingType), + protocol(protocol), numConformances(conformances.size()) { + std::uninitialized_copy(conformances.begin(), conformances.end(), + getTrailingObjects()); +} + +ArrayRef +BuiltinProtocolConformance::getConditionalRequirements() const { + if (conditionalConformances == None) { + // Right now only tuples are builtin and have conditional conformances. + if (auto tuple = getType()->getAs()) { + SmallVector requirements; + + for (size_t i = 0; i != getConformances().size(); i += 1) { + auto req = Requirement(RequirementKind::Conformance, + tuple->getElement(i).getType(), + getProtocol()->getDeclaredType()); + requirements.push_back(req); + } + + conditionalConformances = getProtocol()->getASTContext() + .AllocateCopy(requirements); + } + } + + return *conditionalConformances; +} + // See swift/Basic/Statistic.h for declaration: this enables tracing // ProtocolConformances, is defined here to avoid too much layering violation / // circular linkage dependency. diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index cc30fc7f5d07b..8d35fb3750a33 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -220,6 +220,11 @@ SILModule::lookUpWitnessTable(const ProtocolConformance *C, SILWitnessTable *wtable; auto rootC = C->getRootConformance(); + + // Builtin conformances don't have witness tables in SIL. + if (isa(rootC)) + return nullptr; + // Attempt to lookup the witness table from the table. auto found = WitnessTableMap.find(rootC); if (found == WitnessTableMap.end()) { diff --git a/lib/SILGen/SILGenLazyConformance.cpp b/lib/SILGen/SILGenLazyConformance.cpp index 791ae643c7f4c..f568f26dfe0dc 100644 --- a/lib/SILGen/SILGenLazyConformance.cpp +++ b/lib/SILGen/SILGenLazyConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -31,7 +31,7 @@ void SILGenModule::useConformance(ProtocolConformanceRef conformanceRef) { if (auto *inherited = dyn_cast(conformance)) conformance = inherited->getInheritedConformance(); - // Get the normal conformance. If we don't have one, this is a self + // Get the normal conformance. If we don't have one, this is a self or builtin // conformance, which we can ignore. auto normal = dyn_cast( conformance->getRootConformance()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 3acc4aaeff9c4..1c491b1cf2c4b 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5628,7 +5628,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( // This conformance may be conditional, in which case we need to consider // those requirements as constraints too. - if (conformance.isConcrete()) { + if (conformance.isConcrete() && + !isa(conformance.getConcrete())) { unsigned index = 0; for (const auto &req : conformance.getConditionalRequirements()) { addConstraint(req, diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index 2ab062799f926..783a1da57d4af 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -194,6 +194,8 @@ OTHER(CLANG_TYPE, 253) OTHER(DERIVATIVE_FUNCTION_CONFIGURATION, 254) +OTHER(BUILTIN_PROTOCOL_CONFORMANCE, 255) + #undef RECORD #undef DECLTYPERECORDNODES_HAS_RECORD_VAL #undef RECORD_VAL diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index e9a2b2d030b14..077124df3dc5c 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -562,6 +562,34 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor, return ProtocolConformanceRef(conformance); } + case BUILTIN_PROTOCOL_CONFORMANCE: { + TypeID conformingTypeID; + DeclID protoID; + size_t numConformances; + BuiltinProtocolConformanceLayout::readRecord(scratch, conformingTypeID, + protoID, numConformances); + + Type conformingType = getType(conformingTypeID); + + auto decl = getDeclChecked(protoID); + if (!decl) + return decl.takeError(); + + auto proto = cast(decl.get()); + + // Read the conformances. + SmallVector conformances; + conformances.reserve(numConformances); + for (unsigned i : range(numConformances)) { + (void)i; + conformances.push_back(readConformance(Cursor)); + } + + auto conformance = getContext().getBuiltinConformance(conformingType, proto, + conformances); + return ProtocolConformanceRef(conformance); + } + case NORMAL_PROTOCOL_CONFORMANCE_ID: { NormalConformanceID conformanceID; NormalProtocolConformanceIdLayout::readRecord(scratch, conformanceID); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index d7257e3f490d4..eb6f35d1a66fe 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 583; // @actorIndependent safe/unsafe attribute +const uint16_t SWIFTMODULE_VERSION_MINOR = 584; // builtin protocol conformances /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1628,6 +1628,14 @@ namespace decls_block { TypeIDField // the conforming type >; + using BuiltinProtocolConformanceLayout = BCRecordLayout< + BUILTIN_PROTOCOL_CONFORMANCE, + TypeIDField, // the conforming type + DeclIDField, // the protocol + BCVBR<5> // the number of element conformances + // the (optional) element conformances follow + >; + // Refers to a normal protocol conformance in the given module via its id. using NormalProtocolConformanceIdLayout = BCRecordLayout< NORMAL_PROTOCOL_CONFORMANCE_ID, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index dc881ddb61732..0a4b6c9fee616 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -845,6 +845,8 @@ void Serializer::writeBlockInfoBlock() { decls_block::SPECIALIZED_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::INHERITED_PROTOCOL_CONFORMANCE); + BLOCK_RECORD_WITH_NAMESPACE(sil_block, + decls_block::BUILTIN_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::NORMAL_PROTOCOL_CONFORMANCE_ID); BLOCK_RECORD_WITH_NAMESPACE(sil_block, @@ -1506,6 +1508,17 @@ Serializer::writeConformance(ProtocolConformanceRef conformanceRef, writeConformance(conf->getInheritedConformance(), abbrCodes, genericEnv); break; } + case ProtocolConformanceKind::Builtin: + auto builtin = cast(conformance); + unsigned abbrCode = abbrCodes[BuiltinProtocolConformanceLayout::Code]; + auto typeID = addTypeRef(builtin->getType()); + auto protocolID = addDeclRef(builtin->getProtocol()); + BuiltinProtocolConformanceLayout::emitRecord(Out, ScratchRecord, abbrCode, + typeID, protocolID, + builtin->getConformances().size()); + + writeConformances(builtin->getConformances(), abbrCodes); + break; } } @@ -4588,6 +4601,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 70fa8597c5470..1f3d1aae2051c 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -2732,6 +2732,7 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); + registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); From 4b71d6776ea4b4c47c28889f9543fbb047eb3241 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 23 May 2020 11:10:14 -0400 Subject: [PATCH 02/15] [ABI] Introduce MetadataKind TypeRef --- include/swift/ABI/Metadata.h | 21 ++++++++++++++++++- include/swift/ABI/MetadataValues.h | 8 +++++-- include/swift/Remote/MetadataReader.h | 5 ++++- stdlib/public/runtime/Metadata.cpp | 5 ++++- stdlib/public/runtime/ProtocolConformance.cpp | 11 ++++++++-- .../Compatibility51/ProtocolConformance.cpp | 3 +++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 9fbb260dc66ad..4bfcc243aebe8 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -2325,6 +2325,7 @@ struct TargetTypeMetadataRecord { // in the future) are just never used in these lists. case TypeReferenceKind::DirectObjCClassName: case TypeReferenceKind::IndirectObjCClass: + case TypeReferenceKind::MetadataKind: return nullptr; } @@ -2415,6 +2416,9 @@ struct TargetTypeReference { /// A direct reference to an Objective-C class name. RelativeDirectPointer DirectObjCClassName; + + /// A "reference" to some metadata kind, e.g. tuple. + MetadataKind MetadataKind; }; const TargetContextDescriptor * @@ -2428,12 +2432,18 @@ struct TargetTypeReference { case TypeReferenceKind::DirectObjCClassName: case TypeReferenceKind::IndirectObjCClass: + case TypeReferenceKind::MetadataKind: return nullptr; } return nullptr; } + enum MetadataKind getMetadataKind(TypeReferenceKind kind) const { + assert(kind == TypeReferenceKind::MetadataKind); + return MetadataKind; + } + #if SWIFT_OBJC_INTEROP /// If this type reference is one of the kinds that supports ObjC /// references, @@ -2519,6 +2529,10 @@ struct TargetProtocolConformanceDescriptor final return Flags.getTypeReferenceKind(); } + enum MetadataKind getMetadataKind() const { + return TypeRef.getMetadataKind(getTypeKind()); + } + const char *getDirectObjCClassName() const { return TypeRef.getDirectObjCClassName(getTypeKind()); } @@ -2546,6 +2560,11 @@ struct TargetProtocolConformanceDescriptor final TargetRelativeContextPointer>(); } + /// Whether this conformance is builtin by the compiler + runtime. + bool isBuiltin() const { + return getTypeKind() == TypeReferenceKind::MetadataKind; + } + /// Whether this conformance is non-unique because it has been synthesized /// for a foreign type. bool isSynthesizedNonUnique() const { diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 76c5fd75a3845..00adcbdba61d4 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -384,10 +384,14 @@ enum class TypeReferenceKind : unsigned { /// unused. IndirectObjCClass = 0x03, + /// The conformance is for a non-nominal type whose metadata kind we recorded; + /// getMetadataKind() returns the kind. + MetadataKind = 0x04, + // We only reserve three bits for this in the various places we store it. First_Kind = DirectTypeDescriptor, - Last_Kind = IndirectObjCClass, + Last_Kind = MetadataKind, }; /// Flag that indicates whether an existential type is class-constrained or not. diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 589bef52d8a68..bee120e64e0bb 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -1414,6 +1414,9 @@ class MetadataReader { return metadataFn(metadata); } + case TypeReferenceKind::MetadataKind: { + return None; + } } return None; diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index a4c941cd0f3a3..96d5325fa737e 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -193,6 +193,9 @@ computeMetadataBoundsForSuperclass(const void *ref, break; #endif } + // Type metadata type ref is unsupported here. + case TypeReferenceKind::MetadataKind: + break; } swift_unreachable("unsupported superclass reference kind"); } diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index f3fa79c578a72..bc03e0633dd11 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -83,11 +83,14 @@ template<> void ProtocolConformanceDescriptor::dump() const { case TypeReferenceKind::IndirectTypeDescriptor: printf("unique nominal type descriptor %s", symbolName(getTypeDescriptor())); break; + case TypeReferenceKind::MetadataKind: + printf("metadata kind %i", getMetadataKind()); + break; } printf(" => "); - printf("witness table %pattern s\n", symbolName(getWitnessTablePattern())); + printf("witness table pattern %p\n", symbolName(getWitnessTablePattern())); } #endif @@ -113,6 +116,7 @@ const ClassMetadata *TypeReference::getObjCClass(TypeReferenceKind kind) const { case TypeReferenceKind::DirectTypeDescriptor: case TypeReferenceKind::IndirectTypeDescriptor: + case TypeReferenceKind::MetadataKind: return nullptr; } @@ -153,6 +157,9 @@ ProtocolConformanceDescriptor::getCanonicalTypeMetadata() const { return nullptr; } + case TypeReferenceKind::MetadataKind: { + return nullptr; + } } swift_unreachable("Unhandled TypeReferenceKind in switch."); diff --git a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp index 09bd46ea02700..dd96167686be5 100644 --- a/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility51/ProtocolConformance.cpp @@ -229,6 +229,9 @@ override_getCanonicalTypeMetadata(const ProtocolConformanceDescriptor *conf) { return nullptr; } + case TypeReferenceKind::MetadataKind: { + return nullptr; + } } swift_unreachable("Unhandled TypeReferenceKind in switch."); From e60ef84bd278d5cd4b5799bad21f9871169122d5 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 30 May 2020 16:11:46 -0400 Subject: [PATCH 03/15] Implement Tuple Equatable Conformance --- .../Runtime/BuiltinProtocolConformances.h | 51 ++++++ lib/AST/Module.cpp | 24 ++- lib/IRGen/GenProto.cpp | 33 +++- lib/IRGen/IRGenMangler.cpp | 22 ++- lib/IRGen/IRGenModule.cpp | 7 +- lib/SIL/IR/SIL.cpp | 7 +- .../runtime/BuiltinProtocolConformances.cpp | 146 ++++++++++++++++++ stdlib/public/runtime/CMakeLists.txt | 1 + stdlib/public/runtime/Metadata.cpp | 34 +++- stdlib/public/runtime/ProtocolConformance.cpp | 43 ++++++ test/IDE/complete_expr_tuple.swift | 14 +- test/IRGen/tuple_equatable_conformance.swift | 28 ++++ .../tuple_equatable_conformance.swift | 70 +++++++++ test/Misc/misc_diagnostics.swift | 2 - test/SILOptimizer/pound_assert.swift | 5 + 15 files changed, 464 insertions(+), 23 deletions(-) create mode 100644 include/swift/Runtime/BuiltinProtocolConformances.h create mode 100644 stdlib/public/runtime/BuiltinProtocolConformances.cpp create mode 100644 test/IRGen/tuple_equatable_conformance.swift create mode 100644 test/Interpreter/tuple_equatable_conformance.swift diff --git a/include/swift/Runtime/BuiltinProtocolConformances.h b/include/swift/Runtime/BuiltinProtocolConformances.h new file mode 100644 index 0000000000000..f6d540e1bf599 --- /dev/null +++ b/include/swift/Runtime/BuiltinProtocolConformances.h @@ -0,0 +1,51 @@ +//===--- BuiltinProtocolWitnessTable.h --------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift runtime support for builtin protocol witnesses and related items. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H +#define SWIFT_RUNTIME_BUILTINPROTOCOLCONFORMANCES_H + +#include "swift/ABI/Metadata.h" + +namespace swift { + +#define STR(a) #a +#define XSTR(a) STR(a) +#define SYMBOL(name) XSTR(__USER_LABEL_PREFIX__) name + +// public protocol Equatable {} +#define SWIFT_EQUATABLE_MANGLING SQ + +#define PROTOCOL_DESCRIPTOR_MANGLING Mp + +#define PROTOCOL_DESCRIPTOR_SYM(Proto) \ + MANGLE_SYM(MANGLING_CONCAT2(Proto, PROTOCOL_DESCRIPTOR_MANGLING)) + +#define EQUATABLE_PROTOCOL_DESCRIPTOR \ + PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING) + +#define TUPLE_EQUATABLE_CONF SYMBOL("_swift_tupleEquatable_conf") +#define TUPLE_EQUATABLE_EQUALS SYMBOL("_swift_tupleEquatable_equals") + +/// The protocol witness for static Swift.Equatable.== infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Equatable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +} // end namespace swift + +#endif diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 6b4a60c42d24f..7ceca1f6a62af 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -1013,6 +1013,28 @@ LookupConformanceInModuleRequest::evaluate( if (type->is()) return ProtocolConformanceRef(protocol); + // Tuples have builtin conformances implemented within the runtime. + // These conformances so far consist of Equatable. + if (auto tuple = type->getAs()) { + if (protocol == ctx.getProtocol(KnownProtocolKind::Equatable)) { + SmallVector elementConformances; + + // Ensure that every element in this tuple conforms to Equatable. + for (auto eltTy : tuple->getElementTypes()) { + auto conformance = mod->lookupConformance(eltTy, protocol); + + if (conformance.isInvalid()) + return ProtocolConformanceRef::forInvalid(); + + elementConformances.push_back(conformance); + } + + auto conformance = ctx.getBuiltinConformance(tuple, protocol, + elementConformances); + return ProtocolConformanceRef(conformance); + } + } + auto nominal = type->getAnyNominal(); // If we don't have a nominal type, there are no conformances. diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 15899ea44be2f..724387162f787 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -906,6 +906,16 @@ static bool isDependentConformance( IRGenModule &IGM, const RootProtocolConformance *rootConformance, llvm::SmallPtrSet &visited){ + // Some Builtin conformances are dependent. + if (auto builtin = dyn_cast(rootConformance)) { + // Tuples are conditionally conformed to any builtin conformance. + if (builtin->getType()->is()) + return true; + + // Otherwise, this builtin conformance is not dependant. + return false; + } + // Self-conformances are never dependent. auto conformance = dyn_cast(rootConformance); if (!conformance) @@ -971,12 +981,10 @@ static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) { static llvm::Value * emitConditionalConformancesBuffer(IRGenFunction &IGF, const ProtocolConformance *substConformance) { - auto rootConformance = - dyn_cast(substConformance->getRootConformance()); + auto rootConformance = substConformance->getRootConformance(); - // Not a normal conformance means no conditional requirements means no need - // for a buffer. - if (!rootConformance) + if (!isa(rootConformance) && + !isa(rootConformance)) return llvm::UndefValue::get(IGF.IGM.WitnessTablePtrPtrTy); // Pointers to the witness tables, in the right order, which will be included @@ -986,9 +994,18 @@ emitConditionalConformancesBuffer(IRGenFunction &IGF, auto subMap = substConformance->getSubstitutions(IGF.IGM.getSwiftModule()); SILWitnessTable::enumerateWitnessTableConditionalConformances( - rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) { + rootConformance, [&](unsigned i, CanType type, ProtocolDecl *proto) { auto substType = type.subst(subMap)->getCanonicalType(); auto reqConformance = subMap.lookupConformance(type, proto); + + // Builtin conformances don't have a substitution list, so accomodate + // for that here. + if (auto builtin + = dyn_cast(rootConformance)) { + substType = type->getCanonicalType(); + reqConformance = builtin->getConformances()[i]; + } + assert(reqConformance && "conditional conformance must be valid"); tables.push_back(emitWitnessTableRef(IGF, substType, reqConformance)); @@ -1137,8 +1154,10 @@ class AccessorConformanceInfo : public ConformanceInfo { llvm::Value *getTable(IRGenFunction &IGF, llvm::Value **typeMetadataCache) const override { // If we're looking up a dependent type, we can't cache the result. + // If the conformance is builtin, go ahead and emit an accessor call. if (Conformance->getType()->hasArchetype() || - Conformance->getType()->hasDynamicSelfType()) { + Conformance->getType()->hasDynamicSelfType() || + isa(Conformance)) { return emitWitnessTableAccessorCall(IGF, Conformance, typeMetadataCache); } diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 720a85ecc9887..972c9bf991af0 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -147,6 +147,26 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM, std::string IRGenMangler::mangleProtocolConformanceDescriptor( const RootProtocolConformance *conformance) { + // Builtin conformances are different because they don't use a mangled name + // for their conformance descriptors. For now, we use predefined symbol names + // just in case in the future we have some conflict between actual + // conformances and these builtin ones. + if (isa(conformance)) { + auto &ctx = conformance->getType()->getASTContext(); + + if (conformance->getType()->is()) { + auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + + if (conformance->getProtocol() == equatable) { + return "_swift_tupleEquatable_conf"; + } + + llvm_unreachable("mangling unknown tuple witness table protocol"); + } + + llvm_unreachable("mangling unknown builtin witness table type"); + } + beginMangling(); if (isa(conformance)) { appendProtocolConformance(conformance); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 5df5d617e02ae..a765911fe124a 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -972,6 +972,11 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) { if (wt->getLinkage() == SILLinkage::Shared) return true; + // If we happen to see a builtin witness table here, we can't emit those. + // The runtime has those for us. + if (isa(wt->getConformance())) + return false; + NominalTypeDecl *ConformingTy = wt->getConformingType()->getNominalOrBoundGenericNominal(); diff --git a/lib/SIL/IR/SIL.cpp b/lib/SIL/IR/SIL.cpp index e9c739af20805..ef497ac51eb9b 100644 --- a/lib/SIL/IR/SIL.cpp +++ b/lib/SIL/IR/SIL.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -80,6 +80,11 @@ swift::getLinkageForProtocolConformance(const RootProtocolConformance *C, if (isa(C->getDeclContext()->getModuleScopeContext())) return SILLinkage::Shared; + // If the conforming type is a non-nomianl, give it public linkage. + // These conformances are implemented within the runtime. + if (!C->getType()->getAnyNominal()) + return definition ? SILLinkage::Public : SILLinkage::PublicExternal; + auto typeDecl = C->getType()->getNominalOrBoundGenericNominal(); AccessLevel access = std::min(C->getProtocol()->getEffectiveAccess(), typeDecl->getEffectiveAccess()); diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp new file mode 100644 index 0000000000000..ef8177dfeb357 --- /dev/null +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -0,0 +1,146 @@ +//===--- BuiltinProtocolConformances.cpp ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of some builtin protocol witnesses. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include "swift/Runtime/Casting.h" +#include "swift/Runtime/Debug.h" +#include "swift/Runtime/Metadata.h" + +using namespace swift; + +extern const ProtocolDescriptor +PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING); + +#if defined(__ELF__) +// Create a GOT equivalent for the Equatable reference. +__asm( + " .type got.$sSQMp, @object\n" + " .section .data.rel.ro\n" + " .p2align 3\n" + "got.$sSQMp:\n" + " .quad ($sSQMp)\n" + " .size got.$sSQMp, 8\n" +); + +// Create a GOT equivalent for the Equatable.== method descriptor. +__asm( + " .type got.$sSQ2eeoiySbx_xtFZTq, @object\n" + " .p2align 3\n" + "got.$sSQ2eeoiySbx_xtFZTq:\n" + " .quad ($sSQ2eeoiySbx_xtFZTq)\n" + " .size got.$sSQ2eeoiySbx_xtFZTq, 8\n" +); +#endif + +// Define the conformance descriptor for tuple Equatable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleEquatable_private, @object\n" + " .local __swift_tupleEquatable_private\n" + " .comm __swift_tupleEquatable_private, 128, 16\n" + " .protected " TUPLE_EQUATABLE_CONF "\n" + " .type " TUPLE_EQUATABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleEquatable_private, 128, 4\n" + " .section __TEXT, __const\n" + #endif + " .globl " TUPLE_EQUATABLE_CONF "\n" + " .p2align 2\n" + TUPLE_EQUATABLE_CONF ":\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Equatable protocol descriptor, hence why we add 1 to indicate indirect. + " .long (got.$sSQMp - (" TUPLE_EQUATABLE_CONF ")) + 1\n" + #elif defined(__MACH__) + " .long _$sSQMp@GOTPCREL + 5\n" + #endif + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 1 is the ResilientWitnessesHeader indicating we have 1 resilient + // witness. + " .long 1\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Equatable.== method descriptor, hence why we add 1 to indicate indirect. + " .long ((got.$sSQ2eeoiySbx_xtFZTq - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" + #elif defined(__MACH__) + " .long _$sSQ2eeoiySbx_xtFZTq@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the equals witness defined below. + " .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long (__swift_tupleEquatable_private - (" TUPLE_EQUATABLE_CONF ")) - 36\n" + #if defined(__ELF__) + " .size " TUPLE_EQUATABLE_CONF ", 40\n" + #endif +); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // Grab the specific witness for this element type. + auto equatableTable = reinterpret_cast(conformance); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, const WitnessTable *); + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function + auto result = equals(value1, value2, elt.Type, elt.Type, conformance); + + // If the values aren't equal, this tuple isn't equal. :) + if (!result) + return false; + } + + // Otherwise this tuple has value equality with all elements. + return true; +} diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 56d5110ef94d8..0250f90024630 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -29,6 +29,7 @@ set(swift_runtime_sources AnyHashableSupport.cpp Array.cpp BackDeployment.cpp + BuiltinProtocolConformances.cpp Casting.cpp CompatibilityOverride.cpp CygwinPort.cpp diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 96d5325fa737e..77c930c06b9df 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -20,6 +20,7 @@ #include "swift/Basic/Range.h" #include "swift/Demangling/Demangler.h" #include "swift/ABI/TypeIdentity.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/ExistentialContainer.h" @@ -4477,18 +4478,27 @@ class WitnessTableCacheEntry : const Metadata *type, const ProtocolConformanceDescriptor *conformance, const void * const *instantiationArgs) { - return getWitnessTableSize(conformance); + return getWitnessTableSize(type, conformance); } size_t getExtraAllocationSize() const { - return getWitnessTableSize(Conformance); + return getWitnessTableSize(Type, Conformance); } - static size_t getWitnessTableSize( + static size_t getWitnessTableSize(const Metadata *type, const ProtocolConformanceDescriptor *conformance) { auto protocol = conformance->getProtocol(); auto genericTable = conformance->getGenericWitnessTable(); size_t numPrivateWords = genericTable->getWitnessTablePrivateSizeInWords(); + + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have their private size in words be determined via the number of elements + // the type has. + if (conformance->isBuiltin()) { + auto tuple = cast(type); + numPrivateWords = tuple->NumElements; + } + size_t numRequirementWords = WitnessTableFirstRequirementOffset + protocol->NumRequirements; return (numPrivateWords + numRequirementWords) * sizeof(void*); @@ -4744,6 +4754,14 @@ instantiateWitnessTable(const Metadata *Type, // Number of bytes for any private storage used by the conformance itself. size_t privateSizeInWords = genericTable->getWitnessTablePrivateSizeInWords(); + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have their private size in words be determined via the number of elements + // the type has. + if (conformance->isBuiltin()) { + auto tuple = cast(Type); + privateSizeInWords = tuple->NumElements; + } + // Advance the address point; the private storage area is accessed via // negative offsets. auto table = fullTable + privateSizeInWords; @@ -4786,6 +4804,16 @@ instantiateWitnessTable(const Metadata *Type, if (conditionalRequirement.Flags.hasExtraArgument()) copyNextInstantiationArg(); } + + // Builtin conformance descriptors, namely tuple conformances at the moment, + // have instantiation arguments equal to the number of element conformances. + if (conformance->isBuiltin()) { + auto tuple = cast(Type); + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + copyNextInstantiationArg(); + } + } } // Fill in any default requirements. diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index bc03e0633dd11..d14412f9ff2e2 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -16,6 +16,7 @@ #include "swift/Basic/Lazy.h" #include "swift/Demangling/Demangle.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/HeapObject.h" @@ -375,6 +376,38 @@ searchInConformanceCache(const Metadata *type, return {false, nullptr}; } +extern const ProtocolDescriptor EQUATABLE_PROTOCOL_DESCRIPTOR; + +static bool tupleConformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + auto tuple = cast(type); + + // At the moment, tuples can only conform to Equatable, so reject all other + // protocols. + auto equatable = &EQUATABLE_PROTOCOL_DESCRIPTOR; + if (protocol != equatable) + return false; + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + if (!swift_conformsToProtocol(elt.Type, protocol)) + return false; + } + + return true; +} + +extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + +static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( + const ProtocolDescriptor *protocol) { + if (protocol == &EQUATABLE_PROTOCOL_DESCRIPTOR) { + return &_swift_tupleEquatable_conf; + } + + return nullptr; +} + namespace { /// Describes a protocol conformance "candidate" that can be checked /// against a type metadata. @@ -494,6 +527,16 @@ swift_conformsToProtocolImpl(const Metadata *const type, } } + // If we're asking if a tuple conforms to a protocol, handle that here for + // builtin conformances. + if (auto tuple = dyn_cast(type)) { + if (tupleConformsToProtocol(tuple, protocol)) { + auto descriptor = getTupleConformanceDescriptor(protocol); + C.cacheResult(type, protocol, descriptor->getWitnessTable(type), + /*always cache*/ 0); + } + } + // Try the search again to look for the most specific cached conformance. found = searchInConformanceCache(type, protocol); diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift index 0f2295f8b99c2..7d863a856ba05 100644 --- a/test/IDE/complete_expr_tuple.swift +++ b/test/IDE/complete_expr_tuple.swift @@ -27,7 +27,7 @@ func testTupleNoDot1() { var t = (1, 2.0) t#^TUPLE_NO_DOT_1^# } -// TUPLE_NO_DOT_1: Begin completions, 10 items +// TUPLE_NO_DOT_1: Begin completions, 11 items // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -44,14 +44,14 @@ func testTupleNoDot2() { var t = (foo: 1, bar: 2.0) t#^TUPLE_NO_DOT_2^# } -// TUPLE_NO_DOT_2: Begin completions, 10 items +// TUPLE_NO_DOT_2: Begin completions, 11 items // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: BuiltinOperator/None: = {#(foo: Int, bar: Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, bar: Double)#]; name=self @@ -61,14 +61,14 @@ func testTupleNoDot3() { var t = (foo: 1, 2.0) t#^TUPLE_NO_DOT_3^# } -// TUPLE_NO_DOT_3: Begin completions, 10 items +// TUPLE_NO_DOT_3: Begin completions, 11 items // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: BuiltinOperator/None: = {#(foo: Int, Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, Double)#]; name=self diff --git a/test/IRGen/tuple_equatable_conformance.swift b/test/IRGen/tuple_equatable_conformance.swift new file mode 100644 index 0000000000000..8242fa8139184 --- /dev/null +++ b/test/IRGen/tuple_equatable_conformance.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s + +// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor + +struct Wrapper { + let value: T +} + +extension Wrapper: Equatable where T: Equatable {} + +public func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +public func use(_ thing: T) -> Bool { + // CHECK: [[USE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_WT]]) + equals((thing, thing), (thing, thing)) +} + +public func test() { + // CHECK: [[TEST_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) + let _ = equals((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.0* noalias nocapture undef, {{%.*}}.0* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) + let _ = Wrapper(value: ()) == Wrapper(value: ()) +} diff --git a/test/Interpreter/tuple_equatable_conformance.swift b/test/Interpreter/tuple_equatable_conformance.swift new file mode 100644 index 0000000000000..64992461e55dd --- /dev/null +++ b/test/Interpreter/tuple_equatable_conformance.swift @@ -0,0 +1,70 @@ +// RUN: %target-run-simple-swift | %FileCheck %s +// REQUIRES: executable_test + +func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +// CHECK: true +print(equals((), ())) + +// CHECK: true +print(equals((128, 316), (128, 316))) + +// CHECK: false +print(equals((128, 316), (316, 128))) + +// CHECK: true +print(equals(((1, 2), 3), ((1, 2), 3))) + +// CHECK: false +print(equals(((1, 2), 3), ((1, 2), 4))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueEquatableValue() -> some Equatable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + print(opaqueEquatableValue() == opaqueEquatableValue()) +} + +struct Wrapper { + let value: T +} + +extension Wrapper: Equatable where T: Equatable {} + +// CHECK: true +print(Wrapper(value: ()) == Wrapper(value: ())) + +// CHECK: true +print(Wrapper(value: (128, 316)) == Wrapper(value: (128, 316))) + +// CHECK: false +print(Wrapper(value: (128, 316)) == Wrapper(value: (316, 128))) + +func use(_ thing: T) -> Bool { + equals((thing, thing), (thing, thing)) +} + +// CHECK: true +print(use(128)) + +class Foo: Equatable { + var age: Int + + init(age: Int) { + self.age = age + } + + static func == (lhs: Foo, rhs: Foo) -> Bool { + lhs.age == rhs.age + } +} + +// CHECK: true +print(equals((Foo(age: 128), false, 0), (Foo(age: 128), false, 0))) + +// CHECK: false +print(equals((Foo(age: 128), false, 0), (Foo(age: 316), false, 0))) diff --git a/test/Misc/misc_diagnostics.swift b/test/Misc/misc_diagnostics.swift index 6bf146ba0ff36..944e145477d8d 100644 --- a/test/Misc/misc_diagnostics.swift +++ b/test/Misc/misc_diagnostics.swift @@ -143,8 +143,6 @@ func test17875634() { func test20770032() { if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}} // expected-error@-1 {{expression pattern of type 'ClosedRange' cannot match values of type '(Int, Int)'}} - // expected-error@-2 {{type '(Int, Int)' cannot conform to 'Equatable'; only concrete types such as structs, enums and classes can conform to protocols}} - // expected-note@-3 {{required by operator function '~=' where 'T' = '(Int, Int)'}} } } diff --git a/test/SILOptimizer/pound_assert.swift b/test/SILOptimizer/pound_assert.swift index d593786a3afa0..f73a45dd5bb01 100644 --- a/test/SILOptimizer/pound_assert.swift +++ b/test/SILOptimizer/pound_assert.swift @@ -228,12 +228,17 @@ func test_genericAdd() { #assert(genericAdd(1, 1) == 2) } +// FIXME: The following is disabled for now because tuples conform to Equatable +// now, but the work needed for SIL to optimize away the witness method is not +// there yet. +/* func test_tupleAsGeneric() { func identity(_ t: T) -> T { return t } #assert(identity((1, 2)) == (1, 2)) } +*/ //===----------------------------------------------------------------------===// // Reduced testcase propagating substitutions around. From df9778e2e8b068c6b1df9fe012d8a598605c8ff1 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 6 Jun 2020 18:45:40 -0400 Subject: [PATCH 04/15] [Compatibility53] Add compatibility library for 5.3 and backport tuple Equatable conformance Fix some comments Unnecessary cast --- include/swift/Frontend/BackDeploymentLibs.def | 1 + lib/Basic/Platform.cpp | 12 +- lib/Driver/DarwinToolChains.cpp | 17 +- lib/Frontend/CompilerInvocation.cpp | 2 + stdlib/toolchain/CMakeLists.txt | 1 + .../Compatibility50/ProtocolConformance.cpp | 8 + .../toolchain/Compatibility51/Overrides.cpp | 6 + .../BuiltinProtocolConformances.cpp | 137 +++++++++++ .../toolchain/Compatibility53/CMakeLists.txt | 34 +++ .../Compatibility53/CompatibilityOverride.def | 226 ++++++++++++++++++ .../Compatibility53/CompatibilityOverride.h | 61 +++++ .../toolchain/Compatibility53/Overrides.cpp | 38 +++ stdlib/toolchain/Compatibility53/Overrides.h | 29 +++ .../Compatibility53/ProtocolConformance.cpp | 92 +++++++ .../autolink-runtime-compatibility.swift | 16 ++ test/stdlib/Compatibility50Linking.c | 2 +- 16 files changed, 669 insertions(+), 13 deletions(-) create mode 100644 stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp create mode 100644 stdlib/toolchain/Compatibility53/CMakeLists.txt create mode 100644 stdlib/toolchain/Compatibility53/CompatibilityOverride.def create mode 100644 stdlib/toolchain/Compatibility53/CompatibilityOverride.h create mode 100644 stdlib/toolchain/Compatibility53/Overrides.cpp create mode 100644 stdlib/toolchain/Compatibility53/Overrides.h create mode 100644 stdlib/toolchain/Compatibility53/ProtocolConformance.cpp diff --git a/include/swift/Frontend/BackDeploymentLibs.def b/include/swift/Frontend/BackDeploymentLibs.def index b2d9f8a84acd9..7a2ba2d638c86 100644 --- a/include/swift/Frontend/BackDeploymentLibs.def +++ b/include/swift/Frontend/BackDeploymentLibs.def @@ -26,6 +26,7 @@ BACK_DEPLOYMENT_LIB((5, 0), all, "swiftCompatibility50") BACK_DEPLOYMENT_LIB((5, 1), all, "swiftCompatibility51") +BACK_DEPLOYMENT_LIB((5, 3), all, "swiftCompatibility53") BACK_DEPLOYMENT_LIB((5, 0), executable, "swiftCompatibilityDynamicReplacements") #undef BACK_DEPLOYMENT_LIB diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index ad1db82c717cf..ebd24e0fd7062 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -378,8 +378,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Minor <= 15) { if (Micro <= 3) { return llvm::VersionTuple(5, 1); - } else { + } else if (Micro <= 4) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } else if (Major == 11) { @@ -399,8 +401,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Major <= 13) { if (Minor <= 3) { return llvm::VersionTuple(5, 1); - } else { + } else if (Minor <= 4) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } else if (Triple.isWatchOS()) { @@ -410,8 +414,10 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget( } else if (Major <= 6) { if (Minor <= 1) { return llvm::VersionTuple(5, 1); - } else { + } else if (Minor <= 2) { return llvm::VersionTuple(5, 2); + } else { + return llvm::VersionTuple(5, 3); } } } diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index 95accad29f2bd..6a828f0e7d071 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -378,19 +378,18 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments, // have an older Swift runtime. SmallString<128> SharedResourceDirPath; getResourceDirPath(SharedResourceDirPath, context.Args, /*Shared=*/true); - Optional runtimeCompatibilityVersion; + Optional runtimeCompatibilityVersion + = llvm::VersionTuple(); if (context.Args.hasArg(options::OPT_runtime_compatibility_version)) { auto value = context.Args.getLastArgValue( options::OPT_runtime_compatibility_version); - if (value.equals("5.0")) { - runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); - } else if (value.equals("5.1")) { - runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); - } else if (value.equals("none")) { - runtimeCompatibilityVersion = None; - } else { - // TODO: diagnose unknown runtime compatibility version? + if (runtimeCompatibilityVersion->tryParse(value)) { + if (value.equals("none")) { + runtimeCompatibilityVersion = None; + } else { + // TODO: diagnose unknown runtime compatibility version? + } } } else if (job.getKind() == LinkKind::Executable) { runtimeCompatibilityVersion diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 568c7a6aa7e65..aacb59c859485 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1598,6 +1598,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); } else if (version.equals("5.1")) { runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); + } else if (version.equals("5.3")) { + runtimeCompatibilityVersion = llvm::VersionTuple(5, 3); } else { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, versionArg->getAsString(Args), version); diff --git a/stdlib/toolchain/CMakeLists.txt b/stdlib/toolchain/CMakeLists.txt index 9cda30ee3e91b..fa5782ef212e8 100644 --- a/stdlib/toolchain/CMakeLists.txt +++ b/stdlib/toolchain/CMakeLists.txt @@ -53,4 +53,5 @@ set(COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS "2.0") add_subdirectory(legacy_layouts) add_subdirectory(Compatibility50) add_subdirectory(Compatibility51) +add_subdirectory(Compatibility53) add_subdirectory(CompatibilityDynamicReplacements) diff --git a/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp index a48e801a5a43a..a0e7309cee65c 100644 --- a/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility50/ProtocolConformance.cpp @@ -18,6 +18,7 @@ //===----------------------------------------------------------------------===// #include "Overrides.h" +#include "../Compatibility53/Overrides.h" #include "../../public/runtime/Private.h" #include "swift/Basic/Lazy.h" #include @@ -94,6 +95,13 @@ swift::swift50override_conformsToProtocol(const Metadata *type, static OnceToken_t token; SWIFT_ONCE_F(token, registerAddImageCallback, nullptr); + // The Swift 5.4 runtime added support for builtin conformances. Call 5.3's + // backported implementation to handle that here. + if (auto result = + swift53override_conformsToProtocol(type, protocol, + original_conformsToProtocol)) + return result; + // The implementation of swift_conformsToProtocol in Swift 5.0 would return // a false negative answer when asking whether a subclass conforms using // a conformance from a superclass. Work around this by walking up the diff --git a/stdlib/toolchain/Compatibility51/Overrides.cpp b/stdlib/toolchain/Compatibility51/Overrides.cpp index f9a89ec39e115..df6c3efb5d61d 100644 --- a/stdlib/toolchain/Compatibility51/Overrides.cpp +++ b/stdlib/toolchain/Compatibility51/Overrides.cpp @@ -16,6 +16,7 @@ #include "CompatibilityOverride.h" #include "Overrides.h" +#include "../Compatibility53/Overrides.h" #include #include @@ -33,6 +34,11 @@ struct OverrideSection { OverrideSection Swift51Overrides __attribute__((used, section("__DATA,__swift51_hooks"))) = { .version = 0, + // We use the same hook for conformsToProtocol as we do for a 5.3 + // runtime, so reference the override from the Compatibility53 library. + // If we're back deploying to Swift 5.1, we also have to support 5.3, so + // the Compatibility53 library is always linked when the 51 library is. + .conformsToProtocol = swift53override_conformsToProtocol, .conformsToSwiftProtocol = swift51override_conformsToSwiftProtocol, }; diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp new file mode 100644 index 0000000000000..1d39c759c458e --- /dev/null +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -0,0 +1,137 @@ +//===--- BuiltinProtocolConformances.cpp ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Definitions of some builtin protocol witnesses. +// +//===----------------------------------------------------------------------===// + +#include "Overrides.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include "swift/Runtime/Casting.h" +#include +#include + +using namespace swift; + +static const ProtocolDescriptor *getEquatableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSQMp"))); + return descriptor; +} + +static const WitnessTable *conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "swift_conformsToProtocol"))); + return func(type, protocol); +} + +#define TUPLE_EQUATABLE_WT SYMBOL("_swift_tupleEquatable_wt") + +// Define the conformance descriptor for tuple Equatable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_EQUATABLE_CONF "\n" + " .p2align 2\n" + TUPLE_EQUATABLE_CONF ":\n" + // This is an indirectable relative reference to the Equatable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Equatable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long ((" TUPLE_EQUATABLE_WT ") - (" TUPLE_EQUATABLE_CONF ")) - 8\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Equatable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceEquatableDescriptor() { + auto tupleEquatableConf = const_cast( + reinterpret_cast(&_swift_tupleEquatable_conf)); + auto equatable = getEquatableDescriptor(); + + // This is an indirectable pointer. + *tupleEquatableConf = intptr_t(equatable) - intptr_t(tupleEquatableConf); +} + +template +struct _WitnessTable { + const ProtocolConformanceDescriptor *Conformance; + const void *Witnesses[NumWitnesses]; +}; + +SWIFT_RUNTIME_EXPORT +const _WitnessTable<1> _swift_tupleEquatable_wt = { + &_swift_tupleEquatable_conf, + { + reinterpret_cast(_swift_tupleEquatable_equals) + } +}; + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Equatable for this element type. + auto equatable = getEquatableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, equatable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Equatable...?? + if (!conformance) + swift_unreachable("Tuple equality requires that all elements be \ + Equatable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // Grab the specific witness for this element type. + auto equatableTable = reinterpret_cast(conformance); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, const WitnessTable *); + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto result = equals(value1, value2, elt.Type, elt.Type, conformance); + + // If the values aren't equal, this tuple isn't equal. :) + if (!result) + return false; + } + + // Otherwise this tuple has value equality with all elements. + return true; +} diff --git a/stdlib/toolchain/Compatibility53/CMakeLists.txt b/stdlib/toolchain/Compatibility53/CMakeLists.txt new file mode 100644 index 0000000000000..61da832cf6d46 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CMakeLists.txt @@ -0,0 +1,34 @@ +set(library_name "swiftCompatibility53") + +add_swift_target_library("${library_name}" STATIC + BuiltinProtocolConformances.cpp + Overrides.cpp + ProtocolConformance.cpp + + TARGET_SDKS ${SWIFT_APPLE_PLATFORMS} + + C_COMPILE_FLAGS ${CXX_COMPILE_FLAGS} + LINK_FLAGS ${CXX_LINK_FLAGS} + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + DEPLOYMENT_VERSION_OSX ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_OSX} + DEPLOYMENT_VERSION_IOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_IOS} + DEPLOYMENT_VERSION_TVOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_TVOS} + DEPLOYMENT_VERSION_WATCHOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS} + + INSTALL_IN_COMPONENT compiler + INSTALL_WITH_SHARED) + + +# FIXME: We need a more flexible mechanism to add lipo targets generated by +# add_swift_target_library to the ALL target. Until then this hack is necessary +# to ensure these libraries build. +foreach(sdk ${SWIFT_SDKS}) + set(target_name "${library_name}-${SWIFT_SDK_${sdk}_LIB_SUBDIR}") + if(NOT TARGET "${target_name}") + continue() + endif() + + set_target_properties("${target_name}" + PROPERTIES + EXCLUDE_FROM_ALL FALSE) +endforeach() diff --git a/stdlib/toolchain/Compatibility53/CompatibilityOverride.def b/stdlib/toolchain/Compatibility53/CompatibilityOverride.def new file mode 100644 index 0000000000000..a7a34a8852100 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CompatibilityOverride.def @@ -0,0 +1,226 @@ +//===--- CompatibilityOverrides.def - Compatibility Overrides Database -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines x-macros used for metaprogramming with the set of +// compatibility override functions. +// +//===----------------------------------------------------------------------===// + +/// #define OVERRIDE(name, ret, attrs, namespace, typedArgs, namedArgs) +/// Provides information about an overridable function. +/// - name is the name of the function, without any leading swift_ or +/// namespace. +/// - ret is the return type of the function. +/// - attrs is the attributes, if any, applied to the function definition. +/// - namespace is the namespace, if any, the function is in, including a +/// trailing :: +/// - typedArgs is the argument list, including types, surrounded by +/// parentheses +/// - namedArgs is the list of argument names, with no types, surrounded by +/// parentheses +/// +/// The entries are organized by group. A user may define OVERRIDE to get all +/// entries, or define one or more of OVERRIDE_METADATALOOKUP, OVERRIDE_CASTING, +/// OVERRIDE_OBJC, OVERRIDE_FOREIGN, OVERRIDE_PROTOCOLCONFORMANCE, +/// and OVERRIDE_KEYPATH to get only those entries. + +// NOTE: this file is used to build the definition of OverrideSection in +// CompatibilityOverride.cpp, which is part of the ABI. Do not move or remove entries +// in this file after ABI stability. Additional entries can be added to the end. + +#ifdef OVERRIDE +# define OVERRIDE_METADATALOOKUP OVERRIDE +# define OVERRIDE_CASTING OVERRIDE +# define OVERRIDE_OBJC OVERRIDE +# define OVERRIDE_FOREIGN OVERRIDE +# define OVERRIDE_PROTOCOLCONFORMANCE OVERRIDE +# define OVERRIDE_KEYPATH OVERRIDE +# define OVERRIDE_WITNESSTABLE OVERRIDE +#else +# ifndef OVERRIDE_METADATALOOKUP +# define OVERRIDE_METADATALOOKUP(...) +# endif +# ifndef OVERRIDE_CASTING +# define OVERRIDE_CASTING(...) +# endif +# ifndef OVERRIDE_OBJC +# define OVERRIDE_OBJC(...) +# endif +# ifndef OVERRIDE_FOREIGN +# define OVERRIDE_FOREIGN(...) +# endif +# ifndef OVERRIDE_PROTOCOLCONFORMANCE +# define OVERRIDE_PROTOCOLCONFORMANCE(...) +# endif +# ifndef OVERRIDE_KEYPATH +# define OVERRIDE_KEYPATH(...) +# endif +# ifndef OVERRIDE_WITNESSTABLE +# define OVERRIDE_WITNESSTABLE(...) +# endif +#endif + +OVERRIDE_CASTING(dynamicCast, bool, , , swift::, + (OpaqueValue *dest, OpaqueValue *src, + const Metadata *srcType, + const Metadata *targetType, + DynamicCastFlags flags), + (dest, src, srcType, targetType, flags)) + + +OVERRIDE_CASTING(dynamicCastClass, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_CASTING(dynamicCastClassUnconditional, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + + + +OVERRIDE_CASTING(dynamicCastUnknownClass, const void *, , , swift::, + (const void *object, const Metadata *targetType), + (object, targetType)) + + +OVERRIDE_CASTING(dynamicCastUnknownClassUnconditional, const void *, , , swift::, + (const void *object, const Metadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + + +OVERRIDE_CASTING(dynamicCastMetatype, const Metadata *, , , swift::, + (const Metadata *sourceType, + const Metadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_CASTING(dynamicCastMetatypeUnconditional, const Metadata *, , , swift::, + (const Metadata *sourceType, + const Metadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassMetatype, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassMetatypeUnconditional, + const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_PROTOCOLCONFORMANCE(conformsToProtocol, const WitnessTable *, , , swift::, + (const Metadata * const type, + const ProtocolDescriptor *protocol), + (type, protocol)) + +OVERRIDE_PROTOCOLCONFORMANCE(conformsToSwiftProtocol, + const ProtocolConformanceDescriptor *, , , swift::, + (const Metadata * const type, + const ProtocolDescriptor *protocol, + StringRef moduleName), + (type, protocol, moduleName)) + +OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , , swift::, + (const void *pattern, const void *arguments), + (pattern, arguments)) + +OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift::, + (MetadataRequest request, + Demangler &demangler, + Demangle::NodePointer node, + const void * const *arguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable), + (request, demangler, node, arguments, substGenericParam, substWitnessTable)) +OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeInfo, , SWIFT_CC(swift), swift::, + (MetadataRequest request, + StringRef typeName, + const void * const *arguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable), + (request, typeName, arguments, substGenericParam, substWitnessTable)) + +OVERRIDE_WITNESSTABLE(getAssociatedTypeWitnessSlow, MetadataResponse, + SWIFT_RUNTIME_STDLIB_INTERNAL, SWIFT_CC(swift), swift::, + (MetadataRequest request, WitnessTable *wtable, + const Metadata *conformingType, + const ProtocolRequirement *reqBase, + const ProtocolRequirement *assocType), + (request, wtable, conformingType, reqBase, assocType)) + +OVERRIDE_WITNESSTABLE(getAssociatedConformanceWitnessSlow, const WitnessTable *, + SWIFT_RUNTIME_STDLIB_INTERNAL, SWIFT_CC(swift), swift::, + (WitnessTable *wtable, const Metadata *conformingType, + const Metadata *assocType, + const ProtocolRequirement *reqBase, + const ProtocolRequirement *assocConformance), + (wtable, conformingType, assocType, reqBase, + assocConformance)) +#if SWIFT_OBJC_INTEROP + +OVERRIDE_OBJC(dynamicCastObjCClass, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_OBJC(dynamicCastObjCClassUnconditional, const void *, , , swift::, + (const void *object, + const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + +OVERRIDE_OBJC(dynamicCastObjCClassMetatype, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, + const ClassMetadata *targetType), + (sourceType, targetType)) + + +OVERRIDE_OBJC(dynamicCastObjCClassMetatypeUnconditional, const ClassMetadata *, , , swift::, + (const ClassMetadata *sourceType, const ClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (sourceType, targetType, file, line, column)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClass, const void *, , , swift::, + (const void *object, + const ForeignClassMetadata *targetType), + (object, targetType)) + + +OVERRIDE_FOREIGN(dynamicCastForeignClassUnconditional, const void *, , , swift::, + (const void *object, const ForeignClassMetadata *targetType, + const char *file, unsigned line, unsigned column), + (object, targetType, file, line, column)) + +#endif + +#undef OVERRIDE +#undef OVERRIDE_METADATALOOKUP +#undef OVERRIDE_CASTING +#undef OVERRIDE_OBJC +#undef OVERRIDE_FOREIGN +#undef OVERRIDE_PROTOCOLCONFORMANCE +#undef OVERRIDE_KEYPATH +#undef OVERRIDE_WITNESSTABLE diff --git a/stdlib/toolchain/Compatibility53/CompatibilityOverride.h b/stdlib/toolchain/Compatibility53/CompatibilityOverride.h new file mode 100644 index 0000000000000..4c7c5b3c47d6b --- /dev/null +++ b/stdlib/toolchain/Compatibility53/CompatibilityOverride.h @@ -0,0 +1,61 @@ +//===--- CompatibiltyOverride.h - Back-deploying compatibility fixes --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Support back-deploying compatibility fixes for newer apps running on older runtimes. +// +//===----------------------------------------------------------------------===// + +#ifndef COMPATIBILITY_OVERRIDE_H +#define COMPATIBILITY_OVERRIDE_H + +#include "../../public/runtime/Private.h" +#include "swift/Runtime/Metadata.h" +#include "swift/Runtime/Once.h" +#include + +namespace swift { + +#define COMPATIBILITY_UNPAREN(...) __VA_ARGS__ + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + ccAttrs typedef ret (*Original_ ## name) typedArgs; +#include "CompatibilityOverride.def" + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + ccAttrs typedef ret (*Override_ ## name)(COMPATIBILITY_UNPAREN typedArgs, \ + Original_ ## name originalImpl); +#include "CompatibilityOverride.def" + +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + Override_ ## name getOverride_ ## name(); +#include "CompatibilityOverride.def" + + +/// Used to define an override point. The override point #defines the appropriate +/// OVERRIDE macro from CompatibilityOverride.def to this macro, then includes +/// the file to generate the override points. The original implementation of the +/// functionality must be available as swift_funcNameHereImpl. +#define COMPATIBILITY_OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + attrs ccAttrs ret namespace swift_ ## name typedArgs { \ + static Override_ ## name Override; \ + static swift_once_t Predicate; \ + swift_once(&Predicate, [](void *) { \ + Override = getOverride_ ## name(); \ + }, nullptr); \ + if (Override != nullptr) \ + return Override(COMPATIBILITY_UNPAREN namedArgs, swift_ ## name ## Impl); \ + return swift_ ## name ## Impl namedArgs; \ + } + +} /* end namespace swift */ + +#endif /* COMPATIBILITY_OVERRIDE_H */ diff --git a/stdlib/toolchain/Compatibility53/Overrides.cpp b/stdlib/toolchain/Compatibility53/Overrides.cpp new file mode 100644 index 0000000000000..b88014d009df6 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/Overrides.cpp @@ -0,0 +1,38 @@ +//===--- Overrides.cpp - Compat override table for Swift 5.3 runtime ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file provides compatibility override hooks for Swift 5.3 runtimes. +// +//===----------------------------------------------------------------------===// + +#include "CompatibilityOverride.h" +#include "Overrides.h" + +using namespace swift; + +struct OverrideSection { + uintptr_t version; +#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ + Override_ ## name name; +#include "../../public/runtime/CompatibilityOverride.def" +}; + +OverrideSection Swift53Overrides +__attribute__((used, section("__DATA,__swift53_hooks"))) = { + .version = 0, + .conformsToProtocol = swift53override_conformsToProtocol, +}; + +// Allow this library to get force-loaded by autolinking +__attribute__((weak, visibility("hidden"))) +extern "C" +char _swift_FORCE_LOAD_$_swiftCompatibility53 = 0; diff --git a/stdlib/toolchain/Compatibility53/Overrides.h b/stdlib/toolchain/Compatibility53/Overrides.h new file mode 100644 index 0000000000000..baba40f72e7b6 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/Overrides.h @@ -0,0 +1,29 @@ +//===--- Overrides.cpp - Compat overrides for Swift 5.3 runtime ----s------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file provides compatibility override hooks for Swift 5.3 runtimes. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Metadata.h" + +namespace swift { + +using ConformsToProtocol_t = + const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + +const WitnessTable * +swift53override_conformsToProtocol(const Metadata * const type, + const ProtocolDescriptor *protocol, + ConformsToProtocol_t *original); + +} // end namespace swift diff --git a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp new file mode 100644 index 0000000000000..8520769271543 --- /dev/null +++ b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp @@ -0,0 +1,92 @@ +//===--- ProtocolConformance.cpp - Swift protocol conformance checking ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Checking of Swift protocol conformances. +// +// This implementation is intended to be backward-deployed into Swift 5.3 and +// later runtimes. +// +//===----------------------------------------------------------------------===// + +#include "Overrides.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/BuiltinProtocolConformances.h" +#include +#include + +using namespace swift; + +static const ProtocolDescriptor *getEquatableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSQMp"))); + return descriptor; +} + +static const WitnessTable *conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "swift_conformsToProtocol"))); + return func(type, protocol); +} + +static bool tupleConformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol) { + auto tuple = cast(type); + + // At the moment, tuples can only conform to Equatable, so reject all other + // protocols. + if (protocol != getEquatableDescriptor()) + return false; + + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + if (!conformsToProtocol(elt.Type, protocol)) + return false; + } + + return true; +} + +extern const WitnessTable _swift_tupleEquatable_wt; + +static const WitnessTable *getTupleConformanceWitnessTable( + const ProtocolDescriptor *protocol) { + if (protocol == getEquatableDescriptor()) { + return &_swift_tupleEquatable_wt; + } + + return nullptr; +} + +const WitnessTable * +swift::swift53override_conformsToProtocol(const Metadata *type, + const ProtocolDescriptor *protocol, + ConformsToProtocol_t *original_conformsToProtocol) +{ + // Swift 5.4 introduces tuple Equatable conformance, so ensure that Swift 5.3 + // and later runtimes can handle this as well. + if (auto tuple = dyn_cast(type)) { + if (!tupleConformsToProtocol(type, protocol)) + return nullptr; + + return getTupleConformanceWitnessTable(protocol); + } + + auto result = original_conformsToProtocol(type, protocol); + if (result) + return result; + + return nullptr; +} diff --git a/test/IRGen/autolink-runtime-compatibility.swift b/test/IRGen/autolink-runtime-compatibility.swift index f917f2115481a..a06fc8bb4457d 100644 --- a/test/IRGen/autolink-runtime-compatibility.swift +++ b/test/IRGen/autolink-runtime-compatibility.swift @@ -16,14 +16,17 @@ // Autolinks because compatibility library was explicitly asked for // RUN: %target-swift-frontend -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -runtime-compatibility-version 5.3 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-53 %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.3 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-53 %s public func foo() {} // NO-FORCE-LOAD-NOT: FORCE_LOAD // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility50"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility51"} +// NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility53"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibilityDynamicReplacements"} // FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility50" @@ -42,3 +45,16 @@ public func foo() {} // FORCE-LOAD-51-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" + +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-53: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility53" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-53-DAG: [[AUTOLINK_SWIFT_COMPAT:![0-9]+]] = !{!"-lswiftCompatibility53"} +// FORCE-LOAD-53-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-53-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" diff --git a/test/stdlib/Compatibility50Linking.c b/test/stdlib/Compatibility50Linking.c index deb98344323b7..8963a1b2b6e92 100644 --- a/test/stdlib/Compatibility50Linking.c +++ b/test/stdlib/Compatibility50Linking.c @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-clang %s -all_load %test-resource-dir/%target-sdk-name/libswiftCompatibility50.a %test-resource-dir/%target-sdk-name/libswiftCompatibility51.a -lobjc -o %t/main +// RUN: %target-clang %s -all_load %test-resource-dir/%target-sdk-name/libswiftCompatibility50.a %test-resource-dir/%target-sdk-name/libswiftCompatibility51.a %test-resource-dir/%target-sdk-name/libswiftCompatibility53.a -lobjc -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main // REQUIRES: objc_interop From fd950ebbf35d93991417cc91d8b7f6d757c63d11 Mon Sep 17 00:00:00 2001 From: Azoy Date: Fri, 17 Jul 2020 20:18:17 -0400 Subject: [PATCH 05/15] Implement Tuple Comparable Conformance Add protocol witnesses for all Comparable requirements --- .../Runtime/BuiltinProtocolConformances.h | 76 ++- lib/AST/Module.cpp | 30 +- lib/IRGen/IRGenMangler.cpp | 10 +- .../runtime/BuiltinProtocolConformances.cpp | 499 +++++++++++++++++- stdlib/public/runtime/ProtocolConformance.cpp | 19 +- ...re_clause_contextually_generic_decls.swift | 2 +- test/IDE/complete_expr_tuple.swift | 22 +- test/IRGen/builtin_conformances.swift | 62 +++ test/IRGen/tuple_equatable_conformance.swift | 28 - test/Interpreter/builtin_conformances.swift | 220 ++++++++ .../tuple_equatable_conformance.swift | 70 --- 11 files changed, 884 insertions(+), 154 deletions(-) create mode 100644 test/IRGen/builtin_conformances.swift delete mode 100644 test/IRGen/tuple_equatable_conformance.swift create mode 100644 test/Interpreter/builtin_conformances.swift delete mode 100644 test/Interpreter/tuple_equatable_conformance.swift diff --git a/include/swift/Runtime/BuiltinProtocolConformances.h b/include/swift/Runtime/BuiltinProtocolConformances.h index f6d540e1bf599..5b2dd9ef3db36 100644 --- a/include/swift/Runtime/BuiltinProtocolConformances.h +++ b/include/swift/Runtime/BuiltinProtocolConformances.h @@ -25,16 +25,22 @@ namespace swift { #define XSTR(a) STR(a) #define SYMBOL(name) XSTR(__USER_LABEL_PREFIX__) name -// public protocol Equatable {} -#define SWIFT_EQUATABLE_MANGLING SQ - #define PROTOCOL_DESCRIPTOR_MANGLING Mp #define PROTOCOL_DESCRIPTOR_SYM(Proto) \ MANGLE_SYM(MANGLING_CONCAT2(Proto, PROTOCOL_DESCRIPTOR_MANGLING)) -#define EQUATABLE_PROTOCOL_DESCRIPTOR \ - PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING) +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Equatable {} +#define SWIFT_EQUATABLE_MANGLING SQ + +#define EQUATABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING) + +#define EQUATABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSQMp") +#define EQUATABLE_EE_METHOD_DESCRIPTOR SYMBOL("$sSQ2eeoiySbx_xtFZTq") #define TUPLE_EQUATABLE_CONF SYMBOL("_swift_tupleEquatable_conf") #define TUPLE_EQUATABLE_EQUALS SYMBOL("_swift_tupleEquatable_equals") @@ -46,6 +52,66 @@ bool _swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2, SWIFT_CONTEXT Metadata *swiftSelf, Metadata *Self, void *witnessTable); +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Comparable {} +#define SWIFT_COMPARABLE_MANGLING SL + +#define COMPARABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_COMPARABLE_MANGLING) + +#define COMPARABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSLMp") + +#define COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR SYMBOL("$sSLSQTb") +#define COMPARABLE_LT_METHOD_DESCRIPTOR SYMBOL("$sSL1loiySbx_xtFZTq") +#define COMPARBALE_LTE_METHOD_DESCRIPTOR SYMBOL("$sSL2leoiySbx_xtFZTq") +#define COMPARABLE_GTE_METHOD_DESCRIPTOR SYMBOL("$sSL2geoiySbx_xtFZTq") +#define COMPARABLE_GT_METHOD_DESCRIPTOR SYMBOL("$sSL1goiySbx_xtFZTq") + +#define TUPLE_COMPARABLE_CONF SYMBOL("_swift_tupleComparable_conf") +#define TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE \ + SYMBOL("associated conformance _swift_tupleComparable") +#define TUPLE_COMPARABLE_BASEACCESSOREQUATABLE \ + SYMBOL("_swift_tupleComparable_baseAccessorEquatable") +#define TUPLE_COMPARABLE_LESSTHAN SYMBOL("_swift_tupleComparable_lessThan") +#define TUPLE_COMPARABLE_LESSTHANOREQUAL \ + SYMBOL("_swift_tupleComparable_lessThanOrEqual") +#define TUPLE_COMPARABLE_GREATERTHANOREQUAL \ + SYMBOL("_swift_tupleComparable_greaterThanOrEqual") +#define TUPLE_COMPARABLE_GREATERTHAN \ + SYMBOL("_swift_tupleComparable_greaterThan") + +/// The protocol witness for static Swift.Comparable.< infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_lessThan(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.<= infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.>= infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + +/// The protocol witness for static Swift.Comparable.> infix(A, A) -> Swift.Bool +/// in conformance (A...): Swift.Comparable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool _swift_tupleComparable_greaterThan(OpaqueValue *tuple1, OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable); + } // end namespace swift #endif diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 7ceca1f6a62af..bd7c57e6bebe1 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1014,25 +1014,29 @@ LookupConformanceInModuleRequest::evaluate( return ProtocolConformanceRef(protocol); // Tuples have builtin conformances implemented within the runtime. - // These conformances so far consist of Equatable. + // These conformances so far consist of Equatable and Comparable. if (auto tuple = type->getAs()) { - if (protocol == ctx.getProtocol(KnownProtocolKind::Equatable)) { - SmallVector elementConformances; + auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); - // Ensure that every element in this tuple conforms to Equatable. - for (auto eltTy : tuple->getElementTypes()) { - auto conformance = mod->lookupConformance(eltTy, protocol); + if (protocol != equatable && protocol != comparable) + return ProtocolConformanceRef::forInvalid(); - if (conformance.isInvalid()) - return ProtocolConformanceRef::forInvalid(); + SmallVector elementConformances; - elementConformances.push_back(conformance); - } + // Ensure that every element in this tuple conforms to said protocol. + for (auto eltTy : tuple->getElementTypes()) { + auto conformance = mod->lookupConformance(eltTy, protocol); + + if (conformance.isInvalid()) + return ProtocolConformanceRef::forInvalid(); - auto conformance = ctx.getBuiltinConformance(tuple, protocol, - elementConformances); - return ProtocolConformanceRef(conformance); + elementConformances.push_back(conformance); } + + auto conformance = ctx.getBuiltinConformance(tuple, protocol, + elementConformances); + return ProtocolConformanceRef(conformance); } auto nominal = type->getAnyNominal(); diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 972c9bf991af0..7eb01dbb90a13 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -156,15 +156,21 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( if (conformance->getType()->is()) { auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); + auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); if (conformance->getProtocol() == equatable) { return "_swift_tupleEquatable_conf"; } - llvm_unreachable("mangling unknown tuple witness table protocol"); + if (conformance->getProtocol() == comparable) { + return "_swift_tupleComparable_conf"; + } + + llvm_unreachable("mangling conformance descriptor for unknown tuple \ + protocol"); } - llvm_unreachable("mangling unknown builtin witness table type"); + llvm_unreachable("mangling conformance descriptor for unknown builtin type"); } beginMangling(); diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index ef8177dfeb357..a136916b4e8d5 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -19,29 +19,37 @@ #include "swift/Runtime/Debug.h" #include "swift/Runtime/Metadata.h" +#include + using namespace swift; -extern const ProtocolDescriptor -PROTOCOL_DESCRIPTOR_SYM(SWIFT_EQUATABLE_MANGLING); +using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, + const WitnessTable *); + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// #if defined(__ELF__) // Create a GOT equivalent for the Equatable reference. __asm( - " .type got.$sSQMp, @object\n" + " .type got." EQUATABLE_DESCRIPTOR_SYMBOL ", @object\n" " .section .data.rel.ro\n" " .p2align 3\n" - "got.$sSQMp:\n" - " .quad ($sSQMp)\n" - " .size got.$sSQMp, 8\n" + "got." EQUATABLE_DESCRIPTOR_SYMBOL ":\n" + " .quad (" EQUATABLE_DESCRIPTOR_SYMBOL ")\n" + " .size got." EQUATABLE_DESCRIPTOR_SYMBOL ", 8\n" ); // Create a GOT equivalent for the Equatable.== method descriptor. __asm( - " .type got.$sSQ2eeoiySbx_xtFZTq, @object\n" + " .type got." EQUATABLE_EE_METHOD_DESCRIPTOR ", @object\n" " .p2align 3\n" - "got.$sSQ2eeoiySbx_xtFZTq:\n" - " .quad ($sSQ2eeoiySbx_xtFZTq)\n" - " .size got.$sSQ2eeoiySbx_xtFZTq, 8\n" + "got." EQUATABLE_EE_METHOD_DESCRIPTOR ":\n" + " .quad (" EQUATABLE_EE_METHOD_DESCRIPTOR ")\n" + " .size got." EQUATABLE_EE_METHOD_DESCRIPTOR ", 8\n" ); #endif @@ -65,9 +73,10 @@ __asm( #if defined(__ELF__) // This is an indirectable relative reference to the GOT equivalent for the // Equatable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got.$sSQMp - (" TUPLE_EQUATABLE_CONF ")) + 1\n" + " .long (got." EQUATABLE_DESCRIPTOR_SYMBOL " - \ + (" TUPLE_EQUATABLE_CONF ")) + 1\n" #elif defined(__MACH__) - " .long _$sSQMp@GOTPCREL + 5\n" + " .long " EQUATABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" #endif // 769 is the MetadataKind::Tuple " .long 769\n" @@ -84,9 +93,10 @@ __asm( #if defined(__ELF__) // This is an indirectable relative reference to the GOT equivalent for the // Equatable.== method descriptor, hence why we add 1 to indicate indirect. - " .long ((got.$sSQ2eeoiySbx_xtFZTq - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" + " .long ((got." EQUATABLE_EE_METHOD_DESCRIPTOR " - \ + (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" #elif defined(__MACH__) - " .long _$sSQ2eeoiySbx_xtFZTq@GOTPCREL + 5\n" + " .long " EQUATABLE_EE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" #endif // This is a direct relative reference to the equals witness defined below. " .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n" @@ -104,6 +114,8 @@ __asm( #endif ); +extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; + SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, OpaqueValue *tuple2, @@ -128,10 +140,7 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Grab the specific witness for this element type. auto equatableTable = reinterpret_cast(conformance); auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; - using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, - SWIFT_CONTEXT const Metadata *, - const Metadata *, const WitnessTable *); - auto equals = reinterpret_cast(equalsWitness); + auto equals = reinterpret_cast(equalsWitness); // Call the equal function auto result = equals(value1, value2, elt.Type, elt.Type, conformance); @@ -144,3 +153,457 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Otherwise this tuple has value equality with all elements. return true; } + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +#if defined(__ELF__) +// Create a GOT equivalent for the Comparable reference. +__asm( + " .type got." COMPARABLE_DESCRIPTOR_SYMBOL ", @object\n" + " .section .data.rel.ro\n" + " .p2align 3\n" + "got." COMPARABLE_DESCRIPTOR_SYMBOL ":\n" + " .quad (" COMPARABLE_DESCRIPTOR_SYMBOL ")\n" + " .size got." COMPARABLE_DESCRIPTOR_SYMBOL ", 8\n" +); + +// Create a GOT equivalent for the Comparable base conformance to Equatable. +__asm( + " .type got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" + " .size got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.< method descriptor. +__asm( + " .type got." COMPARABLE_LT_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_LT_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_LT_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARABLE_LT_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.<= method descriptor. +__asm( + " .type got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARBALE_LTE_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARBALE_LTE_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.>= method descriptor. +__asm( + " .type got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_GTE_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_GTE_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Comparable.> method descriptor. +__asm( + " .type got." COMPARABLE_GT_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." COMPARABLE_GT_METHOD_DESCRIPTOR ":\n" + " .quad (" COMPARABLE_GT_METHOD_DESCRIPTOR ")\n" + " .size got." COMPARABLE_GT_METHOD_DESCRIPTOR ", 8\n" +); +#endif + +// Define the associated conformance structure for tuple Comparable. We do this +// in assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .hidden \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .type \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", @object\n" + " .section swift5_typeref, \"a\"" + " .weak \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"" + #elif defined(__MACH__) + " .private_extern \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .section __TEXT, __swift5_typeref\n" + " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .weak_definition \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + #endif + " .p2align 1\n" + "\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\":\n" + " .byte 255\n" + " .byte 7\n" + // This is a direct relative reference to the base accessor for Equatable + // defined below. + " .long ((" TUPLE_COMPARABLE_BASEACCESSOREQUATABLE ") - \ + (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\")) - 2\n" + // This 0 is our null terminator. + " .byte 0\n" + #if defined (__ELF__) + " .size \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", 7\n" + #endif +); + +// Define the conformance descriptor for tuple Comparable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleComparable_private, @object\n" + " .local __swift_tupleComparable_private\n" + " .comm __swift_tupleComparable_private, 128, 16\n" + " .protected " TUPLE_COMPARABLE_CONF "\n" + " .type " TUPLE_COMPARABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleComparable_private, 128, 4\n" + " .section __TEXT, __const\n" + #endif + " .globl " TUPLE_COMPARABLE_CONF "\n" + " .p2align 2\n" + TUPLE_COMPARABLE_CONF ":\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable protocol descriptor, hence why we add 1 to indicate indirect. + " .long (got." COMPARABLE_DESCRIPTOR_SYMBOL " - \ + (" TUPLE_COMPARABLE_CONF ")) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" + #endif + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 5 is the ResilientWitnessesHeader indicating we have 5 resilient + // witnesses. + " .long 5\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable base conformance for Equatable, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 20) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the associated conformance for + // Equatable defined above in assembly. + " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ + (" TUPLE_COMPARABLE_CONF ")) - 24\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.< method descriptor, hence why we add 1 to indicate indirect. + " .long ((got." COMPARABLE_LT_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 28) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_LT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the less than witness defined below. + " .long ((" TUPLE_COMPARABLE_LESSTHAN ") - (" TUPLE_COMPARABLE_CONF ")) - 32\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.<= method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARBALE_LTE_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 36) + 1\n" + #elif defined(__MACH__) + " .long " COMPARBALE_LTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the less than or equal witness + // defined below. + " .long ((" TUPLE_COMPARABLE_LESSTHANOREQUAL ") - \ + (" TUPLE_COMPARABLE_CONF ")) - 40\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.>= method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARABLE_GTE_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 44) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_GTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the greater than or equal witness + // defined below. + " .long ((" TUPLE_COMPARABLE_GREATERTHANOREQUAL ") - \ + (" TUPLE_COMPARABLE_CONF ")) - 48\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Comparable.> method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." COMPARABLE_GT_METHOD_DESCRIPTOR " - \ + (" TUPLE_COMPARABLE_CONF ")) - 52) + 1\n" + #elif defined(__MACH__) + " .long " COMPARABLE_GT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the greater than witness defined + // below. + " .long ((" TUPLE_COMPARABLE_GREATERTHAN ") - \ + (" TUPLE_COMPARABLE_CONF ")) - 56\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long (__swift_tupleComparable_private - (" TUPLE_COMPARABLE_CONF ")) - 68\n" + #if defined(__ELF__) + " .size " TUPLE_COMPARABLE_CONF ", 72\n" + #endif +); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +const WitnessTable * +_swift_tupleComparable_baseAccessorEquatable(Metadata *assocType, + Metadata *conformingType, + void **witnessTable) { + auto tuple = cast(assocType); + std::vector instantiationArgs; + + // Fill the instantiationArgs with the element Equatable tables. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + // Get the element's Comparable table. + auto comparableTable = reinterpret_cast(witnessTable[-1 - i]); + + // The Equatable table is the first requirement, thus it'll be right after + // the conformance descriptor. + auto equatableTable = comparableTable[WitnessTableFirstRequirementOffset]; + + instantiationArgs.push_back(equatableTable); + } + + // Finally, call getWitnessTable to realize the tuple's Equatable table. + auto equatableTable = swift_getWitnessTable(&_swift_tupleEquatable_conf, + assocType, + instantiationArgs.data()); + + return equatableTable; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than function + // and return the result. + auto lessThanWitness = comparableTable[WitnessTableFirstRequirementOffset + 1]; + auto lessThan = reinterpret_cast(lessThanWitness); + + // Call the less than function. + auto isLessThan = lessThan(value1, value2, elt.Type, elt.Type, conformance); + + return isLessThan; + } + + // Otherwise these tuples are completely equal, thus they are not less than + // each other. + return false; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than or equal + // function and return the result. + auto lessThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 2]; + auto lessThanOrEqual = + reinterpret_cast(lessThanOrEqualWitness); + + // Call the less than function. + auto isLessThanOrEqual = lessThanOrEqual(value1, value2, elt.Type, elt.Type, + conformance); + + return isLessThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than or + // equal function and return the result. + auto greaterThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 3]; + auto greaterThanOrEqual = + reinterpret_cast(greaterThanOrEqualWitness); + + // Call the greater than or equal function. + auto isGreaterThanOrEqual = greaterThanOrEqual(value1, value2, elt.Type, + elt.Type, conformance); + + return isGreaterThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than + // function and return the result. + auto greaterThanWitness = + comparableTable[WitnessTableFirstRequirementOffset + 4]; + auto greaterThan = + reinterpret_cast(greaterThanWitness); + + // Call the greater than function. + auto isGreaterThan = greaterThan(value1, value2, elt.Type, elt.Type, + conformance); + + return isGreaterThan; + } + + // Otherwise these tuples are completely equal, thus they are not greater than + // each other. + return false; +} diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index d14412f9ff2e2..6f281e0178156 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -376,16 +376,18 @@ searchInConformanceCache(const Metadata *type, return {false, nullptr}; } -extern const ProtocolDescriptor EQUATABLE_PROTOCOL_DESCRIPTOR; +extern const ProtocolDescriptor EQUATABLE_DESCRIPTOR; +extern const ProtocolDescriptor COMPARABLE_DESCRIPTOR; static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable, so reject all other - // protocols. - auto equatable = &EQUATABLE_PROTOCOL_DESCRIPTOR; - if (protocol != equatable) + // At the moment, tuples can only conform to Equatable and Comparable, so + // reject all other protocols. + auto equatable = &EQUATABLE_DESCRIPTOR; + auto comparable = &COMPARABLE_DESCRIPTOR; + if (protocol != equatable && protocol != comparable) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -398,13 +400,18 @@ static bool tupleConformsToProtocol(const Metadata *type, } extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; +extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( const ProtocolDescriptor *protocol) { - if (protocol == &EQUATABLE_PROTOCOL_DESCRIPTOR) { + if (protocol == &EQUATABLE_DESCRIPTOR) { return &_swift_tupleEquatable_conf; } + if (protocol == &COMPARABLE_DESCRIPTOR) { + return &_swift_tupleComparable_conf; + } + return nullptr; } diff --git a/test/Generics/where_clause_contextually_generic_decls.swift b/test/Generics/where_clause_contextually_generic_decls.swift index 27819a167a03f..87afc552198c7 100644 --- a/test/Generics/where_clause_contextually_generic_decls.swift +++ b/test/Generics/where_clause_contextually_generic_decls.swift @@ -147,7 +147,7 @@ extension Container.NestedStruct3 { _ = Container.NestedAlias2.self // expected-error {{type 'String' does not conform to protocol 'FixedWidthInteger'}} _ = Container>.NestedClass.self // expected-error {{type 'Container' does not conform to protocol 'Equatable'}} _ = Container.NestedStruct.self // expected-error {{type 'Void' does not conform to protocol 'Sequence'}} -_ = Container>.NestedStruct2.self // expected-error {{type 'Void' does not conform to protocol 'Comparable'}} +_ = Container>.NestedStruct2.self _ = Container.NestedStruct2.NestedEnum.self // expected-error {{'Container.NestedStruct2.NestedEnum' requires the types 'String.Element' (aka 'Character') and 'Double' be equivalent}} _ = Container.NestedAlias2.self _ = Container.NestedClass.self diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift index 7d863a856ba05..b3cb669f4b1be 100644 --- a/test/IDE/complete_expr_tuple.swift +++ b/test/IDE/complete_expr_tuple.swift @@ -27,7 +27,7 @@ func testTupleNoDot1() { var t = (1, 2.0) t#^TUPLE_NO_DOT_1^# } -// TUPLE_NO_DOT_1: Begin completions, 11 items +// TUPLE_NO_DOT_1: Begin completions, 14 items // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(Int, Double)#}[#Bool#]{{; name=.+$}} @@ -44,15 +44,15 @@ func testTupleNoDot2() { var t = (foo: 1, bar: 2.0) t#^TUPLE_NO_DOT_2^# } -// TUPLE_NO_DOT_2: Begin completions, 11 items +// TUPLE_NO_DOT_2: Begin completions, 14 items // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(foo: Int, bar: Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: BuiltinOperator/None: = {#(foo: Int, bar: Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, bar: Double)#]; name=self // TUPLE_NO_DOT_2-NEXT: End completions @@ -61,15 +61,15 @@ func testTupleNoDot3() { var t = (foo: 1, 2.0) t#^TUPLE_NO_DOT_3^# } -// TUPLE_NO_DOT_3: Begin completions, 11 items +// TUPLE_NO_DOT_3: Begin completions, 14 items // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(foo: Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: BuiltinOperator/None: = {#(foo: Int, Double)#}[#Void#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, Double)#]; name=self // TUPLE_NO_DOT_3-NEXT: End completions diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift new file mode 100644 index 0000000000000..2053d69457881 --- /dev/null +++ b/test/IRGen/builtin_conformances.swift @@ -0,0 +1,62 @@ +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s + +// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleComparable_conf = external global %swift.protocol_conformance_descriptor + +struct Wrapper { + let value: T +} + +//===----------------------------------------------------------------------===// +// Tuple Equatable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Equatable where T: Equatable {} + +public func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +public func useEquatable(_ thing: T) -> Bool { + // CHECK: [[USE_EQUATABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_EQUATABLE_WT]]) + equals((thing, thing), (thing, thing)) +} + +public func testTupleEquatable() { + // CHECK: [[TEST_TUPLE_EQUATABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_EQUATABLE_WT1]]) + let _ = equals((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.0* noalias nocapture undef, {{%.*}}.0* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_EQUATABLE_WT1]]) + let _ = Wrapper(value: ()) == Wrapper(value: ()) +} + +//===----------------------------------------------------------------------===// +// Tuple Comparable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Comparable where T: Comparable { + static func <(lhs: Wrapper, rhs: Wrapper) -> Bool { + return lhs.value < rhs.value + } +} + +public func compare(_ lhs: T, _ rhs: T) -> Bool { + lhs < rhs +} + +public func useComparable(_ thing: T) -> Bool { + // CHECK: [[USE_COMPARABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleComparable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_COMPARABLE_WT]]) + compare((thing, thing), (thing, thing)) +} + +public func testTupleComparable() { + // CHECK: [[TEST_TUPLE_COMPARABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleComparable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) + let _ = compare((), ()) + + // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.1* noalias nocapture undef, {{%.*}}.1* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) + let _ = Wrapper(value: ()) < Wrapper(value: ()) +} diff --git a/test/IRGen/tuple_equatable_conformance.swift b/test/IRGen/tuple_equatable_conformance.swift deleted file mode 100644 index 8242fa8139184..0000000000000 --- a/test/IRGen/tuple_equatable_conformance.swift +++ /dev/null @@ -1,28 +0,0 @@ -// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s - -// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor - -struct Wrapper { - let value: T -} - -extension Wrapper: Equatable where T: Equatable {} - -public func equals(_ lhs: T, _ rhs: T) -> Bool { - lhs == rhs -} - -public func use(_ thing: T) -> Bool { - // CHECK: [[USE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) - // CHECK-NEXT: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_WT]]) - equals((thing, thing), (thing, thing)) -} - -public func test() { - // CHECK: [[TEST_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleEquatable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) - // CHECK: {{%.*}} = call swiftcc i1 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) - let _ = equals((), ()) - - // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.0* noalias nocapture undef, {{%.*}}.0* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_WT1]]) - let _ = Wrapper(value: ()) == Wrapper(value: ()) -} diff --git a/test/Interpreter/builtin_conformances.swift b/test/Interpreter/builtin_conformances.swift new file mode 100644 index 0000000000000..182cb85904608 --- /dev/null +++ b/test/Interpreter/builtin_conformances.swift @@ -0,0 +1,220 @@ +// RUN: %target-run-simple-swift | %FileCheck %s +// REQUIRES: executable_test + +struct Wrapper { + let value: T +} + +extension Wrapper: Equatable where T: Equatable {} + +extension Wrapper: Comparable where T: Comparable { + static func <(lhs: Wrapper, rhs: Wrapper) -> Bool { + lhs.value < rhs.value + } +} + +class Foo { + var age: Int + + init(age: Int) { + self.age = age + } +} + +extension Foo: Equatable { + static func ==(lhs: Foo, rhs: Foo) -> Bool { + lhs.age == rhs.age + } +} + +extension Foo: Comparable { + static func <(lhs: Foo, rhs: Foo) -> Bool { + lhs.age < rhs.age + } +} + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + +func equals(_ lhs: T, _ rhs: T) -> Bool { + lhs == rhs +} + +// CHECK: true +print(equals((), ())) + +// CHECK: true +print(equals((128, 316), (128, 316))) + +// CHECK: false +print(equals((128, 316), (316, 128))) + +// CHECK: true +print(equals(((1, 2), 3), ((1, 2), 3))) + +// CHECK: false +print(equals(((1, 2), 3), ((1, 2), 4))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueTupleEquatableValue() -> some Equatable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + _ = opaqueTupleEquatableValue() == opaqueTupleEquatableValue() +} + +// CHECK: true +print(Wrapper(value: ()) == Wrapper(value: ())) + +// CHECK: true +print(Wrapper(value: (128, 316)) == Wrapper(value: (128, 316))) + +// CHECK: false +print(Wrapper(value: (128, 316)) == Wrapper(value: (316, 128))) + +func useEquatable(_ thing: T) -> Bool { + equals((thing, thing), (thing, thing)) +} + +// CHECK: true +print(useEquatable(128)) + +// CHECK: true +print(equals((Foo(age: 128), false, 0), (Foo(age: 128), false, 0))) + +// CHECK: false +print(equals((Foo(age: 128), false, 0), (Foo(age: 316), false, 0))) + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +func compareLT(_ lhs: T, _ rhs: T) -> Bool { + lhs < rhs +} + +func compareLTE(_ lhs: T, _ rhs: T) -> Bool { + lhs <= rhs +} + +func compareGTE(_ lhs: T, _ rhs: T) -> Bool { + lhs >= rhs +} + +func compareGT(_ lhs: T, _ rhs: T) -> Bool { + lhs > rhs +} + +// false +print(compareLT((), ())) +// true +print(compareLTE((), ())) +// true +print(compareGTE((), ())) +// false +print(compareGT((), ())) + +// false +print(compareLT((1, 2), (1, 2))) +// true +print(compareLTE((1, 2), (1, 2))) +// true +print(compareGTE((1, 2), (1, 2))) +// false +print(compareGT((1, 2), (1, 2))) + + +// true +print(compareLT((1, 2), (2, 1))) +// true +print(compareLTE((1, 2), (2, 1))) +// false +print(compareGTE((1, 2), (2, 1))) +// false +print(compareGT((1, 2), (2, 1))) + + +// false +print(compareLT(((1, 2), 3), ((1, 2), 3))) +// true +print(compareLTE(((1, 2), 3), ((1, 2), 3))) +// true +print(compareGTE(((1, 2), 3), ((1, 2), 3))) +// false +print(compareGT(((1, 2), 3), ((1, 2), 3))) + + +// true +print(compareLT(((1, 2), 3), ((1, 2), 4))) +// true +print(compareLTE(((1, 2), 3), ((1, 2), 4))) +// false +print(compareGTE(((1, 2), 3), ((1, 2), 4))) +// false +print(compareGT(((1, 2), 3), ((1, 2), 4))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueTupleComparableValue() -> some Comparable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + _ = opaqueTupleComparableValue() < opaqueTupleComparableValue() + _ = opaqueTupleComparableValue() <= opaqueTupleComparableValue() + _ = opaqueTupleComparableValue() >= opaqueTupleComparableValue() + _ = opaqueTupleComparableValue() > opaqueTupleComparableValue() +} + +// CHECK: false +print(Wrapper(value: ()) < Wrapper(value: ())) +// CHECK: true +print(Wrapper(value: ()) <= Wrapper(value: ())) +// CHECK: true +print(Wrapper(value: ()) >= Wrapper(value: ())) +// CHECK: false +print(Wrapper(value: ()) > Wrapper(value: ())) + +// CHECK: false +print(Wrapper(value: (128, 316)) < Wrapper(value: (128, 316))) +// CHECK: true +print(Wrapper(value: (128, 316)) <= Wrapper(value: (128, 316))) +// CHECK: true +print(Wrapper(value: (128, 316)) >= Wrapper(value: (128, 316))) +// CHECK: false +print(Wrapper(value: (128, 316)) > Wrapper(value: (128, 316))) + +// CHECK: true +print(Wrapper(value: (128, 316)) < Wrapper(value: (316, 128))) +// CHECK: true +print(Wrapper(value: (128, 316)) <= Wrapper(value: (316, 128))) +// CHECK: false +print(Wrapper(value: (128, 316)) >= Wrapper(value: (316, 128))) +// CHECK: false +print(Wrapper(value: (128, 316)) > Wrapper(value: (316, 128))) + +func useComparable(_ thing: T) -> Bool { + compareLT((thing, thing), (thing, thing)) +} + +// CHECK: false +print(useComparable(128)) + +// CHECK: false +print(compareLT((Foo(age: 128), 0), (Foo(age: 128), 0))) +// CHECK: true +print(compareLTE((Foo(age: 128), 0), (Foo(age: 128), 0))) +// CHECK: true +print(compareGTE((Foo(age: 128), 0), (Foo(age: 128), 0))) +// CHECK: false +print(compareGT((Foo(age: 128), 0), (Foo(age: 128), 0))) + +// CHECK: true +print(compareLT((Foo(age: 128), 0), (Foo(age: 734), 0))) +// CHECK: true +print(compareLTE((Foo(age: 128), 0), (Foo(age: 734), 0))) +// CHECK: false +print(compareGTE((Foo(age: 128), 0), (Foo(age: 734), 0))) +// CHECK: false +print(compareGT((Foo(age: 128), 0), (Foo(age: 734), 0))) diff --git a/test/Interpreter/tuple_equatable_conformance.swift b/test/Interpreter/tuple_equatable_conformance.swift deleted file mode 100644 index 64992461e55dd..0000000000000 --- a/test/Interpreter/tuple_equatable_conformance.swift +++ /dev/null @@ -1,70 +0,0 @@ -// RUN: %target-run-simple-swift | %FileCheck %s -// REQUIRES: executable_test - -func equals(_ lhs: T, _ rhs: T) -> Bool { - lhs == rhs -} - -// CHECK: true -print(equals((), ())) - -// CHECK: true -print(equals((128, 316), (128, 316))) - -// CHECK: false -print(equals((128, 316), (316, 128))) - -// CHECK: true -print(equals(((1, 2), 3), ((1, 2), 3))) - -// CHECK: false -print(equals(((1, 2), 3), ((1, 2), 4))) - -@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) -func opaqueEquatableValue() -> some Equatable { - (1, 2, 3, 4, 5) -} - -if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { - print(opaqueEquatableValue() == opaqueEquatableValue()) -} - -struct Wrapper { - let value: T -} - -extension Wrapper: Equatable where T: Equatable {} - -// CHECK: true -print(Wrapper(value: ()) == Wrapper(value: ())) - -// CHECK: true -print(Wrapper(value: (128, 316)) == Wrapper(value: (128, 316))) - -// CHECK: false -print(Wrapper(value: (128, 316)) == Wrapper(value: (316, 128))) - -func use(_ thing: T) -> Bool { - equals((thing, thing), (thing, thing)) -} - -// CHECK: true -print(use(128)) - -class Foo: Equatable { - var age: Int - - init(age: Int) { - self.age = age - } - - static func == (lhs: Foo, rhs: Foo) -> Bool { - lhs.age == rhs.age - } -} - -// CHECK: true -print(equals((Foo(age: 128), false, 0), (Foo(age: 128), false, 0))) - -// CHECK: false -print(equals((Foo(age: 128), false, 0), (Foo(age: 316), false, 0))) From 13b5bf4cfd3c3c4088580a8c20c30067beb352c5 Mon Sep 17 00:00:00 2001 From: Azoy Date: Fri, 17 Jul 2020 20:33:04 -0400 Subject: [PATCH 06/15] [Compatibility53] Backport Tuple Comparable Conformance Add protocol witnesses for Comparable requirements remove unused table --- .../BuiltinProtocolConformances.cpp | 336 +++++++++++++++++- .../Compatibility53/ProtocolConformance.cpp | 21 +- 2 files changed, 341 insertions(+), 16 deletions(-) diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index 1d39c759c458e..c3893c8b668b5 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -30,6 +30,13 @@ static const ProtocolDescriptor *getEquatableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getComparableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSLMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -39,6 +46,21 @@ static const WitnessTable *conformsToProtocol(const Metadata *type, return func(type, protocol); } +template +struct _WitnessTable { + const ProtocolConformanceDescriptor *Conformance; + const void *Witnesses[NumWitnesses]; +}; + +using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, + SWIFT_CONTEXT const Metadata *, + const Metadata *, + const WitnessTable *); + +//===----------------------------------------------------------------------===// +// Tuple Equatable Conformance +//===----------------------------------------------------------------------===// + #define TUPLE_EQUATABLE_WT SYMBOL("_swift_tupleEquatable_wt") // Define the conformance descriptor for tuple Equatable. We do this in @@ -66,7 +88,7 @@ extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; // dependency to libswiftCore (which is where the Equatable protocol desciptor // lives), we have to manually implant this before calling any user code. __attribute__((constructor)) -void _emplaceEquatableDescriptor() { +void _emplaceTupleEquatableDescriptor() { auto tupleEquatableConf = const_cast( reinterpret_cast(&_swift_tupleEquatable_conf)); auto equatable = getEquatableDescriptor(); @@ -75,12 +97,6 @@ void _emplaceEquatableDescriptor() { *tupleEquatableConf = intptr_t(equatable) - intptr_t(tupleEquatableConf); } -template -struct _WitnessTable { - const ProtocolConformanceDescriptor *Conformance; - const void *Witnesses[NumWitnesses]; -}; - SWIFT_RUNTIME_EXPORT const _WitnessTable<1> _swift_tupleEquatable_wt = { &_swift_tupleEquatable_conf, @@ -119,10 +135,7 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Grab the specific witness for this element type. auto equatableTable = reinterpret_cast(conformance); auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; - using Fn = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, - SWIFT_CONTEXT const Metadata *, - const Metadata *, const WitnessTable *); - auto equals = reinterpret_cast(equalsWitness); + auto equals = reinterpret_cast(equalsWitness); // Call the equal function. auto result = equals(value1, value2, elt.Type, elt.Type, conformance); @@ -135,3 +148,304 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Otherwise this tuple has value equality with all elements. return true; } + +//===----------------------------------------------------------------------===// +// Tuple Comparable Conformance +//===----------------------------------------------------------------------===// + +#define TUPLE_COMPARABLE_WT SYMBOL("_swift_tupleComparable_wt") + +// Define the conformance descriptor for tuple Comparable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_COMPARABLE_CONF "\n" + " .p2align 2\n" + TUPLE_COMPARABLE_CONF ":\n" + // This is an indirectable relative reference to the Comparable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Comparable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long ((" TUPLE_COMPARABLE_WT ") - (" TUPLE_COMPARABLE_CONF ")) - 8\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Comparable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleComparableDescriptor() { + auto tupleComparableConf = const_cast( + reinterpret_cast(&_swift_tupleComparable_conf)); + auto comparable = getComparableDescriptor(); + + // This is an indirectable pointer. + *tupleComparableConf = intptr_t(comparable) - intptr_t(tupleComparableConf); +} + +// The base Equatable protocol is itself a requirement, thus the requirement +// count is 5 (Equatable + 4 operators) and the witness is the tuple Equatable +// table. +SWIFT_RUNTIME_EXPORT +const _WitnessTable<5> _swift_tupleComparable_wt = { + &_swift_tupleComparable_conf, + { + reinterpret_cast(&_swift_tupleEquatable_wt), + reinterpret_cast(_swift_tupleComparable_lessThan), + reinterpret_cast(_swift_tupleComparable_lessThanOrEqual), + reinterpret_cast(_swift_tupleComparable_greaterThanOrEqual), + reinterpret_cast(_swift_tupleComparable_greaterThan) + } +}; + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than function + // and return the result. + auto lessThanWitness = comparableTable[1 + WitnessTableFirstRequirementOffset]; + auto lessThan = reinterpret_cast(lessThanWitness); + + // Call the less than function. + auto isLessThan = lessThan(value1, value2, elt.Type, elt.Type, conformance); + + return isLessThan; + } + + // Otherwise these tuples are completely equal, thus they are not less than + // each other. + return false; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_lessThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their less than or equal + // function and return the result. + auto lessThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 2]; + auto lessThanOrEqual = + reinterpret_cast(lessThanOrEqualWitness); + + // Call the less than function. + auto isLessThanOrEqual = lessThanOrEqual(value1, value2, elt.Type, elt.Type, + conformance); + + return isLessThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThanOrEqual(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than or + // equal function and return the result. + auto greaterThanOrEqualWitness = + comparableTable[WitnessTableFirstRequirementOffset + 3]; + auto greaterThanOrEqual = + reinterpret_cast(greaterThanOrEqualWitness); + + // Call the greater than or equal function. + auto isGreaterThanOrEqual = greaterThanOrEqual(value1, value2, elt.Type, + elt.Type, conformance); + + return isGreaterThanOrEqual; + } + + // Otherwise these tuples are completely equal. + return true; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, + OpaqueValue *tuple2, + SWIFT_CONTEXT Metadata *swiftSelf, + Metadata *Self, + void *witnessTable) { + auto tuple = cast(Self); + + // Loop through all elements, and check if both tuples element is equal. + for (size_t i = 0; i != tuple->NumElements; i += 1) { + auto elt = tuple->getElement(i); + + // Ensure we actually have a conformance to Comparable for this element type. + auto comparable = getComparableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, comparable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Comparable...?? + if (!conformance) + swift_unreachable("Tuple comparability requires that all elements \ + be Comparable."); + + // Get the respective values from both tuples. + auto value1 = reinterpret_cast( + reinterpret_cast(tuple1) + elt.Offset); + auto value2 = reinterpret_cast( + reinterpret_cast(tuple2) + elt.Offset); + + // First, grab the equatable conformance to prepare to check if the elements + // are equal. (Since this require Equatable conformance, the witness table + // is right after the conformance descriptor, which is at index 0.) + auto comparableTable = reinterpret_cast(conformance); + auto equatableTable = reinterpret_cast(comparableTable[1]); + auto equalsWitness = equatableTable[WitnessTableFirstRequirementOffset]; + auto equals = reinterpret_cast(equalsWitness); + + // Call the equal function. + auto isEqual = equals(value1, value2, elt.Type, elt.Type, + reinterpret_cast(equatableTable)); + + // If these are equal, skip to the next element. + if (isEqual) + continue; + + // Now that we know they are not equal, we can call their greater than + // function and return the result. + auto greaterThanWitness = + comparableTable[WitnessTableFirstRequirementOffset + 4]; + auto greaterThan = + reinterpret_cast(greaterThanWitness); + + // Call the greater than function. + auto isGreaterThan = greaterThan(value1, value2, elt.Type, elt.Type, + conformance); + + return isGreaterThan; + } + + // Otherwise these tuples are completely equal, thus they are not greater than + // each other. + return false; +} diff --git a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp index 8520769271543..76f82e7b8af2c 100644 --- a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp @@ -32,6 +32,13 @@ static const ProtocolDescriptor *getEquatableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getComparableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSLMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -45,9 +52,10 @@ static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable, so reject all other - // protocols. - if (protocol != getEquatableDescriptor()) + // At the moment, tuples can only conform to Equatable and Comparable, so + // reject all other protocols. + if (protocol != getEquatableDescriptor() && + protocol != getComparableDescriptor()) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -60,12 +68,15 @@ static bool tupleConformsToProtocol(const Metadata *type, } extern const WitnessTable _swift_tupleEquatable_wt; +extern const WitnessTable _swift_tupleComparable_wt; static const WitnessTable *getTupleConformanceWitnessTable( const ProtocolDescriptor *protocol) { - if (protocol == getEquatableDescriptor()) { + if (protocol == getEquatableDescriptor()) return &_swift_tupleEquatable_wt; - } + + if (protocol == getComparableDescriptor()) + return &_swift_tupleComparable_wt; return nullptr; } From 58e643e1b07b215d944cff736eb9202ce9035218 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 19 Jul 2020 21:38:16 -0400 Subject: [PATCH 07/15] [Runtime] Tuple Comparable associated conformance is not evenly aligned --- stdlib/public/runtime/BuiltinProtocolConformances.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index a136916b4e8d5..e70a0edad3835 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -291,9 +291,10 @@ __asm( " .long " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" #endif // This is a direct relative reference to the associated conformance for - // Equatable defined above in assembly. + // Equatable defined above in assembly. NOTE: This is minus 23 because the + // associated conformance structure is 1 aligned. " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ - (" TUPLE_COMPARABLE_CONF ")) - 24\n" + (" TUPLE_COMPARABLE_CONF ")) - 23\n" #if defined(__ELF__) // This is an indirectable relative reference to the GOT equivalent for the // Comparable.< method descriptor, hence why we add 1 to indicate indirect. From 4ff28f2b40918b016640a3e933c67a7e49d40ead Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 19 Jul 2020 22:07:26 -0400 Subject: [PATCH 08/15] Implement Tuple Hashable Conformance --- .../Runtime/BuiltinProtocolConformances.h | 39 ++++ lib/AST/Module.cpp | 5 +- lib/IRGen/IRGenMangler.cpp | 5 + .../runtime/BuiltinProtocolConformances.cpp | 200 ++++++++++++++++++ stdlib/public/runtime/ProtocolConformance.cpp | 13 +- test/IRGen/builtin_conformances.swift | 26 +++ test/Interpreter/builtin_conformances.swift | 95 ++++++++- test/expr/unary/keypath/keypath.swift | 1 - 8 files changed, 375 insertions(+), 9 deletions(-) diff --git a/include/swift/Runtime/BuiltinProtocolConformances.h b/include/swift/Runtime/BuiltinProtocolConformances.h index 5b2dd9ef3db36..71cf4fef11631 100644 --- a/include/swift/Runtime/BuiltinProtocolConformances.h +++ b/include/swift/Runtime/BuiltinProtocolConformances.h @@ -112,6 +112,45 @@ bool _swift_tupleComparable_greaterThan(OpaqueValue *tuple1, OpaqueValue *tuple2 SWIFT_CONTEXT Metadata *swiftSelf, Metadata *Self, void *witnessTable); +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +// public protocol Hashable {} +#define SWIFT_HASHABLE_MANGLING SH + +#define HASHABLE_DESCRIPTOR PROTOCOL_DESCRIPTOR_SYM(SWIFT_HASHABLE_MANGLING) + +#define HASHABLE_DESCRIPTOR_SYMBOL SYMBOL("$sSHMp") + +// Swift._hashValue(for: A) -> Swift.Int +#define SWIFT_HASHVALUE_FUNC $ss10_hashValue3forSix_tSHRzlF +// Swift.Hasher.combine(A) -> () +#define SWIFT_HASHER_COMBINE_FUNC $ss6HasherV7combineyyxSHRzlF + +#define HASHABLE_BASE_CONFORMANCE_DESCRIPTOR SYMBOL("$sSHSQTb") +#define HASHABLE_HASHVALUE_METHOD_DESCRIPTOR SYMBOL("$sSH9hashValueSivgTq") +#define HASHABLE_HASH_METHOD_DESCRIPTOR SYMBOL("$sSH4hash4intoys6HasherVz_tFTq") +#define HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR \ + SYMBOL("$sSH13_rawHashValue4seedS2i_tFTq") + +#define TUPLE_HASHABLE_CONF SYMBOL("_swift_tupleHashable_conf") +#define TUPLE_HASHABLE_HASHVALUE SYMBOL("_swift_tupleHashable_hashValue") +#define TUPLE_HASHABLE_HASH SYMBOL("_swift_tupleHashable_hash") + +/// The protocol witness for Swift.Hashable.hashValue.getter: Swift.Int in +/// conformance (A...): Swift.Hashable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable); + +/// The protocol witness for Swift.Hashable.hash(into:) in conformance +/// (A...): Swift.Hashable in Swift. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void _swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable); + } // end namespace swift #endif diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index bd7c57e6bebe1..34a97a61f8526 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1014,12 +1014,13 @@ LookupConformanceInModuleRequest::evaluate( return ProtocolConformanceRef(protocol); // Tuples have builtin conformances implemented within the runtime. - // These conformances so far consist of Equatable and Comparable. + // These conformances so far consist of Equatable, Comparable, and Hashable. if (auto tuple = type->getAs()) { auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); - if (protocol != equatable && protocol != comparable) + if (protocol != equatable && protocol != comparable && protocol != hashable) return ProtocolConformanceRef::forInvalid(); SmallVector elementConformances; diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 7eb01dbb90a13..c6bd374d7df0b 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -157,6 +157,7 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( if (conformance->getType()->is()) { auto equatable = ctx.getProtocol(KnownProtocolKind::Equatable); auto comparable = ctx.getProtocol(KnownProtocolKind::Comparable); + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); if (conformance->getProtocol() == equatable) { return "_swift_tupleEquatable_conf"; @@ -166,6 +167,10 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( return "_swift_tupleComparable_conf"; } + if (conformance->getProtocol() == hashable) { + return "_swift_tupleHashable_conf"; + } + llvm_unreachable("mangling conformance descriptor for unknown tuple \ protocol"); } diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index e70a0edad3835..768db5ae6d086 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -608,3 +608,203 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // each other. return false; } + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +#if defined(__ELF__) +// Create a GOT equivalent for the Hashable reference. +__asm( + " .type got." HASHABLE_DESCRIPTOR_SYMBOL ", @object\n" + " .section .data.rel.ro\n" + " .p2align 3\n" + "got." HASHABLE_DESCRIPTOR_SYMBOL ":\n" + " .quad (" HASHABLE_DESCRIPTOR_SYMBOL ")\n" + " .size got." HASHABLE_DESCRIPTOR_SYMBOL ", 8\n" +); + +// Create a GOT equivalent for the Hashable base conformance to Equatable. +__asm( + " .type got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" + " .quad (" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" + " .size got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Hashable.hashValue method descriptor. +__asm( + " .type got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ":\n" + " .quad (" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ")\n" + " .size got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Hashable.hash(into:) method descriptor. +__asm( + " .type got." HASHABLE_HASH_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_HASH_METHOD_DESCRIPTOR ":\n" + " .quad (" HASHABLE_HASH_METHOD_DESCRIPTOR ")\n" + " .size got." HASHABLE_HASH_METHOD_DESCRIPTOR ", 8\n" +); + +// Create a GOT equivalent for the Hashable._rawHashValue method descriptor. +__asm( + " .type got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", @object\n" + " .p2align 3\n" + "got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ":\n" + " .quad (" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ")\n" + " .size got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", 8\n" +); +#endif + +// Define the conformance descriptor for tuple Hashable. We do this in +// assembly to work around relative reference issues. +__asm( + #if defined(__ELF__) + " .type __swift_tupleHashable_private, @object\n" + " .local __swift_tupleHashable_private\n" + " .comm __swift_tupleHashable_private, 128, 16\n" + " .protected " TUPLE_HASHABLE_CONF "\n" + " .type " TUPLE_HASHABLE_CONF ", @object\n" + " .section .rodata\n" + #elif defined(__MACH__) + " .zerofill __DATA, __bss, __swift_tupleHashable_private, 128, 4\n" + " .section __TEXT, __const\n" + #endif + " .globl " TUPLE_HASHABLE_CONF "\n" + " .p2align 2\n" + TUPLE_HASHABLE_CONF ":\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable protocol descriptor, hence why we add 1 to indicate indirect. + " .long (got." HASHABLE_DESCRIPTOR_SYMBOL " - \ + (" TUPLE_HASHABLE_CONF ")) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" + #endif + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This indicates that we have no witness table pattern. We use a generic + // witness table for builtin conformances. + " .long 0\n" + // 196640 are the ConformanceFlags with the type reference bit set to + // MetadataKind, the has resilient witness bit, and the generic witness table + // bit. + " .long 196640\n" + // This 4 is the ResilientWitnessesHeader indicating we have 4 resilient + // witnesses. + " .long 4\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable base conformance for Equatable, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 20) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the associated conformance for + // Equatable defined above in assembly. NOTE: We intentionally use the + // Comparable implementation for this because the implementation is the same + // for both Hashable and Comparable. Both want to grab the Equatable table + // from its elements whose witness table is located in the same place for both + // protocols. NOTE: This is minus 23 because the associated conformance + // structure is 1 aligned. + " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ + (" TUPLE_HASHABLE_CONF ")) - 23\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable.hashValue method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 28) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the hashValue witness defined below. + " .long ((" TUPLE_HASHABLE_HASHVALUE ") - (" TUPLE_HASHABLE_CONF ")) - 32\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable.hash(into:) method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_HASH_METHOD_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 36) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_HASH_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This is a direct relative reference to the hash(into:) witness defined below. + " .long ((" TUPLE_HASHABLE_HASH ") - (" TUPLE_HASHABLE_CONF ")) - 40\n" + #if defined(__ELF__) + // This is an indirectable relative reference to the GOT equivalent for the + // Hashable._rawHashValue method descriptor, hence why we add 1 to indicate + // indirect. + " .long ((got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR " - \ + (" TUPLE_HASHABLE_CONF ")) - 44) + 1\n" + #elif defined(__MACH__) + " .long " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" + #endif + // This 0 indicates that we are requesting the default implementation for the + // _rawHashValue getter. + " .long 0\n" + // The witness table size in words. + " .short 0\n" + // The witness table private size in words & requires instantiation. + " .short 1\n" + // The witness table instantiator function. + " .long 0\n" + // This is a direct relative reference to the private data for the + // conformance. + " .long (__swift_tupleHashable_private - (" TUPLE_HASHABLE_CONF ")) - 60\n" + #if defined(__ELF__) + " .size " TUPLE_HASHABLE_CONF ", 64\n" + #endif +); + +// These are all function values that we reinterpret later. +extern void *SWIFT_HASHVALUE_FUNC; +extern void *SWIFT_HASHER_COMBINE_FUNC; + +using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, + void *witnessTable); +using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto _hashValue = reinterpret_cast(&SWIFT_HASHVALUE_FUNC); + return _hashValue(tuple, Self, witnessTable); +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void _swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto tupleTy = cast(Self); + auto table = reinterpret_cast(witnessTable); + + // Loop through all elements and hash them into the Hasher. + for (size_t i = 0; i != tupleTy->NumElements; i += 1) { + auto elt = tupleTy->getElement(i); + + // Get the element conformance from the private data in the witness table. + auto conformance = reinterpret_cast(table[-1 - i]); + + // Get the element value from the tuple. + auto value = reinterpret_cast( + reinterpret_cast(tuple) + elt.Offset); + + auto hasherCombine = + reinterpret_cast(&SWIFT_HASHER_COMBINE_FUNC); + + // Call the combine function on the hasher for this element value and we're + // done! + hasherCombine(value, elt.Type, conformance, hasher); + } +} diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 6f281e0178156..5987dc9a91c07 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -378,16 +378,18 @@ searchInConformanceCache(const Metadata *type, extern const ProtocolDescriptor EQUATABLE_DESCRIPTOR; extern const ProtocolDescriptor COMPARABLE_DESCRIPTOR; +extern const ProtocolDescriptor HASHABLE_DESCRIPTOR; static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable and Comparable, so - // reject all other protocols. + // At the moment, tuples can only conform to Equatable, Comparable and + // Hashable, so reject all other protocols. auto equatable = &EQUATABLE_DESCRIPTOR; auto comparable = &COMPARABLE_DESCRIPTOR; - if (protocol != equatable && protocol != comparable) + auto hashable = &HASHABLE_DESCRIPTOR; + if (protocol != equatable && protocol != comparable && protocol != hashable) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -401,6 +403,7 @@ static bool tupleConformsToProtocol(const Metadata *type, extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; +extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf; static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( const ProtocolDescriptor *protocol) { @@ -412,6 +415,10 @@ static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( return &_swift_tupleComparable_conf; } + if (protocol == &HASHABLE_DESCRIPTOR) { + return &_swift_tupleHashable_conf; + } + return nullptr; } diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift index 2053d69457881..44c549c442fb1 100644 --- a/test/IRGen/builtin_conformances.swift +++ b/test/IRGen/builtin_conformances.swift @@ -2,6 +2,7 @@ // CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor // CHECK-LABEL: @_swift_tupleComparable_conf = external global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleHashable_conf = external global %swift.protocol_conformance_descriptor struct Wrapper { let value: T @@ -60,3 +61,28 @@ public func testTupleComparable() { // CHECK: {{%.*}} = call swiftcc i1 {{.*}}({{%.*}}.1* noalias nocapture undef, {{%.*}}.1* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_COMPARABLE_WT1]]) let _ = Wrapper(value: ()) < Wrapper(value: ()) } + +//===----------------------------------------------------------------------===// +// Tuple Hashable conformance +//===----------------------------------------------------------------------===// + +extension Wrapper: Hashable where T: Hashable {} + +public func hashValue(for instance: T) -> Int { + instance.hashValue +} + +public func useHashable(_ thing: T) -> Int { + // CHECK: [[USE_HASHABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) + // CHECK-NEXT: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]]) + hashValue(for: (thing, thing)) +} + +public func testTupleHashable() { + // CHECK: [[TEST_TUPLE_HASHABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) + // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]]) + let _ = hashValue(for: ()) + + // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef) + let _ = Wrapper(value: ()).hashValue +} diff --git a/test/Interpreter/builtin_conformances.swift b/test/Interpreter/builtin_conformances.swift index 182cb85904608..be926737f9615 100644 --- a/test/Interpreter/builtin_conformances.swift +++ b/test/Interpreter/builtin_conformances.swift @@ -6,6 +6,7 @@ struct Wrapper { } extension Wrapper: Equatable where T: Equatable {} +extension Wrapper: Hashable where T: Hashable {} extension Wrapper: Comparable where T: Comparable { static func <(lhs: Wrapper, rhs: Wrapper) -> Bool { @@ -33,6 +34,12 @@ extension Foo: Comparable { } } +extension Foo: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(age) + } +} + //===----------------------------------------------------------------------===// // Tuple Equatable Conformance //===----------------------------------------------------------------------===// @@ -125,7 +132,6 @@ print(compareGTE((1, 2), (1, 2))) // false print(compareGT((1, 2), (1, 2))) - // true print(compareLT((1, 2), (2, 1))) // true @@ -135,7 +141,6 @@ print(compareGTE((1, 2), (2, 1))) // false print(compareGT((1, 2), (2, 1))) - // false print(compareLT(((1, 2), 3), ((1, 2), 3))) // true @@ -145,7 +150,6 @@ print(compareGTE(((1, 2), 3), ((1, 2), 3))) // false print(compareGT(((1, 2), 3), ((1, 2), 3))) - // true print(compareLT(((1, 2), 3), ((1, 2), 4))) // true @@ -218,3 +222,88 @@ print(compareLTE((Foo(age: 128), 0), (Foo(age: 734), 0))) print(compareGTE((Foo(age: 128), 0), (Foo(age: 734), 0))) // CHECK: false print(compareGT((Foo(age: 128), 0), (Foo(age: 734), 0))) + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +var grid = [(x: Int, y: Int): Int]() + +grid[(x: 0, y: 0)] = 0 +// CHECK: 0 +print(grid[(x: 0, y: 0)]!) +// CHECK: 0 +print(grid[(0, 0)]!) + +grid[(x: 1, y: 1)] = 1 +// CHECK: 1 +print(grid[(x: 1, y: 1)]!) +// CHECK: 1 +print(grid[(1, 1)]!) + +let tupleSet: Set = [(x: 0, y: 1), (x: 128, y: 32), (x: 10, y: 0)] + +// CHECK: true +print(tupleSet.contains((x: 0, y: 1))) + +// CHECK: true +print(tupleSet.contains((0, 1))) + +func compareHashes(_ lhs: T, _ rhs: T) -> Bool { + lhs.hashValue == rhs.hashValue +} + +// CHECK: true +print(compareHashes((), ())) + +// CHECK: true +print(compareHashes((1, 2), (1, 2))) + +// CHECK: false +print(compareHashes((1, 2), (1, 3))) + +// CHECK: false +print(compareHashes((1, 2), (2, 1))) + +// CHECK: true +print(compareHashes(((1, 2), 3), ((1, 2), 3))) + +// CHECK: false +print(compareHashes(((1, 2), 3), ((1, 2), 4))) + +// CHECK: false +print(compareHashes(((1, 2), 3), ((3, 2), 1))) + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +func opaqueTupleHashableValue() -> some Hashable { + (1, 2, 3, 4, 5) +} + +if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { + _ = opaqueTupleHashableValue().hashValue == opaqueTupleHashableValue().hashValue +} + +// CHECK: true +print(compareHashes(Wrapper(value: ()), Wrapper(value: ()))) + +// CHECK: true +print(compareHashes(Wrapper(value: (1, 2)), Wrapper(value: (1, 2)))) + +// CHECK: false +print(compareHashes(Wrapper(value: (1, 2)), Wrapper(value: (1, 3)))) + +// CHECK: false +print(compareHashes(Wrapper(value: (1, 2)), Wrapper(value: (2, 1)))) + +func useHashable(_ thing: T) -> Bool { + compareHashes((thing, thing), (thing, thing)) +} + +// CHECK: true +print(useHashable(128)) + +// CHECK: true +print(compareHashes((Foo(age: 128), 1), (Foo(age: 128), 1))) + +// CHECK: false +print(compareHashes((Foo(age: 128), 1), (Foo(age: 0), 1))) diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 08282b4cd1bf2..812b489f47d25 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -873,7 +873,6 @@ func sr11562() { } _ = \S4.[1, 4] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} {{12-12=(}} {{16-16=)}} - // expected-error@-1 {{subscript index of type '(Int, Int)' in a key path must be Hashable}} } // SR-12290: Ban keypaths with contextual root and without a leading dot From 5e03e333cb9478dbc0d586524e0ddf9107f2dd52 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 19 Jul 2020 22:41:29 -0400 Subject: [PATCH 09/15] [Compatibility53] Backport Tuple Hashable Conformance --- .../BuiltinProtocolConformances.cpp | 134 ++++++++++++++++++ .../Compatibility53/ProtocolConformance.cpp | 18 ++- 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index c3893c8b668b5..322b0b18993c2 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -37,6 +37,13 @@ static const ProtocolDescriptor *getComparableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getHashableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -449,3 +456,130 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // each other. return false; } + +//===----------------------------------------------------------------------===// +// Tuple Hashable Conformance +//===----------------------------------------------------------------------===// + +#define TUPLE_HASHABLE_WT SYMBOL("_swift_tupleHashable_wt") + +// Define the conformance descriptor for tuple Hashable. We do this in +// assembly to work around relative reference issues. +__asm( + " .section __DATA,__data\n" + " .globl " TUPLE_HASHABLE_CONF "\n" + " .p2align 2\n" + TUPLE_HASHABLE_CONF ":\n" + // This is an indirectable relative reference to the Hashable protocol + // descriptor. However, this is 0 here because the compatibility libraries + // can't have a dependency on libswiftCore (which is where Hashable lives). + " .long 0\n" + // 769 is the MetadataKind::Tuple + " .long 769\n" + // This is a direct relative reference to the witness table defined below. + " .long ((" TUPLE_HASHABLE_WT ") - (" TUPLE_HASHABLE_CONF ")) - 8\n" + // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. + " .long 32\n" +); + +extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf; + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the Hashable protocol desciptor +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleHashableDescriptor() { + auto tupleHashableConf = const_cast( + reinterpret_cast(&_swift_tupleHashable_conf)); + auto hashable = getHashableDescriptor(); + + // This is an indirectable pointer. + *tupleHashableConf = intptr_t(hashable) - intptr_t(tupleHashableConf); +} + +// The base Equatable protocol is itself a requirement, thus the requirement +// count is 4 (Equatable + hashValue + hash(into:) + _rawHashValue) and the +// witness is the tuple Equatable table. +SWIFT_RUNTIME_EXPORT +_WitnessTable<4> _swift_tupleHashable_wt = { + &_swift_tupleHashable_conf, + { + reinterpret_cast(&_swift_tupleEquatable_wt), + reinterpret_cast(_swift_tupleHashable_hashValue), + reinterpret_cast(_swift_tupleHashable_hash), + nullptr + } +}; + +static void *get_rawHashValueDefaultImplFunc() { + auto impl = SWIFT_LAZY_CONSTANT( + dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF")); + return impl; +} + +// Due to the fact that the compatibility libraries can't have a hard +// dependency to libswiftCore (which is where the _rawHashValue default impl +// lives), we have to manually implant this before calling any user code. +__attribute__((constructor)) +void _emplaceTupleHashable_rawHashValueDefaultImpl() { + _swift_tupleHashable_wt.Witnesses[3] = get_rawHashValueDefaultImplFunc(); +} + +using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, + void *witnessTable); +using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); + +static HashValueFn *get_hashValueFunc() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, STR(SWIFT_HASHVALUE_FUNC)))); + return descriptor; +} + +static HasherCombineFn *getHashCombineFunc() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, STR(SWIFT_HASHER_COMBINE_FUNC)))); + return descriptor; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t swift::_swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto _hashValue = get_hashValueFunc(); + return _hashValue(tuple, Self, witnessTable); +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void swift::_swift_tupleHashable_hash(OpaqueValue *hasher, + SWIFT_CONTEXT OpaqueValue *tuple, + Metadata *Self, void *witnessTable) { + auto tupleTy = cast(Self); + + // Loop through all elements and hash them into the Hasher. + for (size_t i = 0; i != tupleTy->NumElements; i += 1) { + auto elt = tupleTy->getElement(i); + + // Ensure we actually have a conformance to Hashable for this element type. + auto hashable = getHashableDescriptor(); + auto conformance = conformsToProtocol(elt.Type, hashable); + + // If we don't have a conformance then something somewhere messed up in + // deciding that this tuple type was Hashable...?? + if (!conformance) + swift_unreachable("Tuple hasing requires that all elements be Hashable."); + + // Get the element value from the tuple. + auto value = reinterpret_cast( + reinterpret_cast(tuple) + elt.Offset); + + auto hasherCombine = getHashCombineFunc(); + + // Call the combine function on the hasher for this element value and we're + // done! + hasherCombine(value, elt.Type, conformance, hasher); + } +} diff --git a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp index 76f82e7b8af2c..71b90ab19152a 100644 --- a/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp +++ b/stdlib/toolchain/Compatibility53/ProtocolConformance.cpp @@ -39,6 +39,13 @@ static const ProtocolDescriptor *getComparableDescriptor() { return descriptor; } +static const ProtocolDescriptor *getHashableDescriptor() { + auto descriptor = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHMp"))); + return descriptor; +} + static const WitnessTable *conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *); @@ -52,10 +59,11 @@ static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto tuple = cast(type); - // At the moment, tuples can only conform to Equatable and Comparable, so - // reject all other protocols. + // At the moment, tuples can only conform to Equatable, Comparable, and + // Hashable, so reject all other protocols. if (protocol != getEquatableDescriptor() && - protocol != getComparableDescriptor()) + protocol != getComparableDescriptor() && + protocol != getHashableDescriptor()) return false; for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -69,6 +77,7 @@ static bool tupleConformsToProtocol(const Metadata *type, extern const WitnessTable _swift_tupleEquatable_wt; extern const WitnessTable _swift_tupleComparable_wt; +extern const WitnessTable _swift_tupleHashable_wt; static const WitnessTable *getTupleConformanceWitnessTable( const ProtocolDescriptor *protocol) { @@ -78,6 +87,9 @@ static const WitnessTable *getTupleConformanceWitnessTable( if (protocol == getComparableDescriptor()) return &_swift_tupleComparable_wt; + if (protocol == getHashableDescriptor()) + return &_swift_tupleHashable_wt; + return nullptr; } From 746837548b13c762af6607cc8aba561d3ef62d72 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 25 Jul 2020 18:15:44 -0400 Subject: [PATCH 10/15] [ABI] Split TypeReferenceKind from TypeMetadataRecord --- include/swift/ABI/Metadata.h | 20 +++++--------------- include/swift/ABI/MetadataValues.h | 14 +++++++++++++- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 4bfcc243aebe8..4d33e60f7fd73 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2295,38 +2295,28 @@ struct TargetTypeMetadataRecord { union { /// A direct reference to a nominal type descriptor. RelativeDirectPointerIntPair, - TypeReferenceKind> + TypeMetadataRecordKind> DirectNominalTypeDescriptor; /// An indirect reference to a nominal type descriptor. RelativeDirectPointerIntPair * __ptrauth_swift_type_descriptor>, - TypeReferenceKind> + TypeMetadataRecordKind> IndirectNominalTypeDescriptor; - - // We only allow a subset of the TypeReferenceKinds here. - // Should we just acknowledge that this is a different enum? }; public: - TypeReferenceKind getTypeKind() const { + TypeMetadataRecordKind getTypeKind() const { return DirectNominalTypeDescriptor.getInt(); } const TargetContextDescriptor * getContextDescriptor() const { switch (getTypeKind()) { - case TypeReferenceKind::DirectTypeDescriptor: + case TypeMetadataRecordKind::DirectTypeDescriptor: return DirectNominalTypeDescriptor.getPointer(); - case TypeReferenceKind::IndirectTypeDescriptor: + case TypeMetadataRecordKind::IndirectTypeDescriptor: return *IndirectNominalTypeDescriptor.getPointer(); - - // These types (and any others we might add to TypeReferenceKind - // in the future) are just never used in these lists. - case TypeReferenceKind::DirectObjCClassName: - case TypeReferenceKind::IndirectObjCClass: - case TypeReferenceKind::MetadataKind: - return nullptr; } return nullptr; diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 00adcbdba61d4..fc564085c57a1 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -361,7 +361,19 @@ enum : unsigned { NumGenericMetadataPrivateDataWords = 16, }; -/// Kinds of type metadata/protocol conformance records. +/// Kinds of type metadata reocrds. +enum class TypeMetadataRecordKind : unsigned { + /// A direct reference to a nominal type descriptor. + DirectTypeDescriptor = 0x00, + + /// An indirect reference to a nominal type descriptor. + IndirectTypeDescriptor = 0x01, + + First_Kind = DirectTypeDescriptor, + Last_Kind = IndirectTypeDescriptor, +}; + +/// Kinds of references to type metadata. enum class TypeReferenceKind : unsigned { /// The conformance is for a nominal type referenced directly; /// getTypeDescriptor() points to the type context descriptor. From 7a6ea99eab60e7a4e787cd0e3daae3ac2e255a26 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sat, 25 Jul 2020 18:17:56 -0400 Subject: [PATCH 11/15] [Runtime] Clean up the assembly around Tuple builtin conformances forgot + 1 --- include/swift/AST/ProtocolConformance.h | 12 +- .../runtime/BuiltinProtocolConformances.cpp | 322 ++++-------------- stdlib/public/runtime/Metadata.cpp | 4 +- .../BuiltinProtocolConformances.cpp | 10 +- 4 files changed, 79 insertions(+), 269 deletions(-) diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 31fda612c717f..9a0b6a6b2ad50 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -1085,7 +1085,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, } bool hasTypeWitness(AssociatedTypeDecl *assocType) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } /// Retrieve the type witness and type decl (if one exists) @@ -1093,7 +1094,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, TypeWitnessAndDecl getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options=None) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } /// Given that the requirement signature of the protocol directly states @@ -1101,7 +1103,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, /// return its associated conformance. ProtocolConformanceRef getAssociatedConformance(Type assocType, ProtocolDecl *protocol) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } /// Retrieve the witness corresponding to the given value requirement. @@ -1112,7 +1115,8 @@ class BuiltinProtocolConformance final : public RootProtocolConformance, /// Determine whether the witness for the given requirement /// is either the default definition or was otherwise deduced. bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { - llvm_unreachable("builtin-conformances never have associated types"); + llvm_unreachable("builtin-conformances currently don't have associated \ + types"); } static bool classof(const ProtocolConformance *conformance) { diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index 768db5ae6d086..90d54b74d3362 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -28,31 +28,20 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, const Metadata *, const WitnessTable *); +#if defined(__ELF__) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 1" +#elif defined(__MACH__) +#if defined(__aarch64__) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOT - . + 1" +#else +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 5" +#endif +#endif + //===----------------------------------------------------------------------===// // Tuple Equatable Conformance //===----------------------------------------------------------------------===// -#if defined(__ELF__) -// Create a GOT equivalent for the Equatable reference. -__asm( - " .type got." EQUATABLE_DESCRIPTOR_SYMBOL ", @object\n" - " .section .data.rel.ro\n" - " .p2align 3\n" - "got." EQUATABLE_DESCRIPTOR_SYMBOL ":\n" - " .quad (" EQUATABLE_DESCRIPTOR_SYMBOL ")\n" - " .size got." EQUATABLE_DESCRIPTOR_SYMBOL ", 8\n" -); - -// Create a GOT equivalent for the Equatable.== method descriptor. -__asm( - " .type got." EQUATABLE_EE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." EQUATABLE_EE_METHOD_DESCRIPTOR ":\n" - " .quad (" EQUATABLE_EE_METHOD_DESCRIPTOR ")\n" - " .size got." EQUATABLE_EE_METHOD_DESCRIPTOR ", 8\n" -); -#endif - // Define the conformance descriptor for tuple Equatable. We do this in // assembly to work around relative reference issues. __asm( @@ -70,14 +59,9 @@ __asm( " .globl " TUPLE_EQUATABLE_CONF "\n" " .p2align 2\n" TUPLE_EQUATABLE_CONF ":\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Equatable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got." EQUATABLE_DESCRIPTOR_SYMBOL " - \ - (" TUPLE_EQUATABLE_CONF ")) + 1\n" - #elif defined(__MACH__) - " .long " EQUATABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Equatable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(EQUATABLE_DESCRIPTOR_SYMBOL) "\n" // 769 is the MetadataKind::Tuple " .long 769\n" // This indicates that we have no witness table pattern. We use a generic @@ -90,16 +74,11 @@ __asm( // This 1 is the ResilientWitnessesHeader indicating we have 1 resilient // witness. " .long 1\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Equatable.== method descriptor, hence why we add 1 to indicate indirect. - " .long ((got." EQUATABLE_EE_METHOD_DESCRIPTOR " - \ - (" TUPLE_EQUATABLE_CONF ")) - 20) + 1\n" - #elif defined(__MACH__) - " .long " EQUATABLE_EE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Equatable == method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(EQUATABLE_EE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the equals witness defined below. - " .long ((" TUPLE_EQUATABLE_EQUALS ") - (" TUPLE_EQUATABLE_CONF ")) - 24\n" + " .long (" TUPLE_EQUATABLE_EQUALS ") - .\n" // The witness table size in words. " .short 0\n" // The witness table private size in words & requires instantiation. @@ -108,7 +87,7 @@ __asm( " .long 0\n" // This is a direct relative reference to the private data for the // conformance. - " .long (__swift_tupleEquatable_private - (" TUPLE_EQUATABLE_CONF ")) - 36\n" + " .long __swift_tupleEquatable_private - .\n" #if defined(__ELF__) " .size " TUPLE_EQUATABLE_CONF ", 40\n" #endif @@ -158,63 +137,6 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Tuple Comparable Conformance //===----------------------------------------------------------------------===// -#if defined(__ELF__) -// Create a GOT equivalent for the Comparable reference. -__asm( - " .type got." COMPARABLE_DESCRIPTOR_SYMBOL ", @object\n" - " .section .data.rel.ro\n" - " .p2align 3\n" - "got." COMPARABLE_DESCRIPTOR_SYMBOL ":\n" - " .quad (" COMPARABLE_DESCRIPTOR_SYMBOL ")\n" - " .size got." COMPARABLE_DESCRIPTOR_SYMBOL ", 8\n" -); - -// Create a GOT equivalent for the Comparable base conformance to Equatable. -__asm( - " .type got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" - " .size got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.< method descriptor. -__asm( - " .type got." COMPARABLE_LT_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_LT_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_LT_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARABLE_LT_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.<= method descriptor. -__asm( - " .type got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARBALE_LTE_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARBALE_LTE_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARBALE_LTE_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.>= method descriptor. -__asm( - " .type got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_GTE_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_GTE_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARABLE_GTE_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Comparable.> method descriptor. -__asm( - " .type got." COMPARABLE_GT_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." COMPARABLE_GT_METHOD_DESCRIPTOR ":\n" - " .quad (" COMPARABLE_GT_METHOD_DESCRIPTOR ")\n" - " .size got." COMPARABLE_GT_METHOD_DESCRIPTOR ", 8\n" -); -#endif - // Define the associated conformance structure for tuple Comparable. We do this // in assembly to work around relative reference issues. __asm( @@ -235,8 +157,7 @@ __asm( " .byte 7\n" // This is a direct relative reference to the base accessor for Equatable // defined below. - " .long ((" TUPLE_COMPARABLE_BASEACCESSOREQUATABLE ") - \ - (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\")) - 2\n" + " .long (" TUPLE_COMPARABLE_BASEACCESSOREQUATABLE ") - .\n" // This 0 is our null terminator. " .byte 0\n" #if defined (__ELF__) @@ -261,14 +182,9 @@ __asm( " .globl " TUPLE_COMPARABLE_CONF "\n" " .p2align 2\n" TUPLE_COMPARABLE_CONF ":\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got." COMPARABLE_DESCRIPTOR_SYMBOL " - \ - (" TUPLE_COMPARABLE_CONF ")) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Comparable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_DESCRIPTOR_SYMBOL) "\n" // 769 is the MetadataKind::Tuple " .long 769\n" // This indicates that we have no witness table pattern. We use a generic @@ -281,69 +197,36 @@ __asm( // This 5 is the ResilientWitnessesHeader indicating we have 5 resilient // witnesses. " .long 5\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable base conformance for Equatable, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 20) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Comparable base conformance for Equatable. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR) "\n" // This is a direct relative reference to the associated conformance for - // Equatable defined above in assembly. NOTE: This is minus 23 because the + // Equatable defined above in assembly. NOTE: We + 1 here because the // associated conformance structure is 1 aligned. - " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ - (" TUPLE_COMPARABLE_CONF ")) - 23\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.< method descriptor, hence why we add 1 to indicate indirect. - " .long ((got." COMPARABLE_LT_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 28) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_LT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - . + 1\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.< method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_LT_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the less than witness defined below. - " .long ((" TUPLE_COMPARABLE_LESSTHAN ") - (" TUPLE_COMPARABLE_CONF ")) - 32\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.<= method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARBALE_LTE_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 36) + 1\n" - #elif defined(__MACH__) - " .long " COMPARBALE_LTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_COMPARABLE_LESSTHAN ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.<= method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARBALE_LTE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the less than or equal witness // defined below. - " .long ((" TUPLE_COMPARABLE_LESSTHANOREQUAL ") - \ - (" TUPLE_COMPARABLE_CONF ")) - 40\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.>= method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARABLE_GTE_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 44) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_GTE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_COMPARABLE_LESSTHANOREQUAL ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.>= method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_GTE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the greater than or equal witness // defined below. - " .long ((" TUPLE_COMPARABLE_GREATERTHANOREQUAL ") - \ - (" TUPLE_COMPARABLE_CONF ")) - 48\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Comparable.> method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." COMPARABLE_GT_METHOD_DESCRIPTOR " - \ - (" TUPLE_COMPARABLE_CONF ")) - 52) + 1\n" - #elif defined(__MACH__) - " .long " COMPARABLE_GT_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_COMPARABLE_GREATERTHANOREQUAL ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Comparable.> method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(COMPARABLE_GT_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the greater than witness defined // below. - " .long ((" TUPLE_COMPARABLE_GREATERTHAN ") - \ - (" TUPLE_COMPARABLE_CONF ")) - 56\n" + " .long (" TUPLE_COMPARABLE_GREATERTHAN ") - .\n" // The witness table size in words. " .short 0\n" // The witness table private size in words & requires instantiation. @@ -352,7 +235,7 @@ __asm( " .long 0\n" // This is a direct relative reference to the private data for the // conformance. - " .long (__swift_tupleComparable_private - (" TUPLE_COMPARABLE_CONF ")) - 68\n" + " .long __swift_tupleComparable_private - .\n" #if defined(__ELF__) " .size " TUPLE_COMPARABLE_CONF ", 72\n" #endif @@ -365,6 +248,7 @@ _swift_tupleComparable_baseAccessorEquatable(Metadata *assocType, void **witnessTable) { auto tuple = cast(assocType); std::vector instantiationArgs; + instantiationArgs.reserve(tuple->NumElements); // Fill the instantiationArgs with the element Equatable tables. for (size_t i = 0; i != tuple->NumElements; i += 1) { @@ -613,54 +497,6 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // Tuple Hashable Conformance //===----------------------------------------------------------------------===// -#if defined(__ELF__) -// Create a GOT equivalent for the Hashable reference. -__asm( - " .type got." HASHABLE_DESCRIPTOR_SYMBOL ", @object\n" - " .section .data.rel.ro\n" - " .p2align 3\n" - "got." HASHABLE_DESCRIPTOR_SYMBOL ":\n" - " .quad (" HASHABLE_DESCRIPTOR_SYMBOL ")\n" - " .size got." HASHABLE_DESCRIPTOR_SYMBOL ", 8\n" -); - -// Create a GOT equivalent for the Hashable base conformance to Equatable. -__asm( - " .type got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ":\n" - " .quad (" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ")\n" - " .size got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Hashable.hashValue method descriptor. -__asm( - " .type got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ":\n" - " .quad (" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ")\n" - " .size got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Hashable.hash(into:) method descriptor. -__asm( - " .type got." HASHABLE_HASH_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_HASH_METHOD_DESCRIPTOR ":\n" - " .quad (" HASHABLE_HASH_METHOD_DESCRIPTOR ")\n" - " .size got." HASHABLE_HASH_METHOD_DESCRIPTOR ", 8\n" -); - -// Create a GOT equivalent for the Hashable._rawHashValue method descriptor. -__asm( - " .type got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", @object\n" - " .p2align 3\n" - "got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ":\n" - " .quad (" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ")\n" - " .size got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR ", 8\n" -); -#endif - // Define the conformance descriptor for tuple Hashable. We do this in // assembly to work around relative reference issues. __asm( @@ -678,14 +514,9 @@ __asm( " .globl " TUPLE_HASHABLE_CONF "\n" " .p2align 2\n" TUPLE_HASHABLE_CONF ":\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable protocol descriptor, hence why we add 1 to indicate indirect. - " .long (got." HASHABLE_DESCRIPTOR_SYMBOL " - \ - (" TUPLE_HASHABLE_CONF ")) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_DESCRIPTOR_SYMBOL "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Hashable protocol descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_DESCRIPTOR_SYMBOL) "\n" // 769 is the MetadataKind::Tuple " .long 769\n" // This indicates that we have no witness table pattern. We use a generic @@ -698,55 +529,30 @@ __asm( // This 4 is the ResilientWitnessesHeader indicating we have 4 resilient // witnesses. " .long 4\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable base conformance for Equatable, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_BASE_CONFORMANCE_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 20) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // This is an indirectable relative reference to the GOT entry for the + // Hashable base conformance for Equatable. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_BASE_CONFORMANCE_DESCRIPTOR) "\n" // This is a direct relative reference to the associated conformance for // Equatable defined above in assembly. NOTE: We intentionally use the // Comparable implementation for this because the implementation is the same // for both Hashable and Comparable. Both want to grab the Equatable table // from its elements whose witness table is located in the same place for both - // protocols. NOTE: This is minus 23 because the associated conformance + // protocols. NOTE: We + 1 here because the associated conformance // structure is 1 aligned. - " .long ((\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - \ - (" TUPLE_HASHABLE_CONF ")) - 23\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable.hashValue method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_HASHVALUE_METHOD_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 28) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\") - . + 1\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable.hashValue method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_HASHVALUE_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the hashValue witness defined below. - " .long ((" TUPLE_HASHABLE_HASHVALUE ") - (" TUPLE_HASHABLE_CONF ")) - 32\n" - #if defined(__ELF__) - // This is an indirectable relative reference to the GOT equivalent for the - // Hashable.hash(into:) method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_HASH_METHOD_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 36) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_HASH_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + " .long (" TUPLE_HASHABLE_HASHVALUE ") - .\n" + // This is an indirectable relative reference to the GOT entry for the + // Hashable.hash(into:) method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_HASH_METHOD_DESCRIPTOR) "\n" // This is a direct relative reference to the hash(into:) witness defined below. - " .long ((" TUPLE_HASHABLE_HASH ") - (" TUPLE_HASHABLE_CONF ")) - 40\n" - #if defined(__ELF__) + " .long (" TUPLE_HASHABLE_HASH ") - .\n" // This is an indirectable relative reference to the GOT equivalent for the - // Hashable._rawHashValue method descriptor, hence why we add 1 to indicate - // indirect. - " .long ((got." HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR " - \ - (" TUPLE_HASHABLE_CONF ")) - 44) + 1\n" - #elif defined(__MACH__) - " .long " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "@GOTPCREL + 5\n" - #endif + // Hashable._rawHashValue method descriptor. + " .long " INDIRECT_RELREF_GOTPCREL(HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR) "\n" // This 0 indicates that we are requesting the default implementation for the // _rawHashValue getter. " .long 0\n" @@ -758,7 +564,7 @@ __asm( " .long 0\n" // This is a direct relative reference to the private data for the // conformance. - " .long (__swift_tupleHashable_private - (" TUPLE_HASHABLE_CONF ")) - 60\n" + " .long __swift_tupleHashable_private - .\n" #if defined(__ELF__) " .size " TUPLE_HASHABLE_CONF ", 64\n" #endif diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 77c930c06b9df..95a81dd885a99 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -4837,7 +4837,7 @@ WitnessTableCacheEntry::allocate( void **fullTable = reinterpret_cast(this + 1); // Zero out the witness table. - memset(fullTable, 0, getWitnessTableSize(conformance)); + memset(fullTable, 0, getWitnessTableSize(Type, conformance)); // Instantiate the table. return instantiateWitnessTable(Type, Conformance, instantiationArgs, fullTable); @@ -4858,7 +4858,7 @@ getNondependentWitnessTable(const ProtocolConformanceDescriptor *conformance, } // Allocate space for the table. - auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(conformance); + auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(type, conformance); TaggedMetadataAllocator allocator; auto buffer = (void **)allocator.Allocate(tableSize, alignof(void*)); memset(buffer, 0, tableSize); diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index 322b0b18993c2..3419bc5b419e0 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -84,7 +84,7 @@ __asm( // 769 is the MetadataKind::Tuple " .long 769\n" // This is a direct relative reference to the witness table defined below. - " .long ((" TUPLE_EQUATABLE_WT ") - (" TUPLE_EQUATABLE_CONF ")) - 8\n" + " .long (" TUPLE_EQUATABLE_WT ") - .\n" // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. " .long 32\n" ); @@ -176,7 +176,7 @@ __asm( // 769 is the MetadataKind::Tuple " .long 769\n" // This is a direct relative reference to the witness table defined below. - " .long ((" TUPLE_COMPARABLE_WT ") - (" TUPLE_COMPARABLE_CONF ")) - 8\n" + " .long (" TUPLE_COMPARABLE_WT ") - .\n" // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. " .long 32\n" ); @@ -477,7 +477,7 @@ __asm( // 769 is the MetadataKind::Tuple " .long 769\n" // This is a direct relative reference to the witness table defined below. - " .long ((" TUPLE_HASHABLE_WT ") - (" TUPLE_HASHABLE_CONF ")) - 8\n" + " .long (" TUPLE_HASHABLE_WT ") - .\n" // 32 are the ConformanceFlags with the type reference bit set to MetadataKind. " .long 32\n" ); @@ -535,14 +535,14 @@ using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, static HashValueFn *get_hashValueFunc() { auto descriptor = SWIFT_LAZY_CONSTANT( reinterpret_cast( - dlsym(RTLD_DEFAULT, STR(SWIFT_HASHVALUE_FUNC)))); + dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHVALUE_FUNC)))); return descriptor; } static HasherCombineFn *getHashCombineFunc() { auto descriptor = SWIFT_LAZY_CONSTANT( reinterpret_cast( - dlsym(RTLD_DEFAULT, STR(SWIFT_HASHER_COMBINE_FUNC)))); + dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHER_COMBINE_FUNC)))); return descriptor; } From b78da2dcc2b26e6e1479e132e5236aa14196de96 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 2 Aug 2020 23:35:00 -0400 Subject: [PATCH 12/15] [Runtime] Add stubs for indirect symbols on armv7/s, add missing newlines for ELF directives, fix compile crash for arm64e --- .../runtime/BuiltinProtocolConformances.cpp | 100 +++++++++++++++--- 1 file changed, 83 insertions(+), 17 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index 90d54b74d3362..8798c3e2e04e0 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -33,6 +33,9 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, #elif defined(__MACH__) #if defined(__aarch64__) #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOT - . + 1" +#elif defined(__arm__) +// Darwin doesn't support @GOT like syntax for 32 bit ARM. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" #else #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 5" #endif @@ -42,6 +45,22 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, // Tuple Equatable Conformance //===----------------------------------------------------------------------===// +// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy +// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for +// ARM. +#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +__asm( + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" + " .p2align 2\n" + "L" EQUATABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " EQUATABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" EQUATABLE_EE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " EQUATABLE_EE_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + // Define the conformance descriptor for tuple Equatable. We do this in // assembly to work around relative reference issues. __asm( @@ -137,14 +156,42 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Tuple Comparable Conformance //===----------------------------------------------------------------------===// +// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy +// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for +// ARM. +#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +__asm( + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" + " .p2align 2\n" + "L" COMPARABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_BASE_CONFORMANCE_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_LT_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_LT_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARBALE_LTE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARBALE_LTE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_GTE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_GTE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" COMPARABLE_GT_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " COMPARABLE_GT_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + // Define the associated conformance structure for tuple Comparable. We do this // in assembly to work around relative reference issues. __asm( #if defined(__ELF__) " .hidden \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" " .type \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\", @object\n" - " .section swift5_typeref, \"a\"" - " .weak \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"" + " .section swift5_typeref, \"a\"\n" + " .weak \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" #elif defined(__MACH__) " .private_extern \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" " .section __TEXT, __swift5_typeref\n" @@ -497,6 +544,31 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // Tuple Hashable Conformance //===----------------------------------------------------------------------===// +// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy +// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for +// ARM. +#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +__asm( + " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" + " .p2align 2\n" + "L" HASHABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_DESCRIPTOR_SYMBOL "\n" + " .long 0\n" + "L" HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_BASE_CONFORMANCE_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_HASHVALUE_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_HASH_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_HASH_METHOD_DESCRIPTOR "\n" + " .long 0\n" + "L" HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "$non_lazy_ptr:\n" + " .indirect_symbol " HASHABLE_RAWHASHVALUE_METHOD_DESCRIPTOR "\n" + " .long 0\n" +); +#endif + // Define the conformance descriptor for tuple Hashable. We do this in // assembly to work around relative reference issues. __asm( @@ -570,22 +642,19 @@ __asm( #endif ); -// These are all function values that we reinterpret later. -extern void *SWIFT_HASHVALUE_FUNC; -extern void *SWIFT_HASHER_COMBINE_FUNC; +extern "C" SWIFT_CC(swift) +intptr_t SWIFT_HASHVALUE_FUNC(OpaqueValue *value, Metadata *Self, + void *witnessTable); -using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, - void *witnessTable); -using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, - const Metadata *Self, - const WitnessTable *witnessTable, - SWIFT_CONTEXT OpaqueValue *hasher); +extern "C" SWIFT_CC(swift) +void SWIFT_HASHER_COMBINE_FUNC(OpaqueValue *value, const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *hasher); SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) intptr_t _swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple, Metadata *Self, void *witnessTable) { - auto _hashValue = reinterpret_cast(&SWIFT_HASHVALUE_FUNC); - return _hashValue(tuple, Self, witnessTable); + return SWIFT_HASHVALUE_FUNC(tuple, Self, witnessTable); } SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) @@ -606,11 +675,8 @@ void _swift_tupleHashable_hash(OpaqueValue *hasher, auto value = reinterpret_cast( reinterpret_cast(tuple) + elt.Offset); - auto hasherCombine = - reinterpret_cast(&SWIFT_HASHER_COMBINE_FUNC); - // Call the combine function on the hasher for this element value and we're // done! - hasherCombine(value, elt.Type, conformance, hasher); + SWIFT_HASHER_COMBINE_FUNC(value, elt.Type, conformance, hasher); } } From 05cc4aa8af1268d0d9738e9523b4bd57f0e7ad94 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 9 Aug 2020 21:37:18 -0400 Subject: [PATCH 13/15] [Runtime] Emit non lazy stub pointers for 32 bit x86 for tuple conformances --- .../runtime/BuiltinProtocolConformances.cpp | 65 ++++++++++++++----- .../rdar27830834.swift | 2 - 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index 8798c3e2e04e0..fe5e4322de280 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -28,29 +28,52 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, const Metadata *, const WitnessTable *); +// Elf indirect symbol references. #if defined(__ELF__) #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 1" -#elif defined(__MACH__) +#endif + +// MachO indirect symbol references. +#if defined(__MACH__) + +// 64 bit arm MachO #if defined(__aarch64__) #define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOT - . + 1" + +// 32 bit arm MachO #elif defined(__arm__) -// Darwin doesn't support @GOT like syntax for 32 bit ARM. +// MachO doesn't support @GOT like relocations for 32 bit arm. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" +#endif + +// 64 bit x86_64 MachO +#if defined(__x86_64__) +// The + 4 is required for all x86_64 MachO GOTPC relocations. +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 4 + 1" + +// 32 bit x86 MachO +#elif defined(__i386__) +// MachO doesn't support @GOT like relocations for 32 bit x86. #define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" -#else -#define INDIRECT_RELREF_GOTPCREL(SYMBOL) SYMBOL "@GOTPCREL + 5" #endif + #endif //===----------------------------------------------------------------------===// // Tuple Equatable Conformance //===----------------------------------------------------------------------===// -// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy -// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for -// ARM. -#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) __asm( +#if defined(__arm__) " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif " .p2align 2\n" "L" EQUATABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" " .indirect_symbol " EQUATABLE_DESCRIPTOR_SYMBOL "\n" @@ -156,12 +179,17 @@ bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, // Tuple Comparable Conformance //===----------------------------------------------------------------------===// -// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy -// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for -// ARM. -#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) __asm( +#if defined(__arm__) " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif " .p2align 2\n" "L" COMPARABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" " .indirect_symbol " COMPARABLE_DESCRIPTOR_SYMBOL "\n" @@ -544,12 +572,17 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1, // Tuple Hashable Conformance //===----------------------------------------------------------------------===// -// For 32 bit ARM (specifically armv7 and armv7s for iphoneos), emit non-lazy -// pointer stubs to indirectly reference. Darwin doesn't support @GOT syntax for -// ARM. -#if defined(__MACH__) && defined(__arm__) && !defined(__aarch64__) +// For 32 bit ARM and i386 (specifically armv7, armv7s, and armv7k), emit +// non-lazy pointer stubs to indirectly reference. Darwin doesn't support @GOT +// syntax for those archs. +#if defined(__MACH__) && \ + ((defined(__arm__) && !defined(__aarch64__)) || defined(__i386__)) __asm( +#if defined(__arm__) " .section __DATA, __nl_symbol_ptr, non_lazy_symbol_pointers\n" +#elif defined(__i386__) + " .section __IMPORT, __pointers, non_lazy_symbol_pointers\n" +#endif " .p2align 2\n" "L" HASHABLE_DESCRIPTOR_SYMBOL "$non_lazy_ptr:\n" " .indirect_symbol " HASHABLE_DESCRIPTOR_SYMBOL "\n" diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift index f7be7a54079b2..b2aa737da625a 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift @@ -2,5 +2,3 @@ var d = [String:String]() _ = "\(d.map{ [$0 : $0] })" -// expected-error@-1 {{type 'Dictionary.Element' (aka '(key: String, value: String)') cannot conform to 'Hashable'; only concrete types such as structs, enums and classes can conform to protocols}} -// expected-note@-2 {{required by generic struct 'Dictionary' where 'Key' = 'Dictionary.Element' (aka '(key: String, value: String)')}} From 48ea83723e117a258b9798048f03a92d12104512 Mon Sep 17 00:00:00 2001 From: Azoy Date: Wed, 26 Aug 2020 00:10:09 -0400 Subject: [PATCH 14/15] Manually define _rawHashValue in compatibility libraries, AST fixes for builtin protocol conformance Remember to use substituted type in builtin conformance substitution --- lib/AST/ProtocolConformance.cpp | 19 +++++++- lib/AST/SubstitutionMap.cpp | 14 +++++- .../BuiltinProtocolConformances.cpp | 45 +++++++++++-------- test/IRGen/builtin_conformances.swift | 6 +-- 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index f890f5bdbc9ea..1c6156608e043 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1170,9 +1170,24 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, const_cast(this), subMap); } - case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Builtin: + case ProtocolConformanceKind::Self: { return const_cast(this); + } + case ProtocolConformanceKind::Builtin: { + auto origType = getType(); + if (!origType->hasTypeParameter() && + !origType->hasArchetype()) + return const_cast(this); + + auto substType = origType.subst(subs, conformances, options); + if (substType->isEqual(origType)) + return const_cast(this); + + // All builtin conformances are concrete at the moment, so it's safe to + // directly call getConcrete. + return getProtocol()->getModuleContext() + ->lookupConformance(substType, getProtocol()).getConcrete(); + } case ProtocolConformanceKind::Inherited: { // Substitute the base. auto inheritedConformance diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 7653cc6a29961..b512029598d8a 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -420,7 +420,19 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // For the second step, we're looking into the requirement signature for // this protocol. auto concrete = conformance.getConcrete(); - auto normal = concrete->getRootNormalConformance(); + auto root = concrete->getRootConformance(); + + // If we see a builtin conformance here as the root, simply lookup the + // conformance for the substitute type. + if (isa(root)) { + auto substType = type.subst(*this); + if (substType->hasError()) + return ProtocolConformanceRef(proto); + + return proto->getParentModule()->lookupConformance(substType, proto); + } + + auto normal = cast(root); // If we haven't set the signature conformances yet, force the issue now. if (normal->getSignatureConformances().empty()) { diff --git a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp index 3419bc5b419e0..759fe05f44ef0 100644 --- a/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp +++ b/stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp @@ -497,6 +497,27 @@ void _emplaceTupleHashableDescriptor() { *tupleHashableConf = intptr_t(hashable) - intptr_t(tupleHashableConf); } +using RawHashValueFn = SWIFT_CC(swift) intptr_t(intptr_t seed, + const Metadata *Self, + const WitnessTable *witnessTable, + SWIFT_CONTEXT OpaqueValue *value); + +static RawHashValueFn *get_rawHashValueDefaultImplFunc() { + auto func = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF"))); + return func; +} + +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +intptr_t _swift_tupleHashable_rawHashValue(intptr_t seed, + SWIFT_CONTEXT OpaqueValue *tuple, + const Metadata *Self, + const WitnessTable *witnessTable) { + auto _rawHashValue = get_rawHashValueDefaultImplFunc(); + return _rawHashValue(seed, Self, witnessTable, tuple); +} + // The base Equatable protocol is itself a requirement, thus the requirement // count is 4 (Equatable + hashValue + hash(into:) + _rawHashValue) and the // witness is the tuple Equatable table. @@ -507,24 +528,10 @@ _WitnessTable<4> _swift_tupleHashable_wt = { reinterpret_cast(&_swift_tupleEquatable_wt), reinterpret_cast(_swift_tupleHashable_hashValue), reinterpret_cast(_swift_tupleHashable_hash), - nullptr + reinterpret_cast(_swift_tupleHashable_rawHashValue) } }; -static void *get_rawHashValueDefaultImplFunc() { - auto impl = SWIFT_LAZY_CONSTANT( - dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF")); - return impl; -} - -// Due to the fact that the compatibility libraries can't have a hard -// dependency to libswiftCore (which is where the _rawHashValue default impl -// lives), we have to manually implant this before calling any user code. -__attribute__((constructor)) -void _emplaceTupleHashable_rawHashValueDefaultImpl() { - _swift_tupleHashable_wt.Witnesses[3] = get_rawHashValueDefaultImplFunc(); -} - using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self, void *witnessTable); using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, @@ -533,17 +540,17 @@ using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value, SWIFT_CONTEXT OpaqueValue *hasher); static HashValueFn *get_hashValueFunc() { - auto descriptor = SWIFT_LAZY_CONSTANT( + auto func = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHVALUE_FUNC)))); - return descriptor; + return func; } static HasherCombineFn *getHashCombineFunc() { - auto descriptor = SWIFT_LAZY_CONSTANT( + auto func = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, XSTR(SWIFT_HASHER_COMBINE_FUNC)))); - return descriptor; + return func; } SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift index 44c549c442fb1..39175da5d7148 100644 --- a/test/IRGen/builtin_conformances.swift +++ b/test/IRGen/builtin_conformances.swift @@ -74,15 +74,15 @@ public func hashValue(for instance: T) -> Int { public func useHashable(_ thing: T) -> Int { // CHECK: [[USE_HASHABLE_WT:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* {{%.*}}, i8*** {{%.*}}) - // CHECK-NEXT: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]]) + // CHECK-NEXT: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[USE_HASHABLE_WT]]) hashValue(for: (thing, thing)) } public func testTupleHashable() { // CHECK: [[TEST_TUPLE_HASHABLE_WT1:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @_swift_tupleHashable_conf, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8*** undef) - // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]]) + // CHECK: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.opaque* noalias nocapture undef, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* @"$sytN", i32 0, i32 1), i8** [[TEST_TUPLE_HASHABLE_WT1]]) let _ = hashValue(for: ()) - // CHECK: {{%.*}} = call swiftcc i64 {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef) + // CHECK: {{%.*}} = call swiftcc i{{.*}} {{.*}}(%swift.type* {{%.*}}, i8** [[TEST_TUPLE_HASHABLE_WT1]], {{%.*}} noalias nocapture swiftself undef) let _ = Wrapper(value: ()).hashValue } From b98581704633d14d286758800f53bebdfcbd7e00 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 30 Aug 2020 15:21:31 -0400 Subject: [PATCH 15/15] [Runtime] Add Native Windows support for builtin conformances Force C name mangling for Windows --- .../runtime/BuiltinProtocolConformances.cpp | 21 ++++++++++++++++++- stdlib/public/runtime/ProtocolConformance.cpp | 12 +++++------ test/IRGen/builtin_conformances.swift | 6 +++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/stdlib/public/runtime/BuiltinProtocolConformances.cpp b/stdlib/public/runtime/BuiltinProtocolConformances.cpp index fe5e4322de280..7226fa84fabac 100644 --- a/stdlib/public/runtime/BuiltinProtocolConformances.cpp +++ b/stdlib/public/runtime/BuiltinProtocolConformances.cpp @@ -56,7 +56,11 @@ using StaticInfixWitness = SWIFT_CC(swift) bool(OpaqueValue *, OpaqueValue *, // MachO doesn't support @GOT like relocations for 32 bit x86. #define INDIRECT_RELREF_GOTPCREL(SYMBOL) "L" SYMBOL "$non_lazy_ptr - . + 1" #endif +#endif +// Windows native indirect symbol references. +#if defined(_WIN32) +#define INDIRECT_RELREF_GOTPCREL(SYMBOL) "__imp_" SYMBOL " - . + 1" #endif //===----------------------------------------------------------------------===// @@ -97,6 +101,10 @@ __asm( #elif defined(__MACH__) " .zerofill __DATA, __bss, __swift_tupleEquatable_private, 128, 4\n" " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleEquatable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleEquatable_conf,DATA") #endif " .globl " TUPLE_EQUATABLE_CONF "\n" " .p2align 2\n" @@ -135,7 +143,7 @@ __asm( #endif ); -extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) bool swift::_swift_tupleEquatable_equals(OpaqueValue *tuple1, @@ -225,6 +233,9 @@ __asm( " .section __TEXT, __swift5_typeref\n" " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" " .weak_definition \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + #elif defined(_WIN32) + " .section .sw5tyrf$B, \"dr\", discard, \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" + " .globl \"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\"\n" #endif " .p2align 1\n" "\"" TUPLE_COMPARABLE_ASSOCIATEDCONFORMANCE "\":\n" @@ -253,6 +264,10 @@ __asm( #elif defined(__MACH__) " .zerofill __DATA, __bss, __swift_tupleComparable_private, 128, 4\n" " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleComparable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleComparable_conf,DATA") #endif " .globl " TUPLE_COMPARABLE_CONF "\n" " .p2align 2\n" @@ -615,6 +630,10 @@ __asm( #elif defined(__MACH__) " .zerofill __DATA, __bss, __swift_tupleHashable_private, 128, 4\n" " .section __TEXT, __const\n" + #elif defined(_WIN32) + " .lcomm __swift_tupleHashable_private, 128, 16\n" + " .section .rdata, \"dr\"\n" + #pragma comment(linker, "/EXPORT:_swift_tupleHashable_conf,DATA") #endif " .globl " TUPLE_HASHABLE_CONF "\n" " .p2align 2\n" diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 5987dc9a91c07..da21c70bf6acf 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -376,9 +376,9 @@ searchInConformanceCache(const Metadata *type, return {false, nullptr}; } -extern const ProtocolDescriptor EQUATABLE_DESCRIPTOR; -extern const ProtocolDescriptor COMPARABLE_DESCRIPTOR; -extern const ProtocolDescriptor HASHABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor EQUATABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor COMPARABLE_DESCRIPTOR; +extern "C" const ProtocolDescriptor HASHABLE_DESCRIPTOR; static bool tupleConformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { @@ -401,9 +401,9 @@ static bool tupleConformsToProtocol(const Metadata *type, return true; } -extern const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; -extern const ProtocolConformanceDescriptor _swift_tupleComparable_conf; -extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleEquatable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleComparable_conf; +extern "C" const ProtocolConformanceDescriptor _swift_tupleHashable_conf; static const ProtocolConformanceDescriptor *getTupleConformanceDescriptor( const ProtocolDescriptor *protocol) { diff --git a/test/IRGen/builtin_conformances.swift b/test/IRGen/builtin_conformances.swift index 39175da5d7148..f97f905d4a782 100644 --- a/test/IRGen/builtin_conformances.swift +++ b/test/IRGen/builtin_conformances.swift @@ -1,8 +1,8 @@ // RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s -// CHECK-LABEL: @_swift_tupleEquatable_conf = external global %swift.protocol_conformance_descriptor -// CHECK-LABEL: @_swift_tupleComparable_conf = external global %swift.protocol_conformance_descriptor -// CHECK-LABEL: @_swift_tupleHashable_conf = external global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleEquatable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleComparable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor +// CHECK-LABEL: @_swift_tupleHashable_conf = external{{( dllimport)?}} global %swift.protocol_conformance_descriptor struct Wrapper { let value: T