Skip to content

Commit

Permalink
Auto merge of #54424 - RalfJung:sync-promote, r=<try>
Browse files Browse the repository at this point in the history
WIP: do not borrow non-Sync data in constants

We cannot share that data across threads. non-Sync is as bad as non-Freeze in that regard.

This is currently WIP because it ignores a test that is broken by #54419. But it is good enough ti get crater going.

Fixes #49206.

Cc @eddyb @nikomatsakis
  • Loading branch information
bors committed Sep 21, 2018
2 parents 2fa1390 + f6a5c9c commit e4686a6
Show file tree
Hide file tree
Showing 18 changed files with 269 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ define_dep_nodes!( <'tcx>
[] IsCopy { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] IsSized { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] IsFreeze { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] IsSync { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] NeedsDrop { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] Layout { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },

Expand Down
6 changes: 6 additions & 0 deletions src/librustc/ty/query/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_freeze_raw<'tcx> {
}
}

impl<'tcx> QueryDescription<'tcx> for queries::is_sync_raw<'tcx> {
fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
format!("computing whether `{}` is `Sync`", env.value)
}
}

impl<'tcx> QueryDescription<'tcx> for queries::needs_drop_raw<'tcx> {
fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
format!("computing whether `{}` needs drop", env.value)
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/ty/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ define_queries! { <'tcx>
[] fn is_copy_raw: is_copy_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_sized_raw: is_sized_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_freeze_raw: is_freeze_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_sync_raw: is_sync_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn needs_drop_raw: needs_drop_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn layout_raw: layout_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
-> Result<&'tcx ty::layout::LayoutDetails,
Expand Down Expand Up @@ -767,6 +768,10 @@ fn is_freeze_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepCo
DepConstructor::IsFreeze { param_env }
}

fn is_sync_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> {
DepConstructor::IsSync { param_env }
}

fn needs_drop_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> {
DepConstructor::NeedsDrop { param_env }
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/query/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::IsCopy |
DepKind::IsSized |
DepKind::IsFreeze |
DepKind::IsSync |
DepKind::NeedsDrop |
DepKind::Layout |
DepKind::ConstEval |
Expand Down
23 changes: 23 additions & 0 deletions src/librustc/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,14 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
tcx.at(span).is_freeze_raw(param_env.and(self))
}

pub fn is_sync(&'tcx self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span)-> bool
{
tcx.at(span).is_sync_raw(param_env.and(self))
}

/// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely
/// non-copy and *might* have a destructor attached; if it returns
/// `false`, then `ty` definitely has no destructor (i.e. no drop glue).
Expand Down Expand Up @@ -897,6 +905,20 @@ fn is_freeze_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
DUMMY_SP))
}

fn is_sync_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
-> bool
{
let (param_env, ty) = query.into_parts();
let trait_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem);
tcx.infer_ctxt()
.enter(|infcx| traits::type_known_to_meet_bound(&infcx,
param_env,
ty,
trait_def_id,
DUMMY_SP))
}

fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
-> bool
Expand Down Expand Up @@ -1044,6 +1066,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
is_copy_raw,
is_sized_raw,
is_freeze_raw,
is_sync_raw,
needs_drop_raw,
..*providers
};
Expand Down
49 changes: 30 additions & 19 deletions src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ bitflags! {
// they have none of these qualifications, with
// the exception of `STATIC_REF` (in statics only).
struct Qualif: u8 {
// Constant containing interior mutability (UnsafeCell).
const MUTABLE_INTERIOR = 1 << 0;
// Constant containing interior mutability (UnsafeCell) or non-Sync data.
// Both of these prevent sound re-use of the same global static memory for
// the same data across multiple threads.
const UNSHAREABLE_INTERIOR = 1 << 0;

// Constant containing an ADT that implements Drop.
const NEEDS_DROP = 1 << 1;
Expand All @@ -63,9 +65,9 @@ bitflags! {
// promote_consts decided they weren't simple enough.
const NOT_PROMOTABLE = 1 << 4;

// Const items can only have MUTABLE_INTERIOR
// Const items can only have UNSHAREABLE_INTERIOR
// and NOT_PROMOTABLE without producing an error.
const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
const CONST_ERROR = !Qualif::UNSHAREABLE_INTERIOR.bits &
!Qualif::NOT_PROMOTABLE.bits;
}
}
Expand All @@ -75,8 +77,8 @@ impl<'a, 'tcx> Qualif {
fn restrict(&mut self, ty: Ty<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>) {
if ty.is_freeze(tcx, param_env, DUMMY_SP) {
*self = *self - Qualif::MUTABLE_INTERIOR;
if ty.is_freeze(tcx, param_env, DUMMY_SP) && ty.is_sync(tcx, param_env, DUMMY_SP) {
*self = *self - Qualif::UNSHAREABLE_INTERIOR;
}
if !ty.needs_drop(tcx, param_env) {
*self = *self - Qualif::NEEDS_DROP;
Expand Down Expand Up @@ -206,7 +208,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {

/// Add the given type's qualification to self.qualif.
fn add_type(&mut self, ty: Ty<'tcx>) {
self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
self.add(Qualif::UNSHAREABLE_INTERIOR | Qualif::NEEDS_DROP);
self.qualif.restrict(ty, self.tcx, self.param_env);
}

Expand Down Expand Up @@ -679,18 +681,19 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
// Constants cannot be borrowed if they contain interior mutability as
// it means that our "silent insertion of statics" could change
// initializer values (very bad).
if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
// A reference of a MUTABLE_INTERIOR place is instead
if self.qualif.contains(Qualif::UNSHAREABLE_INTERIOR) {
// A reference of a UNSHAREABLE_INTERIOR place is instead
// NOT_CONST (see `if forbidden_mut` below), to avoid
// duplicate errors (from reborrowing, for example).
self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
self.qualif = self.qualif - Qualif::UNSHAREABLE_INTERIOR;
if self.mode != Mode::Fn {
span_err!(self.tcx.sess, self.span, E0492,
"cannot borrow a constant which may contain \
interior mutability, create a static instead");
interior mutability or non-`Sync` data. If your \
data is `Sync`, create a static instead");
}
} else {
// We allow immutable borrows of frozen data.
// We allow immutable borrows of frozen non-Sync data.
forbidden_mut = false;
}
}
Expand All @@ -712,11 +715,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
if self.mir.local_kind(local) == LocalKind::Temp {
if let Some(qualif) = self.local_qualif[local] {
// `forbidden_mut` is false, so we can safely ignore
// `MUTABLE_INTERIOR` from the local's qualifications.
// `UNSHAREABLE_INTERIOR` from the local's qualifications.
// This allows borrowing fields which don't have
// `MUTABLE_INTERIOR`, from a type that does, e.g.:
// `UNSHAREABLE_INTERIOR`, from a type that does, e.g.:
// `let _: &'static _ = &(Cell::new(1), 2).1;`
if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
if (qualif - Qualif::UNSHAREABLE_INTERIOR).is_empty() {
self.promotion_candidates.push(candidate);
}
}
Expand Down Expand Up @@ -794,10 +797,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
self.add(Qualif::NEEDS_DROP);
}

if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
let ty = rvalue.ty(self.mir, self.tcx);
self.add_type(ty);
assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
// We are looking at a concrete type constructor, and we know
// the only way to construct "fresh" non-Freeze data is `UnsafeCell`.
// So we can check for that instead of `Freeze`.
// There is no similar shortcut for Sync, though.
let ty = rvalue.ty(self.mir, self.tcx);
let freeze = Some(def.did) != self.tcx.lang_items().unsafe_cell_type();
let sync = ty.is_sync(self.tcx, self.param_env, DUMMY_SP);
if !(freeze && sync)
{
// Not freeze and sync? Be careful.
self.add(Qualif::UNSHAREABLE_INTERIOR);
}
}
}
Expand Down Expand Up @@ -1247,6 +1257,7 @@ impl MirPass for QualifyAndPromoteConstants {
}
}
let ty = mir.return_ty();
// Not using ty.is_sync() to get the right kind of error message
tcx.infer_ctxt().enter(|infcx| {
let param_env = ty::ParamEnv::empty();
let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_passes/rvalue_promotion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
debug!("type_promotability({})", ty);

if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) &&
ty.is_sync(self.tcx, self.param_env, DUMMY_SP) &&
!ty.needs_drop(self.tcx, self.param_env) {
Promotable
} else {
Expand Down Expand Up @@ -289,6 +290,11 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
let mut outer = check_expr_kind(self, ex, node_ty);
outer &= check_adjustments(self, ex);

// Avoid non-Sync types
if !self.tables.expr_ty(ex).is_sync(self.tcx, self.param_env, DUMMY_SP) {
outer = NotPromotable;
}

// Handle borrows on (or inside the autorefs of) this expression.
if self.mut_rvalue_borrows.remove(&ex.id) {
outer = NotPromotable
Expand Down
29 changes: 29 additions & 0 deletions src/test/ui/consts/const-eval/dont_promote_non_sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#![feature(optin_builtin_traits)]

struct Foo;
impl !Sync for Foo {}

struct Bar(i32);
impl !Sync for Bar {}

struct Baz { field: i32 }
impl !Sync for Baz {}

enum Bla { T(i32), S { field: i32 } }
impl !Sync for Bla {}

// Known values of the given types
fn mk_foo() -> &'static Foo { &Foo } //~ ERROR does not live long enough
fn mk_bar() -> &'static Bar { &Bar(0) } //~ ERROR does not live long enough
fn mk_baz() -> &'static Baz { &Baz { field: 0 } } //~ ERROR does not live long enough
fn mk_bla_t() -> &'static Bla { &Bla::T(0) } //~ ERROR does not live long enough
fn mk_bla_s() -> &'static Bla { &Bla::S { field: 0 } } //~ ERROR does not live long enough

// Unknown values of the given types (test a ZST and a non-ZST)
trait FooT { const C: Foo; }
fn mk_foo2<T: FooT>() -> &'static Foo { &T::C } //~ ERROR does not live long enough

trait BarT { const C: Bar; }
fn mk_bar2<T: BarT>() -> &'static Bar { &T::C } //~ ERROR does not live long enough

fn main() {}
73 changes: 73 additions & 0 deletions src/test/ui/consts/const-eval/dont_promote_non_sync.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
error[E0597]: borrowed value does not live long enough
--> $DIR/dont_promote_non_sync.rs:16:32
|
LL | fn mk_foo() -> &'static Foo { &Foo } //~ ERROR does not live long enough
| ^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
--> $DIR/dont_promote_non_sync.rs:17:32
|
LL | fn mk_bar() -> &'static Bar { &Bar(0) } //~ ERROR does not live long enough
| ^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
--> $DIR/dont_promote_non_sync.rs:18:32
|
LL | fn mk_baz() -> &'static Baz { &Baz { field: 0 } } //~ ERROR does not live long enough
| ^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
--> $DIR/dont_promote_non_sync.rs:19:34
|
LL | fn mk_bla_t() -> &'static Bla { &Bla::T(0) } //~ ERROR does not live long enough
| ^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
--> $DIR/dont_promote_non_sync.rs:20:34
|
LL | fn mk_bla_s() -> &'static Bla { &Bla::S { field: 0 } } //~ ERROR does not live long enough
| ^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
--> $DIR/dont_promote_non_sync.rs:24:42
|
LL | fn mk_foo2<T: FooT>() -> &'static Foo { &T::C } //~ ERROR does not live long enough
| ^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
--> $DIR/dont_promote_non_sync.rs:27:42
|
LL | fn mk_bar2<T: BarT>() -> &'static Bar { &T::C } //~ ERROR does not live long enough
| ^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...

error: aborting due to 7 previous errors

For more information about this error, try `rustc --explain E0597`.
19 changes: 19 additions & 0 deletions src/test/ui/consts/const-eval/non_sync_const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#![feature(optin_builtin_traits)]

struct Foo;
impl !Sync for Foo {}

struct Bar(i32);
impl !Sync for Bar {}

const FOO : &Foo = &Foo; //~ ERROR cannot borrow
const FOO2 : Option<&Foo> = Some(&Foo); //~ ERROR cannot borrow
//~^ ERROR borrowed value does not live long enough
const FOO3 : &Option<Foo> = &Some(Foo); //~ ERROR cannot borrow

const BAR : &Bar = &Bar(42); //~ ERROR cannot borrow
const BAR2 : Option<&Bar> = Some(&Bar(42)); //~ ERROR cannot borrow
//~^ ERROR borrowed value does not live long enough
const BAR3 : &Option<Bar> = &Some(Bar(42)); //~ ERROR cannot borrow

fn main() {}
Loading

0 comments on commit e4686a6

Please sign in to comment.