-
Notifications
You must be signed in to change notification settings - Fork 68
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
Unify group
code snippet with other non-user-constructible classes
#627
base: main
Are you sure you want to change the base?
Unify group
code snippet with other non-user-constructible classes
#627
Conversation
`group` is not user-constructible, but explicit information about its constructors was missing from the code snippet.
Adding the editorial label. The spec was already clear that these types are not user constructible, so this PR just clarifies that in the synopsis. I'll wait a few days before merging in case others have comments. |
Just curious about this implementation detail leakage: I have the feeling that "not user-constructible" does not mean it has no default constructor, just it cannot be constructed by the user. For example the constructor could be |
This situation occurs in several places in the SYCL spec. We want to document that one of the implicitly-defined member functions is not available to the application. We currently shows these cases as There is a similar situation with |
I agree with Ronan here. We need to be precise about what "not user constructible" means, and I think it's different to saying that the default constructor is deleted. I ran some experiments (see https://godbolt.org/z/d1h4o8Ge3), and the results are a little surprising. clang doesn't complain about the construction of struct S2 {
S2() = delete;
};
S2 a; // this is invalid
S2 b{}; // this is (surprisingly) valid, even though the constructor is deleted
struct S3 {
private:
S3() {};
};
S3 c; // this is invalid
S3 d{}; // this is also invalid Does anybody understand this behavior? Regardless, I think that in order to accept |
@Pennycook There was a core issue on this (I can't check at the moment because open-std.org is down). The reason it compiles in C++17 is because |
Oof. Does that mean that we can't use |
I think we can use We might have to call out that these types are not aggregates. |
I don't think they're required to be, but I think they currently can be. After the discussion in #532, we reached the conclusion that classes like
Would we just say something like "These types must not be aggregates." and leave it up to individual implementations to decide how to enforce that? I can imagine some implementations will want to rely on private/protected members, while others will want to rely on a private/protected constructor. |
Stateless means empty, which does not seem like an aggregate to me, since in C++ it will have a size of 1 anyway to enforce uniqueness of objects. Otherwise, I was looking at some types in C++. For example https://en.cppreference.com/w/cpp/types/type_info describes the constructor as deleted but looking more carefully at https://eel.is/c++draft/type.info it is just that at least there is no exposed public constructor. At the end, this is not super important. We could always have a public constructor |
> Would we just say something like "These types must not be aggregates." and leave it up to individual implementations to decide how to enforce that? I think so, especially since this is really just an issue for those compilers supporting C++17. Any version of SYCL that requires C++20 or later will likely just delete the default constructor. |
Looking into 16.3.2.4 Detailed specifications it says the following:
This makes me think that we should actually omit the description of any constructors to say that there aren't any (user-visible) constructors. |
|
WG suggests we change the spec to say not default-constructible and add comment to synopsis that default constructor is not available. |
Following up offline, as discussed... I agree with this, but I still think we should go one step further and also say either:
There are good reasons to allow SYCL implementations to define things like private/protected constructors (because they need to make the object somehow!). I don't think there is a good reason to allow SYCL implementations to (perhaps accidentally) implement If |
I'm very interested in the conclusion to the non-user-constructible wording problem.
"Not default-initializable" makes more sense to me. "Aggregate" is more like an implementation detail; "initializable" describes a user-facing interface.
I'd take it one step further. If the SYCL spec literally contains the code
I think the key question is, what should happen when a user does |
Illustrating my opinion using
I suggest we change this to something along these lines:
My interpretation of this is that a SYCL implementation is not required to provide a default constructor or to allow |
This should not compile at least. group() = delete("The SYCL 2032 specification does not allow default construction because we care a lot for your safety"); I am in favor of using |
@gmlueck, I agreed with you up until this point. I think a reasonable C++ developer would expect the phrasing "The |
Agree with this ^
I'm not sure what to make of this. Are you saying that there are some requirements that the spec places on an implementation that a vendor extension is allowed to override, and there are other requirements that a vendor extension is not allowed to override? How does (can?) the spec capture these two classes of requirements? |
To be honest, I haven't thought about this a lot until now, so I'm not sure how to express my opinion precisely. I think my feelings are roughly in line with the extension guidelines, but I might not be interpreting them correctly. They could be clearer.
The restrictions on device code are a weaker example. The specification says that certain things (e.g., virtual functions) are "not permitted", but doesn't specify the behavior of virtual functions. My interpretation of the guidelines allows an extension to add support for virtual functions (overriding a restriction), without changing specified behavior. (If the specification had said that implementations must fail to compile device code containing virtual functions, I think it would be reasonable to require an implementation to pass a CTS test checking that; an implementation would have to disable the extension to be conformant.) I feel especially strongly when it comes to statements about type traits and concepts, because any change to those changes specified behavior. Whether we're asserting that a class must or must not satisfy a certain trait/concepts, I don't see any point specifying the behavior if extensions can override it. We shouldn't encourage developers to use these traits/concepts if they're unreliable, because it might break overload resolution in some spectacular ways. If we want to give implementations wiggle room in a case like this, we should either say nothing or explicitly state that certain behavior is "not guaranteed". |
Perhaps this language captures my intent better, though it is more wordy.
Responding to @keryell: It does seem nice for users to require all implementations to |
group
is not user-constructible, but explicit information about its constructors was missing from the code snippet.