-
-
Notifications
You must be signed in to change notification settings - Fork 651
Implement DIP1029: Add throw
as Function Attribute
#12934
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
base: master
Are you sure you want to change the base?
Conversation
Thanks for your pull request, @ljmf00! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "master + dmd#12934" |
Buildkite seems to fail due to some side effects, not related to this PR. Although, need to write some more tests to cover all this DIP. For now, I would appreciate a review of my implementation approach. This needs an autosquash rebase on my side to get rid of |
def3ea6
to
6ae8bd0
Compare
6ae8bd0
to
adfff6a
Compare
1506096
to
5f9cdf5
Compare
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.
This still requires some tests for:
- header generation of throw
- templated functions: do they infer default or throw? how does that interact with toplevel throw/nothrow?
- throw virtual functions that are overriden with nothrow
- more cases for
throw:
/nothrow:
interacting with function/template declarations
The DIP does not specify whether inference should deduce throw or default for functions that are not nothrow. I would argue that a function has to be throw or nothrow; default is just an intermediary state where the compiler does not know but should assume throw. As a precedent, in the case of safety, @system
is specifically deduced when inference occurs. In this implementation there seems to be a gap between throw and default. Looking at test/compilable/dip1029.d
line 41 it seems that inference does not deduce throw, but default. This is important for things like header generation, error messages, typeof etc.
src/dmd/dcast.d
Outdated
if (tf1.throw_ == tf2.throw_) | ||
d.throw_ = tf1.throw_; | ||
else if(tf1.throw_ != THROW.nothrow_ && tf2.throw_ != THROW.nothrow_) | ||
d.throw_ = tf1.throw_ | tf1.throw_; |
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 that this line is not covered by tests.
src/dmd/dsymbolsem.d
Outdated
if (tf.isnothrow) | ||
if (tf.throw_ != THROW.default_) | ||
sc.stc &= ~STC.throwGroup; | ||
if (tf.throw_ == THROW.nothrow_) |
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.
This if and the following one could be put in the same block, i.e.:
if (tf.throw_ != THROW.default_)
{
sc.stc &= ~STC.throwGroup;
if (tf.throw == THROW.nothrow_)
...
if (tf.throw == THROW.throw)
...
}
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.
src/dmd/mtype.d
Outdated
@@ -4149,6 +4147,12 @@ extern (C++) final class TypeFunction : TypeNext | |||
if (stc & STC.scopeinferred) | |||
this.isscopeinferred = true; | |||
|
|||
this.throw_ = THROW.default_; |
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 that this line is unnecessary. this.throw_
should be 0 by default.
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. Going to do that for trust too.
src/dmd/mtype.d
Outdated
this.throw_ = THROW.default_; | ||
if (stc & STC.nothrow_) | ||
this.throw_ = THROW.nothrow_; | ||
if (stc & STC.throw_) |
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.
if (stc & STC.throw_) | |
else if (stc & STC.throw_) |
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
src/dmd/mtype.d
Outdated
/// ditto | ||
void isnothrow(bool v) pure nothrow @safe @nogc | ||
|
||
deprecated("This property was removed in favor of throw_ field") |
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 don't think this deprecation is necessary.
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 thought that in case someone uses dmd as a library. I hear that was a thing, so I didn't remove it all to prevent breaking user code, although I don't know its state.
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.
Technically, it could be used, but I doubt that. I guess there's no harm in using deprecatins 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.
Removed.
src/dmd/mtype.d
Outdated
/// set or get if the function has the `nothrow` attribute | ||
bool isnothrow() const pure nothrow @safe @nogc | ||
{ | ||
return throw_ == THROW.nothrow_ || throw_ == THROW.default_; |
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.
Is this right? Shouldn't it be return throw == THROW.nothrow
? Isn't the default throw
?
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 are right. Need to add a test case for this, if isnothrow
is kept. The compiler itself no longer uses isnothrow
function. It is only to give backwards compatibility and prevent breaking code, as I described above.
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.
Removed.
src/dmd/mtype.d
Outdated
void isnothrow(bool v) pure nothrow @safe @nogc | ||
{ | ||
throw_ = v ? THROW.nothrow_ : THROW.throw_; | ||
} |
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.
Note that the isnothrow
function is not covered by tests.
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 compiler itself is no longer using 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.
Removed.
case TOK.throw_: | ||
stc = STC.throw_; | ||
goto L1; | ||
|
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.
Lines 4372-4374 Not covered by tests.
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 still need to check which section this code belongs. I arranged the code based on existing attributes. I will try to write some more tests to cover all these warnings.
|
||
// inferred types and attributes for lambdas | ||
static assert(is(typeof(() {}) == void function() pure nothrow @nogc @safe)); | ||
static assert(is(typeof(() { throw new Exception(""); }) == void function() pure @safe)); |
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.
why is this not inferred as throw
wheres the subsequent one is?
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.
This is inferred as throw
, void function() pure throw @safe
is the same as void function() pure @safe
.
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 added a check on line 45 to prove it.
Thanks for the list 👍 . I'm going to do that.
I choose to use a default mechanism to prevent breaking changes. I thought that this was not specified in the DIP because it is implicitly implied by "no breaking changes". It is important to test this behaviour because existing code might get the void foo() {}
void bar() @system {} Doing a I also see this as acceptable because this is how the is(void function() == void function() @system) // true AFAIK, the intermediate state where the compiler can't assume throw is validated by Tell me if I interpreted your comment/suggestion wrongly. |
In your example there is no inference. In that case, I agree that typeof should return whatever the explicit annotation is. However, in the case of delegates/lambdas it's not that obvious: these always have a body and their attributes are explicitly embedded in the type (the same as Also, for template functions, I think that To summarize: should function declarations that have their attributes inferred (deletgates, lambdas, function templates) explicitly be marked as |
I agree. We could add a |
This code will still have breaking changes. |
As far as I know the language spec makes no guarantees whatsoever about the specific contents of |
Needs #13121 for trailing whitespace free tests. |
#13121 has been merged. |
Yes, but I still need to commit the header generation tests and fix the logic for it. |
ae8edf6
to
6a27fbf
Compare
I added the rest of the test cases. I just need to add cast() and virtual functions.
As I suspected, this fails to compile on Phobos. Should I add a flag to the compiler to make the transition or make |
https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1029.md Signed-off-by: Luís Ferreira <contact@lsferreira.net>
6a27fbf
to
206393a
Compare
We have the following solutions:
Either way we choose it's a bit nasty. I, personally, would be inclined to go the latter route since I expect that the way phobos uses |
Another alternative would be to deprecate |
|
I don't think so, the default, which is the "hidden"
This DIP only creates a contrary attribute to negate |
I can create an attempt to fix the breakage on Phobos, but that doesn't guarantee the fact that this will break user code, although the user should be prepared for those changes, as any other attribute might get introduced. To do this attempt, should I add a synchronization/transition flag to do that transition? If yes, what flag? My suggestion would be to stick with a |
Ping @RazvanN7 :) |
The more I think about this, the more I feel that a deprecation is a more suitable alternative. We added a new function attribute, people need to update their code to take it into account. |
I can agree with the deprecation if we implement this to the other attributes, I would say. |
Yes, definitely, if other attributes will be implemented then the same deprecation path must be taken. |
Ah, so you mean this?
I thought you were talking about implementing the new trait and deprecate the older one. But yeah, I share the same opinion, if that's what you mean. |
Yes. |
Ok, I can go with implementing it that way. I think this is the best way among the other options we have. |
ping @ljmf00 |
@ljmf00 any updates on this? This is so close to completion, it's a pity it got stalled so much. |
https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1029.md
Signed-off-by: Luís Ferreira contact@lsferreira.net
throw
as Function Attribute dlang.org#3082)throw
throw
? how does that interact with toplevelthrow
/nothrow
?nothrow
throw:
/nothrow:
interacting with function/template declarations__traits(getFunctionAttributes, func)
nothrow
tothrow
throw
tonothrow
CC: @WalterBright