Skip to content
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

Compiling ndarray: alignment mismatch between ABI and layout #99836

Closed
cuviper opened this issue Jul 28, 2022 · 14 comments · Fixed by #99959
Closed

Compiling ndarray: alignment mismatch between ABI and layout #99836

cuviper opened this issue Jul 28, 2022 · 14 comments · Fixed by #99959
Labels
C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-critical Critical priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@cuviper
Copy link
Member

cuviper commented Jul 28, 2022

Code

ndarray master as of ddef4d280fb5abc82325287e68ecacfc81882a1e.

Meta

rustc --version --verbose:

rustc 1.64.0-dev
binary: rustc
commit-hash: unknown
commit-date: unknown
host: x86_64-unknown-linux-gnu
release: 1.64.0-dev
LLVM version: 14.0.6

That's commit 2643b16 built with this config.toml:

profile = "compiler"
changelog-seen = 2
[rust]
debug-assertions = true

Error output

thread 'rustc' panicked at 'assertion failed: `(left == right)`
  left: `Align(8 bytes)`,
 right: `Align(1 bytes)`: alignment mismatch between ABI and layout in Layout {
    ...
}', compiler/rustc_middle/src/ty/layout.rs:240:21

Note that this assertion is under if cfg!(debug_assertions):

assert_eq!(
layout.align().abi,
scalar.align(&tcx).abi,
"alignment mismatch between ABI and layout in {layout:#?}"
);

Full output

