Skip to content

Commit 45683f6

Browse files
committed
[Sema] Exclude private initialized vars from memberwise initializer
Exclude properties with initial values from the memberwise initializer if they are less accessible than the most accessible property, up to `internal`. Introduce a compatibility overload that continues to include the same properties as before until the next language mode. This is gated behind the `ExcludePrivateFromMemberwiseInit` feature. rdar://122416579
1 parent e383766 commit 45683f6

20 files changed

+903
-111
lines changed

include/swift/AST/Decl.h

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ static_assert(uint8_t(SelfAccessKind::LastSelfAccessKind) <
273273
"Self Access Kind is too small to fit in SelfAccess kind bits. "
274274
"Please expand ");
275275

276+
enum class MemberwiseInitKind {
277+
/// The regular memberwise initializer.
278+
Regular,
279+
/// A compatibility memberwise initializer that includes all private
280+
/// initialized variables. This only exists for migration purposes, and will
281+
/// be removed in a future language mode.
282+
Compatibility
283+
};
284+
276285
enum class UsingSpecifier : uint8_t {
277286
MainActor,
278287
Nonisolated,
@@ -4634,9 +4643,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
46344643
ArrayRef<VarDecl *> getInitAccessorProperties() const;
46354644

46364645
/// Return a collection of all properties that will be part of the memberwise
4637-
/// initializer.
4638-
ArrayRef<VarDecl *> getMemberwiseInitProperties() const;
4639-
4646+
/// initializer.
4647+
ArrayRef<VarDecl *>
4648+
getMemberwiseInitProperties(MemberwiseInitKind initKind) const;
4649+
46404650
/// Establish a mapping between properties that could be iniitalized
46414651
/// via other properties by means of init accessors. This mapping is
46424652
/// one-to-many because we allow intersecting `initializes(...)`.
@@ -4679,11 +4689,11 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
46794689
}
46804690

46814691
/// Whether this declaration has a synthesized memberwise initializer.
4682-
bool hasMemberwiseInitializer() const;
4692+
bool hasMemberwiseInitializer(MemberwiseInitKind initKind) const;
46834693

46844694
/// Retrieves the synthesized memberwise initializer for this declaration,
46854695
/// or \c nullptr if it does not have one.
4686-
ConstructorDecl *getMemberwiseInitializer() const;
4696+
ConstructorDecl *getMemberwiseInitializer(MemberwiseInitKind initKind) const;
46874697

