Skip to content

dealing with cyclic (self-referential) values with const_heap #143869

Open
@fee1-dead

Description

@fee1-dead

See the following snippet: (currently ICEs)

#![feature(core_intrinsics)]
#![feature(const_heap)]

use std::mem::{align_of, size_of};
use std::intrinsics;

struct SelfReferential {
    me: *const SelfReferential,
}

const Y: &'static SelfReferential = unsafe {
    let size = size_of::<SelfReferential>();
    let align = align_of::<SelfReferential>();
    let ptr_raw = intrinsics::const_allocate(size, align);
    let ptr = ptr_raw as *mut SelfReferential;
    let me_addr = &raw mut (*ptr).me;
    *me_addr = ptr;
    intrinsics::const_make_global(ptr_raw, size, align);
    &*ptr
};

fn main() {}
  • me's provenance says it wasn't derived from an immutable reference. We can't really change that because it can't be mutated after we call const_make_global.
  • The fact above hits "accepted a mutable pointer that should not have accepted" at
    // Ensure that this is derived from a shared reference. Crucially, we check this *before*
    // checking whether the `alloc_id` has already been interned. The point of this check is to
    // ensure that when there are multiple pointers to the same allocation, they are *all*
    // derived from a shared reference. Therefore it would be bad if we only checked the first
    // pointer to any given allocation.
    // (It is likely not possible to actually have multiple pointers to the same allocation,
    // so alternatively we could also check that and ICE if there are multiple such pointers.)
    // See <https://github.com/rust-lang/rust/pull/128543> for why we are checking for "shared
    // reference" and not "immutable", i.e., for why we are allowing interior-mutable shared
    // references: they can actually be created in safe code while pointing to apparently
    // "immutable" values, via promotion or tail expression lifetime extension of
    // `&None::<Cell<T>>`.
    // We also exclude promoteds from this as `&mut []` can be promoted, which is a mutable
    // reference pointing to an immutable (zero-sized) allocation. We rely on the promotion
    // analysis not screwing up to ensure that it is sound to intern promoteds as immutable.
    if intern_kind != InternKind::Promoted
    && inner_mutability == Mutability::Not
    && !prov.shared_ref()
    {
  • We can check if alloc_id has kind Heap { was_made_immut: true } and skip the error that way.
  • intern_shallow will keep returning alloc2 adding to our todo list being weird left and right. I added a .filter in
    match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) {
    Ok(nested) => todo.extend(nested),
    to skip provs that are in just_interned.
  • Apparently that wasn't enough and rustc hits an infinite loop at some point. The stack trace isn't helpful enough and I didn't have enough time to find a way to debug the stack overflow.

Originally posted by @fee1-dead in #143595 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-bugCategory: This is a bug.F-const_heap`#[feature(const_heap)]`WG-const-evalWorking group: Const evaluation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions