Skip to content

Commit

Permalink
Avoid ICEing miri on layout query cycles
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed May 2, 2023
1 parent 7b99493 commit 7bc6d59
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 44 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_middle/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ middle_values_too_big =
middle_cannot_be_normalized =
unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized
middle_cycle =
a cycle occurred during layout computation
middle_strict_coherence_needs_negative_coherence =
to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled
.label = due to this attribute
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub enum LayoutError<'tcx> {
Unknown(Ty<'tcx>),
SizeOverflow(Ty<'tcx>),
NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>),
Cycle,
}

impl IntoDiagnostic<'_, !> for LayoutError<'_> {
Expand All @@ -230,6 +231,9 @@ impl IntoDiagnostic<'_, !> for LayoutError<'_> {
diag.set_arg("failure_ty", e.get_type_for_failure());
diag.set_primary_message(fluent::middle_cannot_be_normalized);
}
LayoutError::Cycle => {
diag.set_primary_message(fluent::middle_cycle);
}
}
diag
}
Expand All @@ -250,6 +254,7 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
t,
e.get_type_for_failure()
),
LayoutError::Cycle => write!(f, "a cycle occurred during layout computation"),
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<ty::Binder<'_, ty::F
}
}

impl<'tcx, T> Value<TyCtxt<'tcx>, DepKind> for Result<T, ty::layout::LayoutError<'_>> {
fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo<DepKind>]) -> Self {
Err(ty::layout::LayoutError::Cycle)
}
}

// item_and_field_ids should form a cycle where each field contains the
// type in the next element in the list
pub fn recursive_type_error(
Expand Down
93 changes: 49 additions & 44 deletions src/librustdoc/html/templates/type_layout.html
Original file line number Diff line number Diff line change
@@ -1,53 +1,58 @@
<h2 id="layout" class="small-section-header"> {# #}
<h2 id="layout" class="small-section-header"> {# #}
Layout<a href="#layout" class="anchor">§</a> {# #}
</h2> {# #}
<div class="docblock"> {# #}
{% match type_layout_size %}
{% when Ok(type_layout_size) %}
<div class="warning"> {# #}
<p> {# #}
<strong>Note:</strong> Most layout information is <strong>completely {#+ #}
unstable</strong> and may even differ between compilations. {#+ #}
The only exception is types with certain <code>repr(...)</code> {#+ #}
attributes. Please see the Rust Reference’s {#+ #}
<a href="https://doc.rust-lang.org/reference/type-layout.html">“Type Layout”</a> {#+ #}
chapter for details on type layout guarantees. {# #}
</p> {# #}
</div> {# #}
<p><strong>Size:</strong> {{ type_layout_size|safe }}</p> {# #}
{% if !variants.is_empty() %}
<p> {# #}
<strong>Size for each variant:</strong> {# #}
</p> {# #}
<ul> {# #}
{% for (name, layout_size) in variants %}
<li> {# #}
<code>{{ name }}</code>: {#+ #}
{{ layout_size|safe }}
</li> {# #}
{% endfor %}
</ul> {# #}
{% endif %}
{# This kind of layout error can occur with valid code, e.g. if you try to
get the layout of a generic type such as `Vec<T>`. #}
{% when Ok(type_layout_size) %}
<div class="warning"> {# #}
<p> {# #}
<strong>Note:</strong> Most layout information is <strong>completely {#+ #}
unstable</strong> and may even differ between compilations. {#+ #}
The only exception is types with certain <code>repr(...)</code> {#+ #}
attributes. Please see the Rust Reference’s {#+ #}
<a href="https://doc.rust-lang.org/reference/type-layout.html">“Type Layout”</a> {#+ #}
chapter for details on type layout guarantees. {# #}
</p> {# #}
</div> {# #}
<p><strong>Size:</strong> {{ type_layout_size|safe }}</p> {# #}
{% if !variants.is_empty() %}
<p> {# #}
<strong>Size for each variant:</strong> {# #}
</p> {# #}
<ul> {# #}
{% for (name, layout_size) in variants %}
<li> {# #}
<code>{{ name }}</code>: {#+ #}
{{ layout_size|safe }}
</li> {# #}
{% endfor %}
</ul> {# #}
{% endif %}
{# This kind of layout error can occur with valid code, e.g. if you try to
get the layout of a generic type such as `Vec<T>`. #}
{% when Err(LayoutError::Unknown(_)) %}
<p> {# #}
<strong>Note:</strong> Unable to compute type layout, {#+ #}
possibly due to this type having generic parameters. {#+ #}
Layout can only be computed for concrete, fully-instantiated types. {# #}
</p> {# #}
<p> {# #}
<strong>Note:</strong> Unable to compute type layout, {#+ #}
possibly due to this type having generic parameters. {#+ #}
Layout can only be computed for concrete, fully-instantiated types. {# #}
</p> {# #}
{# This kind of error probably can't happen with valid code, but we don't
want to panic and prevent the docs from building, so we just let the
user know that we couldn't compute the layout. #}
want to panic and prevent the docs from building, so we just let the
user know that we couldn't compute the layout. #}
{% when Err(LayoutError::SizeOverflow(_)) %}
<p> {# #}
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type was too big. {# #}
</p> {# #}
<p> {# #}
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type was too big. {# #}
</p> {# #}
{% when Err(LayoutError::NormalizationFailure(_, _)) %}
<p> {# #}
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type failed to be normalized. {# #}
</p> {# #}
{% endmatch %}
<p> {# #}
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type failed to be normalized. {# #}
</p> {# #}
{% when Err(LayoutError::Cycle) %}
<p> {# #}
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type's layout depended on the type's layout itself. {# #}
</p> {# #}
{% endmatch %}
</div> {# #}
28 changes: 28 additions & 0 deletions src/tools/miri/tests/fail/layout_cycle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//@error-pattern: a cycle occurred during layout computation
//~^ ERROR: cycle detected when computing layout of

use std::mem;

pub struct S<T: Tr> {
pub f: <T as Tr>::I,
}

pub trait Tr {
type I: Tr;
}

impl<T: Tr> Tr for S<T> {
type I = S<S<T>>;
}

impl Tr for () {
type I = ();
}

fn foo<T: Tr>() -> usize {
mem::size_of::<S<T>>()
}

fn main() {
println!("{}", foo::<S<()>>());
}
28 changes: 28 additions & 0 deletions src/tools/miri/tests/fail/layout_cycle.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
error[E0391]: cycle detected when computing layout of `S<S<()>>`
|
= note: ...which requires computing layout of `<S<()> as Tr>::I`...
= note: ...which again requires computing layout of `S<S<()>>`, completing the cycle

error: post-monomorphization error: a cycle occurred during layout computation
--> RUSTLIB/core/src/mem/mod.rs:LL:CC
|
LL | intrinsics::size_of::<T>()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation
|
= note: inside `std::mem::size_of::<S<S<()>>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
note: inside `foo::<S<()>>`
--> $DIR/layout_cycle.rs:LL:CC
|
LL | mem::size_of::<S<T>>()
| ^^^^^^^^^^^^^^^^^^^^^^
note: inside `main`
--> $DIR/layout_cycle.rs:LL:CC
|
LL | println!("{}", foo::<S<()>>());
| ^^^^^^^^^^^^^^

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0391`.

0 comments on commit 7bc6d59

Please sign in to comment.