-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Fix two ICEs by introducing a new layout error variant for unexpected unsized #135158
base: master
Are you sure you want to change the base?
Conversation
HIR ty lowering was modified cc @fmease |
Feel free to ask me anything. |
This comment has been minimized.
This comment has been minimized.
(The fluent slug needs to be sorted lexicographically) |
5a546aa
to
6d48c1a
Compare
seems somewhat unfortunate diagnostics-wise because:
Introducing this Anyway, cc @lukas-code. |
This comment has been minimized.
This comment has been minimized.
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 particular, let me know if you would like the addition of a test within the
tests/ui/layout/
folder.
Yes, this definitely needs tests, for both of the issues. See here for a guide on adding tests.
@@ -231,6 +231,7 @@ impl fmt::Display for ValidityRequirement { | |||
pub enum LayoutError<'tcx> { | |||
Unknown(Ty<'tcx>), | |||
SizeOverflow(Ty<'tcx>), | |||
UnexpectedUnsized(Ty<'tcx>), |
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.
Making a specific variant for UnexpectedUnsized
seems to specifically targeted to me. There are many more sources of LayoutError::Unknown
and I also managed to trigger the same ICE from this one here:
rust/compiler/rustc_ty_utils/src/layout.rs
Lines 279 to 281 in 243d2ca
let BackendRepr::Scalar(metadata) = metadata_layout.backend_repr else { | |
return Err(error(cx, LayoutError::Unknown(pointee))); | |
}; |
#![feature(ptr_metadata)]
#![feature(trivial_bounds)]
fn return_str()
where
str: std::ptr::Pointee<Metadata = str>,
{
[(); { let _a: Option<&str> = None; 0 }];
}
Rather than replacing all the LayoutError::Unknown
that can cause this ICE, it might make more sense to add new variant TooGeneric
that is only used when attempting to compute the layout of a type parameter or rigid alias (ty::Param
/ ty::Alias
at the bottom of the match in layout_uncached
) so that we can be sure that the cause of these is the MIR being too generic and not some weird trait bound or broken type definition and default to reporting all other layout errors except TooGeneric
in const eval.
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.
Actually, looking at this again, for rigid aliases we can only emit LayoutError::TooGeneric
if the type actually contains type parameters (i.e. if ty.has_param()
) and need to emit LayoutError::Unknown
otherwise, because
for this code, which does not have any type parameters, we should emit the "cannot compute layout of <() as Project>::Assoc
" (or whatever it's going to be called) error:
#![feature(trivial_bounds)]
trait Project {
type Assoc;
}
fn foo() where (): Project {
// currently ICEs, but should be the new error:
[(); size_of::<<() as Project>::Assoc>()];
}
and only if there are actual type parameter, then we should emit the "constant expression depends on a generic parameter" error:
trait Project {
type Assoc;
}
fn foo<T>() where T: Project {
// current error ("constant expression depends on a generic parameter")
[(); size_of::<<T as Project>::Assoc>()];
}
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.
Rather than replacing all the LayoutError::Unknown that can cause this ICE, it might make more sense to add new variant TooGeneric that is only used when attempting to compute the layout of a type parameter or rigid alias (ty::Param / ty::Alias at the bottom of the match in layout_uncached) so that we can be sure that the cause of these is the MIR being too generic and not some weird trait bound or broken type definition and default to reporting all other layout errors except TooGeneric in const eval.
That sounds good to me.
As far as ty::Param
and ty::Alias
is concerned, it is okay.
The ICE in #135020 is generated by layout_of_struct_or_enum
which returns LayoutCalculatorError:: UnexpectedUnsized
, would you like to handle this as TooGeneric
as well?
As far as I know, I don't think that would be correct but let me know.
Maybe we should introduce a new variant to LayoutCalculatorError
?
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.
by
layout_of_struct_or_enum
which returnsLayoutCalculatorError::UnexpectedUnsized
, would you like to handle this asTooGeneric
as well?
No, UnexpectedUnsized
has nothing to do with generics at all.
My idea behind LayoutError::TooGeneric
is that this error means that the layout cannot be computed, because it depends on some generic type parameter T
that has not been instantiated yet.
For example
fn foo<T>() {
[(); size_of::<T>()];
/* ^ the layout of the type parameter `T` cannot be computed,
therefore the constant `size_of::<T>()` cannot be evaluated */
}
And LayoutError::Unknown
would be a catch-all, like it currently is. So for example if we attempt to compute the layout of (str, str)
that would return LayoutError::Unknown
. Or if the layout computation fails for some other reason that is not further specified, that would return LayoutError::Unknown
.
The semantic difference between the two would be that LayoutError::TooGeneric
means that the layout may become available if the type parameters are further instantiated, i.e. in the example above if we called foo::<i32>()
then the layout of the T
will become known, but LayoutError::Unknown
always means "this type is broken, the layout is meaningless".
Then we can map LayoutError::TooGeneric
to ErrorHandled::TooGeneric
here:
rust/compiler/rustc_const_eval/src/const_eval/error.rs
Lines 143 to 145 in 3323bbe
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { | |
ErrorHandled::TooGeneric(span) | |
} |
This const eval error basically means "we cannot evaluate this constant because there are uninstantiated generic parameters" and the layout error
TooGeneric
would mean the same.
On the contrary, LayoutError::Unknown
would fall through to the error reporting case below and actually report the error, because no further monomorphization can save us:
rust/compiler/rustc_const_eval/src/const_eval/error.rs
Lines 152 to 176 in 3323bbe
_ => { | |
let (our_span, frames) = get_span_and_frames(); | |
let span = span.substitute_dummy(our_span); | |
let err = mk(span, frames); | |
let mut err = tcx.dcx().create_err(err); | |
// We allow invalid programs in infallible promoteds since invalid layouts can occur | |
// anyway (e.g. due to size overflow). And we allow OOM as that can happen any time. | |
let allowed_in_infallible = matches!( | |
error, | |
InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_) | |
); | |
let msg = error.diagnostic_message(); | |
error.add_args(&mut err); | |
// Use *our* span to label the interp error | |
err.span_label(our_span, msg); | |
let g = err.emit(); | |
let reported = if allowed_in_infallible { | |
ReportedErrorInfo::allowed_in_infallible(g) | |
} else { | |
ReportedErrorInfo::const_eval_error(g) | |
}; | |
ErrorHandled::Reported(reported, span) | |
} |
At the same time the new LayoutError::TooGeneric
would also be used instead of LayoutError::Unknown
in the SizeSkeleton
, which is used to typecheck transmute
:
rust/compiler/rustc_middle/src/ty/layout.rs
Line 353 in 243d2ca
Err(err @ LayoutError::Unknown(_)) => err, |
And for checking the CMSE function signatures for generic parameters:
Unknown(ty) => { |
All so that we can discern layout errors due to "this type has generics" vs "this type in invalid".
Maybe we should introduce a new variant to
LayoutCalculatorError
?
The layout calculator is more low-level than the layout_of_uncached
function and does the actual layout computation algorithm. It only knows very basic types (scalars/ADTs) and is unaware of generics (type parameters) for example. The LayoutCalculatorError
enum is for errors that stem from the layout optimizing algorithm itself, so adding a new variant there doesn't really seem appropiate.
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.
Okay, so you would like to not handle the issue #135138 in this PR and consequently when the layout_of_struct_or_enum
returns LayoutCalculatorError:: UnexpectedUnsized
?
So handling just ty::Param
& ty::Alias
as TooGeneric
error (in some way) and the problems that this might raise?
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.
Ah, sorry, I understand what you mean now. Yeah my idea is that just ty::Param
& ty::Alias
(if the alias has a param) are handled as TooGeneric
and all other LayoutError::Unknown
will stay the same -- including the one from LayoutCalculatorError::UnexpectedUnsized
.
Currently, we convert all LayoutError::Unknown
(which can happen for various reasons, and not just the type being "too generic") to the const eval error ErrorHandled::TooGeneric
and only report the other variants of LayoutError
as "failed to evaluate constant" errors. The idea with LayoutError::TooGeneric
is that now only layout errors from ty::Param
& ty::Alias
that are actually "too generic" (in the sense that they contain generic type parameters) are mapped to ErrorHandled::TooGeneric
and all remaining LayoutError::Unknown
s, including the one from LayoutCalculatorError::UnexpectedUnsized
should then be reported as "failed to evaluate constant" errors.
So if you implement LayoutError::TooGeneric
like that, then you will fix #135138 without using LayoutError::TooGeneric
for LayoutCalculatorError::UnexpectedUnsized
(which has nothing to do with generics).
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.
Great! It is exactly as I expected now.
Thank you for the clarification :D
6d48c1a
to
fc6c929
Compare
This comment has been minimized.
This comment has been minimized.
3463d0f
to
3d8a88a
Compare
This comment has been minimized.
This comment has been minimized.
Signed-off-by: FedericoBruzzone <federico.bruzzone.i@gmail.com>
Signed-off-by: FedericoBruzzone <federico.bruzzone.i@gmail.com>
Signed-off-by: FedericoBruzzone <federico.bruzzone.i@gmail.com>
3d8a88a
to
0ef08fb
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Signed-off-by: FedericoBruzzone <federico.bruzzone.i@gmail.com>
5a0ba5d
to
514360b
Compare
This PR add the
UnexpectedUnsized
variant to theLayoutError
.Currently, it allows us to gracefully handle the
UnexpectedUnsized
error generated bylayout_of_struct_or_enum
when#![feature(trivial_bounds)]
is specified.With this PR these issues and their respective ICEs are resolved:
encountered unexpected unsized field in layout of
#135020For instance, given this code:
the following error is generated
Then, according to @lukas-code: "Keeping this assertion in some sensible manner seems like it would be more effort than it's worth". So, I decided to remove it and the ICE related to #135138 has been fixed.
rust/compiler/rustc_ty_utils/src/layout.rs
Lines 107 to 111 in 6ca6659