46884698
/// Retrieves the effective memberwise initializer for this declaration, or
46894699
/// \c nullptr if it does not have one.
@@ -6967,7 +6977,9 @@ class VarDecl : public AbstractStorageDecl {
69676977
/// actual declared property (which may or may not be considered "stored"
69686978
/// as the moment) to the backing storage property. Otherwise, the stored
69696979
/// backing property will be treated as the member-initialized property.
6970-
bool isMemberwiseInitialized(bool preferDeclaredProperties) const;
6980+
bool isMemberwiseInitialized(
6981+
MemberwiseInitKind initKind, bool preferDeclaredProperties,
6982+
std::optional<AccessLevel> minAccess = std::nullopt) const;
69716983

69726984
/// Return the range of semantics attributes attached to this VarDecl.
69736985
auto getSemanticsAttrs() const
@@ -7728,6 +7740,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
77287740
enum class SILSynthesizeKind {
77297741
None,
77307742
MemberwiseInitializer,
7743+
CompatibilityMemberwiseInitializer,
77317744
DistributedActorFactory
77327745

77337746
// This enum currently needs to fit in a 2-bit bitfield.
@@ -8100,11 +8113,19 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
81008113

81018114
/// Note that this is a memberwise initializer and thus the body will be
81028115
/// generated by SILGen.
8103-
void setIsMemberwiseInitializer() {
8116+
void setIsMemberwiseInitializer(MemberwiseInitKind initKind) {
81048117
assert(getBodyKind() == BodyKind::None);
81058118
assert(isa<ConstructorDecl>(this));
81068119
setBodyKind(BodyKind::SILSynthesize);
8107-
setSILSynthesizeKind(SILSynthesizeKind::MemberwiseInitializer);
8120+
switch (initKind) {
8121+
case MemberwiseInitKind::Regular:
8122+
setSILSynthesizeKind(SILSynthesizeKind::MemberwiseInitializer);
8123+
break;
8124+
case MemberwiseInitKind::Compatibility:
8125+
setSILSynthesizeKind(
8126+
SILSynthesizeKind::CompatibilityMemberwiseInitializer);
8127+
break;
8128+
}
81088129
}
81098130

81108131
/// Mark that the body should be filled in to be a factory method for creating
@@ -8140,9 +8161,20 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
81408161
/// typechecking.
81418162
bool isBodySkipped() const;
81428163

8143-
bool isMemberwiseInitializer() const {
8144-
return getBodyKind() == BodyKind::SILSynthesize
8145-
&& getSILSynthesizeKind() == SILSynthesizeKind::MemberwiseInitializer;
8164+
/// Checks whether this is a memberwise initializer decl, and if so the kind,
8165+
/// otherwise \c std::nullopt.
8166+
std::optional<MemberwiseInitKind> isMemberwiseInitializer() const {
8167+
if (getBodyKind() != BodyKind::SILSynthesize)
8168+
return std::nullopt;
8169+
8170+
switch (getSILSynthesizeKind()) {
8171+
case SILSynthesizeKind::MemberwiseInitializer:
8172+
return MemberwiseInitKind::Regular;
8173+
case SILSynthesizeKind::CompatibilityMemberwiseInitializer:
8174+
return MemberwiseInitKind::Compatibility;
8175+
default:
8176+
return std::nullopt;
8177+
}
81468178
}
81478179

81488180
/// Determines whether this function represents a distributed actor
@@ -10196,9 +10228,22 @@ getAccessorNameForDiagnostic(AccessorDecl *accessor, bool article,
1019610228
StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, bool article,
1019710229
bool underscored);
1019810230

10199-
/// Retrieve a textual representation for a memberwise initializer in a given
10200-
/// nominal decl.
10201-
void printMemberwiseInit(NominalTypeDecl *nominal, llvm::raw_ostream &out);
10231+
inline void simple_display(llvm::raw_ostream &out,
10232+
MemberwiseInitKind initKind) {
10233+
switch (initKind) {
10234+
case MemberwiseInitKind::Regular:
10235+
out << "regular";
10236+
break;
10237+
case MemberwiseInitKind::Compatibility:
10238+
out << "compatibility";
10239+
break;
10240+
}
10241+
}
10242+
10243+
/// Retrieve a textual representation for a particular kind of memberwise
10244+
/// initializer in a given nominal decl.
10245+
void printMemberwiseInit(NominalTypeDecl *nominal, MemberwiseInitKind initKind,
10246+
llvm::raw_ostream &out);
1020210247

1020310248
void simple_display(llvm::raw_ostream &out,
1020410249
OptionSet<NominalTypeDecl::LookupDirectFlags> options);

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,6 +2706,15 @@ ERROR(spi_only_imports_not_enabled, none,
27062706
"'@_spiOnly' requires setting the frontend flag '-experimental-spi-only-imports'",
27072707
())
27082708

2709+
WARNING(warn_use_of_compat_memberwise_init,Deprecation,
2710+
"synthesized memberwise initializer no longer includes "
2711+
"%select{private properties with initial values|%1}0; uses of it "
2712+
"will be an error in a future Swift language mode", (bool, StringRef))
2713+
NOTE(insert_compat_memberwise_init,none,
2714+
"insert an explicit implementation of the memberwise initializer",())
2715+
NOTE(compat_memberwise_init_used_here,none,
2716+
"memberwise initializer used here",())
2717+
27092718
// Access level on imports
27102719
ERROR(access_level_on_import_unsupported, none,
27112720
"The access level %0 is unsupported on imports: "

include/swift/AST/SourceFileExtras.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class Decl;
2323
/// Extra information associated with a source file that is lazily created and
2424
/// stored in a separately-allocated side structure.
2525
struct SourceFileExtras {
26+
/// Set of compatibility memberwise initializers that we have emitted a
27+
/// warning for.
28+
llvm::DenseSet<const ConstructorDecl *> DiagnosedCompatMemberwiseInits;
2629
};
2730

2831
}

include/swift/AST/TypeCheckRequests.h

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,19 +1990,39 @@ class InitAccessorPropertiesRequest :
19901990

19911991
/// Request to obtain a list of properties that will be reflected in the parameters of a
19921992
/// memberwise initializer.
1993-
class MemberwiseInitPropertiesRequest :
1994-
public SimpleRequest<MemberwiseInitPropertiesRequest,
1995-
ArrayRef<VarDecl *>(NominalTypeDecl *),
1996-
RequestFlags::Cached> {
1993+
class MemberwiseInitPropertiesRequest
1994+
: public SimpleRequest<MemberwiseInitPropertiesRequest,
1995+
ArrayRef<VarDecl *>(NominalTypeDecl *,
1996+
MemberwiseInitKind),
1997+
RequestFlags::Cached> {
19971998
public:
19981999
using SimpleRequest::SimpleRequest;
19992000

20002001
private:
20012002
friend SimpleRequest;
20022003

20032004
// Evaluation.
2004-
ArrayRef<VarDecl *>
2005-
evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const;
2005+
ArrayRef<VarDecl *> evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
2006+
MemberwiseInitKind initKind) const;
2007+
2008+
public:
2009+
bool isCached() const { return true; }
2010+
};
2011+
2012+
/// The maximum access level the memberwise initializer can be for a given
2013+
/// nominal decl.
2014+
class MemberwiseInitMaxAccessLevel
2015+
: public SimpleRequest<MemberwiseInitMaxAccessLevel,
2016+
AccessLevel(NominalTypeDecl *),
2017+
RequestFlags::Cached> {
2018+
public:
2019+
using SimpleRequest::SimpleRequest;
2020+
2021+
private:
2022+
friend SimpleRequest;
2023+
2024+
// Evaluation.
2025+
AccessLevel evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const;
20062026

20072027
public:
20082028
bool isCached() const { return true; }
@@ -2843,7 +2863,8 @@ class HasUserDefinedDesignatedInitRequest
28432863

28442864
/// Checks whether this type has a synthesized memberwise initializer.
28452865
class HasMemberwiseInitRequest
2846-
: public SimpleRequest<HasMemberwiseInitRequest, bool(StructDecl *),
2866+
: public SimpleRequest<HasMemberwiseInitRequest,
2867+
bool(StructDecl *, MemberwiseInitKind),
28472868
RequestFlags::Cached> {
28482869
public:
28492870
using SimpleRequest::SimpleRequest;
@@ -2852,7 +2873,8 @@ class HasMemberwiseInitRequest
28522873
friend SimpleRequest;
28532874

28542875
// Evaluation.
2855-
bool evaluate(Evaluator &evaluator, StructDecl *decl) const;
2876+
bool evaluate(Evaluator &evaluator, StructDecl *decl,
2877+
MemberwiseInitKind initKind) const;
28562878

28572879
public:
28582880
// Caching.
@@ -2862,7 +2884,8 @@ class HasMemberwiseInitRequest
28622884
/// Synthesizes a memberwise initializer for a given type.
28632885
class SynthesizeMemberwiseInitRequest
28642886
: public SimpleRequest<SynthesizeMemberwiseInitRequest,
2865-
ConstructorDecl *(NominalTypeDecl *),
2887+
ConstructorDecl *(NominalTypeDecl *,
2888+
MemberwiseInitKind),
28662889
RequestFlags::Cached> {
28672890
public:
28682891
using SimpleRequest::SimpleRequest;
@@ -2871,7 +2894,8 @@ class SynthesizeMemberwiseInitRequest
28712894
friend SimpleRequest;
28722895

28732896
// Evaluation.
2874-
ConstructorDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const;
2897+
ConstructorDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
2898+
MemberwiseInitKind initKind) const;
28752899

28762900
public:
28772901
// Caching.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,10 @@ SWIFT_REQUEST(TypeChecker, InitAccessorPropertiesRequest,
342342
ArrayRef<VarDecl *>(NominalTypeDecl *),
343343
Cached, NoLocationInfo)
344344
SWIFT_REQUEST(TypeChecker, MemberwiseInitPropertiesRequest,
345-
ArrayRef<VarDecl *>(NominalTypeDecl *),
345+
ArrayRef<VarDecl *>(NominalTypeDecl *, MemberwiseInitKind),
346+
Cached, NoLocationInfo)
347+
SWIFT_REQUEST(TypeChecker, MemberwiseInitMaxAccessLevel,
348+
AccessLevel(NominalTypeDecl *),
346349
Cached, NoLocationInfo)
347350
SWIFT_REQUEST(TypeChecker, StructuralTypeRequest, Type(TypeAliasDecl *), Cached,
348351
NoLocationInfo)
@@ -394,7 +397,7 @@ SWIFT_REQUEST(TypeChecker, AreAllStoredPropertiesDefaultInitableRequest,
394397
SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest,
395398
bool(NominalTypeDecl *), Cached, NoLocationInfo)
396399
SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest,
397-
bool(StructDecl *), Cached, NoLocationInfo)
400+
bool(StructDecl *, MemberwiseInitKind), Cached, NoLocationInfo)
398401
SWIFT_REQUEST(TypeChecker, BraceHasExplicitReturnStmtRequest,
399402
bool(const BraceStmt *),
400403
Cached, NoLocationInfo)
@@ -434,7 +437,8 @@ SWIFT_REQUEST(TypeChecker, SPIGroupsRequest,
434437
llvm::ArrayRef<Identifier>(Decl *),
435438
SeparatelyCached | SplitCached, NoLocationInfo)
436439
SWIFT_REQUEST(TypeChecker, SynthesizeMemberwiseInitRequest,
437-
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
440+
ConstructorDecl *(NominalTypeDecl *, MemberwiseInitKind),
441+
Cached, NoLocationInfo)
438442
SWIFT_REQUEST(TypeChecker, ResolveEffectiveMemberwiseInitRequest,
439443
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
440444
SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest,

include/swift/Basic/Features.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,10 @@ EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false)
533533
/// Enable @_lifetime attribute
534534
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true)
535535

536+
/// Excludes '(file)private' properties with initial values from the memberwise
537+
/// initializer if there is a more accessible initializable property.
538+
EXPERIMENTAL_FEATURE(ExcludePrivateFromMemberwiseInit, true)
539+
536540
/// Allow macro based aliases to be imported into Swift
537541
EXPERIMENTAL_FEATURE(ImportMacroAliases, true)
538542

0 commit comments

Comments
 (0)