Stream: t-compiler/const-eval

Topic: Generic consts


RalfJ (Oct 12 2019 at 15:05, on Zulip):

@oli I didn't know that we "basically" already have generic consts... look at this:
https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#associatedconstant.UNINIT

RalfJ (Oct 12 2019 at 15:05, on Zulip):

doesn't this throw a wrench into our validation story? We usually validate all const items, but we obviously cannot validate this one

RalfJ (Oct 12 2019 at 15:05, on Zulip):

and yet it is promoted etc the same way as all the constants that we did properly check

centril (Oct 12 2019 at 15:35, on Zulip):

Yeah associated consts totally entail generic consts with weirder syntax

RalfJ (Oct 12 2019 at 15:39, on Zulip):

:explosion:

RalfJ (Oct 12 2019 at 15:39, on Zulip):

those were our plans for const soundness, I think^^

RalfJ (Oct 12 2019 at 15:40, on Zulip):

or at least for first looking at soundness and then adding features that make it harder

centril (Oct 12 2019 at 15:41, on Zulip):

fwiw we don't have generic statics at least but I'm not sure that helps ^^

RalfJ (Oct 12 2019 at 15:41, on Zulip):

lol

RalfJ (Oct 12 2019 at 15:41, on Zulip):

what would those even mean

RalfJ (Oct 12 2019 at 15:42, on Zulip):

re: having unique but fixed addresses and things

centril (Oct 12 2019 at 15:43, on Zulip):

@RalfJ I think it means unique address per instantiation of parameters (modulo lifetimes cause erasure)

RalfJ (Oct 12 2019 at 15:52, on Zulip):

across all translation units? what about the usual diamond problem?

centril (Oct 12 2019 at 16:08, on Zulip):

Well if there were no problems then it would already be a thing. ^^

I believe it wouldn't be unique across cdylibs which afaik holds for normal statics as well. dylib's do throw a wrench into the issue tho (which @rkruppe is more familiar with)

rkruppe (Oct 12 2019 at 16:10, on Zulip):

Yeah dylibs are a dealbreaker for generic statics

RalfJ (Oct 12 2019 at 16:16, on Zulip):

I was thinking of the good ol' "crate A defines the static, crate B1 and B2 independently make an instance of it with the same generic param, crate C depends on B1+B2"

rkruppe (Oct 12 2019 at 16:16, on Zulip):

Oh that's solvable

RalfJ (Oct 12 2019 at 16:16, on Zulip):

looks like each of B1's and B2's LLVM IR would then have the static in it, leading to...

RalfJ (Oct 12 2019 at 16:16, on Zulip):

okay? good :D

rkruppe (Oct 12 2019 at 16:17, on Zulip):

All non-leaf crates just make a note that they reference that instantiation, compute the symbol name, and leave actually defining it to the leaf crate

rkruppe (Oct 12 2019 at 16:17, on Zulip):

The dylib problem is that you can't defer to the leaf crate that way with dylibs

rkruppe (Oct 12 2019 at 16:19, on Zulip):

Or should it be root crate? Whatever, the bin/staticlib/cdylib

centril (Oct 12 2019 at 16:19, on Zulip):

iirc @mw had plans to kill dylibs or something

rkruppe (Oct 12 2019 at 16:20, on Zulip):

Several people (including me) have been loudly wishing for their death for years

centril (Oct 12 2019 at 16:20, on Zulip):

yeah but I mean actual plans ^^

centril (Oct 12 2019 at 16:20, on Zulip):

or like a roadmap goal

centril (Oct 12 2019 at 16:21, on Zulip):

(but I think we digress from the const validation stuff this topic was really about... woops, my bad... Zulip is hard!)

centril (Oct 14 2019 at 04:45, on Zulip):

cc @ecstatic-morse :slight_smile:

ecstatic-morse (Oct 14 2019 at 04:49, on Zulip):

I have read this thread, but I don't really understand all the background here :half_frown:. Most of my knowledge is about the static side of const validation, but this seems more about the dynamic part? I suppose if @RalfJ needs more feedback he could try to explain it to me, but probably someone else is more capable.

RalfJ (Oct 14 2019 at 20:34, on Zulip):

@ecstatic-morse for const and static, we actually iterate all of these in the crate you are compiling to test their sanity, even if they are not used

RalfJ (Oct 14 2019 at 20:34, on Zulip):

e.g. when you have a lib crate, you may have many consts that only your clients will use; this makes sure we check them

RalfJ (Oct 14 2019 at 20:35, on Zulip):

but when they are generic, we can't... we'd have to check every possible instance, with all possible types, and that's infinitely many...

ecstatic-morse (Oct 14 2019 at 20:54, on Zulip):

@RalfJ Is sanity used to mean "const-safety" here? On the static side, we assume the worst about T re: its qualifications. This doesn't really apply to MIRI though. Can you give an example where this would be a problem?

RalfJ (Oct 14 2019 at 21:02, on Zulip):

yeah it's basically const-safety