thread 'rustc' panicked at 'assertion failed: `(left == right)`
  left: `Align(8 bytes)`,
 right: `Align(1 bytes)`: alignment mismatch between ABI and layout in Layout {
    fields: Arbitrary {
        offsets: [
            Size(0 bytes),
        ],
        memory_index: [
            0,
        ],
    },
    variants: Multiple {
        tag: Initialized {
            value: Int(
                I8,
                false,
            ),
            valid_range: 0..=6,
        },
        tag_encoding: Niche {
            dataful_variant: 1,
            niche_variants: 0..=0,
            niche_start: 0,
        },
        tag_field: 0,
        variants: [
            Layout {
                fields: Arbitrary {
                    offsets: [
                        Size(0 bytes),
                    ],
                    memory_index: [
                        0,
                    ],
                },
                variants: Single {
                    index: 0,
                },
                abi: Aggregate {
                    sized: true,
                },
                largest_niche: None,
                align: AbiAndPrefAlign {
                    abi: Align(8 bytes),
                    pref: Align(8 bytes),
                },
                size: Size(0 bytes),
            },
            Layout {
                fields: Arbitrary {
                    offsets: [
                        Size(0 bytes),
                    ],
                    memory_index: [
                        0,
                    ],
                },
                variants: Single {
                    index: 1,
                },
                abi: Scalar(
                    Initialized {
                        value: Int(
                            I8,
                            false,
                        ),
                        valid_range: 1..=6,
                    },
                ),
                largest_niche: Some(
                    Niche {
                        offset: Size(0 bytes),
                        value: Int(
                            I8,
                            false,
                        ),
                        valid_range: 1..=6,
                    },
                ),
                align: AbiAndPrefAlign {
                    abi: Align(1 bytes),
                    pref: Align(8 bytes),
                },
                size: Size(1 bytes),
            },
        ],
    },
    abi: Scalar(
        Initialized {
            value: Int(
                I8,
                false,
            ),
            valid_range: 0..=6,
        },
    ),
    largest_niche: Some(
        Niche {
            offset: Size(0 bytes),
            value: Int(
                I8,
                false,
            ),
            valid_range: 0..=6,
        },
    ),
    align: AbiAndPrefAlign {
        abi: Align(8 bytes),
        pref: Align(8 bytes),
    },
    size: Size(1 bytes),
}', compiler/rustc_middle/src/ty/layout.rs:240:21
stack backtrace:
   0: rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::assert_failed_inner
   3: core::panicking::assert_failed::<rustc_target::abi::Align, rustc_target::abi::Align>
   4: rustc_middle::ty::layout::sanity_check_layout::check_layout_abi
   5: rustc_middle::ty::context::tls::with_context::<rustc_middle::ty::context::tls::with_related_context<rustc_middle::ty::layout::layout_of::{closure#0}, core::result::Result<rustc_target::abi::TyAndLayout<rustc_middle::ty::Ty>, rustc_middle::ty::layout::LayoutError>>::{closure#0}, core::result::Result<rustc_target::abi::TyAndLayout<rustc_middle::ty::Ty>, rustc_middle::ty::layout::LayoutError>>::{closure#0}
   6: rustc_middle::ty::layout::layout_of
   7: <rustc_middle::dep_graph::dep_node::DepKind as rustc_query_system::dep_graph::DepKind>::with_deps::<<rustc_query_system::dep_graph::graph::DepGraph<rustc_middle::dep_graph::dep_node::DepKind>>::with_task_impl<rustc_middle::ty::context::TyCtxt, rustc_middle::ty::ParamEnvAnd<rustc_middle::ty::Ty>, core::result::Result<rustc_target::abi::TyAndLayout<rustc_middle::ty::Ty>, rustc_middle::ty::layout::LayoutError>>::{closure#0}, core::result::Result<rustc_target::abi::TyAndLayout<rustc_middle::ty::Ty>, rustc_middle::ty::layout::LayoutError>>
   8: <rustc_query_system::dep_graph::graph::DepGraph<rustc_middle::dep_graph::dep_node::DepKind>>::with_task::<rustc_middle::ty::context::TyCtxt, rustc_middle::ty::ParamEnvAnd<rustc_middle::ty::Ty>, core::result::Result<rustc_target::abi::TyAndLayout<rustc_middle::ty::Ty>, rustc_middle::ty::layout::LayoutError>>
   9: rustc_query_system::query::plumbing::get_query::<rustc_query_impl::queries::layout_of, rustc_query_impl::plumbing::QueryCtxt>
  10: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::layout_of
  11: <rustc_mir_transform::const_prop::CanConstProp>::check
  12: <rustc_mir_transform::const_prop_lint::ConstProp as rustc_mir_transform::pass_manager::MirLint>::run_lint
  13: rustc_mir_transform::pass_manager::run_passes
  14: rustc_mir_transform::run_post_borrowck_cleanup_passes
  15: rustc_mir_transform::mir_drops_elaborated_and_const_checked
  16: <rustc_middle::dep_graph::dep_node::DepKind as rustc_query_system::dep_graph::DepKind>::with_deps::<<rustc_query_system::dep_graph::graph::DepGraph<rustc_middle::dep_graph::dep_node::DepKind>>::with_task_impl<rustc_middle::ty::context::TyCtxt, rustc_middle::ty::WithOptConstParam<rustc_span::def_id::LocalDefId>, &rustc_data_structures::steal::Steal<rustc_middle::mir::Body>>::{closure#0}, &rustc_data_structures::steal::Steal<rustc_middle::mir::Body>>
  17: <rustc_query_system::dep_graph::graph::DepGraph<rustc_middle::dep_graph::dep_node::DepKind>>::with_task::<rustc_middle::ty::context::TyCtxt, rustc_middle::ty::WithOptConstParam<rustc_span::def_id::LocalDefId>, &rustc_data_structures::steal::Steal<rustc_middle::mir::Body>>
  18: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::plumbing::QueryCtxt, rustc_query_system::query::caches::DefaultCache<rustc_middle::ty::WithOptConstParam<rustc_span::def_id::LocalDefId>, &rustc_data_structures::steal::Steal<rustc_middle::mir::Body>>>
  19: rustc_query_system::query::plumbing::get_query::<rustc_query_impl::queries::mir_drops_elaborated_and_const_checked, rustc_query_impl::plumbing::QueryCtxt>
  20: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::mir_drops_elaborated_and_const_checked
  21: rustc_mir_transform::optimized_mir
  22: <rustc_middle::dep_graph::dep_node::DepKind as rustc_query_system::dep_graph::DepKind>::with_deps::<<rustc_query_system::dep_graph::graph::DepGraph<rustc_middle::dep_graph::dep_node::DepKind>>::with_task_impl<rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId, &rustc_middle::mir::Body>::{closure#0}, &rustc_middle::mir::Body>
  23: <rustc_query_system::dep_graph::graph::DepGraph<rustc_middle::dep_graph::dep_node::DepKind>>::with_task::<rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId, &rustc_middle::mir::Body>
  24: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::plumbing::QueryCtxt, rustc_query_system::query::caches::DefaultCache<rustc_span::def_id::DefId, &rustc_middle::mir::Body>>
  25: rustc_query_system::query::plumbing::get_query::<rustc_query_impl::queries::optimized_mir, rustc_query_impl::plumbing::QueryCtxt>
  26: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::optimized_mir
  27: <rustc_metadata::rmeta::encoder::EncodeContext>::encode_crate_root
  28: rustc_metadata::rmeta::encoder::encode_metadata_impl
  29: rustc_data_structures::sync::join::<rustc_metadata::rmeta::encoder::encode_metadata::{closure#0}, rustc_metadata::rmeta::encoder::encode_metadata::{closure#1}, (), ()>
  30: rustc_metadata::rmeta::encoder::encode_metadata
  31: rustc_metadata::fs::encode_and_write_metadata
  32: rustc_interface::passes::start_codegen
  33: <rustc_interface::passes::QueryContext>::enter::<<rustc_interface::queries::Queries>::ongoing_codegen::{closure#0}::{closure#0}, core::result::Result<alloc::boxed::Box<dyn core::any::Any>, rustc_errors::ErrorGuaranteed>>
  34: <rustc_interface::queries::Query<alloc::boxed::Box<dyn core::any::Any>>>::compute::<<rustc_interface::queries::Queries>::ongoing_codegen::{closure#0}>
  35: <rustc_interface::interface::Compiler>::enter::<rustc_driver::run_compiler::{closure#1}::{closure#2}, core::result::Result<core::option::Option<rustc_interface::queries::Linker>, rustc_errors::ErrorGuaranteed>>
  36: rustc_span::with_source_map::<core::result::Result<(), rustc_errors::ErrorGuaranteed>, rustc_interface::interface::create_compiler_and_run<core::result::Result<(), rustc_errors::ErrorGuaranteed>, rustc_driver::run_compiler::{closure#1}>::{closure#1}>
  37: rustc_interface::interface::create_compiler_and_run::<core::result::Result<(), rustc_errors::ErrorGuaranteed>, rustc_driver::run_compiler::{closure#1}>
  38: <scoped_tls::ScopedKey<rustc_span::SessionGlobals>>::set::<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_errors::ErrorGuaranteed>, rustc_driver::run_compiler::{closure#1}>::{closure#0}, core::result::Result<(), rustc_errors::ErrorGuaranteed>>
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: rustc 1.64.0-dev running on x86_64-unknown-linux-gnu

note: compiler flags: --crate-type lib -C embed-bitcode=no -C debuginfo=2 -C incremental

note: some of the compiler flags provided by cargo are hidden

query stack during panic:
#0 [layout_of] computing layout of `core::result::Result<slice::SliceInfo<[slice::SliceInfoElem; 0], Din, Dout>, error::ShapeError>`
#1 [mir_drops_elaborated_and_const_checked] elaborating drops for `slice::<impl at src/slice.rs:557:9: 558:60>::try_from`
#2 [optimized_mir] optimizing MIR for `slice::<impl at src/slice.rs:557:9: 558:60>::try_from`
end of query stack

@cuviper cuviper added I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. C-bug Category: This is a bug. labels Jul 28, 2022
@cuviper
Copy link
Member Author

cuviper commented Jul 28, 2022

cc @RalfJung since you re-enabled these assertions in #98968.

@cuviper
Copy link
Member Author

cuviper commented Jul 28, 2022

Here's a smaller reproducer:

fn main() {
    enum Error { A, B }

    type Foo = Result<[usize; 0], Error>;

    dbg!(std::mem::size_of::<Foo>());
    dbg!(std::mem::align_of::<Foo>());
}
Output

warning: variants `A` and `B` are never constructed
 --> align.rs:2:18
  |
2 |     enum Error { A, B }
  |          -----   ^  ^
  |          |
  |          variants in this enum
  |
  = note: `#[warn(dead_code)]` on by default

thread 'rustc' panicked at 'assertion failed: `(left == right)`
  left: `Align(8 bytes)`,
 right: `Align(1 bytes)`: alignment mismatch between ABI and layout in Layout {
    fields: Arbitrary {
        offsets: [
            Size(0 bytes),
        ],
        memory_index: [
            0,
        ],
    },
    variants: Multiple {
        tag: Initialized {
            value: Int(
                I8,
                false,
            ),
            valid_range: 0..=2,
        },
        tag_encoding: Niche {
            dataful_variant: 1,
            niche_variants: 0..=0,
            niche_start: 2,
        },
        tag_field: 0,
        variants: [
            Layout {
                fields: Arbitrary {
                    offsets: [
                        Size(0 bytes),
                    ],
                    memory_index: [
                        0,
                    ],
                },
                variants: Single {
                    index: 0,
                },
                abi: Aggregate {
                    sized: true,
                },
                largest_niche: None,
                align: AbiAndPrefAlign {
                    abi: Align(8 bytes),
                    pref: Align(8 bytes),
                },
                size: Size(0 bytes),
            },
            Layout {
                fields: Arbitrary {
                    offsets: [
                        Size(0 bytes),
                    ],
                    memory_index: [
                        0,
                    ],
                },
                variants: Single {
                    index: 1,
                },
                abi: Scalar(
                    Initialized {
                        value: Int(
                            I8,
                            false,
                        ),
                        valid_range: 0..=1,
                    },
                ),
                largest_niche: Some(
                    Niche {
                        offset: Size(0 bytes),
                        value: Int(
                            I8,
                            false,
                        ),
                        valid_range: 0..=1,
                    },
                ),
                align: AbiAndPrefAlign {
                    abi: Align(1 bytes),
                    pref: Align(8 bytes),
                },
                size: Size(1 bytes),
            },
        ],
    },
    abi: Scalar(
        Initialized {
            value: Int(
                I8,
                false,
            ),
            valid_range: 0..=2,
        },
    ),
    largest_niche: Some(
        Niche {
            offset: Size(0 bytes),
            value: Int(
                I8,
                false,
            ),
            valid_range: 0..=2,
        },
    ),
    align: AbiAndPrefAlign {
        abi: Align(8 bytes),
        pref: Align(8 bytes),
    },
    size: Size(1 bytes),
}', compiler/rustc_middle/src/ty/layout.rs:240:21
stack backtrace:
   0:     0x7ff8704d57b4 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h57183f18665a904e
   1:     0x7ff870542308 - core::fmt::write::h1fc3741f5707a7e9
   2:     0x7ff8704d88e1 - std::io::Write::write_fmt::h5e582d931bc0e652
   3:     0x7ff8704d5609 - std::sys_common::backtrace::print::h915370a8cb158d0f
   4:     0x7ff8704c2914 - std::panicking::default_hook::{{closure}}::hd0ff95d9e670b00d
   5:     0x7ff8704c26c3 - std::panicking::default_hook::h1a88faa49859dbbf
   6:     0x7ff870f9f514 - rustc_driver[ba7942d2ed61c3a8]::DEFAULT_HOOK::{closure#0}::{closure#0}
   7:     0x7ff8704c2d4f - std::panicking::rust_panic_with_hook::h216d9410c99eb34c
   8:     0x7ff870498a37 - std::panicking::begin_panic_handler::{{closure}}::h7a2b559b1f7ec0ab
   9:     0x7ff870498974 - std::sys_common::backtrace::__rust_end_short_backtrace::h94cd04d25d25882a
  10:     0x7ff8704c2982 - rust_begin_unwind
  11:     0x7ff870493663 - core::panicking::panic_fmt::hb503280b62085f97
  12:     0x7ff870532a6e - core::panicking::assert_failed_inner::h2c15a679abd16964
  13:     0x7ff870ed41ab - core[6398db64b51061bb]::panicking::assert_failed::<rustc_target[e3ac180738d9092e]::abi::Align, rustc_target[e3ac180738d9092e]::abi::Align>
  14:     0x7ff8735c1c53 - rustc_middle[f8389151b4735317]::ty::layout::sanity_check_layout::check_layout_abi
  15:     0x7ff8735d049e - rustc_middle[f8389151b4735317]::ty::context::tls::with_context::<rustc_middle[f8389151b4735317]::ty::context::tls::with_related_context<rustc_middle[f8389151b4735317]::ty::layout::layout_of::{closure#0}, core[6398db64b51061bb]::result::Result<rustc_target[e3ac180738d9092e]::abi::TyAndLayout<rustc_middle[f8389151b4735317]::ty::Ty>, rustc_middle[f8389151b4735317]::ty::layout::LayoutError>>::{closure#0}, core[6398db64b51061bb]::result::Result<rustc_target[e3ac180738d9092e]::abi::TyAndLayout<rustc_middle[f8389151b4735317]::ty::Ty>, rustc_middle[f8389151b4735317]::ty::layout::LayoutError>>::{closure#0}
  16:     0x7ff8735d1e5b - rustc_middle[f8389151b4735317]::ty::layout::layout_of
  17:     0x7ff8727c9568 - rustc_query_system[b1d5cf42652e4b5a]::query::plumbing::get_query::<rustc_query_impl[d9de55591e7c271a]::queries::layout_of, rustc_query_impl[d9de55591e7c271a]::plumbing::QueryCtxt>
  18:     0x7ff8725f02a3 - <rustc_query_impl[d9de55591e7c271a]::Queries as rustc_middle[f8389151b4735317]::ty::query::QueryEngine>::layout_of
  19:     0x7ff87121315a - <rustc_codegen_llvm[4c68097bd99075c]::context::CodegenCx as rustc_middle[f8389151b4735317]::ty::layout::LayoutOf>::spanned_layout_of
  20:     0x7ff871200dee - <rustc_codegen_ssa[8ac719cfe9297717]::mir::FunctionCx<rustc_codegen_llvm[4c68097bd99075c]::builder::Builder>>::codegen_rvalue_operand
  21:     0x7ff8712076d4 - <rustc_codegen_ssa[8ac719cfe9297717]::mir::FunctionCx<rustc_codegen_llvm[4c68097bd99075c]::builder::Builder>>::codegen_block
  22:     0x7ff8711fc976 - rustc_codegen_ssa[8ac719cfe9297717]::mir::codegen_mir::<rustc_codegen_llvm[4c68097bd99075c]::builder::Builder>
  23:     0x7ff87121e776 - rustc_codegen_ssa[8ac719cfe9297717]::base::codegen_instance::<rustc_codegen_llvm[4c68097bd99075c]::builder::Builder>
  24:     0x7ff8712145a9 - <rustc_middle[f8389151b4735317]::mir::mono::MonoItem as rustc_codegen_ssa[8ac719cfe9297717]::mono_item::MonoItemExt>::define::<rustc_codegen_llvm[4c68097bd99075c]::builder::Builder>
  25:     0x7ff8711cfec4 - rustc_codegen_llvm[4c68097bd99075c]::base::compile_codegen_unit::module_codegen
  26:     0x7ff8711eb404 - <rustc_query_system[b1d5cf42652e4b5a]::dep_graph::graph::DepGraph<rustc_middle[f8389151b4735317]::dep_graph::dep_node::DepKind>>::with_task::<rustc_middle[f8389151b4735317]::ty::context::TyCtxt, rustc_span[e49f3a209ef89a16]::symbol::Symbol, rustc_codegen_ssa[8ac719cfe9297717]::ModuleCodegen<rustc_codegen_llvm[4c68097bd99075c]::ModuleLlvm>>
  27:     0x7ff8711cfaf7 - rustc_codegen_llvm[4c68097bd99075c]::base::compile_codegen_unit
  28:     0x7ff87121da9f - rustc_codegen_ssa[8ac719cfe9297717]::base::codegen_crate::<rustc_codegen_llvm[4c68097bd99075c]::LlvmCodegenBackend>
  29:     0x7ff8711a14fd - <rustc_codegen_llvm[4c68097bd99075c]::LlvmCodegenBackend as rustc_codegen_ssa[8ac719cfe9297717]::traits::backend::CodegenBackend>::codegen_crate
  30:     0x7ff87101bbdb - <rustc_session[f601aaa54490cdf2]::session::Session>::time::<alloc[ebca99b57d59156]::boxed::Box<dyn core[6398db64b51061bb]::any::Any>, rustc_interface[72861cfa91fc646]::passes::start_codegen::{closure#0}>
  31:     0x7ff8710b3fcf - rustc_interface[72861cfa91fc646]::passes::start_codegen
  32:     0x7ff87108b96d - <rustc_interface[72861cfa91fc646]::passes::QueryContext>::enter::<<rustc_interface[72861cfa91fc646]::queries::Queries>::ongoing_codegen::{closure#0}::{closure#0}, core[6398db64b51061bb]::result::Result<alloc[ebca99b57d59156]::boxed::Box<dyn core[6398db64b51061bb]::any::Any>, rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>
  33:     0x7ff87107da4e - <rustc_interface[72861cfa91fc646]::queries::Query<alloc[ebca99b57d59156]::boxed::Box<dyn core[6398db64b51061bb]::any::Any>>>::compute::<<rustc_interface[72861cfa91fc646]::queries::Queries>::ongoing_codegen::{closure#0}>
  34:     0x7ff870f3be4c - <rustc_interface[72861cfa91fc646]::interface::Compiler>::enter::<rustc_driver[ba7942d2ed61c3a8]::run_compiler::{closure#1}::{closure#2}, core[6398db64b51061bb]::result::Result<core[6398db64b51061bb]::option::Option<rustc_interface[72861cfa91fc646]::queries::Linker>, rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>
  35:     0x7ff870f1d687 - rustc_span[e49f3a209ef89a16]::with_source_map::<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, rustc_interface[72861cfa91fc646]::interface::create_compiler_and_run<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, rustc_driver[ba7942d2ed61c3a8]::run_compiler::{closure#1}>::{closure#1}>
  36:     0x7ff870f3cd15 - rustc_interface[72861cfa91fc646]::interface::create_compiler_and_run::<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, rustc_driver[ba7942d2ed61c3a8]::run_compiler::{closure#1}>
  37:     0x7ff870f16f1f - <scoped_tls[e6a8abd170e2c0fc]::ScopedKey<rustc_span[e49f3a209ef89a16]::SessionGlobals>>::set::<rustc_interface[72861cfa91fc646]::interface::run_compiler<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, rustc_driver[ba7942d2ed61c3a8]::run_compiler::{closure#1}>::{closure#0}, core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>
  38:     0x7ff870f90719 - std[a7e9441a3df41b71]::sys_common::backtrace::__rust_begin_short_backtrace::<rustc_interface[72861cfa91fc646]::util::run_in_thread_pool_with_globals<rustc_interface[72861cfa91fc646]::interface::run_compiler<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, rustc_driver[ba7942d2ed61c3a8]::run_compiler::{closure#1}>::{closure#0}, core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>::{closure#0}, core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>
  39:     0x7ff870f1e231 - std[a7e9441a3df41b71]::panicking::try::<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, core[6398db64b51061bb]::panic::unwind_safe::AssertUnwindSafe<<std[a7e9441a3df41b71]::thread::Builder>::spawn_unchecked_<rustc_interface[72861cfa91fc646]::util::run_in_thread_pool_with_globals<rustc_interface[72861cfa91fc646]::interface::run_compiler<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, rustc_driver[ba7942d2ed61c3a8]::run_compiler::{closure#1}>::{closure#0}, core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>::{closure#0}, core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>::{closure#1}::{closure#0}>>
  40:     0x7ff870f3547e - <<std[a7e9441a3df41b71]::thread::Builder>::spawn_unchecked_<rustc_interface[72861cfa91fc646]::util::run_in_thread_pool_with_globals<rustc_interface[72861cfa91fc646]::interface::run_compiler<core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>, rustc_driver[ba7942d2ed61c3a8]::run_compiler::{closure#1}>::{closure#0}, core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>::{closure#0}, core[6398db64b51061bb]::result::Result<(), rustc_errors[685f6944170c16d8]::ErrorGuaranteed>>::{closure#1} as core[6398db64b51061bb]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
  41:     0x7ff8704efd08 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::h2a92cd1facb82b24
  42:     0x7ff8704d3e67 - std::sys::unix::thread::Thread::new::thread_start::h7ad153c748885102
  43:     0x7ff870293e2d - start_thread
  44:     0x7ff8703191b0 - __clone3
  45:                0x0 - <unknown>

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: rustc 1.64.0-dev running on x86_64-unknown-linux-gnu

query stack during panic:
#0 [layout_of] computing layout of `core::result::Result<[usize; 0], main::Error>`
end of query stack
warning: 1 warning emitted

Note that Foo has size 1 and align 8, which seems really bad...

@cuviper
Copy link
Member Author

cuviper commented Jul 28, 2022

This can make safe misaligned references: playground

fn main() {
    #[derive(Debug)]
    #[allow(unused)]
    enum Error { A, B }

    type Foo = Result<[usize; 0], Error>;

    dbg!(std::mem::size_of::<Foo>());
    dbg!(std::mem::align_of::<Foo>());
    
    let x: [Foo; 2] = [Ok([]), Ok([])];
    let r0: &[usize] = x[0].as_ref().unwrap();
    let r1: &[usize] = x[1].as_ref().unwrap();
    eprintln!("{r0:p} {r1:p}");
}
[src/main.rs:8] std::mem::size_of::<Foo>() = 1
[src/main.rs:9] std::mem::align_of::<Foo>() = 8
0x7ffe2c4ba120 0x7ffe2c4ba121

@cuviper cuviper added the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label Jul 28, 2022
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Jul 28, 2022
@apiraino
Copy link
Contributor

WG-prioritization assigning priority (Zulip discussion).

@rustbot label -I-prioritize +P-critical

@rustbot rustbot added P-critical Critical priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Jul 28, 2022
@RalfJung
Copy link
Member

RalfJung commented Jul 28, 2022 via email

@cuviper
Copy link
Member Author

cuviper commented Jul 28, 2022

Yeah, now that I've explored further, I agree the assertions caught a real layout bug. AIUI, the size should always be a multiple of the alignment, since we don't have separate notions of size and stride.

For testing older rustc, I simplified it to this:

fn main() {
    type Foo = Result<[usize; 0], bool>;

    println!(
        "size:{} align:{}",
        std::mem::size_of::<Foo>(),
        std::mem::align_of::<Foo>()
    );
}

Up to Rust 1.23, size and align were 8, and then 1.24 dropped to size 1. That corresponds to the time that niche layout was implemented, #45225 (and dropped from 1.23 in #46925), although I didn't bisect that specifically. cc @eddyb

@eddyb
Copy link
Member

eddyb commented Jul 28, 2022

Ahh, zero-sized aligned types, the bane of layout algorithms...

Likely needs an audit for everywhere we special-case ZSTs and maybe just make them require align == 1 for now.

In this specific case though, we could keep the niche optimization, just need to increase align of the whole type to the max variants[v].fields[f].align (across all variants v and their fields f).

Then it will look like an Option<([usize; 0], bool)>, i.e. one byte for the either-bool-or-2u8 plus 7 padding bytes.

@cuviper
Copy link
Member Author

cuviper commented Jul 28, 2022

Likely needs an audit for everywhere we special-case ZSTs and maybe just make them require align == 1 for now.

I don't understand how you could require that -- [T; 0] must still be aligned like T, right?.

@RalfJung
Copy link
Member

Yes, and for that reason [T; 0] is not a ZST for many T. In the WG-UCG we started using the term "1-ZST" to refer to types with size 0 and alignment 1; that's really what we usually mean when we say "ZST".

@cuviper
Copy link
Member Author

cuviper commented Jul 28, 2022

Oh I see, the places looking for ZSTs should be looking for "1-ZST", where they want no effect on layout.

@eddyb
Copy link
Member

eddyb commented Jul 29, 2022

Sorry for the confusion - when I wrote:

Likely needs an audit for everywhere we special-case ZSTs and maybe just make them require align == 1 for now.

I should've been more specific instead of "them", and phrased it more like:

Likely needs an audit for everywhere we special-case ZSTs and change those special cases to also check align == 1 for now.

(or used the 1-ZST naming, but I completely forgot we had that)


(click to open old notes, ended up tracking down the bug and was wrong)

Those special cases, FWIW, are almost always ignoring ZSTs in a larger aggregate (which is also something I should've been clearer about).

For example, the one you're hitting for Result<[usize; 0], Error> is most likely deciding¹ that the Ok variant is "dataless"/C-like because it contains only ZSTs, and the layout ends up being based² off of the only "dataful" variant (Err), i.e. somewhere between Error and MaybeUninit<Error>, without considering the "dataless" variants at all³.

You could say that it's ignoring any ZSTs and then later assuming it only ignored 1-ZSTs.
This is obviously bad, and so the solutions I previously described were:

  1. pessimize some types by only ignoring 1-ZSTs in such situations
  2. don't make assumptions about what was ignored, and correctly compute their properties

But I wanted to be sure I was accurate, so I checked:

  • ¹ correct, though it only cares about Err being "dataful", and ignores ZST-only variants
    if !f.is_zst() {
    if dataful_variant.is_none() {
    dataful_variant = Some(v);
  • ² correct-ish (see below), e.g. this part: (st[i] is the "dataful" variant, i.e. Err)
    let offset = st[i].fields().offset(field_index) + niche.offset;
    let size = st[i].size();
  • ³ wait, I'm wrong? we totally seem to be combining variant alignments:
    let mut align = dl.aggregate_align;
    let st = variants
    .iter_enumerated()
    .map(|(j, v)| {
    let mut st = self.univariant_uninterned(
    ty,
    v,
    &def.repr(),
    StructKind::AlwaysSized,
    )?;
    st.variants = Variants::Single { index: j };
    align = align.max(st.align);

Oh, whoops, misunderstood the bug: alignment is 8 so we do in fact consider [usize; 0] for alignment, but we never adjust the size to be a multiple of alignment.

(This playground is what I used to inspect the layout, pretty useful - also, Error could be replaced by bool in the minimal repro, just noticed)


This is what tagged union enum layout does, after it merges variants' align/size:

// Align the maximum variant size to the largest alignment.
size = size.align_to(align.abi);

To fix the niche-filling codepath, you need to duplicate the above, just after let size = st[i].size();

However, be careful around abi: the match st[i].abi() { cases for Abi::{Scalar,ScalarPair} will need to also have an if align == st[i].align() && size == st[i].size() guard (as Abi::{Scalar,ScalarPair} have expected align/sizes determined entirely by the Abi value and the target spec).

The fact that align == st[i].align() isn't checked today for Abi::{Scalar,ScalarPair} is technically wrong AFAICT, but I can't think of a way to actually exploit layout.align being larger than what is required for a specific layout.abi.
(AFAICT the LLVM backend always makes alignments explicit where possible, and always inserts explicit padding where there are any gaps between fields, the only way any of that could go wrong is if Rust alignment was less than the LLVM alignment for the same type, but only the opposite can be true as LLVM has less information and we also force packed LLVM types at the slightest hint of underaligned fields)

@cuviper
Copy link
Member Author

cuviper commented Jul 29, 2022

Here's what I've got so far:

diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index dde55dd96554..833edd228050 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1231,11 +1231,15 @@ fn layout_of_uncached(&self, ty: Ty<'tcx>) -> Result<Layout<'tcx>, LayoutError<'
                                 .collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
 
                             let offset = st[i].fields().offset(field_index) + niche.offset;
-                            let size = st[i].size();
+
+                            // Align the total size to the largest alignment.
+                            let size = st[i].size().align_to(align.abi);
 
                             let abi = if st.iter().all(|v| v.abi().is_uninhabited()) {
                                 Abi::Uninhabited
-                            } else {
+                            } else if align == st[i].align() && size == st[i].size() {
+                                // When the total alignment and size match, we can use the
+                                // same ABI as the scalar variant with the reserved niche.
                                 match st[i].abi() {
                                     Abi::Scalar(_) => Abi::Scalar(niche_scalar),
                                     Abi::ScalarPair(first, second) => {
@@ -1249,6 +1253,8 @@ fn layout_of_uncached(&self, ty: Ty<'tcx>) -> Result<Layout<'tcx>, LayoutError<'
                                     }
                                     _ => Abi::Aggregate { sized: true },
                                 }
+                            } else {
+                                Abi::Aggregate { sized: true }
                             };
 
                             let largest_niche = Niche::from_scalar(dl, offset, niche_scalar);

For the example of Result<[usize; 0], bool>, it ends up using a direct tag instead, still size == align == 8.

The original ndarray build also passes all assertions with this change.

@eddyb
Copy link
Member

eddyb commented Jul 30, 2022

For the example of Result<[usize; 0], bool>, it ends up using a direct tag instead, still size == align == 8.

Ah, does it hit the check between niche-filling and tag, that IIRC might prefer tag (if the sizes are equal) because it likely results in better codegen/more niches in many cases?

Looks like it:

// Pick the smaller layout; otherwise,
// pick the layout with the larger niche; otherwise,
// pick tagged as it has simpler codegen.
cmp::min_by_key(tagged_layout, niche_filling_layout, |layout| {
let niche_size = layout.largest_niche.map_or(0, |n| n.available(dl));
(layout.size, cmp::Reverse(niche_size))

In that case, it should get the same layout as s/bool/u8/ (since it ends up not using the niche).
With some quick testing, I was able to confirm that [usize; 0] in such a tagged union enum gets placed at the very end (i.e. offset 8 in a size 8 type), which matches the respective handwritten #[repr(C)] struct, so I'm happy to say that all looks good.


We might still want a test where the tagged form doesn't happen.

I was able to emulate the fix described above by making the enum "cross-pollinate" alignments:

enum FixedResult<T, E> {
    Ok(T, [E; 0]),
    Err(E, [T; 0]),
}

Such a definition shouldn't be notably different from Result<T, E> (modulo bugs like this very one).
(note that you can't just use Result<(T, [E; 0]), (E, [T; 0])> to emulate the fix, as that will put padding inside variant fields, instead of in between them, which can pessimize tagged layouts)

With that, I was able to come up with an extended example on playground:

// Tagged repr is clever enough to grow tags to fill any padding, e.g.:
// 1.   `T_FF` (one byte of Tag, one byte of padding, two bytes of align=2 Field)
//   -> `TTFF` (Tag has expanded to two bytes, i.e. like `#[repr(u16)]`)
// 2.    `TFF` (one byte of Tag, two bytes of align=1 Field)
//   -> Tag has no room to expand!
//   (this outcome can be forced onto 1. by wrapping Field in `Packed<...>`)
#[repr(packed)]
struct Packed<T>(T);

#[rustc_layout(debug)]
type NicheLosesToTagged = FixedResult<[u64; 0], Packed<std::num::NonZeroU16>>;

#[repr(u16)]
enum U16IsZero { _Zero = 0 }

#[rustc_layout(debug)]
type NicheWinsOverTagged = FixedResult<[u64; 0], Packed<U16IsZero>>;

Relevant parts of the output (cleaned up a bit):

layout_of(FixedResult<[u64; 0], Packed<std::num::NonZeroU16>>) = Layout {
    // ...
    variants: Multiple {
        tag: Initialized {
            value: Int(I8, false),
            valid_range: 0..=1,
        },
        tag_encoding: Direct,
        // ...
    },
    // ...
    align: AbiAndPrefAlign {
        abi: Align(8 bytes),
        pref: Align(8 bytes),
    },
    size: Size(8 bytes),
}
layout_of(FixedResult<[u64; 0], Packed<U16IsZero>>) = Layout {
    // ...
    variants: Multiple {
        tag: Initialized {
            value: Int(I16, false),
            valid_range: 0..=1,
        },
        tag_encoding: Niche {
            dataful_variant: 1,
            niche_variants: 0..=0,
            niche_start: 1,
        },
        // ..
    },
    // ...
    align: AbiAndPrefAlign {
        abi: Align(8 bytes),
        pref: Align(8 bytes),
    },
    size: Size(8 bytes),
}

To be clear, you should be able to get those exact results with Result instead of FixedResult, on your branch.

@cuviper
Copy link
Member Author

cuviper commented Jul 30, 2022

That's a nice test, thanks! And indeed, nightly currently makes those both niche with align==8 && size==2, while my branch makes them direct and niche respectively with align == size == 8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-critical Critical priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants