-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
do not borrow non-Sync data in constants #54424
Changes from all commits
292512b
15313da
a63e720
2cfeaa2
bea5fd0
94e88cc
1a7706f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ use hir::map::DefPathData; | |
use hir::{self, Node}; | ||
use ich::NodeIdHashingMode; | ||
use traits::{self, ObligationCause}; | ||
use ty::{self, Ty, TyCtxt, GenericParamDefKind, TypeFoldable}; | ||
use ty::{self, Ty, TyCtxt, GenericParamDefKind, TypeFoldable, Predicate}; | ||
use ty::subst::{Substs, UnpackedKind}; | ||
use ty::query::TyCtxtAt; | ||
use ty::TyKind::*; | ||
|
@@ -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). | ||
|
@@ -853,6 +861,50 @@ impl<'a, 'tcx> ty::TyS<'tcx> { | |
debug!("is_type_representable: {:?} is {:?}", self, r); | ||
r | ||
} | ||
|
||
/// Returns a potentially different type that | ||
/// can be used to check whether a 0-argument constructor of this type is | ||
/// a `Sync` value. | ||
/// Concretely, some type parameters get replaced by `()`. We assume that | ||
/// if a type parameter has no bounds (except for `Sized`), and if the same | ||
/// type is `Sync` for that type parameter replaced by `()`, then the constructor | ||
/// valie is also `Sync` at the original type. This is a kind of parametricity | ||
/// assumption. | ||
/// For now, we only do anything if *no* type parameter has any bound other than | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. anything -> something |
||
/// `Sized`. That's just simpler to check. | ||
pub fn generalize_for_value_based_sync( | ||
&'tcx self, | ||
tcx: TyCtxt<'a, 'tcx, 'tcx>, | ||
) -> Ty<'tcx> { | ||
match self.sty { | ||
ty::Adt(def, substs) => { | ||
for (bound, _) in def.predicates(tcx).predicates.iter() { | ||
match bound { | ||
Predicate::Trait(pred) | ||
if Some(pred.def_id()) == tcx.lang_items().sized_trait() => | ||
{ | ||
// A `Sized` bound. Ignore. | ||
continue; | ||
} | ||
_ => {} | ||
} | ||
// There is a relevant bound. Do not do anything. | ||
debug!("Type {:?} not generalizable because of bound {:?}", self, bound); | ||
return self; | ||
} | ||
// If we got here, there are no bounds and we can replace all | ||
// type parameters by `()`. | ||
let unit_substs = substs.iter() | ||
.map(|param| match param.unpack() { | ||
UnpackedKind::Lifetime(_) => param.clone(), | ||
UnpackedKind::Type(_) => tcx.mk_unit().into(), | ||
}); | ||
let unit_substs = tcx.mk_substs(unit_substs); | ||
tcx.mk_ty(ty::Adt(def, unit_substs)) | ||
} | ||
_ => self, // don't change | ||
} | ||
} | ||
} | ||
|
||
fn is_copy_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | ||
|
@@ -897,6 +949,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 | ||
|
@@ -1044,6 +1110,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 | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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; | ||
} | ||
} | ||
|
@@ -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; | ||
|
@@ -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); | ||
} | ||
|
||
|
@@ -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; | ||
} | ||
} | ||
|
@@ -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); | ||
} | ||
} | ||
|
@@ -788,16 +791,36 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | |
} | ||
} | ||
|
||
Rvalue::Aggregate(ref kind, _) => { | ||
Rvalue::Aggregate(ref kind, ref args) => { | ||
if let AggregateKind::Adt(def, ..) = **kind { | ||
if def.has_dtor(self.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)); | ||
let ty = rvalue.ty(self.mir, self.tcx); | ||
// Value-based reasoning: | ||
// 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`. | ||
let freeze = Some(def.did) != self.tcx.lang_items().unsafe_cell_type(); | ||
// For Sync, we generally have to fall back on the type, but have a | ||
// special exception for 0-argument ADT cosntructors. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cosntructors -> constructors |
||
let sync = { | ||
let generalized_ty = if args.len() == 0 { | ||
// A type cosntructor with no arguments is directly a value. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cosntructor |
||
// We generalize its type before checking Sync to allow | ||
// `None` to pass no matter the type parameter. | ||
ty.generalize_for_value_based_sync(self.tcx) | ||
} else { | ||
ty | ||
}; | ||
generalized_ty.is_sync(self.tcx, self.param_env, DUMMY_SP) | ||
}; | ||
if !(freeze && sync) | ||
{ | ||
// Not freeze and sync? Be careful. | ||
debug!("Rvalue {:?} has unsharable interior", rvalue); | ||
self.add(Qualif::UNSHAREABLE_INTERIOR); | ||
} | ||
} | ||
} | ||
|
@@ -1248,6 +1271,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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
valie -> value