Rust: Constants and statics are not required to be Sized.

Created on 21 Jun 2016  路  10Comments  路  Source: rust-lang/rust

This looks unintentional, and has been broken in all cases, until a few releases ago, AFAICT.
Should this be covered by WF rules? cc @rust-lang/lang @ubsan @solson

Unsized constants cause an ICE, in both old trans and MIR trans:

// rustc_trans/consts.rs:445: const expr(35: &S) of type &str has size 8 instead of 16
// rustc_trans/mir/constant.rs:175: loading from `str`
//     ((i8*:i8* getelementptr inbounds ([3 x i8], [3 x i8]* @str7144, i32 0, i32 0)))
//     in constant
const S: str = *"foo";
fn main() { println!("{}", &S); }

Unsized statics appear to work, but only in old trans:

use std::fmt::Debug;
static FOO: Debug+Sync = *(&1 as &(Debug+Sync));
static S: str = *"foo";
fn main() { println!("{:?} {}", &FOO, &S); }

I believe this is possible because a constant DST lvalue in old trans is merely the fat pointer.
It seems useful to allow an unsized static, but IMO it should require a sized RHS instead, i.e.:

static A: [i32] = [1, 2, 3];
fn main() { let x = A[0]; ... }
// as sugar for:
static A_SIZED: [i32; 3] = [1, 2, 3];
static A: &'static [i32] = &A_SIZED;
fn main() { let x = (*A)[0]; ... }

And, of course, this should all go through the RFC process, to figure out all the finer details.

I-ICE T-lang

Most helpful comment

I think in the meantime unsized statics should probably error, perhaps dependent on a crater run, though I doubt there's many people relying on said bug. It's much easier to relax restrictions than tighten them.

All 10 comments

Should it be

// as sugar for:
const A: &'static [i32] = &[1, 2, 3];
fn main() { let x = (*A)[0]; ... }

? I don't think A needs to be an lvalue.

It makes more sense for constants, but I gave the example with static because that's the only one that works today (although by accident).

Hmm? I mean the static is desugared into a const. const A: &'static [i32] = &[1, 2, 3]; works today.

The idea being that a static and a const reference both give access to one lvalue.

@Ericson2314 No, static has different semantics: it allows an UnsafeCell somewhere within and is guaranteed not to be duplicated.

Hmm?

#![feature(const_fn)]

use std::fmt::Debug;
use std::cell::UnsafeCell;

#[derive(Debug)]
struct Doop(UnsafeCell<u32>);

unsafe impl Send for Doop {} 
unsafe impl Sync for Doop {} 


//const  ASDF: &'static (Debug + Sync) = &Doop(UnsafeCell::new(1)); 
  static ASDF: &'static (Debug + Sync) = &Doop(UnsafeCell::new(1)); 

fn main() {
    println!("{:#?}", ASDF);
}

Both of these gives me a "error: cannot borrow a constant which contains interior mutability, create a static instead". This is what I'd suspect as the lvalue from the promoted Doop(..) has nothing to do with ASDF being a static or a const.

@Ericson2314 Let me rewrite my desugaring to account for that, oops.

#![feature(const_fn)]

use std::fmt::Debug;
use std::cell::UnsafeCell;

#[derive(Debug)]
struct Doop(UnsafeCell<u32>);

unsafe impl Send for Doop {} 
unsafe impl Sync for Doop {} 

static ASDF_INNER: Doop = Doop(UnsafeCell::new(1));
//static ASDF: &'static (Debug + Sync) = &ASDF_INNER; 
  const ASDF: &'static (Debug + Sync) = &ASDF_INNER; 

fn main() {
    println!("{:#?}", ASDF);
}

Oh and my confusion really just stemmed from forgetting about the constants referring to statics rule. [Kind of a funny rule when const S: &'static T = &... works, but this is an orthogonal issue.]

I think in the meantime unsized statics should probably error, perhaps dependent on a crater run, though I doubt there's many people relying on said bug. It's much easier to relax restrictions than tighten them.

Will prepare a patch to turn this into an error, hopefully nobody has been abusing it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

withoutboats picture withoutboats  路  202Comments

nikomatsakis picture nikomatsakis  路  259Comments

withoutboats picture withoutboats  路  308Comments

nikomatsakis picture nikomatsakis  路  274Comments

aturon picture aturon  路  417Comments