Skip to content

Commit df6be21

Browse files
authored
[AutoDiff] Change TangentVector memberwise initializer access level. (#28908)
Previously, memberwise initializers of synthesized `TangentVector` structs were internal by default. Now, they are public by default, improving usability. Note that public synthesized memberwise initializers have no precedent in Swift. For normal public structs, it's possible to manually define a public memberwise initializer, skipping synthesis. However, since `TangentVector` structs are always synthesized with a memberwise initializer, manually defining a public memberwise initializer in an extension leads to a redeclaration error. A broader discussion on memberwise initializer access levels may be worthwhile. Resolves TF-1077.
1 parent 5a7b258 commit df6be21

6 files changed

+101
-24
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,33 @@ ConstructorDecl *swift::createMemberwiseImplicitConstructor(
278278
ctx);
279279
}
280280

281+
// SWIFT_ENABLE_TENSORFLOW
282+
ConstructorDecl *swift::getOrCreateEffectiveMemberwiseInitializer(
283+
ASTContext &ctx, NominalTypeDecl *nominal) {
284+
// Compute the access level for the memberwise initializer: the minimum of:
285+
// - Public, by default. This enables public nominal types to have public
286+
// memberwise initializers.
287+
// - The access level of each memberwise-initialized property in the nominal
288+
// type declaration.
289+
auto accessLevel = AccessLevel::Public;
290+
for (auto *member : nominal->getMembers()) {
291+
auto var = dyn_cast<VarDecl>(member);
292+
if (!var ||
293+
!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
294+
continue;
295+
accessLevel = std::min(accessLevel, var->getFormalAccess());
296+
}
297+
if (auto *initDecl = nominal->getEffectiveMemberwiseInitializer()) {
298+
initDecl->overwriteAccess(accessLevel);
299+
return initDecl;
300+
}
301+
auto *initDecl = createMemberwiseImplicitConstructor(ctx, nominal);
302+
initDecl->overwriteAccess(accessLevel);
303+
nominal->addMember(initDecl);
304+
return initDecl;
305+
}
306+
// SWIFT_ENABLE_TENSORFLOW END
307+
281308
/// Create a stub body that emits a fatal error message.
282309
static std::pair<BraceStmt *, bool>
283310
synthesizeStubBody(AbstractFunctionDecl *fn, void *) {

lib/Sema/CodeSynthesis.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ Expr *buildArgumentForwardingExpr(ArrayRef<ParamDecl*> params,
6565

6666
ConstructorDecl *createMemberwiseImplicitConstructor(ASTContext &ctx,
6767
NominalTypeDecl *decl);
68+
69+
// SWIFT_ENABLE_TENSORFLOW
70+
// Get the effective memberwise initializer of the given nominal type, or create
71+
// it if it does not exist.
72+
// Sets the access level of the memberwise initializer to the minimum of:
73+
// - Public, by default. This enables public nominal types to have public
74+
// memberwise initializers.
75+
// - NOTE(TF-1077): The `public` default is important for `TangentVector`
76+
// structs synthesized during `Differentiable` derived conformances.
77+
// Manually extending `TangentVector` structs to define a public
78+
// memberwise initializer causes a redeclaration error.
79+
// - The access level of each memberwise-initialized property in the nominal
80+
// type declaration.
81+
ConstructorDecl *getOrCreateEffectiveMemberwiseInitializer(
82+
ASTContext &ctx, NominalTypeDecl *nominal);
83+
// SWIFT_ENABLE_TENSORFLOW END
84+
6885
} // end namespace swift
6986

7087
#endif

lib/Sema/DerivedConformanceElementaryFunctions.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,6 @@ static ValueDecl *getElementaryFunctionRequirement(
100100
}
101101
}
102102

103-
// Get the effective memberwise initializer of the given nominal type, or create
104-
// it if it does not exist.
105-
static ConstructorDecl *getOrCreateEffectiveMemberwiseInitializer(
106-
ASTContext &ctx, NominalTypeDecl *nominal) {
107-
if (auto *initDecl = nominal->getEffectiveMemberwiseInitializer())
108-
return initDecl;
109-
auto *initDecl = createMemberwiseImplicitConstructor(ctx, nominal);
110-
nominal->addMember(initDecl);
111-
return initDecl;
112-
}
113-
114103
bool DerivedConformance::canDeriveElementaryFunctions(NominalTypeDecl *nominal,
115104
DeclContext *DC) {
116105
// Nominal type must be a struct. (Zero stored properties is okay.)

lib/Sema/DerivedConformanceRingMathProtocols.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,6 @@ static ValueDecl *getProtocolRequirement(ProtocolDecl *proto, Identifier name) {
7575
return lookup.front();
7676
}
7777

78-
// Get the effective memberwise initializer of the given nominal type, or create
79-
// it if it does not exist.
80-
static ConstructorDecl *getOrCreateEffectiveMemberwiseInitializer(
81-
ASTContext &ctx, NominalTypeDecl *nominal) {
82-
if (auto *initDecl = nominal->getEffectiveMemberwiseInitializer())
83-
return initDecl;
84-
auto *initDecl = createMemberwiseImplicitConstructor(
85-
ctx, nominal);
86-
nominal->addMember(initDecl);
87-
return initDecl;
88-
}
89-
9078
// Return true if given nominal type has a `let` stored with an initial value.
9179
// TODO: Move function to shared place for use with other derived conformances.
9280
static bool hasLetStoredPropertyWithInitialValue(NominalTypeDecl *nominal) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SWIFT_ENABLE_TENSORFLOW
2+
// RUN: %target-swift-frontend -print-ast %s | %FileCheck %s
3+
4+
// Test `Differentiable` derived conformances.
5+
// Verify access levels of synthesized `TangentVector` types and their memberwise initializers.
6+
// `TangentVector` memberwise initializer access level should match `TangentVector` access level.
7+
8+
public struct PublicStruct: Differentiable {}
9+
internal struct InternalStruct: Differentiable {}
10+
private struct PrivateStruct: Differentiable {}
11+
12+
// CHECK-LABEL: public struct PublicStruct : Differentiable {
13+
// CHECK: internal init()
14+
// CHECK: public struct TangentVector : Differentiable, AdditiveArithmetic, PointwiseMultiplicative, ElementaryFunctions {
15+
// CHECK: public init()
16+
// CHECK: }
17+
// CHECK: }
18+
19+
// CHECK-LABEL: internal struct InternalStruct : Differentiable {
20+
// CHECK: internal init()
21+
// CHECK: internal struct TangentVector : Differentiable, AdditiveArithmetic, PointwiseMultiplicative, ElementaryFunctions {
22+
// CHECK: public init()
23+
// CHECK: }
24+
// CHECK: }
25+
26+
// CHECK-LABEL: private struct PrivateStruct : Differentiable {
27+
// CHECK: internal init()
28+
// CHECK: fileprivate struct TangentVector : Differentiable, AdditiveArithmetic, PointwiseMultiplicative, ElementaryFunctions {
29+
// CHECK: public init()
30+
// CHECK: }
31+
// CHECK: }
32+
33+
public class PublicClass: Differentiable {}
34+
internal class InternalClass: Differentiable {}
35+
private class PrivateClass: Differentiable {}
36+
37+
// CHECK-LABEL: public class PublicClass : Differentiable {
38+
// CHECK: internal init()
39+
// CHECK: public struct TangentVector : Differentiable, AdditiveArithmetic, PointwiseMultiplicative, ElementaryFunctions {
40+
// CHECK: public init()
41+
// CHECK: }
42+
// CHECK: }
43+
44+
// CHECK-LABEL: internal class InternalClass : Differentiable {
45+
// CHECK: internal init()
46+
// CHECK: internal struct TangentVector : Differentiable, AdditiveArithmetic, PointwiseMultiplicative, ElementaryFunctions {
47+
// CHECK: public init()
48+
// CHECK: }
49+
// CHECK: }
50+
51+
// CHECK-LABEL: private class PrivateClass : Differentiable {
52+
// CHECK: internal init()
53+
// CHECK: fileprivate struct TangentVector : Differentiable, AdditiveArithmetic, PointwiseMultiplicative, ElementaryFunctions {
54+
// CHECK: public init()
55+
// CHECK: }
56+
// CHECK: }

test/Sema/struct_differentiable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ extension NoMemberwiseInitializerExtended: Differentiable
414414
extension NoMemberwiseInitializerExtended: EuclideanDifferentiable
415415
where T : Differentiable & AdditiveArithmetic {}
416416

417-
// Test derived conformances in disallowed contexts.
417+
// Verify that cross-file derived conformances are disallowed.
418418

419419
// expected-error @+2 {{type 'OtherFileNonconforming' does not conform to protocol 'Differentiable'}}
420420
// expected-error @+1 {{implementation of 'Differentiable' cannot be automatically synthesized in an extension in a different file to the type}}

0 commit comments

Comments
 (0)