Skip to content

Commit e5cb871

Browse files
authored
[AutoDiff upstream] Add flag-gated AdditiveArithmetic derivation. (swiftlang#30628)
Add `AdditiveArithmetic` derived conformances for structs, gated by the `-enable-experimential-additive-arithmetic-derivation` flag. Structs whose stored properties all conform to `AdditiveArithmetic` can derive `AdditiveArithmetic`: - `static var zero: Self` - `static func +(lhs: Self, rhs: Self) -> Self` - `static func -(lhs: Self, rhs: Self) -> Self` - An "effective memberwise initializer": - Either a synthesized memberwise initializer or a user-defined initializer with the same type. Effective memberwise initializers are used only by derived conformances for `Self`-returning protocol requirements like `AdditiveArithmetic.+`, which require memberwise initialization. Resolves TF-844. Unblocks TF-845: upstream `Differentiable` derived conformances.
1 parent 07596cb commit e5cb871

20 files changed

+681
-7
lines changed

include/swift/AST/Decl.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3516,6 +3516,27 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
35163516
/// or \c nullptr if it does not have one.
35173517
ConstructorDecl *getMemberwiseInitializer() const;
35183518

3519+
/// Retrieves the effective memberwise initializer for this declaration, or
3520+
/// \c nullptr if it does not have one.
3521+
///
3522+
/// An effective memberwise initializer is either a synthesized memberwise
3523+
/// initializer or a user-defined initializer with the same type.
3524+
///
3525+
/// The access level of the memberwise initializer is set to the minimum of:
3526+
/// - Public, by default. This enables public nominal types to have public
3527+
/// memberwise initializers.
3528+
/// - The `public` default is important for synthesized member types, e.g.
3529+
/// `TangentVector` structs synthesized during `Differentiable` derived
3530+
/// conformances. Manually extending these types to define a public
3531+
/// memberwise initializer causes a redeclaration error.
3532+
/// - The minimum access level of memberwise-initialized properties in the
3533+
/// nominal type declaration.
3534+
///
3535+
/// Effective memberwise initializers are used only by derived conformances
3536+
/// for `Self`-returning protocol requirements like `AdditiveArithmetic.+`.
3537+
/// Such derived conformances require memberwise initialization.
3538+
ConstructorDecl *getEffectiveMemberwiseInitializer();
3539+
35193540
/// Whether this declaration has a synthesized zero parameter default
35203541
/// initializer.
35213542
bool hasDefaultInitializer() const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,6 +2674,8 @@ ERROR(cannot_synthesize_in_crossfile_extension,none,
26742674
"implementation of %0 cannot be automatically synthesized in an extension "
26752675
"in a different file to the type", (Type))
26762676

2677+
ERROR(broken_additive_arithmetic_requirement,none,
2678+
"AdditiveArithmetic protocol is broken: unexpected requirement", ())
26772679
ERROR(broken_case_iterable_requirement,none,
26782680
"CaseIterable protocol is broken: unexpected requirement", ())
26792681
ERROR(broken_raw_representable_requirement,none,

include/swift/AST/KnownIdentifiers.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,6 @@ IDENTIFIER(withKeywordArguments)
133133
IDENTIFIER(wrapped)
134134
IDENTIFIER(wrappedValue)
135135
IDENTIFIER(wrapperValue)
136-
IDENTIFIER(differential)
137-
IDENTIFIER(pullback)
138136

139137
// Kinds of layout constraints
140138
IDENTIFIER_WITH_NAME(UnknownLayout, "_UnknownLayout")
@@ -206,7 +204,10 @@ IDENTIFIER_(nsError)
206204
IDENTIFIER(OSLogMessage)
207205

208206
// Differentiable programming
207+
IDENTIFIER(differential)
208+
IDENTIFIER(pullback)
209209
IDENTIFIER(TangentVector)
210+
IDENTIFIER(zero)
210211

211212
#undef IDENTIFIER
212213
#undef IDENTIFIER_

include/swift/AST/KnownProtocols.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ PROTOCOL_(SwiftNewtypeWrapper)
7878
PROTOCOL(CodingKey)
7979
PROTOCOL(Encodable)
8080
PROTOCOL(Decodable)
81-
PROTOCOL(AdditiveArithmetic)
8281

8382
PROTOCOL_(ObjectiveCBridgeable)
8483
PROTOCOL_(DestructorSafeContainer)
8584

8685
PROTOCOL(StringInterpolationProtocol)
8786

87+
PROTOCOL(AdditiveArithmetic)
8888
PROTOCOL(Differentiable)
8989

9090
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)

include/swift/AST/TypeCheckRequests.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,31 @@ class SynthesizeMemberwiseInitRequest
16001600
bool isCached() const { return true; }
16011601
};
16021602

