-
Notifications
You must be signed in to change notification settings - Fork 10.6k
expression checking for @compilerEvaluable funcs #17256
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
|
Would it be better to leave this analysis to the SIL pass that evaluates compiler-evaluable expressions? That way, as the constant evaluator improves, we don't need to update two places. |
|
@swift-ci please test tensorflow |
|
@jckarter The reason we're trying it this way is that there may be some types of code that we don't want to allow in general but that happen to generate evaluable SIL in special cases. Leaving the evaluation to the SIL pass would let these special cases through. This would make it hard for users to understand what is allowed. Even worse, changes in the interpreter might break these special cases. Admittedly, I don't have a specific example of such a thing. I'll see if I can think of one. Also, the SIL evaluation pass doesn't currently check code that it doesn't execute (e.g. |
|
@jckarter we're not super tied to this and such AST level analysis has limitations, but there is a nice advantage to it: we can diagnose obviously non-constexpr cases without having to evaluate a static assert that uses it. This seems like a pretty nice thing. You're right that this requires that we keep it in sync with the SIL level logic. |
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.
Err, this is very very stubbed out... if this is what you want for this patch, please make this the subject of your next 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.
will do
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.
Please file a JIRA tracking this so we can discuss it when the implementation is further baked. I don't want these to fall off the radar. I think they should be handleable with the @compilerEvaluable(magicBehavior) feature that we'll need anyway.
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.
|
It seems to me that the model we'd ultimately want for this is that constant expressions are allowed based on the transitive compiler-evaluability of the operations they perform, not the syntactic forms the code happens to use at the AST level. Transparent inlining would be the primary mandatory pass I can think of that would lead to potentially confusing SIL-level behavior, but it seems to me that this should eventually supplant transparent. I can see the point that you can raise an error sooner for things that obviously aren't constexpr at the AST level, but that feels like a brittle thing to be checking for at the AST level as the model expands (especially the whitelisting of certain types and builtins that are allowed). In general I feel like for erroneous code it's better to spend more time providing more accurate diagnostics than to try to peephole diagnostics at multiple levels; in other places where we've attempted this, like casting diagnostics, we've had persistent problems keeping the behavior at different levels of the system in sync. |
|
I can understand what you mean. The evaluator is really powerful in practice with the stdlib. What is your opinion about using a new attribute (@compilerEvaluable or whatever) as a statement of intent above for public functions above and beyond @inlinable? Do you think it makes for an API author to make a declaration that "future versions of this API will not start doing non-constexpr things"? If that makes sense, would it makes sense for the compiler to help detect errors? Can you think of another way to do this check? |
f267858 to
42508a0
Compare
mhong
left a comment
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.
Nice change! Got a few questions and minor suggestions.
test/Sema/compiler_evaluable.swift
Outdated
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.
var?
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.
done
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.
still keep this private? exposing it via the const getter getCompilerEvaluable() is a good API design.
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 think it is still private?
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.
Right. Looks 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.
when will this default case still be reachable (i thought ExprNodes.def would cover all cases)?
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 #include "swift/AST/ExprNodes.def" above only defines cases for UNCHECKED_EXPR expressions
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.
What do you mean? https://github.com/apple/swift/blob/tensorflow/include/swift/AST/ExprNodes.def contains more expr types than UNCHECKED_EXPR?
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.
Yeah, ExprNodes.def contains a bunch of types of nodes. ExprNodes.def defines macros that expand them all to nothing. I define the UNCHECKED_EXPR macro so that it expands to a case, and then include ExprNodes.def. So only the UNCHECKED_EXPRs end up contributing cases to the switch.
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.
Got it. Thanks for clarifying.
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.
dyn_cast_or_null instead of dyn_cast is used heer because the input can be NULL -- what would be an example?
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 the parent is not an expression, it's null. for example, the top expression node for the condition of an if statement has the if statement as the parent.
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.
what does "compiler representable" mean -- is it defined/documented somewhere?
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 proposal describes it in this section: https://gist.github.com/marcrasi/b0da27a45bb9925b3387b916e2797789#proposed-solution---intuitive-approach
I also plan to add more detailed comments describing "compiler representable" in a PR where I implement a real check for it.
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.
Thanks. Why is float not compiler representable?
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.
Float's not representable because we didn't propose to support it in the proposal. I think there are no fundamental difficulties with it -- we can just add the support whenever we want.
That's the model that makes sense to me for public API. AIUI that's what you were proposing on swift-evolution, right? |
|
@jckarter The proposal as written (still very much up for debate) requires this attribute on all functions. I think it makes sense to scope it back to just being required on public functions, but that would mean that it would still be on all the stdlib stuff of interest (replacing @inlinable). That said, we still have the pertinent question: if we have this attribute on public APIs, does it make sense for the compiler to do static checking to ensure that the method isn't doing anything unsupported, even if there is no call to it? If we should do that checking, how should it be implemented? Does it have to be perfect, or does it just have to be useful? |
|
@lattner I agree that it makes sense to only require the attribute on public declarations. It should be possible to check and diagnose the compiler-evaluability of the implementation while compiling the defining module, though, so importing modules don't have to. I think it'd be easier and more accurate to do that check by looking over the SIL than looking over the AST. |
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.
also def UNCHECKED_EXPR later?
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.
Ah I see swift/AST/ExprNodes.def undefs it at the end. It feels a bit strange that the responsibility is not at the call-site, but if that's the convention, i'm fine with it.
48e900f to
2c5eac2
Compare
|
@swift-ci please test tensorflow |
1 similar comment
|
@swift-ci please test tensorflow |
2c5eac2 to
bfa5c83
Compare
|
@swift-ci please test tensorflow |
1 similar comment
|
@swift-ci please test tensorflow |
It's mostly just a list of allowed expressions. DeclRefExpr has interesting logic.
It's mostly just a list of allowed expressions. DeclRefExpr has interesting logic.
It's mostly just a list of allowed expressions. DeclRefExpr has interesting logic.