Skip to content

const destructor error when using while let Some with generic type #109427

Open
@fee1-dead

Description

@fee1-dead

I tried this code: play

struct MyIterator<T>(std::marker::PhantomData<T>);

impl<T> MyIterator<T> {
    const fn next(&elf) -> Option<T> {
        None
    }
}

const fn consume<T>(x: MyIterator<T>) {
    while let Some(it) = x.next() {
        std::mem::forget(it);
    }
}

I expected to see this happen: compiles

Instead, this happened:

error[E0493]: destructor of `Option<T>` cannot be evaluated at compile-time
  --> src/lib.rs:12:26
   |
12 |     while let Some(it) = x.next() {
   |                          ^^^^^^^^ the destructor for this type cannot be evaluated in constant functions
13 |         std::mem::forget(it);
14 |     }
   |     - value is dropped here

This is a limitation in running checks on the MIR for dropping generic types. If we assert that T: ~const Destruct, we can get the MIR:

// MIR FOR CTFE
fn consume(_1: MyIterator<T>) -> () {
    debug x => _1;                       // in scope 0 at src/lib.rs:11:51: 11:56
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:11:73: 11:73
    let mut _2: ();                      // in scope 0 at src/lib.rs:11:1: 15:2
    let mut _3: std::option::Option<T>;  // in scope 0 at src/lib.rs:12:26: 12:34
    let mut _4: &mut MyIterator<T>;      // in scope 0 at src/lib.rs:12:26: 12:34
    let mut _5: isize;                   // in scope 0 at src/lib.rs:12:15: 12:23
    let _7: ();                          // in scope 0 at src/lib.rs:13:9: 13:29
    let mut _8: T;                       // in scope 0 at src/lib.rs:13:26: 13:28
    let mut _9: !;                       // in scope 0 at src/lib.rs:12:5: 14:6
    let _10: ();                         // in scope 0 at src/lib.rs:12:5: 14:6
    let mut _11: !;                      // in scope 0 at src/lib.rs:12:5: 14:6
    let mut _12: isize;                  // in scope 0 at src/lib.rs:14:5: 14:6
    let mut _13: isize;                  // in scope 0 at src/lib.rs:14:5: 14:6
    let mut _14: isize;                  // in scope 0 at src/lib.rs:14:5: 14:6
    scope 1 {
        debug it => _6;                  // in scope 1 at src/lib.rs:12:20: 12:22
        let _6: T;                       // in scope 1 at src/lib.rs:12:20: 12:22
    }

    bb0: {
        goto -> bb1;                     // scope 0 at src/lib.rs:12:5: 14:6
    }

    bb1: {
        StorageLive(_3);                 // scope 1 at src/lib.rs:12:26: 12:34
        StorageLive(_4);                 // scope 1 at src/lib.rs:12:26: 12:34
        _4 = &mut _1;                    // scope 1 at src/lib.rs:12:26: 12:34
        ConstEvalCounter;                // scope 1 at src/lib.rs:12:26: 12:34
        _3 = MyIterator::<T>::next(move _4) -> bb2; // scope 1 at src/lib.rs:12:26: 12:34
                                         // mir::Constant
                                         // + span: src/lib.rs:12:28: 12:32
                                         // + literal: Const { ty: for<'a> fn(&'a mut MyIterator<T>) -> Option<T> {MyIterator::<T>::next}, val: Value(<ZST>) }
    }

    bb2: {
        StorageDead(_4);                 // scope 1 at src/lib.rs:12:33: 12:34
        _5 = discriminant(_3);           // scope 1 at src/lib.rs:12:15: 12:23
        switchInt(move _5) -> [1: bb3, otherwise: bb5]; // scope 1 at src/lib.rs:12:15: 12:23
    }

    bb3: {
        StorageLive(_6);                 // scope 1 at src/lib.rs:12:20: 12:22
        _6 = move ((_3 as Some).0: T);   // scope 1 at src/lib.rs:12:20: 12:22
        StorageLive(_7);                 // scope 1 at src/lib.rs:13:9: 13:29
        StorageLive(_8);                 // scope 1 at src/lib.rs:13:26: 13:28
        _8 = move _6;                    // scope 1 at src/lib.rs:13:26: 13:28
        ConstEvalCounter;                // scope 1 at src/lib.rs:13:9: 13:29
        _7 = std::mem::forget::<T>(move _8) -> [return: bb4, unwind: bb8]; // scope 1 at src/lib.rs:13:9: 13:29
                                         // mir::Constant
                                         // + span: src/lib.rs:13:9: 13:25
                                         // + literal: Const { ty: fn(T) {std::mem::forget::<T>}, val: Value(<ZST>) }
    }

    bb4: {
        StorageDead(_8);                 // scope 1 at src/lib.rs:13:28: 13:29
        StorageDead(_7);                 // scope 1 at src/lib.rs:13:29: 13:30
        _2 = const ();                   // scope 1 at src/lib.rs:12:35: 14:6
        StorageDead(_6);                 // scope 0 at src/lib.rs:14:5: 14:6
        _12 = discriminant(_3);          // scope 0 at src/lib.rs:14:5: 14:6
        StorageDead(_3);                 // scope 0 at src/lib.rs:14:5: 14:6
        ConstEvalCounter;                // scope 0 at src/lib.rs:12:5: 14:6
        goto -> bb1;                     // scope 0 at src/lib.rs:12:5: 14:6
    }

    bb5: {
        StorageLive(_10);                // scope 0 at src/lib.rs:12:5: 14:6
        _0 = const ();                   // scope 0 at src/lib.rs:12:5: 14:6
        StorageDead(_10);                // scope 0 at src/lib.rs:14:5: 14:6
        drop(_3) -> [return: bb6, unwind: bb7]; // scope 0 at src/lib.rs:14:5: 14:6
    }

    bb6: {
        StorageDead(_3);                 // scope 0 at src/lib.rs:14:5: 14:6
        return;                          // scope 0 at src/lib.rs:15:2: 15:2
    }

    bb7 (cleanup): {
        resume;                          // scope 0 at src/lib.rs:11:1: 15:2
    }

    bb8 (cleanup): {
        _14 = discriminant(_3);          // scope 0 at src/lib.rs:14:5: 14:6
        goto -> bb7;                     // scope 0 at src/lib.rs:14:5: 14:6
    }
}

Notice drop(_3) drops the Option even though we know it is None at that point.

Related: #92766

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.htmlA-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-enhancementCategory: An issue proposing an enhancement or a PR with one.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions