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

Existential type can have inconsistent concrete type #52632

Open
dtolnay opened this Issue Jul 22, 2018 · 7 comments

Comments

Projects
None yet
3 participants
@dtolnay
Copy link
Member

dtolnay commented Jul 22, 2018

#![feature(existential_type)]

use std::fmt::Debug;

// Parser workaround. https://github.com/rust-lang/rust/issues/52631
macro_rules! this_is_an_item {
    ($i:item) => { $i };
}

fn main() {
    this_is_an_item! {
        existential type Existential: Debug;
    }

    fn _unused() -> Existential { String::new() }

    let null = || -> Existential { 0 };
    println!("{:?}", null());
}
Segmentation fault (core dumped)

Mentioning the existential types tracking issue #34511
Mentioning @oli-obk and @cramertj

@oli-obk

This comment has been minimized.

Copy link
Contributor

oli-obk commented Jul 22, 2018

O_o I don't think I considered closures at all.

Should closures be defining uses? Or just uses (and thus only be able to return that type by calling a function that defines the existential)?

@dtolnay

This comment has been minimized.

Copy link
Member Author

dtolnay commented Jul 22, 2018

As a motivating use case, existential types defined by a closure are required if we want to extend lazy_static! to support referring to local variables.

#![feature(existential_type, untagged_unions)]

#[macro_use]
extern crate lazy_static;

use std::ops::Deref;

#[derive(Debug)]
struct ExpensiveResult;

// TODO: move this inside of `lazy_local!`.
existential type Init: FnOnce() -> ExpensiveResult;

macro_rules! lazy_local {
    (ref $name:ident : $ty:ty = $init:expr;) => {
        #[allow(unions_with_drop_fields)]
        union BrieflyUninit {
            uninit: (),
            // If $name is never deref'd, this initializer is never dropped.
            value: Init,
        }

        static mut INIT: BrieflyUninit = BrieflyUninit { uninit: () };
        let init = move || -> Init { move || $init };
        unsafe {
            std::ptr::write(&mut INIT.value, init());
        }

        lazy_static! {
            static ref $name: $ty = unsafe { std::ptr::read(&INIT.value)() };
        }
    };
}

fn lazy_deref_with_args(arg: i32) -> &'static impl Deref<Target = ExpensiveResult> {
    lazy_local! {
        ref STATE: ExpensiveResult = { println!("arg={}", arg); ExpensiveResult };
    }

    &STATE
}

fn main() {
    let state = lazy_deref_with_args(1);
    println!("{:?}", **state);
}
@cramertj

This comment was marked as off-topic.

Copy link
Member

cramertj commented Jul 23, 2018

@oli-obk

Should closures be defining uses?

Yeah, any in-scope use (including in a let binding) can be a defining use.

@dtolnay

This comment was marked as off-topic.

Copy link
Member Author

dtolnay commented Jul 23, 2018

A let binding like this? It looks like this is not currently picked up. It is why I had to write let init = move || -> Init { move || $init } rather than simply let init: Init = move || $init in my previous comment.

#![feature(existential_type)]

existential type Clonable: Clone;

fn main() {
    let _x: Clonable = 0i32;
}
error[E0308]: mismatched types
 --> src/main.rs:6:24
  |
6 |     let _x: Clonable = 0i32;
  |                        ^^^^ expected anonymized type, found i32
  |
  = note: expected type `Clonable`
             found type `i32`

error: could not find defining uses
 --> src/main.rs:3:1
  |
3 | existential type Clonable: Clone;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@cramertj

This comment was marked as off-topic.

Copy link
Member

cramertj commented Jul 23, 2018

@dtolnay Yeah, the RFC specifies that that should work.

@oli-obk

This comment has been minimized.

Copy link
Contributor

oli-obk commented Jul 24, 2018

I chose a simpler logic: any in scope use must be a defining use.

@oli-obk

This comment has been minimized.

Copy link
Contributor

oli-obk commented Jan 25, 2019

We've moved from segmentation faults to an ICE:

internal compiler error: broken MIR in DefId(0/1:9 ~ playground[d6cf]::main[0]::{{closure}}[0]) (bb0[0]): equate_inputs_and_outputs: `Existential==i32` failed with `NoSolution`

@oli-obk oli-obk added the I-ICE label Jan 25, 2019

@oli-obk oli-obk self-assigned this Jan 25, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment