Skip to content
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

Implement DIP 1038: @mustuse #13589

Merged
merged 25 commits into from
Apr 7, 2022
Merged

Conversation

pbackus
Copy link
Contributor

@pbackus pbackus commented Jan 30, 2022

Depends on dlang/druntime#3712.

Supersedes #11765.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @pbackus! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your 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 locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#13589"

@pbackus pbackus marked this pull request as ready for review January 30, 2022 20:59
@pbackus pbackus requested a review from ibuclaw as a code owner January 30, 2022 21:25
Copy link
Contributor

@MoonlightSentinel MoonlightSentinel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rough first review, looks good overal.

Some missing bits:

  • spec PR
  • changelog

src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
import core.attribute;

@mustUse int n;
@mustUse alias A = int;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a warning / error? Seems like an error that is very easy to do and silently allows broken code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct per the formal spec in DIP 1038, and is consistent with how other attributes work (e.g., @nogc).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct per the formal spec in DIP 1038,

The "Diagnostics" section (and the DIP in general) doesn't define rules for alias declarations - unless the absence is to be interpreted as "no diagnostics" instead of "not considered".

and is consistent with how other attributes work (e.g., @nogc).

That's only partially true. E.g. the @nogc attribute is discarded but misuse usually results in a subsequent error because @nogc code tries to call a non-@nogc function pointer / delegate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relevant rule, from the "Formal Specification" section, is:

It is a compile-time error to discard an expression if all of the following are true:
[...]

  • its type is a struct or union type whose declaration is annotated with @mustUse.

Whether that type is referred to via an alias or directly, it is the attribute on the type itself that matters.

Copy link
Contributor

@MoonlightSentinel MoonlightSentinel Jan 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed but that also assumes knowledge of the wording in the DIP. The current behavior might not be as obvious for common users.

Whether @mustUse alias ... should raise additional diagnostics is not defined / forbidden by the DIP. The current implementation already raises errors when annotating e.g. functions with @mustUse for the same reason - it's something a user would do but isn't supported and would have no effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed but that also assumes knowledge of the wording in the DIP.

The wording from the DIP's "Formal Specification" section will be incorporated into the language spec.

Whether @mustUse alias ... should raise additional diagnostics is not defined / forbidden by the DIP.

The compiler is, of course, free to issue additional diagnostics above and beyond what the spec requires.

As a general rule, DMD does not currently issue diagnostics for attributes used on declarations that do not support them. If we wish to revisit that policy, fine, but I do not think this PR is the place for it.

The current implementation already raises errors when annotating e.g. functions with @mustUse for the same reason - it's not supported and would have no effect.

@mustUse on functions is specifically reserved by the DIP for potential future expansion. @mustUse on aliases is not reserved.

Copy link
Contributor

@RazvanN7 RazvanN7 Feb 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @pbackus. The DIP specifically states: "It is a compile-time error to annotate any function declaration or aggregate declaration other than a struct or union declaration with the @mustuse attribute. The purpose of this rule is to reserve such usage for possible future expansion.". So the implementation correctly covers the spec.

On the topic of ignoring attributes, this comes up once in a while. The general consensus is that we should ignore them because you also have the possibility to use attributes on-masse (e.g@safe:). If you issue an error for all the declarations for which the attribute doesn't make sense then you are basically making en-masse attributes unusable.

src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/expressionsem.d Show resolved Hide resolved
@pbackus
Copy link
Contributor Author

pbackus commented Jan 30, 2022

@MoonlightSentinel Thanks for the review! I've pushed some new commits addressing your comments.

@pbackus
Copy link
Contributor Author

pbackus commented Jan 31, 2022

Spec PR: dlang/dlang.org#3201

Copy link
Contributor

@RazvanN7 RazvanN7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@RazvanN7
Copy link
Contributor

RazvanN7 commented Feb 1, 2022

cc @WalterBright

@pbackus
Copy link
Contributor Author

pbackus commented Feb 6, 2022

If I understand the DIP process correctly, this PR should not require @WalterBright's direct approval as long as reviewers are satisfied that it correctly implements DIP 1038. Perhaps @mdparker can clarify.

@mdparker
Copy link
Member

mdparker commented Feb 7, 2022

I have nothing to do with the implementation side, and that is separate from the DIP process. That falls under Razvan's purview as PR manager.

@RazvanN7
Copy link
Contributor

RazvanN7 commented Feb 7, 2022

To the best of my knowledge, this PR correctly implements DIP1038, however, I would feel more comfortable to merge this if @WalterBright would give his approval. However, since Walter is very busy with importC he might not want to be sidetracked this, therefore, I will wait for another 72h before merging this.

@RazvanN7 RazvanN7 added the 72h no objection -> merge The PR will be merged if there are no objections raised. label Feb 7, 2022
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
src/dmd/must_use.d Outdated Show resolved Hide resolved
auto argExp = (*tiargs)[0].isExpression();
auto op = argExp ? argExp.isStringExp() : null;
scope plusPlus = new StringExp(Loc.initial, "++");
scope minusMinus = new StringExp(Loc.initial, "--");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned these allocations will become a major performance problem and memory consumption problem, as they are called for a large fraction of all the expression nodes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed from scope to static const.

src/dmd/must_use.d Outdated Show resolved Hide resolved
// e.g., (auto tmp = a, ++a, tmp)
auto left = comma.e1 ? comma.e1.isCommaExp() : null;
auto middle = left ? left.e2 : null;
if (middle && isIncrementOrDecrement(middle))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These three lines have multiple redundant tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid it's not clear to me which tests here are redundant. By my count, there are 3, and all test separate pointers:

  • comma.e1
  • comma.e1.isCommaExp() (left)
  • comma.e1.isCommaExp().e2 (middle)

Is there some undocumented invariant that would allow one or more of these tests to be skipped? (E.g., "e1 and e2 of a CommaExp are never null".)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!comma.e1 is tested which then sets left to null, which is then tested for null to set middle, then middle is tested for null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, if comma.e1 is false, none of the rest of the code needs to be executed.

auto fd = ce.f;
auto id = fd ? fd.ident : null;
// opXXX are reserved identifiers, so it's ok to match too much here
if (id && id.toString().startsWith("op") && id.toString().endsWith("Assign"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about the performance implications of doing this an astounding number of times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to == comparison with Id.assign and friends.

@pbackus
Copy link
Contributor Author

pbackus commented Apr 6, 2022

Rebased to resolve merge conflicts.

@pbackus
Copy link
Contributor Author

pbackus commented Apr 6, 2022

Ping @WalterBright @dkorpel @RazvanN7

@dkorpel
Copy link
Contributor

dkorpel commented Apr 6, 2022

The files src/dmd/expressionsem.d.orig and src/dmd/statementsem.d.orig look accidentally committed.

@pbackus
Copy link
Contributor Author

pbackus commented Apr 6, 2022

Whoops; good catch. They're gone now.

@dkorpel
Copy link
Contributor

dkorpel commented Apr 6, 2022

good catch.

Kind of hard not to notice when the diff says +18,000 lines 🙂

I've e-mail Walter asking him to either re-review, or trust Razvan and me to approve this. If he doesn't respond, I'll bring it up in the foundation meeting Friday.

@ghost
Copy link

ghost commented Apr 6, 2022

I'd suggest to merge all the compilable tests in a single file

@pbackus
Copy link
Contributor Author

pbackus commented Apr 6, 2022

@SixthDot The rationale behind splitting them is that shorter tests are easier to work with when you're running the compiler in a debugger.

@ghost
Copy link

ghost commented Apr 6, 2022

I'll not insist much since this is just a detail but but as counter argument: you have plenty of option in case you would need to debug a bigger test file

  • p stuff.loc
  • comment code
  • version(all) / version(none)

@pbackus
Copy link
Contributor Author

pbackus commented Apr 6, 2022

@SixthDot Yes, that's exactly my point: with a short test file, you don't need to do any of that. So short tests are easier to work with.

Can you articulate a compelling benefit of combining the tests, which outweighs the benefit of splitting them?

@maxhaton
Copy link
Member

maxhaton commented Apr 6, 2022

Merging runnable tests is usually a good thing but for compilables the overhead of the files isn't very high unless you're doing them all serially so I prefer separate test files.

@dlang-bot dlang-bot merged commit 7be718c into dlang:master Apr 7, 2022
New language features automation moved this from In progress to Done Apr 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
9 participants