-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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 a feature for a sound specialization subset #68970
Conversation
☔ The latest upstream changes (presumably #69004) made this pull request unmergeable. Please resolve the merge conflicts. |
Is there any feeling whether this would be stabilized ahead of soundness issues being fixed in specialization?
If this feature becomes stable, it might just be possible to rework |
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.
First round of reviews! Mostly requests for documentation, but also some substantive questions.
pub enum TraitSpecializationKind { | ||
/// The default. Specializing on this trait is not allowed. | ||
None, | ||
/// Specializing on this trait is allowed because it doesn't have any |
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 find this term ("Specializing on this trait") a bit ambiguous. I think it means "Specialized impls of this trait are allowed"? Maybe we can update the comment to be more precise.
You mentioned that this is not sound in general. I'm not sure if that's true, actually -- for marker traits, I don't think specialization causes any problems. Maybe I don't know what you mean by that, though.
} | ||
} | ||
|
||
// Get substs for the two impls so that they have the same `TraitRef`. |
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 this function would benefit from a bigger comment (also, doc comment). I think it should be something like:
Given a specializing impl impl1
, and the base impl impl2
, returns two substitutions (S1, S2)
that equate their trait references. The returned types are expressed in terms of the generics of impl1.
Example:
impl<A, B> Foo<A> for B { /* impl2 */ }
impl<C> Foo<Vec<C>> for C { /* impl1 */ }
Here we would return S1 = [C]
and S2 = [Vec<C>, C]
.
parent_substs: &Vec<GenericArg<'tcx>>, | ||
span: Span, | ||
) { | ||
let mut base_params = cgp::parameters_for(parent_substs, true); |
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 would be good to comment why you chose this function. I think the example would be something like:
impl<A> Foo for A { }
impl<B> Foo for dyn Iterator<Item = (B, B)> { }
and here we want to say that, even though B
repeats, it's ok because it's constrained?
Another example might be like dyn Foo<B, Out = B>
-- this would also be approved.
But, thinking on it more, is that really correct? Like consider the type dyn Foo<&'a u32, Out = &'b u32>
. How can we decide whether the impl applies? We'd have to know that 'a = 'b
.
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.
parameters_for(_, true)
doesn't have any special handling of any TyKind
. I'm using it to conveniently handle the 3 different kinds of params.
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.
Oh, I see. Man, I hate random booleans in the signature, but pre-existing I suppose. :)
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.
And quite possibly my fault.
83ef0cd
to
c9cd389
Compare
@bors r+ |
📌 Commit c9cd389dd690725c15d5dd0cf030376ea7fdeccb has been approved by |
☔ The latest upstream changes (presumably #69076) made this pull request unmergeable. Please resolve the merge conflicts. |
Currently the only difference between it and `specialization` is that it only allows specializing functions.
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
☀️ Test successful - checks-azure |
// Limit `min_specialization` to only specializing functions. | ||
gate_feature_fn!( | ||
&self, | ||
|x: &Features| x.specialization || (is_fn && x.min_specialization), |
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.
Are there soundness issues with specializing associated constants? If not, could they be permissible in min_specialization
?
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 reason to only for functions being a simpler than constants and types is that we only ever work out which impl to select for a (non-const) function when generating code. For constants we can evaluate them during type checking and during codegen and I wanted to avoid potential bugs with any mismatches there.
This implements a new feature (
min_specialization
) that restricts specialization to a subset that is reasonable for the standard library to use.The plan is to then:
libcore
andliballoc
to compile withmin_specialization
.feature(specialization)
(and other unsound, type system extending features) in the standard library.specialization
.min_specialization
The rest of this is an overview from a comment in this PR
Basic approach
To enforce this requirement on specializations we take the following approach:
impl2
so that the implemented trait and self-type match those forimpl1
.'static
in the substs ofimpl2
.impl1
occur at most once in the unconstrained substs forimpl2
. A parameter is constrained if its value is completely determined by an associated type projection predicate.impl1
also exist onimpl2
(after matching substs).Example
Suppose we have the following always applicable impl:
We get that the subst for
impl2
are[T, std::vec::IntoIter<T>]
.T
is constrained to be<I as Iterator>::Item
, so we check onlystd::vec::IntoIter<T>
for repeated parameters, which it doesn't have. The predicates ofimpl1
are onlyT: Sized
, which is also a predicate of impl2`. So this specialization is sound.Extensions
Unfortunately not all specializations in the standard library are allowed by this. So there are two extensions to these rules that allow specializing on some traits.
rustc_specialization_trait
If a trait is always applicable, then it's sound to specialize on it. We check trait is always applicable in the same way as impls, except that step 4 is now "all predicates on
impl1
are always applicable". We require thatspecialization
ormin_specialization
is enabled to implement these traits.rustc_unsafe_specialization_marker
There are also some specialization on traits with no methods, including the
FusedIterator
trait which is advertised as allowing optimizations. We allow marking marker traits with an unstable attribute that means we ignore them in point 3 of the checks above. This is unsound but we allow it in the short term because it can't cause use after frees with purely safe code in the same way as specializing on traits methods can.r? @nikomatsakis
cc #31844 #67194