//===--- DerivedConformances.cpp - Derived conformance utilities ----------===// // // This source file is part of the Swift.org open source project // // 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 // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "TypeChecker.h" #include "TypeCheckConcurrency.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" #include "swift/AST/Stmt.h" #include "swift/AST/Expr.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/ClangImporter/ClangModule.h" #include "DerivedConformances.h" using namespace swift; enum NonconformingMemberKind { AssociatedValue, StoredProperty }; DerivedConformance::DerivedConformance( const NormalProtocolConformance *conformance, NominalTypeDecl *nominal, ProtocolDecl *protocol) : Context(nominal->getASTContext()), Conformance(conformance), Nominal(nominal), Protocol(protocol) { auto *DC = Conformance->getDeclContext(); ConformanceDecl = DC->getInnermostDeclarationDeclContext(); assert(ConformanceDecl); assert(DC->getSelfNominalTypeDecl() == nominal); } DeclContext *DerivedConformance::getConformanceContext() const { return Conformance->getDeclContext(); } ModuleDecl *DerivedConformance::getParentModule() const { return getConformanceContext()->getParentModule(); } void DerivedConformance::addMembersToConformanceContext( ArrayRef children) { auto IDC = cast(ConformanceDecl); for (auto child : children) IDC->addMember(child); } void DerivedConformance::addMemberToConformanceContext( Decl *member, Decl *hint) { auto IDC = cast(ConformanceDecl); IDC->addMember(member, hint, /*insertAtHead=*/false); } void DerivedConformance::addMemberToConformanceContext( Decl *member, bool insertAtHead) { auto IDC = cast(ConformanceDecl); IDC->addMember(member, /*hint=*/nullptr, insertAtHead); } Type DerivedConformance::getProtocolType() const { return Protocol->getDeclaredInterfaceType(); } bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, NominalTypeDecl *Nominal, ProtocolDecl *Protocol) { const auto derivableKind = Protocol->getKnownDerivableProtocolKind(); if (!derivableKind) return false; // When the necessary requirements are met, the conformance to OptionSet // is serendipitously derived via memberwise initializer synthesis. if (*derivableKind == KnownDerivableProtocolKind::OptionSet) { return false; } if (*derivableKind == KnownDerivableProtocolKind::Hashable) { // We can always complete a partial Hashable implementation, and we can // synthesize a full Hashable implementation for structs and enums with // Hashable components. return canDeriveHashable(Nominal); } if (*derivableKind == KnownDerivableProtocolKind::Actor) return canDeriveActor(DC, Nominal); if (*derivableKind == KnownDerivableProtocolKind::Identifiable) return canDeriveIdentifiable(Nominal, DC); if (*derivableKind == KnownDerivableProtocolKind::DistributedActor) return canDeriveDistributedActor(Nominal, DC); if (*derivableKind == KnownDerivableProtocolKind::DistributedActorSystem) return canDeriveDistributedActorSystem(Nominal, DC); if (*derivableKind == KnownDerivableProtocolKind::AdditiveArithmetic) return canDeriveAdditiveArithmetic(Nominal, DC); // Eagerly return true here. Actual synthesis conditions are checked in // `DerivedConformance::deriveDifferentiable`: they are complicated and depend // on the requirement being derived. if (*derivableKind == KnownDerivableProtocolKind::Differentiable) return true; if (*derivableKind == KnownDerivableProtocolKind::Encodable) { return canDeriveEncodable(Nominal); } if (*derivableKind == KnownDerivableProtocolKind::Decodable) { return canDeriveDecodable(Nominal); } if (auto *enumDecl = dyn_cast(Nominal)) { switch (*derivableKind) { // The presence of a raw type is an explicit declaration that // the compiler should derive a RawRepresentable conformance. case KnownDerivableProtocolKind::RawRepresentable: return canDeriveRawRepresentable(DC, Nominal); // Enums without associated values can implicitly derive Equatable // conformance. case KnownDerivableProtocolKind::Equatable: return canDeriveEquatable(DC, Nominal); case KnownDerivableProtocolKind::Comparable: return !enumDecl->hasPotentiallyUnavailableCaseValue() && canDeriveComparable(DC, enumDecl); // "Simple" enums without availability attributes can explicitly derive // a CaseIterable conformance. // // FIXME: Lift the availability restriction. case KnownDerivableProtocolKind::CaseIterable: return !enumDecl->hasPotentiallyUnavailableCaseValue() && enumDecl->hasOnlyCasesWithoutAssociatedValues(); // @objc enums can explicitly derive their _BridgedNSError conformance. case KnownDerivableProtocolKind::BridgedNSError: return enumDecl->isObjC() && enumDecl->hasCases() && enumDecl->hasOnlyCasesWithoutAssociatedValues(); // Enums without associated values and enums with a raw type of String // or Int can explicitly derive CodingKey conformance. case KnownDerivableProtocolKind::CodingKey: { Type rawType = enumDecl->getRawType(); if (rawType) { return rawType->isString() || rawType->isInt(); } // hasOnlyCasesWithoutAssociatedValues will return true for empty enums; // empty enums are allowed to conform as well. return enumDecl->hasOnlyCasesWithoutAssociatedValues(); } default: return false; } } else if (isa(Nominal)) { switch (*derivableKind) { case KnownDerivableProtocolKind::Equatable: // Structs can explicitly derive Equatable conformance. return canDeriveEquatable(DC, Nominal); default: return false; } } return false; } SmallVector DerivedConformance::storedPropertiesNotConformingToProtocol( DeclContext *DC, StructDecl *theStruct, ProtocolDecl *protocol) { auto storedProperties = theStruct->getStoredProperties(); SmallVector nonconformingProperties; for (auto propertyDecl : storedProperties) { if (!propertyDecl->isUserAccessible()) continue; auto type = propertyDecl->getValueInterfaceType(); if (!type) nonconformingProperties.push_back(propertyDecl); if (!checkConformance(DC->mapTypeIntoContext(type), protocol)) { nonconformingProperties.push_back(propertyDecl); } } return nonconformingProperties; } void DerivedConformance::tryDiagnoseFailedDerivation(DeclContext *DC, NominalTypeDecl *nominal, ProtocolDecl *protocol) { auto knownProtocol = protocol->getKnownProtocolKind(); if (!knownProtocol) return; if (*knownProtocol == KnownProtocolKind::Equatable) { tryDiagnoseFailedEquatableDerivation(DC, nominal); } if (*knownProtocol == KnownProtocolKind::Hashable) { tryDiagnoseFailedHashableDerivation(DC, nominal); } if (*knownProtocol == KnownProtocolKind::Comparable) { tryDiagnoseFailedComparableDerivation(DC, nominal); } if (*knownProtocol == KnownProtocolKind::DistributedActor) { tryDiagnoseFailedDistributedActorDerivation(DC, nominal); } if (*knownProtocol == KnownProtocolKind::DistributedActorSystem) { tryDiagnoseFailedDistributedActorSystemDerivation(DC, nominal); } } void DerivedConformance::diagnoseAnyNonConformingMemberTypes( DeclContext *DC, NominalTypeDecl *nominal, ProtocolDecl *protocol) { ASTContext &ctx = DC->getASTContext(); if (auto *enumDecl = dyn_cast(nominal)) { auto nonconformingAssociatedTypes = associatedValuesNotConformingToProtocol(DC, enumDecl, protocol); for (auto *typeToDiagnose : nonconformingAssociatedTypes) { SourceLoc reprLoc; if (auto *repr = typeToDiagnose->getTypeRepr()) reprLoc = repr->getStartLoc(); ctx.Diags.diagnose( reprLoc, diag::missing_member_type_conformance_prevents_synthesis, NonconformingMemberKind::AssociatedValue, typeToDiagnose->getInterfaceType(), protocol->getDeclaredInterfaceType(), nominal->getDeclaredInterfaceType()); } } if (auto *structDecl = dyn_cast(nominal)) { auto nonconformingStoredProperties = storedPropertiesNotConformingToProtocol(DC, structDecl, protocol); for (auto *propertyToDiagnose : nonconformingStoredProperties) { ctx.Diags.diagnose( propertyToDiagnose->getLoc(), diag::missing_member_type_conformance_prevents_synthesis, NonconformingMemberKind::StoredProperty, propertyToDiagnose->getInterfaceType(), protocol->getDeclaredInterfaceType(), nominal->getDeclaredInterfaceType()); } } } void DerivedConformance::diagnoseIfSynthesisUnsupportedForDecl( NominalTypeDecl *nominal, ProtocolDecl *protocol) { auto shouldDiagnose = false; if (protocol->isSpecificProtocol(KnownProtocolKind::Equatable) || protocol->isSpecificProtocol(KnownProtocolKind::Hashable)) { shouldDiagnose = isa(nominal); } if (protocol->isSpecificProtocol(KnownProtocolKind::Comparable)) { shouldDiagnose = !isa(nominal); } if (shouldDiagnose) { auto &ctx = nominal->getASTContext(); ctx.Diags.diagnose( nominal->getLoc(), diag::automatic_protocol_synthesis_unsupported, protocol->getName().str(), nominal->getDescriptiveKind()); } } ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, ValueDecl *requirement) { // Note: whenever you update this function, also update // deriveProtocolRequirement. ASTContext &ctx = nominal->getASTContext(); const auto name = requirement->getName(); // Local function that retrieves the requirement with the same name as // the provided requirement, but within the given known protocol. auto getRequirement = [&](KnownProtocolKind kind) -> ValueDecl * { // Dig out the protocol. auto proto = ctx.getProtocol(kind); if (!proto) return nullptr; auto conformance = lookupConformance( nominal->getDeclaredInterfaceType(), proto); if (conformance) { auto DC = conformance.getConcrete()->getDeclContext(); // Check whether this nominal type derives conformances to the protocol. if (!DerivedConformance::derivesProtocolConformance(DC, nominal, proto)) return nullptr; } // Retrieve the requirement. return proto->getSingleRequirement(name); }; // Properties. if (isa(requirement)) { // RawRepresentable.rawValue if (name.isSimpleName(ctx.Id_rawValue)) return getRequirement(KnownProtocolKind::RawRepresentable); // Hashable.hashValue if (name.isSimpleName(ctx.Id_hashValue)) return getRequirement(KnownProtocolKind::Hashable); // CaseIterable.allValues if (name.isSimpleName(ctx.Id_allCases)) return getRequirement(KnownProtocolKind::CaseIterable); // _BridgedNSError._nsErrorDomain if (name.isSimpleName(ctx.Id_nsErrorDomain)) return getRequirement(KnownProtocolKind::BridgedNSError); // CodingKey.stringValue if (name.isSimpleName(ctx.Id_stringValue)) return getRequirement(KnownProtocolKind::CodingKey); // CodingKey.intValue if (name.isSimpleName(ctx.Id_intValue)) return getRequirement(KnownProtocolKind::CodingKey); // AdditiveArithmetic.zero if (name.isSimpleName(ctx.Id_zero)) return getRequirement(KnownProtocolKind::AdditiveArithmetic); // Actor.unownedExecutor if (name.isSimpleName(ctx.Id_unownedExecutor)) { if (nominal->isDistributedActor()) { return getRequirement(KnownProtocolKind::DistributedActor); } else { return getRequirement(KnownProtocolKind::Actor); } } // DistributedActor.id if (name.isSimpleName(ctx.Id_id)) return getRequirement(KnownProtocolKind::DistributedActor); // DistributedActor.actorSystem if (name.isSimpleName(ctx.Id_actorSystem)) return getRequirement(KnownProtocolKind::DistributedActor); return nullptr; } // Functions. if (auto func = dyn_cast(requirement)) { if (func->isOperator() && name.getBaseName() == "<") return getRequirement(KnownProtocolKind::Comparable); if (func->isOperator() && name.getBaseName() == "==") return getRequirement(KnownProtocolKind::Equatable); // AdditiveArithmetic.+ // AdditiveArithmetic.- if (func->isOperator() && name.getArgumentNames().size() == 2 && (name.getBaseName() == "+" || name.getBaseName() == "-")) { return getRequirement(KnownProtocolKind::AdditiveArithmetic); } // Differentiable.move(by:) if (name.isCompoundName() && name.getBaseName() == ctx.Id_move) { auto argumentNames = name.getArgumentNames(); if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_by) return getRequirement(KnownProtocolKind::Differentiable); } // Encodable.encode(to: Encoder) if (name.isCompoundName() && name.getBaseName() == ctx.Id_encode) { auto argumentNames = name.getArgumentNames(); if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_to) return getRequirement(KnownProtocolKind::Encodable); } // Hashable.hash(into: inout Hasher) if (name.isCompoundName() && name.getBaseName() == ctx.Id_hash) { auto argumentNames = name.getArgumentNames(); if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_into) return getRequirement(KnownProtocolKind::Hashable); } // static DistributedActor.resolve(id:using:) if (name.isCompoundName() && name.getBaseName() == ctx.Id_resolve && func->isStatic()) { auto argumentNames = name.getArgumentNames(); if (argumentNames.size() == 2 && argumentNames[0] == ctx.Id_id && argumentNames[1] == ctx.Id_using) { return getRequirement(KnownProtocolKind::DistributedActor); } } // DistributedActor.actorSystem if (name.isCompoundName() && name.getBaseName() == ctx.Id_invokeHandlerOnReturn) return getRequirement(KnownProtocolKind::DistributedActorSystem); return nullptr; } // Initializers. if (auto ctor = dyn_cast(requirement)) { auto argumentNames = name.getArgumentNames(); if (argumentNames.size() == 1) { if (argumentNames[0] == ctx.Id_rawValue) return getRequirement(KnownProtocolKind::RawRepresentable); // CodingKey.init?(stringValue:), CodingKey.init?(intValue:) if (ctor->isFailable() && !ctor->isImplicitlyUnwrappedOptional() && (argumentNames[0] == ctx.Id_stringValue || argumentNames[0] == ctx.Id_intValue)) return getRequirement(KnownProtocolKind::CodingKey); // Decodable.init(from: Decoder) if (argumentNames[0] == ctx.Id_from) return getRequirement(KnownProtocolKind::Decodable); } return nullptr; } // Associated types. if (isa(requirement)) { // RawRepresentable.RawValue if (name.isSimpleName(ctx.Id_RawValue)) return getRequirement(KnownProtocolKind::RawRepresentable); // CaseIterable.AllCases if (name.isSimpleName(ctx.Id_AllCases)) return getRequirement(KnownProtocolKind::CaseIterable); // Differentiable.TangentVector if (name.isSimpleName(ctx.Id_TangentVector)) return getRequirement(KnownProtocolKind::Differentiable); return nullptr; } return nullptr; } DeclRefExpr * DerivedConformance::createSelfDeclRef(AbstractFunctionDecl *fn) { ASTContext &C = fn->getASTContext(); auto selfDecl = fn->getImplicitSelfDecl(); return new (C) DeclRefExpr(selfDecl, DeclNameLoc(), /*implicit*/true); } CallExpr * DerivedConformance::createBuiltinCall(ASTContext &ctx, BuiltinValueKind builtin, ArrayRef typeArgs, ArrayRef args) { auto name = ctx.getIdentifier(getBuiltinName(builtin)); auto decl = getBuiltinValueDecl(ctx, name); assert(decl); ConcreteDeclRef declRef = decl; auto fnType = decl->getInterfaceType(); if (auto genericFnType = fnType->getAs()) { auto generics = genericFnType->getGenericSignature(); auto subs = SubstitutionMap::get(generics, typeArgs, LookUpConformanceInModule{}); declRef = ConcreteDeclRef(decl, subs); fnType = genericFnType->substGenericArgs(subs); } else { assert(typeArgs.empty()); } auto resultType = fnType->castTo()->getResult(); Expr *ref = new (ctx) DeclRefExpr(declRef, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, fnType); auto *argList = ArgumentList::forImplicitUnlabeled(ctx, args); auto *call = CallExpr::createImplicit(ctx, ref, argList); call->setType(resultType); call->setThrows(nullptr); return call; } CallExpr *DerivedConformance::createDiagnoseUnavailableCodeReachedCallExpr( ASTContext &ctx) { FuncDecl *diagnoseDecl = ctx.getDiagnoseUnavailableCodeReached(); assert(diagnoseDecl); auto diagnoseDeclRefExpr = new (ctx) DeclRefExpr(diagnoseDecl, DeclNameLoc(), true); diagnoseDeclRefExpr->setType(diagnoseDecl->getInterfaceType()); auto argList = ArgumentList::createImplicit(ctx, {}); auto callExpr = CallExpr::createImplicit(ctx, diagnoseDeclRefExpr, argList); callExpr->setType(ctx.getNeverType()); callExpr->setThrows(nullptr); return callExpr; } AccessorDecl * DerivedConformance::addGetterToReadOnlyDerivedProperty(VarDecl *property) { auto getter = declareDerivedPropertyGetter(property); property->setImplInfo(StorageImplInfo::getImmutableComputed()); property->setAccessors(SourceLoc(), {getter}, SourceLoc()); return getter; } AccessorDecl * DerivedConformance::declareDerivedPropertyGetter(VarDecl *property) { auto &C = property->getASTContext(); auto parentDC = property->getDeclContext(); ParameterList *params = ParameterList::createEmpty(C); auto getterDecl = AccessorDecl::create( C, /*FuncLoc=*/SourceLoc(), /*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, property, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(), params, property->getInterfaceType(), parentDC); getterDecl->setImplicit(); getterDecl->setIsTransparent(false); getterDecl->copyFormalAccessFrom(property); return getterDecl; } static VarDecl::Introducer mapIntroducer(DerivedConformance::SynthesizedIntroducer intro) { switch (intro) { case DerivedConformance::SynthesizedIntroducer::Let: return VarDecl::Introducer::Let; case DerivedConformance::SynthesizedIntroducer::Var: return VarDecl::Introducer::Var; } llvm_unreachable("Invalid synthesized introducer!"); } std::pair DerivedConformance::declareDerivedProperty(SynthesizedIntroducer intro, Identifier name, Type propertyInterfaceType, bool isStatic, bool isFinal) { auto parentDC = getConformanceContext(); VarDecl *propDecl = new (Context) VarDecl( /*IsStatic*/ isStatic, mapIntroducer(intro), SourceLoc(), name, parentDC); propDecl->setImplicit(); propDecl->setSynthesized(); propDecl->copyFormalAccessFrom(Nominal, /*sourceIsParentContext*/ true); propDecl->setInterfaceType(propertyInterfaceType); auto propertyContextType = getConformanceContext()->mapTypeIntoContext(propertyInterfaceType); Pattern *propPat = NamedPattern::createImplicit(Context, propDecl, propertyContextType); propPat = TypedPattern::createImplicit(Context, propPat, propertyContextType); auto *pbDecl = PatternBindingDecl::createImplicit( Context, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, parentDC); return {propDecl, pbDecl}; } bool DerivedConformance::checkAndDiagnoseDisallowedContext( ValueDecl *synthesizing) const { // In general, conformances can't be synthesized in extensions across files; // but we have to allow it as a special case for Equatable and Hashable on // enums with no associated values to preserve source compatibility. bool allowCrossfileExtensions = false; if (Protocol->isSpecificProtocol(KnownProtocolKind::Equatable) || Protocol->isSpecificProtocol(KnownProtocolKind::Hashable)) { auto ED = dyn_cast(Nominal); allowCrossfileExtensions = ED && ED->hasOnlyCasesWithoutAssociatedValues(); } if (!allowCrossfileExtensions && Nominal->getModuleScopeContext() != getConformanceContext()->getModuleScopeContext()) { ConformanceDecl->diagnose(diag::cannot_synthesize_in_crossfile_extension, Nominal, synthesizing->getName(), getProtocolType()); Nominal->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); // Try to insert a stub. auto Extension = cast(getConformanceContext()); auto FixitLocation = Extension->getBraces().Start; llvm::SmallString<128> Text; { llvm::raw_svector_ostream SS(Text); swift::printRequirementStub(synthesizing, Nominal, Nominal->getDeclaredType(), Extension->getStartLoc(), SS); if (!Text.empty()) { ConformanceDecl->diagnose(diag::missing_witnesses_general) .fixItInsertAfter(FixitLocation, Text.str()); } } return true; } // A non-final class can't have a protocol-witnesses initializer in an // extension. if (auto CD = dyn_cast(Nominal)) { if (!CD->isSemanticallyFinal() && isa(synthesizing) && isa(ConformanceDecl)) { ConformanceDecl->diagnose( diag::cannot_synthesize_init_in_extension_of_nonfinal, getProtocolType(), synthesizing->getName()); return true; } } if (auto ED = dyn_cast(Nominal)) { if (ED->getAllCases().empty() && (Protocol->isSpecificProtocol(KnownProtocolKind::Encodable) || Protocol->isSpecificProtocol(KnownProtocolKind::Decodable))) { ED->diagnose(diag::codable_synthesis_empty_enum_not_supported, getProtocolType(), Nominal->getBaseIdentifier()); return false; } } return false; } /// Returns a generated guard statement that checks whether the given lhs and /// rhs expressions are equal. If not equal, the else block for the guard /// returns `guardReturnValue`. /// \p C The AST context. /// \p lhsExpr The first expression to compare for equality. /// \p rhsExpr The second expression to compare for equality. /// \p guardReturnValue The expression to return if the two sides are not equal GuardStmt *DerivedConformance::returnIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr, Expr *guardReturnValue) { SmallVector conditions; SmallVector statements; auto *returnStmt = ReturnStmt::createImplicit(C, guardReturnValue); statements.push_back(returnStmt); // Next, generate the condition being checked. // lhs == rhs auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( DeclNameRef(C.Id_EqualsOperator), DeclRefKind::BinaryOperator, DeclNameLoc()); auto *cmpExpr = BinaryExpr::create(C, lhsExpr, cmpFuncExpr, rhsExpr, /*implicit*/ true); conditions.emplace_back(cmpExpr); // Build and return the complete guard statement. // guard lhs == rhs else { return lhs < rhs } auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body); } /// Returns a generated guard statement that checks whether the given lhs and /// rhs expressions are equal. If not equal, the else block for the guard /// returns `false`. /// \p C The AST context. /// \p lhsExpr The first expression to compare for equality. /// \p rhsExpr The second expression to compare for equality. GuardStmt *DerivedConformance::returnFalseIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr) { // return false auto falseExpr = new (C) BooleanLiteralExpr(false, SourceLoc(), true); return returnIfNotEqualGuard(C, lhsExpr, rhsExpr, falseExpr); } /// Returns a generated guard statement that checks whether the given expr is true. /// If it is false, the else block for the guard returns `nil`. /// \p C The AST context. /// \p testExpr The expression that should be tested. /// \p baseType The wrapped type of the to-be-returned Optional. GuardStmt *DerivedConformance::returnNilIfFalseGuardTypeChecked(ASTContext &C, Expr *testExpr, Type optionalWrappedType) { auto nilExpr = new (C) NilLiteralExpr(SourceLoc(), /*implicit=*/true); nilExpr->setType(optionalWrappedType->wrapInOptionalType()); SmallVector conditions; SmallVector statements; auto *returnStmt = ReturnStmt::createImplicit(C, nilExpr); statements.push_back(returnStmt); // Next, generate the condition being checked. conditions.emplace_back(testExpr); // Build and return the complete guard statement. // guard lhs == rhs else { return lhs < rhs } auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body); } /// Returns a generated guard statement that checks whether the given lhs and /// rhs expressions are equal. If not equal, the else block for the guard /// returns lhs < rhs. /// \p C The AST context. /// \p lhsExpr The first expression to compare for equality. /// \p rhsExpr The second expression to compare for equality. GuardStmt *DerivedConformance::returnComparisonIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr) { // return lhs < rhs auto ltFuncExpr = new (C) UnresolvedDeclRefExpr( DeclNameRef(C.Id_LessThanOperator), DeclRefKind::BinaryOperator, DeclNameLoc()); auto *ltExpr = BinaryExpr::create(C, lhsExpr, ltFuncExpr, rhsExpr, /*implicit*/ true); return returnIfNotEqualGuard(C, lhsExpr, rhsExpr, ltExpr); } /// Build a type-checked integer literal. static IntegerLiteralExpr *buildIntegerLiteral(ASTContext &C, unsigned index) { Type intType = C.getIntType(); auto literal = IntegerLiteralExpr::createFromUnsigned(C, index, SourceLoc()); literal->setType(intType); literal->setBuiltinInitializer(C.getIntBuiltinInitDecl(C.getIntDecl())); return literal; } /// Create AST statements which convert from an enum to an Int with a switch. /// \p stmts The generated statements are appended to this vector. /// \p parentDC Either an extension or the enum itself. /// \p enumDecl The enum declaration. /// \p enumVarDecl The enum input variable. /// \p funcDecl The parent function. /// \p indexName The name of the output variable. /// \return A DeclRefExpr of the output variable (of type Int). DeclRefExpr *DerivedConformance::convertEnumToIndex(SmallVectorImpl &stmts, DeclContext *parentDC, EnumDecl *enumDecl, VarDecl *enumVarDecl, AbstractFunctionDecl *funcDecl, const char *indexName) { ASTContext &C = enumDecl->getASTContext(); Type enumType = enumVarDecl->getTypeInContext(); Type intType = C.getIntType(); auto indexVar = new (C) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Var, SourceLoc(), C.getIdentifier(indexName), funcDecl); indexVar->setInterfaceType(intType); indexVar->setImplicit(); // generate: var indexVar Pattern *indexPat = NamedPattern::createImplicit(C, indexVar, intType); indexPat = TypedPattern::createImplicit(C, indexPat, intType); auto *indexBind = PatternBindingDecl::createImplicit( C, StaticSpellingKind::None, indexPat, /*InitExpr*/ nullptr, funcDecl); unsigned index = 0; SmallVector cases; for (auto elt : enumDecl->getAllElements()) { if (auto *unavailableElementCase = DerivedConformance::unavailableEnumElementCaseStmt(enumType, elt, funcDecl)) { cases.push_back(unavailableElementCase); continue; } // generate: case .: auto *pat = EnumElementPattern::createImplicit( enumType, elt, /*subPattern*/ nullptr, /*DC*/ funcDecl); auto labelItem = CaseLabelItem(pat); // generate: indexVar = auto indexExpr = buildIntegerLiteral(C, index++); auto indexRef = new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/true, AccessSemantics::Ordinary, LValueType::get(intType)); auto assignExpr = new (C) AssignExpr(indexRef, SourceLoc(), indexExpr, /*implicit*/ true); assignExpr->setType(TupleType::getEmpty(C)); auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), SourceLoc()); cases.push_back(CaseStmt::create(C, CaseParentKind::Switch, SourceLoc(), labelItem, SourceLoc(), SourceLoc(), body, /*case body vardecls*/ std::nullopt)); } // generate: switch enumVar { } auto enumRef = new (C) DeclRefExpr(enumVarDecl, DeclNameLoc(), /*implicit*/true, AccessSemantics::Ordinary, enumVarDecl->getTypeInContext()); auto switchStmt = SwitchStmt::createImplicit(LabeledStmtInfo(), enumRef, cases, C); stmts.push_back(indexBind); stmts.push_back(switchStmt); return new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/ true, AccessSemantics::Ordinary, intType); } /// Returns the ParamDecl for each associated value of the given enum whose type /// does not conform to a protocol /// \p theEnum The enum whose elements and associated values should be checked. /// \p protocol The protocol being requested. /// \return The ParamDecl of each associated value whose type does not conform. SmallVector DerivedConformance::associatedValuesNotConformingToProtocol( DeclContext *DC, EnumDecl *theEnum, ProtocolDecl *protocol) { SmallVector nonconformingAssociatedValues; for (auto elt : theEnum->getAllElements()) { auto PL = elt->getParameterList(); if (!PL) continue; for (auto param : *PL) { auto type = param->getInterfaceType(); if (checkConformance(DC->mapTypeIntoContext(type), protocol).isInvalid()) { nonconformingAssociatedValues.push_back(param); } } } return nonconformingAssociatedValues; } /// Returns true if, for every element of the given enum, it either has no /// associated values or all of them conform to a protocol. /// \p theEnum The enum whose elements and associated values should be checked. /// \p protocol The protocol being requested. /// \return True if all associated values of all elements of the enum conform. bool DerivedConformance::allAssociatedValuesConformToProtocol(DeclContext *DC, EnumDecl *theEnum, ProtocolDecl *protocol) { return associatedValuesNotConformingToProtocol(DC, theEnum, protocol).empty(); } /// Returns the pattern used to match and bind the associated values (if any) of /// an enum case. /// \p enumElementDecl The enum element to match. /// \p varPrefix The prefix character for variable names (e.g., a0, a1, ...). /// \p varContext The context into which payload variables should be declared. /// \p boundVars The array to which the pattern's variables will be appended. /// \p useLabels If the argument has a label, use it instead of the generated /// name. Pattern *DerivedConformance::enumElementPayloadSubpattern( EnumElementDecl *enumElementDecl, char varPrefix, DeclContext *varContext, SmallVectorImpl &boundVars, bool useLabels) { auto parentDC = enumElementDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); // No arguments, so no subpattern to match. if (!enumElementDecl->hasAssociatedValues()) return nullptr; auto payloadType = enumElementDecl->getPayloadInterfaceType(); if (auto tupleType = payloadType->getAs()) { // Either multiple (labeled or unlabeled) arguments, or one labeled // argument. Return a tuple pattern that matches the enum element in arity, // types, and labels. For example: // case a(x: Int) => (x: let a0) // case b(Int, String) => (let a0, let a1) SmallVector elementPatterns; int index = 0; for (auto tupleElement : tupleType->getElements()) { VarDecl *payloadVar; if (useLabels && tupleElement.hasName()) { payloadVar = new (C) VarDecl(/*IsStatic*/ false, VarDecl::Introducer::Let, SourceLoc(), tupleElement.getName(), varContext); payloadVar->setInterfaceType(tupleElement.getType()); } else { payloadVar = indexedVarDecl(varPrefix, index++, tupleElement.getType(), varContext); } boundVars.push_back(payloadVar); auto namedPattern = new (C) NamedPattern(payloadVar); namedPattern->setImplicit(); auto letPattern = BindingPattern::createImplicit( C, VarDecl::Introducer::Let, namedPattern); elementPatterns.push_back(TuplePatternElt(tupleElement.getName(), SourceLoc(), letPattern)); } auto pat = TuplePattern::createImplicit(C, elementPatterns); pat->setImplicit(); return pat; } // Otherwise, a one-argument unlabeled payload. Return a paren pattern whose // underlying type is the same as the payload. For example: // case a(Int) => (let a0) auto payloadVar = indexedVarDecl(varPrefix, 0, payloadType, varContext); boundVars.push_back(payloadVar); auto namedPattern = new (C) NamedPattern(payloadVar); namedPattern->setImplicit(); auto letPattern = new (C) BindingPattern(SourceLoc(), VarDecl::Introducer::Let, namedPattern); return ParenPattern::createImplicit(C, letPattern); } CaseStmt *DerivedConformance::unavailableEnumElementCaseStmt( Type enumType, EnumElementDecl *elt, DeclContext *parentDC, unsigned subPatternCount) { assert(subPatternCount > 0); ASTContext &C = parentDC->getASTContext(); if (!elt->isUnreachableAtRuntime() || elt->getParentEnum()->isUnreachableAtRuntime()) return nullptr; // If the stdlib isn't new enough to contain the helper function for // diagnosing execution of unavailable code then just synthesize this case // normally. if (!C.getDiagnoseUnavailableCodeReached()) return nullptr; auto createElementPattern = [&]() -> EnumElementPattern * { // . return EnumElementPattern::createImplicit( enumType, elt, /*subPattern*/ nullptr, /*DC*/ parentDC); }; Pattern *labelItemPattern; if (subPatternCount > 1) { SmallVector tuplePatternElts; for (unsigned i = 0; i < subPatternCount; i++) { tuplePatternElts.push_back(TuplePatternElt(createElementPattern())); } // (., ..., .) auto caseTuplePattern = TuplePattern::createImplicit(C, tuplePatternElts); caseTuplePattern->setImplicit(); labelItemPattern = caseTuplePattern; } else { labelItemPattern = createElementPattern(); } auto labelItem = CaseLabelItem(labelItemPattern); auto *callExpr = DerivedConformance::createDiagnoseUnavailableCodeReachedCallExpr(C); auto body = BraceStmt::create(C, SourceLoc(), {callExpr}, SourceLoc()); return CaseStmt::create(C, CaseParentKind::Switch, SourceLoc(), labelItem, SourceLoc(), SourceLoc(), body, {}, /*implicit*/ true); } /// Creates a named variable based on a prefix character and a numeric index. /// \p prefixChar The prefix character for the variable's name. /// \p index The numeric index to append to the variable's name. /// \p type The type of the variable. /// \p varContext The context of the variable. /// \return A VarDecl named with the prefix and number. VarDecl *DerivedConformance::indexedVarDecl(char prefixChar, int index, Type type, DeclContext *varContext) { ASTContext &C = varContext->getASTContext(); llvm::SmallString<8> indexVal; indexVal.append(1, prefixChar); APInt(32, index).toString(indexVal, 10, /*signed*/ false); auto indexStr = C.AllocateCopy(indexVal); auto indexStrRef = StringRef(indexStr.data(), indexStr.size()); auto varDecl = new (C) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, SourceLoc(), C.getIdentifier(indexStrRef), varContext); varDecl->setInterfaceType(type); return varDecl; } bool swift::memberwiseAccessorsRequireActorIsolation(NominalTypeDecl *nominal) { if (!getActorIsolation(nominal).isActorIsolated()) return false; for (auto property : nominal->getStoredProperties()) { if (!property->isUserAccessible()) continue; if (!property->isLet()) return true; } return false; }