Skip to content

const-checking accepts some code that executes non-const drops #164194

Open

Description

@RalfJung

Example 1

struct NotConstDestruct;

impl Drop for NotConstDestruct {
    fn drop(&mut self) {}
}

pub const _: () = {
    let mut a = Some(NotConstDestruct); //~ ERROR destructor of
    let b = NotConstDestruct;
    let p = &raw mut a;
    let x = unsafe { std::ptr::read(p) };
    std::mem::forget(a);
    std::mem::forget(x);
    a = None;
    unsafe { std::ptr::write(p, Some(b)) };
};

This should be fixed by #164081.

Example 2

use std::num::NonZero;

#[repr(transparent)]
struct NotConstDestruct(NonZero<u8>);

impl Drop for NotConstDestruct {
    fn drop(&mut self) {
        eprintln!("this is not const");
    }
}

enum MyOpt {
    None(()),
    Some(NotConstDestruct),
}

const _: () = {
    assert!(std::mem::size_of::<MyOpt>() == 1);
    let mut x = MyOpt::None(());
    let ptr = match x {
        MyOpt::None(ref mut field) => field as *mut (),
        _ => unreachable!(),
    };
    unsafe { ptr.cast::<u8>().write(1) };
};

See here for why this happens. There is no fix yet.

Cc @tmiasko @oli-obk

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 12, 2026
  2. added
    I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️
    A-destructorsArea: Destructors (`Drop`, …)
    T-langRelevant to the language team
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    C-bugCategory: This is a bug.
    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 12, 2026
  3. theemathas commented on Oct 12, 2026

    @theemathas
    Contributor

    Another way to ICE (variant of "example 2"):

    struct NotConstDestruct;
    
    impl Drop for NotConstDestruct {
        fn drop(&mut self) {}
    }
    
    const _: () = {
        let mut x: (Option<NotConstDestruct>, u8) = (None, 0_u8);
        unsafe {
            (&raw mut x.1).sub(1).write(1);
        }
    };
  4. theemathas commented on Oct 12, 2026

    @theemathas
    Contributor

    Another variant:

    #![feature(const_trait_impl, const_closures)]
    
    struct NotConstDestruct;
    
    impl Drop for NotConstDestruct {
        fn drop(&mut self) {}
    }
    
    const _: () = {
        let x = None::<NotConstDestruct>;
        let mut y = 0_u8;
        let f = const move || {
            &x;
            unsafe { (&raw mut y).sub(1).write(1_u8); }
        };
        call_once(f);
    };
    
    const fn call_once(f: impl const FnOnce()) {
        f();
    }
  5. theemathas commented on Oct 12, 2026

    @theemathas
    Contributor

    Another variant:

    #![feature(const_trait_impl, const_destruct)]
    
    struct NotConstDestruct;
    impl Drop for NotConstDestruct {
        fn drop(&mut self) {}
    }
    
    struct Weird(u8);
    const impl Drop for Weird {
        fn drop(&mut self) {
            unsafe { (&raw mut self.0).sub(1).write(1_u8) ; }
        }
    }
    
    const _: () = {
        let x = Weird(0_u8);
        let y = None::<NotConstDestruct>;
        let _ = move || {
            &x;
            &y;
        };
    };
  6. theemathas commented on Oct 12, 2026

    @theemathas
    Contributor

    Another way to ICE:

    #![feature(const_trait_impl, const_destruct)]
    
    struct NotConstDestruct;
    impl Drop for NotConstDestruct {
        fn drop(&mut self) {}
    }
    
    struct Weird(u8);
    const impl Drop for Weird {
        fn drop(&mut self) {
            unsafe { (&raw mut self.0).add(1).write(1_u8) ; }
        }
    }
    
    const _: () = {
        let _ = (Weird(0_u8), None::<NotConstDestruct>);
    };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)A-destructorsArea: Destructors (`Drop`, …)C-bugCategory: This is a bug.I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions