-
-
Notifications
You must be signed in to change notification settings - Fork 704
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
Provide utilities to auto implement opEquals, opCmp and toHash #7049
Conversation
|
Thanks for your pull request and interest in making D better, @edi33416! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
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 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 + phobos#7049" |
|
Out of curiosity, what is the vision for how this will relate to DMD clone.d's |
std/typecons.d
Outdated
| } | ||
| else static if (is (Rhs == struct)) | ||
| { | ||
| alias ContravariantRhsT = auto ref const Unqual!Rhs; |
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.
Unfortunately, I don't think you can use storage classes like auto ref, ref, lazy or scope with aliases.
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.
interesting. If these attributes are accepted but "noop" then their presence should be prevented by the compiler.
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.
Unfortunately, I don't think you can use storage classes like
auto ref,ref,lazyorscopewith aliases.
It didn't even occur to me that this might be ignored. I agree with @Basile-z that this should throw a compiler error if it's not supported: silently ignoring can lead to hard to discover bugs.
@ZombineDev do you know what is the reason to ignore the storage classes?
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.
Because they are not type constructors, but parameter storage classes and alias can "point" to only types, symbols and occasionally values of which storage classes are neither.
For example, you can't declare a function local or class/struct member variable with the same storage classes as a parameter.
This is unfortunately by design and will take an exceptional DIP to convince W&A otherwise. Manu has tried to argue for making ref a first class citizen (i.e. a type constructor), so it can play nice with std.meta metaprogramming for years in the newsgroup, but has not succeeded so far. Many others have tried to independently argue that scope should be a transitive (again meaning a type constructor like const), but have again failed.
For example, my interpretation of the outcome of this bug report https://issues.dlang.org/show_bug.cgi?id=17764 is that Walter values parameter storage classes not being type constructors more then memory safety.
I wonder what would be @atilaneves take on this.
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 have an opinion on it yet, but would love to hear arguments for/against/about 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.
In general, the problem with parameter storage classes is that they don't compose well. (Other storage classes like static are fine as they are, because you rarely have composition problems there.)
In case of DIP1000 / scope the problem is more dramatic as the compiler simply gives up and allows memory-unsafe code in @safe functions.
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.
Manu has tried to argue for making ref a first class citizen
about ref, as you should know, it has been partially addressed lately (ref for alias function is handled but still not as a type ctor, rather as a special case).
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.
ref for alias function is handled but still not as a type ctor, rather as a special case
@Basile-z I didn't know this, thanks for point this out! Can you point me to the PR that changed this lately?
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.
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 know this enhancement, but it's unrelated. The core issue is the following:
alias RI = ref int;
pragma (msg, RI); // prints `int` during compilation
void inc(RI ri) { ri++; }
void main()
{
int x = 10;
x.writeln; // prints `10` at run-time
x.inc;
x.writeln; // prints `10` at run-time
}That doesn't work either:
alias Ref(T) = ref T;
void inc(Ref!int ri) { ri++; }
pragma (msg, typeof(&inc)); // prints `void function(int ri)` at compile-time
I might be wrong, but this will not cause trouble for The user can even provide the name of a method/property to be used inside the operators. The reason for this is: the user used to have a field Another benefit, imho, is that the implementation is in the library and it's easier for a contributor to understand. What do you think? |
I think it could be convenient. Syntax-wise it might be worth considering an approach based around annotating specific fields (either whitelist or blacklist) like in Java's Project Lombok: import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
@EqualsAndHashCode.Exclude private Shape shape = new Square(5, 10);
private String[] tags;
@EqualsAndHashCode.Exclude private int id;
public String getName() {
return this.name;
}
@EqualsAndHashCode(callSuper=true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
} |
At DConf, @atilaneves also asked me if I have thought about using UDAs to exclude fields. The reason I'm saying we can have it as an enhancement is because I think we could see how the users use the current implementation and if they feel that UDAs would increase their productivity / make the code nicer. As a final note, I'm not opposing to using UDAs those are just my thoughts. |
This is easily disambiguated by using the fully qualified name when/if necessary. |
|
@atilaneves is UDAs support required as part of this PR or can it come as a future PR? |
|
@edi33416 I wouldn't say UDA support is required, no. |
c69b6b3
to
a298287
Compare
|
@edi33416 What are your plans with this PR? |
|
@edi33416 Is there anything blocking this PR? |
a298287
to
0789366
Compare
|
@RazvanN7 I don't think there are any blockers for this PR. I've just rebased with master and pushed the updated branch. |
| assert(t1.toHash() == t2.toHash()); | ||
| } | ||
|
|
||
| template GetAllFields(T) |
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 a public symbol, therefore it needs to be documented. Same goes for the rest.
|
Since this PR is adding so many public symbols, it requires a changelog entry. |
|
This assert seems to be tripping: |
5135fcb
to
93328de
Compare
|
I've been wondering lately if this should go in What do you think? |
|
@atilaneves what's your stance on this PR? If you need some background: these methods will come in handy once users start using |
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 one PR for all three functions and not one for each?
| @@ -5574,6 +5574,779 @@ private | |||
| extern(C) pure nothrow Object typecons_d_toObject(void* p); | |||
| } | |||
|
|
|||
|
|
|||
| // Implement{Ordered, Equals, Hash} helper function | |||
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 seems to be the wrong comment?
| { | ||
| // TODO: fix this legacy bad behavior, see | ||
| // https://issues.dlang.org/show_bug.cgi?id=17244 | ||
| static assert(is(U1 == U2), "Internal error."); |
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.
Surely there's a better message than "Internal error"?
| enum len = U.length; | ||
|
|
||
| int r = 0; | ||
| static foreach (i, unused; U) |
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.
static foreach(i; 0 .. len)?
|
@edi33416 please address @atilaneves 's comments. Also, can you split this up into 3 PRs? |
|
PR closed as stalled. If you wish to resurrect it you are welcome to do so, however it is recommend to split this up in to multiple PR's. |
This still needs a changelog entry and ddoc'ed style unittests.