diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index e21becfbe2039..c0ea831296f01 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -374,6 +374,12 @@ bool conflicting(ASTContext &ctx, bool *wouldConflictInSwift5 = nullptr, bool skipProtocolExtensionCheck = false); +/// The kind of special compiler synthesized property in a \c distributed actor, +/// currently this includes \c id and \c actorSystem. +enum class SpecialDistributedProperty { + Id, ActorSystem +}; + /// The kind of artificial main to generate. enum class ArtificialMainKind : uint8_t { NSApplicationMain, @@ -3056,6 +3062,12 @@ class ValueDecl : public Decl { /// `distributed var get { }` accessors. bool isDistributedGetAccessor() const; + /// Whether this is the special synthesized 'id' or 'actorSystem' property + /// of a distributed actor. If \p onlyCheckName is set, then any + /// matching user-defined property with the name is also considered. + std::optional + isSpecialDistributedProperty(bool onlyCheckName = false) const; + bool hasName() const { return bool(Name); } bool isOperator() const { return Name.isOperator(); } @@ -5430,7 +5442,6 @@ enum class KnownDerivableProtocolKind : uint8_t { Decodable, AdditiveArithmetic, Differentiable, - Identifiable, Actor, DistributedActor, DistributedActorSystem, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8344217e8d18e..ba121d384b9ad 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5710,6 +5710,8 @@ ERROR(actor_cannot_inherit_distributed_actor_protocol,none, (DeclName)) ERROR(broken_distributed_actor_requirement,none, "DistributedActor protocol is broken: unexpected requirement", ()) +ERROR(broken_distributed_actor_system_requirement,none, + "DistributedActorSystem protocol is broken: unexpected requirement", ()) ERROR(distributed_actor_system_conformance_missing_adhoc_requirement,none, "%kind0 is missing witness for protocol requirement %1", (const ValueDecl *, DeclName)) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 928484825f3b1..477e557325195 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2999,9 +2999,6 @@ enum class ImplicitMemberAction : uint8_t { ResolveCodingKeys, ResolveEncodable, ResolveDecodable, - ResolveDistributedActor, - ResolveDistributedActorID, - ResolveDistributedActorSystem, }; class ResolveImplicitMemberRequest diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a187dcff91ace..dd1ca33bbf77d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6449,6 +6449,16 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { if (isa(this)) return; + auto baseName = member.getBaseName(); + auto &Context = getASTContext(); + + // For a distributed actor `id` and `actorSystem` can be synthesized without + // causing cycles so do them above the cycle guard. + if (member.isSimpleName(Context.Id_id)) + (void)getDistributedActorIDProperty(); + if (member.isSimpleName(Context.Id_actorSystem)) + (void)getDistributedActorSystemProperty(); + // Silently break cycles here because we can't be sure when and where a // request to synthesize will come from yet. // FIXME: rdar://56844567 @@ -6458,14 +6468,12 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { Bits.NominalTypeDecl.IsComputingSemanticMembers = true; SWIFT_DEFER { Bits.NominalTypeDecl.IsComputingSemanticMembers = false; }; - auto baseName = member.getBaseName(); - auto &Context = getASTContext(); std::optional action = std::nullopt; if (baseName.isConstructor()) action.emplace(ImplicitMemberAction::ResolveImplicitInit); if (member.isSimpleName() && !baseName.isSpecial()) { - if (baseName.getIdentifier() == getASTContext().Id_CodingKeys) { + if (baseName.getIdentifier() == Context.Id_CodingKeys) { action.emplace(ImplicitMemberAction::ResolveCodingKeys); } } else { @@ -6474,8 +6482,6 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { if (baseName.isConstructor()) { if ((member.isSimpleName() || argumentNames.front() == Context.Id_from)) { action.emplace(ImplicitMemberAction::ResolveDecodable); - } else if (argumentNames.front() == Context.Id_system) { - action.emplace(ImplicitMemberAction::ResolveDistributedActorSystem); } } else if (!baseName.isSpecial() && baseName.getIdentifier() == Context.Id_encode && @@ -6483,13 +6489,6 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { argumentNames.front() == Context.Id_to)) { action.emplace(ImplicitMemberAction::ResolveEncodable); } - } else if (member.isSimpleName() || argumentNames.size() == 2) { - if (baseName.isConstructor()) { - if (argumentNames[0] == Context.Id_resolve && - argumentNames[1] == Context.Id_using) { - action.emplace(ImplicitMemberAction::ResolveDistributedActor); - } - } } } @@ -7554,8 +7553,6 @@ ProtocolDecl::getKnownDerivableProtocolKind() const { return KnownDerivableProtocolKind::AdditiveArithmetic; case KnownProtocolKind::Differentiable: return KnownDerivableProtocolKind::Differentiable; - case KnownProtocolKind::Identifiable: - return KnownDerivableProtocolKind::Identifiable; case KnownProtocolKind::Actor: return KnownDerivableProtocolKind::Actor; case KnownProtocolKind::DistributedActor: diff --git a/lib/AST/DistributedDecl.cpp b/lib/AST/DistributedDecl.cpp index 3d9cf93799cc5..52036473dacf5 100644 --- a/lib/AST/DistributedDecl.cpp +++ b/lib/AST/DistributedDecl.cpp @@ -15,12 +15,13 @@ //===----------------------------------------------------------------------===// #include "swift/AST/DistributedDecl.h" -#include "swift/AST/AccessRequests.h" -#include "swift/AST/AccessScope.h" #include "swift/AST/ASTContext.h" -#include "swift/AST/ASTWalker.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/AccessRequests.h" +#include "swift/AST/AccessScope.h" #include "swift/AST/ConformanceLookup.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Expr.h" #include "swift/AST/ForeignAsyncConvention.h" @@ -29,7 +30,6 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" -#include "swift/AST/ASTMangler.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" @@ -39,9 +39,10 @@ #include "swift/AST/ResilienceExpansion.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" -#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/SwiftNameTranslation.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Assertions.h" +#include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Parse/Lexer.h" // FIXME: Bad dependency #include "clang/Lex/MacroInfo.h" @@ -51,7 +52,6 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" -#include "swift/Basic/StringExtras.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Module.h" @@ -198,7 +198,7 @@ Type swift::getDistributedActorSystemType(NominalTypeDecl *actor) { auto DA = C.getDistributedActorDecl(); if (!DA) - return ErrorType::get(C); // FIXME(distributed): just use Type() + return ErrorType::get(C); // Dig out the actor system type. Type selfType = actor->getSelfInterfaceType(); @@ -229,17 +229,16 @@ Type swift::getDistributedActorSerializationType( auto resultTy = getAssociatedTypeOfDistributedSystemOfActor( actorOrExtension, ctx.Id_SerializationRequirement); + if (resultTy->hasError()) + return resultTy; // Protocols are allowed to either not provide a `SerializationRequirement` // at all or provide it in a conformance requirement. - if ((!resultTy || resultTy->hasDependentMember()) && + if (resultTy->hasDependentMember() && actorOrExtension->getSelfProtocolDecl()) { auto sig = actorOrExtension->getGenericSignatureOfContext(); auto actorProtocol = ctx.getProtocol(KnownProtocolKind::DistributedActor); - if (!actorProtocol) - return Type(); - auto serializationTy = actorProtocol->getAssociatedType(ctx.Id_SerializationRequirement) ->getDeclaredInterfaceType(); @@ -331,29 +330,40 @@ swift::getAssociatedDistributedInvocationDecoderDecodeNextArgumentFunction( Type swift::getAssociatedTypeOfDistributedSystemOfActor( DeclContext *actorOrExtension, Identifier member) { auto &ctx = actorOrExtension->getASTContext(); + auto getLoc = [&]() { return extractNearestSourceLoc(actorOrExtension); }; auto actorProtocol = ctx.getProtocol(KnownProtocolKind::DistributedActor); - if (!actorProtocol) - return Type(); + if (!actorProtocol) { + ctx.Diags.diagnose(getLoc(), diag::broken_stdlib_type, "DistributedActor"); + return ErrorType::get(ctx); + } AssociatedTypeDecl *actorSystemDecl = actorProtocol->getAssociatedType(ctx.Id_ActorSystem); - if (!actorSystemDecl) - return Type(); + if (!actorSystemDecl) { + ctx.Diags.diagnose(getLoc(), diag::broken_distributed_actor_requirement); + return ErrorType::get(ctx); + } auto actorSystemProtocol = ctx.getDistributedActorSystemDecl(); - if (!actorSystemProtocol) - return Type(); + if (!actorSystemProtocol) { + ctx.Diags.diagnose(getLoc(), diag::broken_stdlib_type, + "DistributedActorSystem"); + return ErrorType::get(ctx); + } AssociatedTypeDecl *memberTypeDecl = actorSystemProtocol->getAssociatedType(member); - if (!memberTypeDecl) - return Type(); + if (!memberTypeDecl) { + ctx.Diags.diagnose(getLoc(), + diag::broken_distributed_actor_system_requirement); + return ErrorType::get(ctx); + } Type memberTy = DependentMemberType::get( - DependentMemberType::get(actorProtocol->getSelfInterfaceType(), - actorSystemDecl), - memberTypeDecl); + DependentMemberType::get(actorProtocol->getSelfInterfaceType(), + actorSystemDecl), + memberTypeDecl); auto sig = actorOrExtension->getGenericSignatureOfContext(); @@ -365,7 +375,7 @@ Type swift::getAssociatedTypeOfDistributedSystemOfActor( lookupConformance( actorType->getDeclaredInterfaceType(), actorProtocol); if (actorConformance.isInvalid()) - return Type(); + return ErrorType::get(ctx); auto subs = SubstitutionMap::getProtocolSubstitutions(actorConformance); @@ -432,11 +442,7 @@ swift::getDistributedSerializationRequirements( } bool swift::checkDistributedSerializationRequirementIsExactlyCodable( - ASTContext &C, - Type type) { - if (!type) - return false; - + ASTContext &C, Type type) { if (type->hasError()) return false; @@ -1367,6 +1373,42 @@ bool ValueDecl::isDistributedGetAccessor() const { return false; } +std::optional +ValueDecl::isSpecialDistributedProperty(bool onlyCheckName) const { + if (!isa(this)) + return std::nullopt; + + auto *DC = getDeclContext(); + auto &ctx = DC->getASTContext(); + + auto kind = [&]() -> std::optional { + auto name = getName(); + if (name.isSimpleName(ctx.Id_id)) + return SpecialDistributedProperty::Id; + if (name.isSimpleName(ctx.Id_actorSystem)) + return SpecialDistributedProperty::ActorSystem; + + return std::nullopt; + }(); + if (!kind || onlyCheckName) + return kind; + + // The properties can only be synthesized in the nominal itself. + auto *CD = dyn_cast(DC); + if (!CD || !CD->isDistributedActor()) + return std::nullopt; + + // The synthesized bit doesn't get preserved by serialization or module + // interfaces, we only need to check it when compiling a SourceFile though + // since we'll diagnose any conflicting user-defined versions. + if (!isSynthesized() && DC->getParentSourceFile() && + !DC->isInSwiftinterface()) { + return std::nullopt; + } + + return kind; +} + ConstructorDecl * NominalTypeDecl::getDistributedRemoteCallTargetInitFunction() const { auto mutableThis = const_cast(this); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 96836b24a1c6d..fbb490e0932ed 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1310,15 +1310,6 @@ void swift::simple_display(llvm::raw_ostream &out, case ImplicitMemberAction::ResolveDecodable: out << "resolve Decodable.init(from:)"; break; - case ImplicitMemberAction::ResolveDistributedActor: - out << "resolve DistributedActor"; - break; - case ImplicitMemberAction::ResolveDistributedActorID: - out << "resolve DistributedActor.id"; - break; - case ImplicitMemberAction::ResolveDistributedActorSystem: - out << "resolve DistributedActor.actorSystem"; - break; } } diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 0ae90b2db278c..d80309d21a61d 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -74,10 +74,8 @@ void SILGenFunction::emitDistributedRemoteActorDeinit( continue; // Just to double-check, we only want to destroy `id` and `actorSystem` - if (vd->getBaseIdentifier() == C.Id_id || - vd->getBaseIdentifier() == C.Id_actorSystem) { + if (vd->isSpecialDistributedProperty()) destroyClassMember(cleanupLoc, borrowedSelf, vd); - } } if (cd->isRootDefaultActor()) { diff --git a/lib/Sema/AssociatedTypeInference.cpp b/lib/Sema/AssociatedTypeInference.cpp index f8d995762630b..407d22e483967 100644 --- a/lib/Sema/AssociatedTypeInference.cpp +++ b/lib/Sema/AssociatedTypeInference.cpp @@ -1238,19 +1238,6 @@ class AssociatedTypeInference { ArrayRef unresolvedAssocTypes, SmallVectorImpl &solutions); - /// We may need to determine a type witness, regardless of the existence of a - /// default value for it, e.g. when a 'distributed actor' is looking up its - /// 'ID', the default defined in an extension for 'Identifiable' would be - /// located using the lookup resolve. This would not be correct, since the - /// type actually must be based on the associated 'ActorSystem'. - /// - /// TODO(distributed): perhaps there is a better way to avoid this mixup? - /// Note though that this issue seems to only manifest in "real" builds - /// involving multiple files/modules, and not in tests within the Swift - /// project itself. - bool canAttemptEagerTypeWitnessDerivation( - DeclContext *DC, AssociatedTypeDecl *assocType); - public: /// Describes a mapping from associated type declarations to their /// type witnesses (as interface types). @@ -1669,6 +1656,17 @@ AssociatedTypeInference::getPotentialTypeWitnessesFromRequirement( }); } + // The `id` and `actorSystem` DistributedActor properties are never viable + // for type witness inference since their synthesis relies on the type + // witnesses already being resolved. + if (auto *nominal = dc->getSelfNominalTypeDecl()) { + if (nominal->isDistributedActor() && + req->isSpecialDistributedProperty(/*onlyCheckName*/ true)) { + LLVM_DEBUG(llvm::dbgs() << "skipping special distributed property\n"); + return {}; + } + } + TypeReprCycleCheckWalker cycleCheck(dc->getASTContext(), allUnresolved); InferredAssociatedTypesByWitnesses result; @@ -2635,12 +2633,6 @@ deriveTypeWitness(const NormalProtocolConformance *Conformance, return derived.deriveDifferentiable(AssocType); case KnownProtocolKind::DistributedActor: return derived.deriveDistributedActor(AssocType); - case KnownProtocolKind::Identifiable: - // Identifiable only has derivation logic for distributed actors, - // because how it depends on the ActorSystem the actor is associated with. - // If the nominal wasn't a distributed actor, we should not end up here, - // but either way, then we'd return null (fail derivation). - return derived.deriveDistributedActor(AssocType); default: return std::make_pair(nullptr, nullptr); } @@ -3990,20 +3982,6 @@ bool AssociatedTypeInference::diagnoseAmbiguousSolutions( return false; } -bool AssociatedTypeInference::canAttemptEagerTypeWitnessDerivation( - DeclContext *DC, AssociatedTypeDecl *assocType) { - - /// Rather than locating the TypeID via the default implementation of - /// Identifiable, we need to find the type based on the associated ActorSystem - if (auto *nominal = DC->getSelfNominalTypeDecl()) - if (nominal->isDistributedActor() && - assocType->getProtocol()->isSpecificProtocol(KnownProtocolKind::Identifiable)) { - return true; - } - - return false; -} - auto AssociatedTypeInference::solve() -> std::optional { LLVM_DEBUG(llvm::dbgs() << "============ Start " << conformance->getType() << ": " << conformance->getProtocol()->getName() @@ -4022,16 +4000,6 @@ auto AssociatedTypeInference::solve() -> std::optional { if (conformance->hasTypeWitness(assocType)) continue; - if (canAttemptEagerTypeWitnessDerivation(dc, assocType)) { - auto derivedType = computeDerivedTypeWitness(assocType); - if (derivedType.first) { - recordTypeWitness(conformance, assocType, - derivedType.first->mapTypeOutOfContext(), - derivedType.second); - continue; - } - } - // Try to resolve this type witness via name lookup, which is the // most direct mechanism, overriding all others. switch (resolveTypeWitnessViaLookup(conformance, assocType)) { diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 7c86d441b379e..8adecd324e952 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -1449,20 +1449,6 @@ ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator, TypeChecker::addImplicitConstructors(target); auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable); (void)evaluateTargetConformanceTo(decodableProto); - } - break; - case ImplicitMemberAction::ResolveDistributedActor: - case ImplicitMemberAction::ResolveDistributedActorSystem: - case ImplicitMemberAction::ResolveDistributedActorID: { - // init(transport:) and init(resolve:using:) may be synthesized as part of - // derived conformance to the DistributedActor protocol. - // If the target should conform to the DistributedActor protocol, check the - // conformance here to attempt synthesis. - // FIXME(distributed): invoke the requirement adding explicitly here - TypeChecker::addImplicitConstructors(target); - auto *distributedActorProto = - Context.getProtocol(KnownProtocolKind::DistributedActor); - (void)evaluateTargetConformanceTo(distributedActorProto); break; } } diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 166a1a96ddbac..2ee00b12efddd 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -35,150 +35,6 @@ using namespace swift; -/******************************************************************************/ -/************************ PROPERTY SYNTHESIS **********************************/ -/******************************************************************************/ - -static VarDecl* - lookupDistributedActorProperty(NominalTypeDecl *decl, DeclName name) { - assert(decl && "decl was null"); - auto &C = decl->getASTContext(); - - auto clazz = dyn_cast(decl); - if (!clazz) - return nullptr; - - auto refs = decl->lookupDirect(name); - if (refs.size() != 1) - return nullptr; - - auto var = dyn_cast(refs.front()); - if (!var) - return nullptr; - - Type expectedType = Type(); - if (name == C.Id_id) { - expectedType = getDistributedActorIDType(decl); - } else if (name == C.Id_actorSystem) { - expectedType = getDistributedActorSystemType(decl); - } else { - llvm_unreachable("Unexpected distributed actor property lookup!"); - } - if (!expectedType) - return nullptr; - - if (!var->getInterfaceType()->isEqual(expectedType)) - return nullptr; - - return var; - } - -// Note: This would be nice to implement in DerivedConformanceDistributedActor, -// but we can't since those are lazily triggered and an implementation exists -// for the 'id' property because 'Identifiable.id' has an extension that impls -// it for ObjectIdentifier, and we have to instead emit this stored property. -// -// The "derived" mechanisms are not really geared towards emitting for -// what already has a witness. -static VarDecl *addImplicitDistributedActorIDProperty( - ClassDecl *nominal) { - if (!nominal || !nominal->isDistributedActor()) - return nullptr; - - auto &C = nominal->getASTContext(); - - // ==== Synthesize and add 'id' property to the actor decl - Type propertyType = getDistributedActorIDType(nominal); - if (!propertyType || propertyType->hasError()) - return nullptr; - - auto *propDecl = new (C) - VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, - SourceLoc(), C.Id_id, nominal); - propDecl->setImplicit(); - propDecl->setSynthesized(); - propDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); - propDecl->setInterfaceType(propertyType); - - auto propContextTy = nominal->mapTypeIntoContext(propertyType); - - Pattern *propPat = NamedPattern::createImplicit(C, propDecl, propContextTy); - propPat = TypedPattern::createImplicit(C, propPat, propContextTy); - - PatternBindingDecl *pbDecl = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, - nominal); - - // mark as nonisolated, allowing access to it from everywhere - propDecl->addAttribute(NonisolatedAttr::createImplicit(C)); - // mark as @_compilerInitialized, since we synthesize the initializing - // assignment during SILGen. - propDecl->addAttribute(new (C) CompilerInitializedAttr(/*IsImplicit=*/true)); - - // IMPORTANT: The `id` MUST be the first field of any distributed actor, - // because when we allocate remote proxy instances, we don't allocate memory - // for anything except the first two fields: id and actorSystem, so they - // MUST be those fields. - // - // Their specific order also matters, because it is enforced this way in IRGen - // and how we emit them in AST MUST match what IRGen expects or cross-module - // things could be using wrong offsets and manifest as reading trash memory on - // id or system accesses. - nominal->addMember(propDecl, /*hint=*/nullptr, /*insertAtHead=*/true); - nominal->addMember(pbDecl, /*hint=*/nullptr, /*insertAtHead=*/true); - return propDecl; -} - -static VarDecl *addImplicitDistributedActorActorSystemProperty( - ClassDecl *nominal) { - if (!nominal) - return nullptr; - if (!nominal->isDistributedActor()) - return nullptr; - - auto &C = nominal->getASTContext(); - - // ==== Synthesize and add 'actorSystem' property to the actor decl - Type propertyType = getDistributedActorSystemType(nominal); - - auto *propDecl = new (C) - VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, - SourceLoc(), C.Id_actorSystem, nominal); - propDecl->setImplicit(); - propDecl->setSynthesized(); - propDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); - propDecl->setInterfaceType(propertyType); - - auto propContextTy = nominal->mapTypeIntoContext(propertyType); - - Pattern *propPat = NamedPattern::createImplicit(C, propDecl, propContextTy); - propPat = TypedPattern::createImplicit(C, propPat, propContextTy); - - PatternBindingDecl *pbDecl = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, - nominal); - - // mark as nonisolated, allowing access to it from everywhere - propDecl->addAttribute(NonisolatedAttr::createImplicit(C)); - - auto idProperty = nominal->getDistributedActorIDProperty(); - // If the id was not yet synthesized, we need to ensure that eventually - // the order of fields will be: id, actorSystem (because IRGen needs the - // layouts to match with the AST we produce). We do this by inserting FIRST, - // and then as the ID gets synthesized, it'll also force FIRST and therefore - // the order will be okey -- ID and then system. - auto insertAtHead = idProperty == nullptr; - - // IMPORTANT: The `id` MUST be the first field of any distributed actor. - // So we find the property and add the system AFTER it using the hint. - // - // If the `id` was not synthesized yet, we'll end up inserting at head, - // but the id synthesis will force itself to be FIRST anyway, so it works out. - nominal->addMember(propDecl, /*hint=*/idProperty, /*insertAtHead=*/insertAtHead); - nominal->addMember(pbDecl, /*hint=*/idProperty, /*insertAtHead=*/insertAtHead); - return propDecl; -} - /******************************************************************************/ /*********************** DISTRIBUTED THUNK SYNTHESIS **************************/ /******************************************************************************/ @@ -879,7 +735,7 @@ static bool canSynthesizeDistributedThunk(AbstractFunctionDecl *distributedTarge auto serializationTy = getDistributedActorSerializationType(distributedTarget->getDeclContext()); - return serializationTy && !serializationTy->hasDependentMember(); + return !serializationTy->hasError() && !serializationTy->hasDependentMember(); } /******************************************************************************/ @@ -944,79 +800,121 @@ FuncDecl *GetDistributedThunkRequest::evaluate(Evaluator &evaluator, llvm_unreachable("Unable to synthesize distributed thunk"); } -VarDecl *GetDistributedActorIDPropertyRequest::evaluate( - Evaluator &evaluator, NominalTypeDecl *actor) const { - if (!actor->isDistributedActor()) +static VarDecl *lookupDistributedActorProperty(NominalTypeDecl *decl, + DeclName name) { + VarDecl *result = nullptr; + for (auto *ref : decl->lookupDirect(name)) { + auto *prop = dyn_cast(ref); + if (!prop || prop->getDeclContext() != decl) + continue; + + if (!result) { + result = prop; + continue; + } return nullptr; + } + return result; +} - auto &C = actor->getASTContext(); - +VarDecl * +GetDistributedActorIDPropertyRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *nominal) const { // not via `ensureDistributedModuleLoaded` to avoid generating a warning, // we won't be emitting the offending decl after all. + auto &C = nominal->getASTContext(); if (!C.getLoadedModule(C.Id_Distributed)) return nullptr; - auto classDecl = dyn_cast(actor); - if (!classDecl) + if (!isa(nominal) || !nominal->isDistributedActor()) return nullptr; - // We may enter this request multiple times, e.g. in multi-file projects, - // so in order to avoid synthesizing a property many times, first perform - // a lookup and return if it already exists. - if (auto existingProp = lookupDistributedActorProperty(classDecl, C.Id_id)) { - return existingProp; - } + // If we're in a deserialized module or swift interface we expect to be able + // to find this through name lookup. + auto *DC = nominal->getDeclContext(); + if (!DC->getParentSourceFile() || DC->isInSwiftinterface()) + return lookupDistributedActorProperty(nominal, C.Id_id); + + // ==== Synthesize and add 'id' property to the actor decl + auto *propDecl = new (C) VarDecl(/*IsStatic*/ false, VarDecl::Introducer::Let, + SourceLoc(), C.Id_id, nominal); + propDecl->setImplicit(); + propDecl->setSynthesized(); + propDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); + + Pattern *propPat = NamedPattern::createImplicit(C, propDecl); - return addImplicitDistributedActorIDProperty(classDecl); + PatternBindingDecl *pbDecl = PatternBindingDecl::createImplicit( + C, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, nominal); + + // mark as nonisolated, allowing access to it from everywhere + propDecl->addAttribute(NonisolatedAttr::createImplicit(C)); + // mark as @_compilerInitialized, since we synthesize the initializing + // assignment during SILGen. + propDecl->addAttribute(new (C) CompilerInitializedAttr(/*IsImplicit=*/true)); + + // IMPORTANT: The `id` MUST be the first field of any distributed actor, + // because when we allocate remote proxy instances, we don't allocate memory + // for anything except the first two fields: id and actorSystem, so they + // MUST be those fields. + // + // Their specific order also matters, because it is enforced this way in IRGen + // and how we emit them in AST MUST match what IRGen expects or cross-module + // things could be using wrong offsets and manifest as reading trash memory on + // id or system accesses. + nominal->addMember(propDecl, /*hint=*/nullptr, /*insertAtHead=*/true); + nominal->addMember(pbDecl, /*hint=*/nullptr, /*insertAtHead=*/true); + return propDecl; } VarDecl *GetDistributedActorSystemPropertyRequest::evaluate( Evaluator &evaluator, NominalTypeDecl *nominal) const { - auto &C = nominal->getASTContext(); - - auto DAS = C.getDistributedActorSystemDecl(); - // not via `ensureDistributedModuleLoaded` to avoid generating a warning, // we won't be emitting the offending decl after all. + auto &C = nominal->getASTContext(); if (!C.getLoadedModule(C.Id_Distributed)) return nullptr; - if (!nominal->isDistributedActor()) + if (!isa(nominal) || !nominal->isDistributedActor()) return nullptr; - if (auto proto = dyn_cast(nominal)) { - auto DistributedActorProto = C.getDistributedActorDecl(); - for (auto system : DistributedActorProto->lookupDirect(C.Id_actorSystem)) { - if (auto var = dyn_cast(system)) { - auto conformance = checkConformance( - proto->mapTypeIntoContext(var->getInterfaceType()), - DAS); + // If we're in a deserialized module or swift interface we expect to be able + // to find this through name lookup. + auto *DC = nominal->getDeclContext(); + if (!DC->getParentSourceFile() || DC->isInSwiftinterface()) + return lookupDistributedActorProperty(nominal, C.Id_actorSystem); - if (conformance.isInvalid()) - continue; + // ==== Synthesize and add 'actorSystem' property to the actor decl + auto *propDecl = new (C) VarDecl(/*IsStatic*/ false, VarDecl::Introducer::Let, + SourceLoc(), C.Id_actorSystem, nominal); + propDecl->setImplicit(); + propDecl->setSynthesized(); + propDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); - return var; - } - } + Pattern *propPat = NamedPattern::createImplicit(C, propDecl); - return nullptr; - } + PatternBindingDecl *pbDecl = PatternBindingDecl::createImplicit( + C, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, nominal); - auto classDecl = dyn_cast(nominal); - if (!classDecl) - return nullptr; + // mark as nonisolated, allowing access to it from everywhere + propDecl->addAttribute(NonisolatedAttr::createImplicit(C)); - // We may be triggered after synthesis was handled via `DerivedConformances`, - // in which case we should locate the existing property, rather than add - // another one. Generally derived conformances are triggered early and are right - // but for some reason sometimes we get a request before synthesis was triggered - // there... so this is to workaround that issue, and ensure we're always - // synthesising correctly, regardless of entry-point. - if (auto existingProp = lookupDistributedActorProperty(classDecl, C.Id_actorSystem)) { - return existingProp; - } + auto idProperty = nominal->getDistributedActorIDProperty(); + // If the id was not yet synthesized, we need to ensure that eventually + // the order of fields will be: id, actorSystem (because IRGen needs the + // layouts to match with the AST we produce). We do this by inserting FIRST, + // and then as the ID gets synthesized, it'll also force FIRST and therefore + // the order will be okey -- ID and then system. + auto insertAtHead = idProperty == nullptr; - return addImplicitDistributedActorActorSystemProperty(classDecl); + // IMPORTANT: The `id` MUST be the first field of any distributed actor. + // So we find the property and add the system AFTER it using the hint. + // + // If the `id` was not synthesized yet, we'll end up inserting at head, + // but the id synthesis will force itself to be FIRST anyway, so it works out. + nominal->addMember(propDecl, /*hint=*/idProperty, insertAtHead); + nominal->addMember(pbDecl, /*hint=*/idProperty, insertAtHead); + return propDecl; } NormalProtocolConformance *GetDistributedActorImplicitCodableRequest::evaluate( diff --git a/lib/Sema/DerivedConformance/DerivedConformance.cpp b/lib/Sema/DerivedConformance/DerivedConformance.cpp index 216c3f6f18ad3..2b8df914ddcf6 100644 --- a/lib/Sema/DerivedConformance/DerivedConformance.cpp +++ b/lib/Sema/DerivedConformance/DerivedConformance.cpp @@ -99,8 +99,6 @@ bool DerivedConformance::derivesProtocolConformance( 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) @@ -353,14 +351,6 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, } } - // 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; } diff --git a/lib/Sema/DerivedConformance/DerivedConformance.h b/lib/Sema/DerivedConformance/DerivedConformance.h index 39c8110c5c19f..cf98052613047 100644 --- a/lib/Sema/DerivedConformance/DerivedConformance.h +++ b/lib/Sema/DerivedConformance/DerivedConformance.h @@ -324,10 +324,6 @@ class DerivedConformance { /// \returns the derived member, which will also be added to the type. ValueDecl *deriveDecodable(ValueDecl *requirement); - /// Identifiable may need to have the `ID` type witness synthesized explicitly - static bool canDeriveIdentifiable(NominalTypeDecl *nominal, - DeclContext *dc); - /// Whether we can derive the given DistributedActor requirement in the given context. static bool canDeriveDistributedActor(NominalTypeDecl *nominal, DeclContext *dc); diff --git a/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp b/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp index 88a9358d45fca..45682a0864894 100644 --- a/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp +++ b/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp @@ -29,19 +29,6 @@ using namespace swift; -bool DerivedConformance::canDeriveIdentifiable( - NominalTypeDecl *nominal, DeclContext *dc) { - // we only synthesize for concrete 'distributed actor' decls (which are class) - if (!isa(nominal)) - return false; - - auto &C = nominal->getASTContext(); - if (!C.getLoadedModule(C.Id_Distributed)) - return false; - - return nominal->isDistributedActor(); -} - bool DerivedConformance::canDeriveDistributedActor( NominalTypeDecl *nominal, DeclContext *dc) { auto &C = nominal->getASTContext(); @@ -90,9 +77,6 @@ static FuncDecl *deriveDistributedActor_resolve(DerivedConformance &derived) { auto idType = getDistributedActorIDType(decl); auto actorSystemType = getDistributedActorSystemType(decl); - if (!idType || !actorSystemType) - return nullptr; - // (id: Self.ID, using system: Self.ActorSystem) auto *params = ParameterList::create( C, @@ -451,77 +435,6 @@ static FuncDecl *deriveDistributedActorSystem_invokeHandlerOnReturn( return funcDecl; } -/******************************************************************************/ -/******************************* PROPERTIES ***********************************/ -/******************************************************************************/ - -static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) { - assert(derived.Nominal->isDistributedActor()); - auto &C = derived.Context; - - // ``` - // nonisolated let id: Self.ID // Self.ActorSystem.ActorID - // ``` - auto propertyType = getDistributedActorIDType(derived.Nominal); - - VarDecl *propDecl; - PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( - DerivedConformance::SynthesizedIntroducer::Let, C.Id_id, propertyType, - /*isStatic=*/false, /*isFinal=*/true); - - // mark as nonisolated, allowing access to it from everywhere - propDecl->addAttribute(NonisolatedAttr::createImplicit(C)); - - derived.addMemberToConformanceContext(pbDecl, /*insertAtHead=*/true); - derived.addMemberToConformanceContext(propDecl, /*insertAtHead=*/true); - return propDecl; -} - -static ValueDecl *deriveDistributedActor_actorSystem( - DerivedConformance &derived) { - auto &C = derived.Context; - - auto classDecl = dyn_cast(derived.Nominal); - assert(classDecl && derived.Nominal->isDistributedActor()); - - if (!C.getLoadedModule(C.Id_Distributed)) - return nullptr; - - // ``` - // nonisolated let actorSystem: ActorSystem - // ``` - // (no need for @actorIndependent because it is an immutable let) - auto propertyType = getDistributedActorSystemType(classDecl); - - VarDecl *propDecl; - PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( - DerivedConformance::SynthesizedIntroducer::Let, C.Id_actorSystem, - propertyType, /*isStatic=*/false, /*isFinal=*/true); - - // mark as nonisolated, allowing access to it from everywhere - propDecl->addAttribute(NonisolatedAttr::createImplicit(C)); - - // IMPORTANT: `id` MUST be the first field of a distributed actor, and - // `actorSystem` MUST be the second field, because for a remote instance - // we don't allocate memory after those two fields, so their order is very - // important. The `hint` below makes sure the system is inserted right after. - if (auto id = derived.Nominal->getDistributedActorIDProperty()) { - derived.addMemberToConformanceContext(propDecl, /*hint=*/id); - derived.addMemberToConformanceContext(pbDecl, /*hint=*/id); - } else { - // `id` will be synthesized next, and will insert at head, - // so in order for system to be SECOND (as it must be), - // we'll insert at head right now and as id gets synthesized we'll get - // the correct order: id, actorSystem. - derived.addMemberToConformanceContext(propDecl, /*insertAtHead=*/true); - derived.addMemberToConformanceContext(pbDecl, /*insertAtHead==*/true); - } - - return propDecl; -} - /******************************************************************************/ /***************************** ASSOC TYPES ************************************/ /******************************************************************************/ @@ -558,26 +471,6 @@ deriveDistributedActorType_ActorSystem( return defaultDistributedActorSystemTypeDecl->getDeclaredInterfaceType(); } -static Type -deriveDistributedActorType_ID( - DerivedConformance &derived) { - if (!derived.Nominal->isDistributedActor()) - return nullptr; - - // Look for a type DefaultDistributedActorSystem within the parent context. - auto systemTy = getDistributedActorSystemType(derived.Nominal); - - // There is no known actor system type, so fail to synthesize. - if (!systemTy || systemTy->hasError()) - return nullptr; - - if (auto systemNominal = systemTy->getAnyNominal()) { - return getDistributedActorSystemActorIDType(systemNominal); - } - - return nullptr; -} - static Type deriveDistributedActorType_SerializationRequirement( DerivedConformance &derived) { @@ -849,13 +742,8 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) { if (auto var = dyn_cast(requirement)) { ValueDecl *derivedValue = nullptr; - if (var->getName() == Context.Id_id) { - derivedValue = deriveDistributedActor_id(*this); - } else if (var->getName() == Context.Id_actorSystem) { - derivedValue = deriveDistributedActor_actorSystem(*this); - } else if (var->getName() == Context.Id_unownedExecutor) { + if (var->getName() == Context.Id_unownedExecutor) derivedValue = deriveDistributedActor_unownedExecutor(*this); - } if (derivedValue) { assertRequiredSynthesizedPropertyOrder(Context, Nominal); @@ -889,10 +777,6 @@ std::pair DerivedConformance::deriveDistributedActor( deriveDistributedActorType_SerializationRequirement(*this), nullptr); } - if (assocType->getName() == Context.Id_ID) { - return std::make_pair(deriveDistributedActorType_ID(*this), nullptr); - } - Context.Diags.diagnose(assocType->getLoc(), diag::broken_distributed_actor_requirement); return std::make_pair(Type(), nullptr); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 7856a8927541f..24538a19b4b93 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -7852,8 +7852,7 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) { // The synthesized "id" and "actorSystem" are the only exceptions, // because the implementation mirrors them. if (nominal->isDistributedActor() && - !(var->getName() == Ctx.Id_id || - var->getName() == Ctx.Id_actorSystem)) { + !var->isSpecialDistributedProperty()) { diagnoseAndRemoveAttr(attr, diag::nonisolated_distributed_actor_storage); return; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index f64ef9f8efc39..7d97e43388021 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2905,6 +2905,10 @@ static ArrayRef evaluateMembersRequest( ResolveImplicitMemberRequest{nominal, ImplicitMemberAction::ResolveCodingKeys}, {}); + + // Synthesize distributed actor 'id' and 'actorSystem' if needed. + (void)nominal->getDistributedActorIDProperty(); + (void)nominal->getDistributedActorSystemProperty(); } // Expand synthesized member macros. @@ -2940,14 +2944,11 @@ static ArrayRef evaluateMembersRequest( if (auto *vd = dyn_cast(member)) { // Add synthesized members to a side table and sort them by their mangled // name, since they could have been added to the class in any order. - if (vd->isSynthesized() && - // FIXME: IRGen requires the distributed actor synthesized - // properties to be in a specific order that is different - // from ordering by their mangled name, so preserve the order - // they were added in. - !(nominal && - (vd == nominal->getDistributedActorIDProperty() || - vd == nominal->getDistributedActorSystemProperty()))) { + // FIXME: IRGen requires the distributed actor synthesized properties to + // be in a specific order that is different from ordering by their + // mangled name, so preserve the order + // they were added in. + if (vd->isSynthesized() && !vd->isSpecialDistributedProperty()) { synthesizedMembers.add(vd); return; } diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index cbe192dbb3aac..f3f692a166281 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -461,13 +461,6 @@ static void installCodingKeysIfNecessary(NominalTypeDecl *NTD) { (void)evaluateOrDefault(NTD->getASTContext().evaluator, req, {}); } -// TODO(distributed): same ugly hack as Codable does... -static void installDistributedActorIfNecessary(NominalTypeDecl *NTD) { - auto req = - ResolveImplicitMemberRequest{NTD, ImplicitMemberAction::ResolveDistributedActor}; - (void)evaluateOrDefault(NTD->getASTContext().evaluator, req, {}); -} - // Check for static properties that produce empty option sets // using a rawValue initializer with a value of '0' static void checkForEmptyOptionSet(const VarDecl *VD) { @@ -1071,8 +1064,15 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current, return req->getName() == VD->getName(); }); } - declToDiagnose->diagnose(diag::invalid_redecl_implicit, current, - isProtocolRequirement, other); + auto *conflictDecl = current == declToDiagnose ? other : current; + if (conflictDecl->isSpecialDistributedProperty()) { + declToDiagnose->diagnose( + diag::distributed_actor_user_defined_special_property, + other->getName()); + } else { + declToDiagnose->diagnose(diag::invalid_redecl_implicit, current, + isProtocolRequirement, other); + } // Emit a specialized note if the one of the declarations is // the backing storage property ('_foo') or projected value @@ -3082,7 +3082,6 @@ class DeclChecker : public DeclVisitor { TypeChecker::addImplicitConstructors(SD); installCodingKeysIfNecessary(SD); - installDistributedActorIfNecessary(SD); TypeChecker::checkDeclAttributes(SD); diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index 54ca3fd900676..0be7c0530a426 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -410,12 +410,8 @@ static bool checkDistributedTargetResultType( bool diagnose) { auto &C = valueDecl->getASTContext(); - if (serializationRequirement && serializationRequirement->hasError()) { - return false; - } - if (!serializationRequirement || serializationRequirement->hasError()) { + if (serializationRequirement->hasError()) return false; // error of the type would be diagnosed elsewhere - } Type resultType; if (auto func = dyn_cast(valueDecl)) { @@ -434,11 +430,8 @@ static bool checkDistributedTargetResultType( SmallVector serializationRequirements; // Collect extra "SerializationRequirement: SomeProtocol" requirements - if (serializationRequirement && !serializationRequirement->hasError()) { - auto srl = serializationRequirement->getExistentialLayout(); - llvm::copy(srl.getProtocols(), - std::back_inserter(serializationRequirements)); - } + auto srl = serializationRequirement->getExistentialLayout(); + llvm::copy(srl.getProtocols(), std::back_inserter(serializationRequirements)); auto isCodableRequirement = checkDistributedSerializationRequirementIsExactlyCodable( @@ -470,7 +463,7 @@ static bool checkDistributedTargetResultType( } } - return false; + return false; } bool swift::checkDistributedActorSystem(const NominalTypeDecl *system) { @@ -551,7 +544,7 @@ bool CheckDistributedFunctionRequest::evaluate( for (auto param: *func->getParameters()) { // --- Check the parameter conforming to serialization requirements - if (serializationReqType && !serializationReqType->hasError()) { + if (!serializationReqType->hasError()) { // If the requirement is exactly `Codable` we diagnose it ia bit nicer. auto serializationRequirementIsCodable = checkDistributedSerializationRequirementIsExactlyCodable( @@ -665,39 +658,6 @@ bool swift::checkDistributedActorProperty(VarDecl *var, bool diagnose) { return false; } -void swift::checkDistributedActorProperties(const NominalTypeDecl *decl) { - auto &C = decl->getASTContext(); - - if (!decl->getDeclContext()->getParentSourceFile()) { - // Don't diagnose when checking without source file (e.g. from module, importer etc). - return; - } - - if (decl->getDeclContext()->isInSwiftinterface()) { - // Don't diagnose properties in swiftinterfaces. - return; - } - - if (isa(decl)) { - // protocols don't matter for stored property checking - return; - } - - for (auto member : decl->getMembers()) { - if (auto prop = dyn_cast(member)) { - if (prop->isSynthesized()) - continue; - - auto id = prop->getName(); - if (id == C.Id_actorSystem || id == C.Id_id) { - prop->diagnose(diag::distributed_actor_user_defined_special_property, - id); - prop->setInvalid(); - } - } - } -} - // ==== ------------------------------------------------------------------------ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal) { @@ -763,16 +723,6 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal } } } - - // ==== Properties - checkDistributedActorProperties(nominal); - // --- Synthesize the 'id' property here rather than via derived conformance - // because the 'DerivedConformanceDistributedActor' won't trigger for 'id' - // because it has a default impl via 'Identifiable' (ObjectIdentifier) - // which we do not want. - // Also, the 'id' var must be added before the 'actorSystem'. - // See NOTE (id-before-actorSystem) for more details. - (void)nominal->getDistributedActorIDProperty(); } bool TypeChecker::checkDistributedFunc(FuncDecl *func) { @@ -861,7 +811,7 @@ GetDistributedActorInvocationDecoderRequest::evaluate(Evaluator &evaluator, auto &ctx = actor->getASTContext(); auto decoderTy = getAssociatedTypeOfDistributedSystemOfActor( actor, ctx.Id_InvocationDecoder); - return decoderTy ? decoderTy->getAnyNominal() : nullptr; + return decoderTy->getAnyNominal(); } FuncDecl * @@ -886,7 +836,7 @@ GetDistributedActorConcreteArgumentDecodingMethodRequest::evaluate( auto serializationTy = getAssociatedTypeOfDistributedSystemOfActor( actor, ctx.Id_SerializationRequirement); - if (!serializationTy || !serializationTy->is()) + if (!serializationTy->is()) return nullptr; SmallVector serializationRequirements; diff --git a/lib/Sema/TypeCheckDistributed.h b/lib/Sema/TypeCheckDistributed.h index 18f8c0b391d56..20864e01698eb 100644 --- a/lib/Sema/TypeCheckDistributed.h +++ b/lib/Sema/TypeCheckDistributed.h @@ -37,9 +37,6 @@ class NominalTypeDecl; // Diagnose an error if the Distributed module is not loaded. bool ensureDistributedModuleLoaded(const ValueDecl *decl); -/// Check for illegal property declarations (e.g. re-declaring transport or id) -void checkDistributedActorProperties(const NominalTypeDecl *decl); - /// Type-check additional ad-hoc protocol requirements. /// Ad-hoc requirements are protocol requirements currently not expressible /// in the Swift type-system. diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 9f4bf072c2d25..615275a6d29dc 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4712,15 +4712,6 @@ deriveProtocolRequirement(const NormalProtocolConformance *Conformance, case KnownDerivableProtocolKind::Differentiable: return derived.deriveDifferentiable(Requirement); - case KnownDerivableProtocolKind::Identifiable: - if (derived.Nominal->isDistributedActor()) { - return derived.deriveDistributedActor(Requirement); - } else { - // No synthesis is required for other types; we should only end up - // attempting synthesis if the nominal was a distributed actor. - llvm_unreachable("Identifiable is synthesized for distributed actors"); - } - case KnownDerivableProtocolKind::DistributedActor: return derived.deriveDistributedActor(Requirement); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 4bbb1e5f49477..abee23507a823 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -29,6 +29,7 @@ #include "swift/AST/DeclExportabilityVisitor.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/DistributedDecl.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Initializer.h" @@ -212,30 +213,10 @@ static void enumerateStoredPropertiesAndMissing( // If we have a distributed actor, find the id and actorSystem // properties. We always want them first, and in a specific // order. - if (decl->isDistributedActor()) { - VarDecl *distributedActorId = nullptr; - VarDecl *distributedActorSystem = nullptr; - ASTContext &ctx = decl->getASTContext(); - for (auto *member : implDecl->getMembers()) { - if (auto *var = dyn_cast(member)) { - if (!var->isStatic() && var->hasStorage()) { - if (var->getName() == ctx.Id_id) { - distributedActorId = var; - } else if (var->getName() == ctx.Id_actorSystem) { - distributedActorSystem = var; - } - } - - if (distributedActorId && distributedActorSystem) - break; - } - } - - if (distributedActorId) - addStoredProperty(distributedActorId); - if (distributedActorSystem) - addStoredProperty(distributedActorSystem); - } + if (auto idVar = decl->getDistributedActorIDProperty()) + addStoredProperty(idVar); + if (auto actorSystemVar = decl->getDistributedActorSystemProperty()) + addStoredProperty(actorSystemVar); for (auto *member : implDecl->getMembers()) { if (auto *var = dyn_cast(member)) { @@ -406,16 +387,58 @@ MemberwiseInitPropertiesRequest::evaluate(Evaluator &evaluator, return decl->getASTContext().AllocateCopy(results); } +static Type getLazyInterfaceTypeForSynthesizedVar(VarDecl *var) { + // For DistributedActor, the `id` and `actorSystem` properties have their + // types computed lazily. + if (auto distPropKind = var->isSpecialDistributedProperty()) { + auto *NTD = var->getDeclContext()->getSelfNominalTypeDecl(); + ASSERT(NTD); + switch (distPropKind.value()) { + case SpecialDistributedProperty::Id: + return getDistributedActorIDType(NTD); + case SpecialDistributedProperty::ActorSystem: + return getDistributedActorSystemType(NTD); + } + llvm_unreachable("Unhandled case in switch!"); + } + return Type(); +} + +static Pattern * +getLazilySynthesizedPattern(PatternBindingDecl *PBD, Pattern *P) { + // Check to see if we have a pattern that binds a single synthesized var. + auto *NP = dyn_cast(P->getSemanticsProvidingPattern()); + if (!NP) + return P; + + auto *var = NP->getDecl(); + if (!var->isSynthesized()) + return P; + + auto interfaceTy = getLazyInterfaceTypeForSynthesizedVar(var); + if (!interfaceTy) + return P; + + auto *DC = var->getDeclContext(); + auto &ctx = DC->getASTContext(); + auto patternTy = DC->mapTypeIntoContext(interfaceTy); + return TypedPattern::createImplicit(ctx, P, patternTy); +} + /// Validate the \c entryNumber'th entry in \c binding. const PatternBindingEntry *PatternBindingEntryRequest::evaluate( Evaluator &eval, PatternBindingDecl *binding, unsigned entryNumber) const { const auto &pbe = binding->getPatternList()[entryNumber]; auto &Context = binding->getASTContext(); + // Resolve a lazily synthesized pattern if necessary. + auto *pattern = binding->getPattern(entryNumber); + pattern = getLazilySynthesizedPattern(binding, pattern); + binding->setPattern(entryNumber, pattern); + // Resolve the pattern. - auto *pattern = TypeChecker::resolvePattern(binding->getPattern(entryNumber), - binding->getDeclContext(), - /*isStmtCondition*/ true); + pattern = TypeChecker::resolvePattern(pattern, binding->getDeclContext(), + /*isStmtCondition*/ true); if (!pattern) { binding->setInvalid(); binding->getPattern(entryNumber)->setType(ErrorType::get(Context)); diff --git a/test/Distributed/Macros/distributed_macro_expansion_DistributedProtocol_errors.swift b/test/Distributed/Macros/distributed_macro_expansion_DistributedProtocol_errors.swift index fe1278ebba830..3a2928a5fcd98 100644 --- a/test/Distributed/Macros/distributed_macro_expansion_DistributedProtocol_errors.swift +++ b/test/Distributed/Macros/distributed_macro_expansion_DistributedProtocol_errors.swift @@ -26,7 +26,7 @@ distributed actor Caplin { typealias ActorSystem = FakeActorSystem } -@Resolvable // expected-note 4{{in expansion of macro 'Resolvable' on protocol 'Fail' here}} +@Resolvable // expected-note 5{{in expansion of macro 'Resolvable' on protocol 'Fail' here}} protocol Fail: DistributedActor { distributed func method() -> String } @@ -36,6 +36,7 @@ expected-expansion@-2:2{{ expected-note@1:13{{you can provide a module-wide default actor system by declaring:}} expected-error@1:19{{type '$Fail' does not conform to protocol 'DistributedActor'}} expected-note@1:19{{add stubs for conformance}} + expected-error@1:19{{type '$Fail' does not conform to protocol 'Identifiable'}} }} */ diff --git a/test/Distributed/distributed_actor_system_missing.swift b/test/Distributed/distributed_actor_system_missing.swift index b5129ce8a5481..b25f3e90dd042 100644 --- a/test/Distributed/distributed_actor_system_missing.swift +++ b/test/Distributed/distributed_actor_system_missing.swift @@ -11,6 +11,7 @@ distributed actor DA { // expected-error@-2 {{type 'DA' does not conform to protocol 'DistributedActor'}} // expected-note@-3 {{add stubs for conformance}} // expected-note@-4{{you can provide a module-wide default actor system by declaring:}} + // expected-error@-5 {{type 'DA' does not conform to protocol 'Identifiable'}} // Note to add the typealias is diagnosed on the protocol: // _Distributed.DistributedActor:3:20: note: diagnostic produced elsewhere: protocol requires nested type 'ActorSystem'; do you want to add it? @@ -24,6 +25,7 @@ distributed actor Server { // expected-error 2 {{distributed actor 'Server' does // expected-error@-1 {{type 'Server' does not conform to protocol 'DistributedActor'}} // expected-note@-2{{you can provide a module-wide default actor system by declaring:}} // expected-note@-3 {{add stubs for conformance}} + // expected-error@-4 {{type 'Server' does not conform to protocol 'Identifiable'}} typealias ActorSystem = DoesNotExistDataSystem // expected-error@-1{{cannot find type 'DoesNotExistDataSystem' in scope}} typealias SerializationRequirement = any Codable diff --git a/test/Distributed/distributed_actor_system_missing_system_type.swift b/test/Distributed/distributed_actor_system_missing_system_type.swift index 9984db3ea7a68..fa80ffcd759f0 100644 --- a/test/Distributed/distributed_actor_system_missing_system_type.swift +++ b/test/Distributed/distributed_actor_system_missing_system_type.swift @@ -14,6 +14,7 @@ distributed actor MyActor { // expected-note@-2{{you can provide a module-wide default actor system by declaring:}} // expected-error@-3{{type 'MyActor' does not conform to protocol 'DistributedActor'}} // expected-note@-4 {{add stubs for conformance}} + // expected-error@-5 {{type 'MyActor' does not conform to protocol 'Identifiable'}} distributed var foo: String { return "xyz" @@ -25,6 +26,7 @@ distributed actor BadSystemActor { // expected-note@-2{{you can provide a module-wide default actor system by declaring:}} // expected-error@-3{{type 'BadSystemActor' does not conform to protocol 'DistributedActor'}} // expected-note@-4 {{add stubs for conformance}} + // expected-error@-5 {{type 'BadSystemActor' does not conform to protocol 'Identifiable'}} // This system does not exist: but we should not crash, but just diagnose about it: typealias ActorSystem = ClusterSystem // expected-error{{cannot find type 'ClusterSystem' in scope}} diff --git a/test/Distributed/distributed_actor_system_missing_type_no_crash.swift b/test/Distributed/distributed_actor_system_missing_type_no_crash.swift index b6f52d384c478..65279eeff3dc4 100644 --- a/test/Distributed/distributed_actor_system_missing_type_no_crash.swift +++ b/test/Distributed/distributed_actor_system_missing_type_no_crash.swift @@ -13,6 +13,7 @@ distributed actor Fish { // expected-error@-3{{type 'Fish' does not conform to protocol 'DistributedActor'}} // expected-note@-4{{you can provide a module-wide default actor system by declaring:\ntypealias DefaultDistributedActorSystem = <#ConcreteActorSystem#>}} // expected-note@-5 {{add stubs for conformance}} + // expected-error@-6 {{type 'Fish' does not conform to protocol 'Identifiable'}} distributed func tell(_ text: String, by: Fish) { // What would the fish say, if it could talk? diff --git a/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift b/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift index c87c5b3b3aa69..6bd1953570e99 100644 --- a/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift +++ b/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift @@ -54,6 +54,7 @@ distributed actor ProtocolWithChecksSeqReqDA_MissingSystem: ProtocolWithChecksSe // // expected-error@-6{{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'DistributedActor'}} // expected-note@-7 {{add stubs for conformance}} + // expected-error@-8 {{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'Identifiable'}} // Entire conformance is doomed, so we didn't proceed to checking the functions; that's fine distributed func testAT() async throws -> NotCodable { .init() } diff --git a/test/Distributed/rdar162800185.swift b/test/Distributed/rdar162800185.swift new file mode 100644 index 0000000000000..8d8e276b65ebd --- /dev/null +++ b/test/Distributed/rdar162800185.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// Make sure we can resolve the conformance to DistributedActor from a secondary. +// RUN: %target-swift-frontend -c -module-name main -target %target-swift-5.7-abi-triple %t/a.swift %t/b.swift -o %/tmp +// RUN: %target-swift-frontend -c -module-name main -target %target-swift-5.7-abi-triple -primary-file %t/a.swift %t/b.swift -o %/tmp +// RUN: %target-swift-frontend -c -module-name main -target %target-swift-5.7-abi-triple %t/a.swift -primary-file %t/b.swift -o %/tmp + +// REQUIRES: concurrency +// REQUIRES: distributed + +//--- a.swift +import Distributed + +distributed actor A {} + +//--- b.swift +import Distributed + +func foo(_: T.Type) where T.ActorSystem == LocalTestingDistributedActorSystem {} +func bar() { foo(A.self) } diff --git a/test/decl/protocol/special/DistributedActor.swift b/test/decl/protocol/special/DistributedActor.swift index a3efe499a8390..f268a058497f2 100644 --- a/test/decl/protocol/special/DistributedActor.swift +++ b/test/decl/protocol/special/DistributedActor.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-ignore-unknown +// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-ignore-unknown -verify-ignore-unrelated // REQUIRES: concurrency // REQUIRES: distributed @@ -67,6 +67,29 @@ distributed actor D5: P1 { nonisolated distributed actor D6 {} // expected-error {{'nonisolated' modifier cannot be applied to this declaration}}{{1-13=}} +// Can't define `id` and `actorSystem` in an extension either. +distributed actor D7 {} +extension D7 { + nonisolated var id: String { fatalError() } + // expected-error@-1 {{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} + + nonisolated var actorSystem: OtherActorIdentity { fatalError() } + // expected-error@-1 {{property 'actorSystem' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} +} + +// FIXME: Unfortunately redeclaration checking is run after conformance checking +// so we also get a conformace error here. +distributed actor D8 {} // expected-error {{type 'D8' does not conform to protocol 'DistributedActor'}} +extension D8 { + nonisolated var id: ID { fatalError() } + // expected-error@-1 {{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} + // expected-note@-2 {{candidate exactly matches}} + + nonisolated var actorSystem: DefaultDistributedActorSystem { fatalError() } + // expected-error@-1 {{property 'actorSystem' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} + // expected-note@-2 {{candidate exactly matches}} +} + // ==== Tests ------------------------------------------------------------------ // Make sure the conformances have been added implicitly. diff --git a/validation-test/compiler_crashers_fixed/verificationFailure-94e552.swift b/validation-test/compiler_crashers_fixed/verificationFailure-94e552.swift new file mode 100644 index 0000000000000..26d3d09581371 --- /dev/null +++ b/validation-test/compiler_crashers_fixed/verificationFailure-94e552.swift @@ -0,0 +1,10 @@ +// {"kind":"emit-silgen","signature":"swift::verificationFailure(llvm::Twine const&, swift::SILInstruction const*, swift::SILArgument const*, llvm::function_ref)"} +// RUN: not %target-swift-frontend -emit-silgen %s +// REQUIRES: OS=macosx +import Distributed +distributed actor a { +} +extension a { + nonisolated var id: ActorSystem.ActorID { + } +}