Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.

Commit 8978400

Browse files
committed
Merge branch 'master' of github.com:apple/swift into tensorflow-stage
* 'master' of github.com:apple/swift: [AutoDiff] [Sema] Limit implicit `@differentiable` attribute creation. (swiftlang#33776)
2 parents 925e344 + 9afad73 commit 8978400

File tree

6 files changed

+157
-30
lines changed

6 files changed

+157
-30
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3133,12 +3133,11 @@ ERROR(overriding_decl_missing_differentiable_attr,none,
31333133
"overriding declaration is missing attribute '%0'", (StringRef))
31343134
NOTE(protocol_witness_missing_differentiable_attr,none,
31353135
"candidate is missing attribute '%0'", (StringRef))
3136-
NOTE(protocol_witness_missing_differentiable_attr_nonpublic_other_file,none,
3137-
"non-public %1 %2 must have explicit '%0' attribute to satisfy "
3138-
"requirement %3 %4 (in protocol %6) because it is declared in a different "
3139-
"file than the conformance of %5 to %6",
3140-
(StringRef, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, DeclName,
3141-
Type, Type))
3136+
NOTE(protocol_witness_missing_differentiable_attr_invalid_context,none,
3137+
"candidate is missing explicit '%0' attribute to satisfy requirement %1 "
3138+
"(in protocol %3); explicit attribute is necessary because candidate is "
3139+
"declared in a different type context or file than the conformance of %2 "
3140+
"to %3", (StringRef, DeclName, Type, Type))
31423141

31433142
// @derivative
31443143
ERROR(derivative_attr_expected_result_tuple,none,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -385,23 +385,19 @@ matchWitnessDifferentiableAttr(DeclContext *dc, ValueDecl *req,
385385
supersetConfig = witnessConfig;
386386
}
387387
if (!foundExactConfig) {
388-
bool success = false;
389388
// If no exact witness derivative configuration was found, check
390389
// conditions for creating an implicit witness `@differentiable` attribute
391-
// with the exact derivative configuration:
392-
// - If the witness has a "superset" derivative configuration.
393-
// - If the witness is less than public and is declared in the same file
394-
// as the conformance.
395-
// - `@differentiable` attributes are really only significant for public
396-
// declarations: it improves usability to not require explicit
397-
// `@differentiable` attributes for less-visible declarations.
398-
bool createImplicitWitnessAttribute =
399-
supersetConfig || witness->getFormalAccess() < AccessLevel::Public;
400-
// If the witness has less-than-public visibility and is declared in a
401-
// different file than the conformance, produce an error.
402-
if (!supersetConfig && witness->getFormalAccess() < AccessLevel::Public &&
403-
dc->getModuleScopeContext() !=
404-
witness->getDeclContext()->getModuleScopeContext()) {
390+
// with the exact derivative configuration.
391+
392+
// If witness is declared in a different file or type context than the
393+
// conformance, we should not create an implicit `@differentiable`
394+
// attribute on the witness. Produce an error.
395+
auto sameTypeContext =
396+
dc->getInnermostTypeContext() ==
397+
witness->getDeclContext()->getInnermostTypeContext();
398+
auto sameModule = dc->getModuleScopeContext() ==
399+
witness->getDeclContext()->getModuleScopeContext();
400+
if (!sameTypeContext || !sameModule) {
405401
// FIXME(TF-1014): `@differentiable` attribute diagnostic does not
406402
// appear if associated type inference is involved.
407403
if (auto *vdWitness = dyn_cast<VarDecl>(witness)) {
@@ -413,6 +409,20 @@ matchWitnessDifferentiableAttr(DeclContext *dc, ValueDecl *req,
413409
reqDiffAttr);
414410
}
415411
}
412+
413+
// Otherwise, the witness must:
414+
// - Have a "superset" derivative configuration.
415+
// - Have less than public visibility.
416+
// - `@differentiable` attributes are really only significant for
417+
// public declarations: it improves usability to not require
418+
// explicit `@differentiable` attributes for less-visible
419+
// declarations.
420+
//
421+
// If these conditions are met, an implicit `@differentiable` attribute
422+
// with the exact derivative configuration can be created.
423+
bool success = false;
424+
bool createImplicitWitnessAttribute =
425+
supersetConfig || witness->getFormalAccess() < AccessLevel::Public;
416426
if (createImplicitWitnessAttribute) {
417427
auto derivativeGenSig = witnessAFD->getGenericSignature();
418428
if (supersetConfig)
@@ -2474,19 +2484,20 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
24742484
llvm::raw_string_ostream os(reqDiffAttrString);
24752485
reqAttr->print(os, req, omitWrtClause);
24762486
os.flush();
2477-
// If the witness has less-than-public visibility and is declared in a
2478-
// different file than the conformance, emit a specialized diagnostic.
2479-
if (witness->getFormalAccess() < AccessLevel::Public &&
2480-
conformance->getDeclContext()->getModuleScopeContext() !=
2481-
witness->getDeclContext()->getModuleScopeContext()) {
2487+
// If the witness is declared in a different file or type context than the
2488+
// conformance, emit a specialized diagnostic.
2489+
auto sameModule = conformance->getDeclContext()->getModuleScopeContext() !=
2490+
witness->getDeclContext()->getModuleScopeContext();
2491+
auto sameTypeContext =
2492+
conformance->getDeclContext()->getInnermostTypeContext() !=
2493+
witness->getDeclContext()->getInnermostTypeContext();
2494+
if (sameModule || sameTypeContext) {
24822495
diags
24832496
.diagnose(
24842497
witness,
24852498
diag::
2486-
protocol_witness_missing_differentiable_attr_nonpublic_other_file,
2487-
reqDiffAttrString, witness->getDescriptiveKind(),
2488-
witness->getName(), req->getDescriptiveKind(),
2489-
req->getName(), conformance->getType(),
2499+
protocol_witness_missing_differentiable_attr_invalid_context,
2500+
reqDiffAttrString, req->getName(), conformance->getType(),
24902501
conformance->getProtocol()->getDeclaredInterfaceType())
24912502
.fixItInsert(match.Witness->getStartLoc(), reqDiffAttrString + ' ');
24922503
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import _Differentiation
2+
3+
protocol Protocol1: Differentiable {
4+
// expected-note @+2 {{protocol requires function 'internalMethod1' with type '(Float) -> Float'}}
5+
@differentiable(wrt: (self, x))
6+
func internalMethod1(_ x: Float) -> Float
7+
8+
// expected-note @+3 {{protocol requires function 'internalMethod2' with type '(Float) -> Float'}}
9+
@differentiable(wrt: x)
10+
@differentiable(wrt: (self, x))
11+
func internalMethod2(_ x: Float) -> Float
12+
13+
// expected-note @+3 {{protocol requires function 'internalMethod3' with type '(Float) -> Float'}}
14+
@differentiable(wrt: x)
15+
@differentiable(wrt: (self, x))
16+
func internalMethod3(_ x: Float) -> Float
17+
}
18+
19+
protocol Protocol2: Differentiable {
20+
@differentiable(wrt: (self, x))
21+
func internalMethod4(_ x: Float) -> Float
22+
}
23+
24+
// Note:
25+
// - No `ConformingStruct: Protocol1` conformance exists in this file, so this
26+
// file should compile just file.
27+
// - A `ConformingStruct: Protocol1` conformance in a different file should be
28+
// diagnosed to prevent linker errors. Without a diagnostic, compilation of
29+
// the other file creates external references to symbols for implicit
30+
// `@differentiable` attributes, even though no such symbols exist.
31+
// Context: https://github.com/apple/swift/pull/29771#issuecomment-585059721
32+
33+
struct ConformingStruct: Differentiable {
34+
// Error for missing `@differentiable` attribute.
35+
// expected-note @+1 {{candidate is missing explicit '@differentiable' attribute to satisfy requirement 'internalMethod1' (in protocol 'Protocol1'); explicit attribute is necessary because candidate is declared in a different type context or file than the conformance of 'ConformingStruct' to 'Protocol1'}} {{3-3=@differentiable }}
36+
func internalMethod1(_ x: Float) -> Float {
37+
x
38+
}
39+
40+
// Error for missing `@differentiable` superset attribute.
41+
// expected-note @+2 {{candidate is missing explicit '@differentiable' attribute to satisfy requirement 'internalMethod2' (in protocol 'Protocol1'); explicit attribute is necessary because candidate is declared in a different type context or file than the conformance of 'ConformingStruct' to 'Protocol1'}} {{3-3=@differentiable }}
42+
@differentiable(wrt: x)
43+
func internalMethod2(_ x: Float) -> Float {
44+
x
45+
}
46+
47+
// Error for missing `@differentiable` subset attribute.
48+
// expected-note @+2 {{candidate is missing explicit '@differentiable(wrt: x)' attribute to satisfy requirement 'internalMethod3' (in protocol 'Protocol1'); explicit attribute is necessary because candidate is declared in a different type context or file than the conformance of 'ConformingStruct' to 'Protocol1'}} {{3-3=@differentiable(wrt: x) }}
49+
@differentiable(wrt: (self, x))
50+
func internalMethod3(_ x: Float) -> Float {
51+
x
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import _Differentiation
2+
3+
protocol P1: Differentiable {
4+
@differentiable(wrt: self)
5+
// expected-note @+1 {{protocol requires function 'callAsFunction' with type '(Float) -> Float'}}
6+
func callAsFunction(_ input: Float) -> Float
7+
}
8+
9+
protocol P2: P1 {}
10+
11+
extension P2 {
12+
@differentiable(wrt: (self, input))
13+
// expected-note @+1 {{candidate is missing explicit '@differentiable(wrt: self)' attribute to satisfy requirement 'callAsFunction' (in protocol 'P1'); explicit attribute is necessary because candidate is declared in a different type context or file than the conformance of 'ConformingStruct' to 'P1'}}
14+
public func callAsFunction(_ input: Float) -> Float {
15+
return input
16+
}
17+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Test missing protocol requirement `@differentiable` attribute errors for
2+
// non-public protocol witnesses, when the protocol conformance is declared in a
3+
// separate file from witnesses.
4+
//
5+
// Implicit `@differentiable` attributes cannot be generated for protocol
6+
// witnesses when the conformance is declared from a separate file from the
7+
// witness. Otherwise, compilation of the file containing the conformance
8+
// creates references to external symbols for implicit `@differentiable`
9+
// attributes, even though no such symbols exist.
10+
//
11+
// Context: https://github.com/apple/swift/pull/29771#issuecomment-585059721
12+
13+
// Note: `swiftc main.swift other_file.swift` runs three commands:
14+
// - `swiftc -frontend -primary-file main.swift other_file.swift -o ...`
15+
// - `swiftc -frontend main.swift -primary-file other_file.swift -o ...`
16+
// - `/usr/bin/ld ...`
17+
//
18+
// `%target-build-swift` performs `swiftc main.swift other_file.swift`, so it is expected to fail (hence `not`).
19+
// `swiftc -frontend -primary-file main.swift other_file.swift` should fail, so `-verify` is needed.
20+
// `swiftc -frontend main.swift -primary-file other_file.swift` should succeed, so no need for `-verify`.
21+
22+
// RUN: %target-swift-frontend -c -verify -primary-file %s %S/Inputs/other_file.swift
23+
// RUN: %target-swift-frontend -c %s -primary-file %S/Inputs/other_file.swift
24+
// RUN: not %target-build-swift %s %S/Inputs/other_file.swift
25+
26+
// Error: conformance is in different file than witnesses.
27+
// expected-error @+1 {{type 'ConformingStruct' does not conform to protocol 'Protocol1'}}
28+
extension ConformingStruct: Protocol1 {}
29+
30+
// No error: conformance is in same file as witnesses.
31+
extension ConformingStruct: Protocol2 {
32+
func internalMethod4(_ x: Float) -> Float {
33+
x
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %target-swift-frontend -c -verify -primary-file %s %S/Inputs/other_file_protocol_default_implementation_witness.swift
2+
3+
// SR-13455: Test missing protocol requirement `@differentiable` attribute
4+
// errors for protocol witnesses declared in a different file than the protocol
5+
// conformance.
6+
//
7+
// This test case specifically tests protocol extension method witnesses.
8+
9+
import _Differentiation
10+
11+
// expected-error @+1 {{type 'ConformingStruct' does not conform to protocol 'P1'}}
12+
struct ConformingStruct: P2 {}

0 commit comments

Comments
 (0)