diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 0ce342507dfc7..e5cb28ab9e382 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -348,11 +348,44 @@ class TypeDeclsFromWhereClauseRequest : ExtensionDecl *ext) const; }; +/// The owning decl for a given custom attribute, or a DeclContext for a +/// custom attribute in e.g a closure or inheritance clause. +class CustomAttrOwner final { + llvm::PointerUnion Owner; + +public: + CustomAttrOwner() : Owner(nullptr) {} + CustomAttrOwner(const Decl *D) : Owner(D) {} + CustomAttrOwner(DeclContext *DC) : Owner(DC) {} + + const Decl *getAsDecl() const { + return Owner.dyn_cast(); + } + + DeclContext *getDeclContext() const; + + friend bool operator==(const CustomAttrOwner &lhs, + const CustomAttrOwner &rhs) { + return lhs.Owner == rhs.Owner; + } + + friend bool operator!=(const CustomAttrOwner &lhs, + const CustomAttrOwner &rhs) { + return !(lhs == rhs); + } + + friend hash_code hash_value(const CustomAttrOwner &owner) { + return llvm::hash_value(owner.Owner); + } +}; + +void simple_display(llvm::raw_ostream &out, const CustomAttrOwner &owner); + /// Request the nominal type declaration to which the given custom /// attribute refers. class CustomAttrNominalRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -362,7 +395,7 @@ class CustomAttrNominalRequest : // Evaluation. NominalTypeDecl * - evaluate(Evaluator &evaluator, CustomAttr *attr, DeclContext *dc) const; + evaluate(Evaluator &evaluator, CustomAttr *attr, CustomAttrOwner owner) const; public: // Caching diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index cc2665001e14c..8e1ef2362533e 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -544,6 +544,10 @@ EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false) /// Enable @_lifetime attribute SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true) +/// Excludes 'private' properties with initial values from the memberwise +/// initializer. +EXPERIMENTAL_FEATURE(ExcludePrivateFromMemberwiseInit, false) + /// Allow macro based aliases to be imported into Swift EXPERIMENTAL_FEATURE(ImportMacroAliases, true) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 5cbfe78d72d38..17d75324cf6a5 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -574,7 +574,7 @@ Type Decl::getResolvedCustomAttrType(CustomAttr *attr) const { auto dc = getDeclContext(); auto *nominal = evaluateOrDefault( - getASTContext().evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); + getASTContext().evaluator, CustomAttrNominalRequest{attr, this}, nullptr); if (!nominal) return Type(); @@ -8431,10 +8431,22 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const { // If this is a stored property, and not a backing property in a case where // we only want to see the declared properties, it can be memberwise // initialized. - if (hasStorage() && preferDeclaredProperties && - isBackingStorageForDeclaredProperty(this)) - return false; - + if (hasStorage()) { + if (isBackingStorageForDeclaredProperty(this)) { + if (preferDeclaredProperties) + return false; + } else if (getASTContext().LangOpts.hasFeature(Feature::ExcludePrivateFromMemberwiseInit)) { + // Private variables with initial values are never memberwise initialized. + if (getFormalAccess() == AccessLevel::Private) { + if (hasInitialValue()) + return false; + if (auto *PBD = getParentPatternBinding()) { + if (PBD->isDefaultInitializable()) + return false; + } + } + } + } // If this is a computed property with `init` accessor, it's // memberwise initializable when it could be used to initialize // other stored properties. @@ -8696,10 +8708,9 @@ VarDecl::getAttachedPropertyWrapperTypeInfo(unsigned i) const { return PropertyWrapperTypeInfo(); auto attr = attrs[i]; - auto dc = getDeclContext(); ASTContext &ctx = getASTContext(); nominal = evaluateOrDefault( - ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); + ctx.evaluator, CustomAttrNominalRequest{attr, this}, nullptr); } if (!nominal) diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 0184fe7e940b2..68ec218e33ef0 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -127,6 +127,7 @@ UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault) UNINTERESTING_FEATURE(KeyPathWithMethodMembers) UNINTERESTING_FEATURE(ImportMacroAliases) UNINTERESTING_FEATURE(NoExplicitNonIsolated) +UNINTERESTING_FEATURE(ExcludePrivateFromMemberwiseInit) // TODO: Return true for inlinable function bodies with module selectors in them UNINTERESTING_FEATURE(ModuleSelector) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 4f785711cf9f1..5c4ed948f495e 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -3961,9 +3961,24 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c parsedGenericParams->getRAngleLoc()); } +static bool shouldPreferPropertyWrapperOverMacro(CustomAttrOwner owner) { + // If we have a VarDecl in a local context, prefer to look for a property + // wrapper if one exists. This is necessary since we don' properly support + // peer declarations in local contexts, so want to use a property wrapper if + // one exists. + if (auto *D = dyn_cast_or_null(owner.getAsDecl())) { + if (!isa(D) && D->getDeclContext()->isLocalContext()) + return true; + } + return false; +} + NominalTypeDecl * CustomAttrNominalRequest::evaluate(Evaluator &evaluator, - CustomAttr *attr, DeclContext *dc) const { + CustomAttr *attr, + CustomAttrOwner owner) const { + auto *dc = owner.getDeclContext(); + // Look for names at module scope, so we don't trigger name lookup for // nested scopes. At this point, we're looking to see whether there are // any suitable macros. @@ -3972,7 +3987,8 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, auto macroName = (macro) ? macro->getNameRef() : DeclNameRef(); auto macros = namelookup::lookupMacros(dc, moduleName, macroName, getAttachedMacroRoles()); - if (!macros.empty()) + auto shouldPreferPropWrapper = shouldPreferPropertyWrapperOverMacro(owner); + if (!macros.empty() && !shouldPreferPropWrapper) return nullptr; // Find the types referenced by the custom attribute. @@ -3992,6 +4008,15 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, auto nominals = resolveTypeDeclsToNominal(evaluator, ctx, decls.first, ResolveToNominalOptions(), modulesFound, anyObject); + if (shouldPreferPropWrapper) { + auto hasPropWrapper = llvm::any_of(nominals, [](NominalTypeDecl *NTD) { + return NTD->getAttrs().hasAttribute(); + }); + if (!macros.empty() && !hasPropWrapper) + return nullptr; + + ASSERT(macros.empty() && "now preferring property wrapper"); + } if (nominals.size() == 1 && !isa(nominals.front())) return nominals.front(); diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 956deb29ae982..407fc90d976b0 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -230,6 +230,26 @@ void ExtendedNominalRequest::writeDependencySink( tracker.addPotentialMember(value); } +//----------------------------------------------------------------------------// +// CustomAttrOwner +//----------------------------------------------------------------------------// + +DeclContext *CustomAttrOwner::getDeclContext() const { + if (auto *D = getAsDecl()) + return D->getDeclContext(); + + return Owner.dyn_cast(); +} + +void swift::simple_display(llvm::raw_ostream &out, + const CustomAttrOwner &owner) { + if (auto *D = owner.getAsDecl()) { + simple_display(out, D); + } else { + simple_display(out, owner.getDeclContext()); + } +} + //----------------------------------------------------------------------------// // Destructor computation. //----------------------------------------------------------------------------// diff --git a/lib/Basic/LangOptions.cpp b/lib/Basic/LangOptions.cpp index 114b81a20ae53..4d4f0bd345c7e 100644 --- a/lib/Basic/LangOptions.cpp +++ b/lib/Basic/LangOptions.cpp @@ -42,6 +42,8 @@ LangOptions::LangOptions() { #define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description) #include "swift/Basic/Features.def" + enableFeature(Feature::ExcludePrivateFromMemberwiseInit); + // Special case: remove macro support if the compiler wasn't built with a // host Swift. #if !SWIFT_BUILD_SWIFT_SYNTAX diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 4190a9ce40b1f..fc4a3dc010fd2 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -4588,7 +4588,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { // Figure out which nominal declaration this custom attribute refers to. auto *nominal = evaluateOrDefault( - Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); + Ctx.evaluator, CustomAttrNominalRequest{attr, D}, nullptr); if (!nominal) { if (attr->isInvalid()) @@ -8742,8 +8742,7 @@ static void forEachCustomAttribute( auto *mutableAttr = const_cast(attr); auto *nominal = evaluateOrDefault( - ctx.evaluator, - CustomAttrNominalRequest{mutableAttr, decl->getDeclContext()}, nullptr); + ctx.evaluator, CustomAttrNominalRequest{mutableAttr, decl}, nullptr); if (!nominal) continue; diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index e813d4edf700d..eeb953e044df6 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -311,16 +311,16 @@ VarDecl *GlobalActorInstanceRequest::evaluate( } std::optional> -swift::checkGlobalActorAttributes(SourceLoc loc, DeclContext *dc, +swift::checkGlobalActorAttributes(SourceLoc loc, CustomAttrOwner owner, ArrayRef attrs) { - ASTContext &ctx = dc->getASTContext(); + ASTContext &ctx = owner.getDeclContext()->getASTContext(); CustomAttr *globalActorAttr = nullptr; NominalTypeDecl *globalActorNominal = nullptr; for (auto attr : attrs) { // Figure out which nominal declaration this custom attribute refers to. auto *nominal = evaluateOrDefault(ctx.evaluator, - CustomAttrNominalRequest{attr, dc}, + CustomAttrNominalRequest{attr, owner}, nullptr); if (!nominal) @@ -352,11 +352,11 @@ std::optional> GlobalActorAttributeRequest::evaluate( Evaluator &evaluator, llvm::PointerUnion subject) const { - DeclContext *dc = nullptr; + CustomAttrOwner owner; DeclAttributes *declAttrs = nullptr; SourceLoc loc; if (auto decl = subject.dyn_cast()) { - dc = decl->getDeclContext(); + owner = decl; declAttrs = &decl->getAttrs(); // HACK: `getLoc`, when querying the attr from a serialized decl, // depending on deserialization order, may launch into arbitrary @@ -372,7 +372,7 @@ GlobalActorAttributeRequest::evaluate( loc = decl->getLoc(/* SerializedOK */ false); } else { auto closure = cast(subject); - dc = closure; + owner = closure; declAttrs = &closure->getAttrs(); loc = closure->getLoc(); } @@ -385,7 +385,7 @@ GlobalActorAttributeRequest::evaluate( } // Look for a global actor attribute. - auto result = checkGlobalActorAttributes(loc, dc, attrs); + auto result = checkGlobalActorAttributes(loc, owner, attrs); if (!result) return std::nullopt; diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 50b01165ba306..7deb56d25fa17 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -32,6 +32,7 @@ namespace swift { class AbstractFunctionDecl; class ConstructorDecl; +class CustomAttrOwner; class ActorIsolation; class AnyFunctionType; class ASTContext; @@ -609,7 +610,7 @@ bool diagnoseSendabilityErrorBasedOn( /// and perform any necessary resolution and diagnostics, returning the /// global actor attribute and type it refers to (or \c std::nullopt). std::optional> -checkGlobalActorAttributes(SourceLoc loc, DeclContext *dc, +checkGlobalActorAttributes(SourceLoc loc, CustomAttrOwner owner, ArrayRef attrs); /// Get the explicit global actor specified for a closure. diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index ed8dbea775570..e51fe485e9c7d 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -443,7 +443,7 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, auto mutableAttr = const_cast(attr); // Figure out which nominal declaration this custom attribute refers to. auto *nominal = evaluateOrDefault( - ctx.evaluator, CustomAttrNominalRequest{mutableAttr, dc}, nullptr); + ctx.evaluator, CustomAttrNominalRequest{mutableAttr, var}, nullptr); // If we didn't find a nominal type with a @propertyWrapper attribute, // skip this custom attribute. diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index ec0ecf94003c2..147c32c6758aa 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -184,7 +184,7 @@ AttachedResultBuilderRequest::evaluate(Evaluator &evaluator, auto mutableAttr = const_cast(attr); // Figure out which nominal declaration this custom attribute refers to. auto *nominal = evaluateOrDefault(ctx.evaluator, - CustomAttrNominalRequest{mutableAttr, dc}, + CustomAttrNominalRequest{mutableAttr, decl}, nullptr); if (!nominal) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index b49c5ba34a9cb..8193c3e773b81 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -35,6 +35,7 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/PackExpansionMatcher.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" diff --git a/test/attr/accessibility_print.swift b/test/attr/accessibility_print.swift index c4ac2b916e1bd..315b96b247729 100644 --- a/test/attr/accessibility_print.swift +++ b/test/attr/accessibility_print.swift @@ -53,7 +53,6 @@ public struct BE_PublicStructPrivateMembers { // CHECK: private{{(\*/)?}} var x private var x = 0 // CHECK: internal init() - // CHECK: private init(x: Int = 0) } // CHECK: {{^[}]}} // CHECK-LABEL: {{^}}fileprivate{{(\*/)?}} struct BF_FilePrivateStruct {