RalfJ (Oct 14 2019 at 21:02, on Zulip):

it's the validity.rs thing

RalfJ (Oct 14 2019 at 21:02, on Zulip):

the biggie in terms of problems is promotion

RalfJ (Oct 14 2019 at 21:03, on Zulip):

if we promote STATIC * 2 and the static (type u32) is actually the result of a ptr-to-int cast, we have to abort compilation

ecstatic-morse (Oct 15 2019 at 19:57, on Zulip):

Ugh sorry. I'm so easily distracted. I still don't understand why this falls down around generic consts specifically. Are we worried about the body of a const being const-unsafe for some T but not others? On the static analysis side, we reason about const bodies pre-monomorphization, so it's not quite :boom: I think? A specific example would help me here.

ecstatic-morse (Oct 15 2019 at 20:02, on Zulip):

@RalfJ

RalfJ (Oct 16 2019 at 17:29, on Zulip):

why this falls down around generic consts specifically

consider

const FOO: bool = {...}
const BAR<T: Trait>: bool = {...}

if we want to check that FOO is a proper bool that's easy: just run the code, and check if the result is 0x00 or 0x01.
if we want to check that BAR is a proper bool... we can't as we'll have to try every possible type T.

ecstatic-morse (Oct 16 2019 at 17:33, on Zulip):

Right, but the issue is that we are unable to do dynamic checks in this case for libraries correct? Not that static validation allows you to write a const-unsafe generic initializer today?

RalfJ (Oct 16 2019 at 17:35, on Zulip):

with unsafe code, static checks will never be guaranteeing anything

RalfJ (Oct 16 2019 at 17:35, on Zulip):

and const allowing unsafe is not a bug, IMO

ecstatic-morse (Oct 16 2019 at 17:41, on Zulip):

I'm just trying to establish whether there's currently a soundness bug, since AFAICT all unsafe operations are forbidden in const contexts. I understand that future plans for allowing unsafe code are greatly complicated by this.

centril (Oct 16 2019 at 17:44, on Zulip):

@ecstatic-morse unconst operations are forbidden but not unsafe ones

centril (Oct 16 2019 at 17:45, on Zulip):

e.g. https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.new_unchecked

ecstatic-morse (Oct 16 2019 at 17:47, on Zulip):

Ah okay.

ecstatic-morse (Oct 16 2019 at 17:49, on Zulip):

I mean the safety of that particular example doesn't depend on T, but it shouldn't be too hard to come up with an example where it does.

ecstatic-morse (Oct 16 2019 at 17:50, on Zulip):

in any case it doesn't execute until it gets monomorphized downstream

RalfJ (Oct 16 2019 at 17:58, on Zulip):

I dont know of a soundness bug. but there could be promotion issues.

RalfJ (Oct 16 2019 at 17:59, on Zulip):

I should probably try to construct an example... so how does that assoc const stuff work?^^

RalfJ (Oct 16 2019 at 18:01, on Zulip):
// Crate 1
struct S<T>(T);

impl<T> S<T> {
    // The crate with this code has no warning.
    const C: bool = {
        union Transmute { i: u8, o: bool }
        unsafe { Transmute { i: 5 }.o }
    };
}

// Crate 2
fn main() {
    let _x: &'static bool = &S::<i32>::C;
}
RalfJ (Oct 16 2019 at 18:01, on Zulip):

so this doesnt actually end up quite as bad as I thought; even if we didnt promote this code would still error

RalfJ (Oct 16 2019 at 18:02, on Zulip):

because then we do the validity check

RalfJ (Oct 16 2019 at 18:02, on Zulip):

so in some sense the behavior is consistent by aggressively failing early... not sure if that's the best strategy^^

ecstatic-morse (Oct 16 2019 at 18:10, on Zulip):
// crate 1

struct Generic<T>(T);
union Transmuter<T: Copy> { t: T, byte: u8 }

impl<T: Copy> Generic<T> {
    const FIVE: T = {
        unsafe { Transmuter { byte: 5 }.t }
    };
}

// crate 2

fn promote_unconst<T: Copy>() -> &'static T {
    &Generic::<T>::FIVE
}
ecstatic-morse (Oct 16 2019 at 18:11, on Zulip):

Is this any worse? It's really just another layer of monomorphization.

RalfJ (Oct 16 2019 at 18:14, on Zulip):

mostly it's another layer of "thunking"

RalfJ (Oct 16 2019 at 18:14, on Zulip):

or laziness

RalfJ (Oct 16 2019 at 18:14, on Zulip):

normal const we check eagerly: all const ever defined are checked

RalfJ (Oct 16 2019 at 18:15, on Zulip):

generic const we can only check lazily: only when a const gets used, we check it

oli (Oct 17 2019 at 12:47, on Zulip):

these are the kind of post monomorphization errors we can't really eliminate

oli (Oct 17 2019 at 12:48, on Zulip):

similar ones have been around since 1.0

Last update: Nov 15 2019 at 20:15UTC