1603+
/// Resolves the effective memberwise initializer for a given type.
1604+
///
1605+
/// An effective memberwise initializer is either a synthesized memberwise
1606+
/// initializer or a user-defined initializer with the same type.
1607+
///
1608+
/// See `NominalTypeDecl::getEffectiveMemberwiseInitializer` for details.
1609+
class ResolveEffectiveMemberwiseInitRequest
1610+
: public SimpleRequest<ResolveEffectiveMemberwiseInitRequest,
1611+
ConstructorDecl *(NominalTypeDecl *),
1612+
CacheKind::Cached> {
1613+
public:
1614+
using SimpleRequest::SimpleRequest;
1615+
1616+
private:
1617+
friend SimpleRequest;
1618+
1619+
// Evaluation.
1620+
llvm::Expected<ConstructorDecl *> evaluate(Evaluator &evaluator,
1621+
NominalTypeDecl *decl) const;
1622+
1623+
public:
1624+
// Caching.
1625+
bool isCached() const { return true; }
1626+
};
1627+
16031628
/// Checks whether this type has a synthesized zero parameter default
16041629
/// initializer.
16051630
class HasDefaultInitRequest

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ SWIFT_REQUEST(TypeChecker, SPIGroupsRequest,
214214
Cached, NoLocationInfo)
215215
SWIFT_REQUEST(TypeChecker, SynthesizeMemberwiseInitRequest,
216216
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
217+
SWIFT_REQUEST(TypeChecker, ResolveEffectiveMemberwiseInitRequest,
218+
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
217219
SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest,
218220
bool(NominalTypeDecl *), Cached, NoLocationInfo)
219221
SWIFT_REQUEST(TypeChecker, SynthesizeDefaultInitRequest,

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ namespace swift {
327327
/// `@differentiable` declaration attribute, etc.
328328
bool EnableExperimentalDifferentiableProgramming = false;
329329

330+
/// Whether to enable experimental `AdditiveArithmetic` derived
331+
/// conformances.
332+
bool EnableExperimentalAdditiveArithmeticDerivedConformances = false;
333+
330334
/// Enable verification when every SubstitutionMap is constructed.
331335
bool VerifyAllSubstitutionMaps = false;
332336

include/swift/Option/Options.td

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,10 +495,16 @@ def disable_bridging_pch : Flag<["-"], "disable-bridging-pch">,
495495
HelpText<"Disable automatic generation of bridging PCH files">;
496496

497497
// Experimental feature options
498-
def enable_experimental_differentiable_programming : Flag<["-"], "enable-experimental-differentiable-programming">,
498+
def enable_experimental_differentiable_programming :
499+
Flag<["-"], "enable-experimental-differentiable-programming">,
499500
Flags<[FrontendOption]>,
500501
HelpText<"Enable experimental differentiable programming features">;
501502

503+
def enable_experimental_additive_arithmetic_derivation :
504+
Flag<["-"], "enable-experimental-additive-arithmetic-derivation">,
505+
Flags<[FrontendOption]>,
506+
HelpText<"Enable experimental 'AdditiveArithmetic' derived conformances">;
507+
502508
def enable_experimental_concise_pound_file : Flag<["-"],
503509
"enable-experimental-concise-pound-file">,
504510
Flags<[FrontendOption]>,

lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4059,6 +4059,14 @@ ConstructorDecl *NominalTypeDecl::getMemberwiseInitializer() const {
40594059
ctx.evaluator, SynthesizeMemberwiseInitRequest{mutableThis}, nullptr);
40604060
}
40614061

4062+
ConstructorDecl *NominalTypeDecl::getEffectiveMemberwiseInitializer() {
4063+
auto &ctx = getASTContext();
4064+
auto *mutableThis = const_cast<NominalTypeDecl *>(this);
4065+
return evaluateOrDefault(ctx.evaluator,
4066+
ResolveEffectiveMemberwiseInitRequest{mutableThis},
4067+
nullptr);
4068+
}
4069+
40624070
bool NominalTypeDecl::hasDefaultInitializer() const {
40634071
// Currently only structs and classes can have default initializers.
40644072
if (!isa<StructDecl>(this) && !isa<ClassDecl>(this))

lib/Frontend/CompilerInvocation.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,15 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
442442
if (Args.hasArg(OPT_fine_grained_dependency_include_intrafile))
443443
Opts.FineGrainedDependenciesIncludeIntrafileOnes = true;
444444

445-
if (Args.hasArg(OPT_enable_experimental_differentiable_programming))
445+
if (Args.hasArg(OPT_enable_experimental_additive_arithmetic_derivation))
446+
Opts.EnableExperimentalAdditiveArithmeticDerivedConformances = true;
447+
448+
if (Args.hasArg(OPT_enable_experimental_differentiable_programming)) {
446449
Opts.EnableExperimentalDifferentiableProgramming = true;
450+
// Differentiable programming implies `AdditiveArithmetic` derived
451+
// conformances.
452+
Opts.EnableExperimentalAdditiveArithmeticDerivedConformances = true;
453+
}
447454

448455
Opts.DebuggerSupport |= Args.hasArg(OPT_debugger_support);
449456
if (Opts.DebuggerSupport)

0 commit comments

Comments
 (0)