-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[AutoDiff][SR-13152] Better diagnostic for static
decl modifier mismatch.
#36128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…match. Improved diagnostic for when the registered derivative function and the function it derivates (the original) differ in terms of `static` declaration modifier usage. Suggesting as well a fix-it, to either remove or add the `static` keyword. The registered derivative needs to be marked as `static` in two cases: 1. When the original function is a constructor. 2. When the original function is static as well. When the original function is an instance method, the registered derivate must be as well. Given the following example: ```swift struct Foo: Differentiable { static func sf1(_ x : Float) -> Float { return x * x * x } } extension Foo { @Derivative(of: sf1) func vjpF1(_ x: Float) -> (value: Float, pullback: (Float.TangentVector) -> (Foo.TangentVector, Float.TangentVector)) { fatalError() } } ``` Before this PR: ```bash sr_13152.swift:25:21: error: referenced declaration 'sf1' could not be resolved @Derivative(of: sf1) ^ sr_13152.swift:19:17: note: candidate static method does not have expected type '(Foo) -> (Float) -> Float' static func sf1(_ x : Float) -> Float { ``` After this PR: ```bash sr_13152.swift:26:10: error: function must match usege of static declaration modifier from original function 'vjpF1' func vjpF1(_ x: Float) -> (value: Float, pullback: (Float.TangentVector) -> (Foo.TangentVector, Float.TangentVector)) { ^ sr_13152.swift:26:5: note: mark the function 'vjpF1' as static func vjpF1(_ x: Float) -> (value: Float, pullback: (Float.TangentVector) -> (Foo.TangentVector, Float.TangentVector)) { ^ static ``` Resolves [SR-13152](https://bugs.swift.org/browse/SR-13152).
@@ -4007,7 +4007,9 @@ static bool checkFunctionSignature( | |||
if (!std::equal(required->getParams().begin(), required->getParams().end(), | |||
candidateFnTy->getParams().begin(), | |||
[&](AnyFunctionType::Param x, AnyFunctionType::Param y) { | |||
return x.getOldType()->isEqual(mapType(y.getOldType())); | |||
auto xInstanceTy = x.getOldType()->getMetatypeInstanceType(); | |||
auto yInstanceTy = y.getOldType()->getMetatypeInstanceType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when either required
or candidateFnTy
are defined as static
, the parameters are MetaTypes, hence I try to look into the instance type to be able to make this comparison work. Otherwise, this will fail and trigger the diagnostic we see today. Do you see any issues with this here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend checking for static-ness mismatches in differentiability witnesses before checking for signature mismatches. That'll let you diagnose type mismatches as a separate thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signature mismatches are done during the process of looking up for the original function that the derivative is trying to derivate, that's why I modified this check so that the lookup does discards the candidate due to static-ness mismatch. Once the original candidate is found, I can do the static mismatch checks I introduced in this PR.
lib/Sema/TypeCheckAttr.cpp
Outdated
@@ -4581,6 +4583,42 @@ void AttributeChecker::visitDifferentiableAttr(DifferentiableAttr *attr) { | |||
(void)attr->getParameterIndices(); | |||
} | |||
|
|||
/// Checks if original candidate and registered derivative match in terms |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think there is a better place to move this utility functions to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The treatment of constructors as static functions for the purpose of differentiability witness matching is pretty unique. I think this is where it needs to be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, great! thanks.
@@ -3264,6 +3264,14 @@ ERROR(autodiff_attr_result_not_differentiable,none, | |||
"'Differentiable', but %0 does not conform to 'Differentiable'", (Type)) | |||
ERROR(autodiff_attr_opaque_result_type_unsupported,none, | |||
"cannot differentiate functions returning opaque result types", ()) | |||
ERROR(autodiff_attr_static_decl_mismatch,none, | |||
"function must match usage of static declaration modifier " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about
"function must match usage of static declaration modifier " | |
"derivative function %0 operates on %select{an instance|a type}1, not %select{an instance|a type}2 as required" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can point back at the original function with a note.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the suggestion. I changed the working of the error diagnostic and adapted the subsequent notes accordingly in cfd7932
lib/Sema/TypeCheckAttr.cpp
Outdated
/// Produces diagnostics for mismatch in static/instance method declaration | ||
/// between original candidate and registered derivative. | ||
static void diagnoseStaticDeclMismatch(AbstractFunctionDecl *originalCandidate, | ||
FuncDecl *registred) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Please run git-clang-format
on this patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made sure the code is clang-formatted. Fixed in cfd7932.
of commit: 14d6bf2
- git-clang-format on this PR. - rewording diagnostic messages.
(/*derivative*/ DeclName, /*derivativeOperatesOnTy*/bool, | ||
/*originalOperatesOnTy*/bool)) | ||
NOTE(autodiff_attr_static_decl_original,none, | ||
"original function %0 operates on %select{an instance|a}1 type", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure "operates on a type" is the right user-facing error message, as it sounds quite uncommon in existing Swift diagnostics. Plus, "an instance type" doesn't sound quite right — shouldn't it just be "an instance"?
Instead of "operates on something", how about something like "original function 'foo(_:)' is {an instance|a static} method"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, your suggestion seems more aligned with wording already existing in the diagnostic error messages.
I'll make this note disappear and include your suggestion in the error
diagnostic itself. Thanks!
lib/Sema/TypeCheckAttr.cpp
Outdated
static bool checkStaticDeclMismatch(AbstractFunctionDecl *originalCandidate, | ||
AbstractFunctionDecl *registered) { | ||
return (isa<ConstructorDecl>(originalCandidate) || | ||
originalCandidate->isStatic()) != registered->isStatic(); | ||
} | ||
|
||
/// Produces diagnostics for mismatch in static/instance method declaration | ||
/// between original candidate and registered derivative. | ||
static void diagnoseStaticDeclMismatch(AbstractFunctionDecl *originalCandidate, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeCheckerAttr.cpp is not an AD-specific file, so if we are to define these as static functions we should make sure these two AD-specific helper functions have AD-specific names. That said, for better locality and readability, I'd recommend defining these as lambdas in typeCheckDerivativeAttr
right above the call sites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started defining these helper functions as you suggested as lambdas within typeCheckDerivativeAttr
but the diagnostics apply as well for the @transpose
attribute. Type-checking for @transpose
attribute happens in visitTransposeAttr
hence the definitions here so that we can reuse them in both places. I could define twice the a lambda that performs the checks as it is not much code. wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m planning on revamping @transpose
typing rules so the diagnostics may not apply to both anymore. So duplicating the core for now sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have pushed a new commit that duplicates the check and diagnostics for both attribute type check routines. As well, I have split the diagnostic messages for both attributes given that in one case one refers to the derivative function and to the transpose function in the other, plus it would be easier to remove them for the '@transpose' attribute if they need to go as a result of your refactoring.
// expected-note @+1 {{original function 'staticMethod' operates on a type}} | ||
static func staticMethod(_ x: T) -> T { x } | ||
|
||
@derivative(of: init) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In these diagnostics we should make the error appear at the location that triggered the error. For example, in this case vjpInit(_:)
is a perfectly fine Swift function if it were compiled directly. The compilation error is caused by the addition of the @derivative(of: init)
attribute, so the error should appear on the attribute, not on vjpInit(_:)
.
@derivative(of: init)
^~~~
The error should also say "as required by what" instead of just "as required" in order to provide the user with the sufficient context. In this case, maybe it can say something like:
error: unexpected derivative function declaration; 'StaticMismatch.init(_:)' requires that the derivative function be 'static'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, you are right. It seems a bit weird to have a note produced "far away" from the error itself. I'll address your comment in a soon-to-come commit :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have modified the wording of the error messages and notes to be more aligned to what you suggested. The error diagnostic is now pointing at the method stated in the 'of:' parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@derivative(of: init) ^~~~
I overlooked your suggestion here of highlighting the original function (sorry for that :/ ). I've seen other diagnostics in this context do highlighting as well so I just pushed #36259 for the sake of consistency.
@@ -3264,6 +3264,18 @@ ERROR(autodiff_attr_result_not_differentiable,none, | |||
"'Differentiable', but %0 does not conform to 'Differentiable'", (Type)) | |||
ERROR(autodiff_attr_opaque_result_type_unsupported,none, | |||
"cannot differentiate functions returning opaque result types", ()) | |||
ERROR(autodiff_attr_static_decl_mismatch,none, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't these specific to the @derivative
attribute? Should they be moved near derivative_attr_*
diagnostics (around line 3171)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned before the same diagnostic can apply to the @transpose
attr. hence the definition in this section as the wording is not specific to @derivative
nor @transpose
attributes. Otherwise I would have to define them twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. The diagnostic name can use some clarification, something along the lines of autodiff_attr_instance_method_mismatching_original
, basically spelling out the exact mismatch condition as succinctly as possible.
* splitting diagnostic messages for `@derivative` and `@transpose` attr. * duplicating the diagnostic logic using lambda functions within the respective attr checking methods. * change wording of error/notes to use non-`static`|`static`.
@swift-ci please test |
Build failed |
@swift-ci please test Linux |
Thanks Victor! |
Small follow up of [PR-36128](swiftlang#36128) to be consistent with other diagnostics emitted when type-checking `@derivative` and `@transpose` attributes.
Small follow up of [PR-36128](#36128) to be consistent with other diagnostics emitted when type-checking `@derivative` and `@transpose` attributes.
Improved diagnostic for when the registered derivative function and
the function it derivates (the original) differ in terms of
static
declaration modifier usage. Suggesting as well a fix-it,to either remove or add the
static
keyword.The registered derivative needs to be marked as
static
in two cases:When the original function is an instance method, the registered derivate
must be as well.
Given the following example:
Before this PR:
After this PR:
Resolves SR-13152.