diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 05ba517e2638..da59ebb9b9cd 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -591,15 +591,20 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } Operand::Constant(ref constant) => { - if let Literal::Item { def_id, substs } = constant.literal { - // Don't peek inside generic (associated) constants. - if substs.types().next().is_some() { + if let Literal::Item { def_id, substs: _ } = constant.literal { + // Don't peek inside trait associated constants. + if self.tcx.trait_of_item(def_id).is_some() { self.add_type(constant.ty); } else { let bits = self.tcx.at(constant.span).mir_const_qualif(def_id); let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif"); self.add(qualif); + + // Just in case the type is more specific than + // the definition, e.g. impl associated const + // with type parameters, take it into account. + self.qualif.restrict(constant.ty, self.tcx, self.param_env); } // Let `const fn` transitively have destructors, diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 763f885b4d00..8ba8d4fce0db 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -87,19 +87,14 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { } } - // Adds the worst effect out of all the values of one type. - fn add_type(&mut self, ty: Ty<'gcx>) { - if !ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) { - self.promotable = false; - } - - if ty.needs_drop(self.tcx, self.param_env) { - self.promotable = false; - } + // Returns true iff all the values of the type are promotable. + fn type_has_only_promotable_values(&mut self, ty: Ty<'gcx>) -> bool { + ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) && + !ty.needs_drop(self.tcx, self.param_env) } fn handle_const_fn_call(&mut self, def_id: DefId, ret_ty: Ty<'gcx>) { - self.add_type(ret_ty); + self.promotable &= self.type_has_only_promotable_values(ret_ty); self.promotable &= if let Some(fn_id) = self.tcx.hir.as_local_node_id(def_id) { FnLikeNode::from_node(self.tcx.hir.get(fn_id)).map_or(false, |fn_like| { @@ -333,20 +328,30 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node match def { Def::VariantCtor(..) | Def::StructCtor(..) | Def::Fn(..) | Def::Method(..) => {} - Def::AssociatedConst(_) => v.add_type(node_ty), - Def::Const(did) => { - v.promotable &= if let Some(node_id) = v.tcx.hir.as_local_node_id(did) { - match v.tcx.hir.expect_item(node_id).node { - hir::ItemConst(_, body) => { + + Def::Const(did) | + Def::AssociatedConst(did) => { + let promotable = if v.tcx.trait_of_item(did).is_some() { + // Don't peek inside trait associated constants. + false + } else if let Some(node_id) = v.tcx.hir.as_local_node_id(did) { + match v.tcx.hir.maybe_body_owned_by(node_id) { + Some(body) => { v.visit_nested_body(body); v.tcx.rvalue_promotable_to_static.borrow()[&body.node_id] } - _ => false + None => false } } else { v.tcx.const_is_rvalue_promotable_to_static(did) }; + + // Just in case the type is more specific than the definition, + // e.g. impl associated const with type parameters, check it. + // Also, trait associated consts are relaxed by this. + v.promotable &= promotable || v.type_has_only_promotable_values(node_ty); } + _ => { v.promotable = false; } diff --git a/src/test/run-pass/rvalue-static-promotion.rs b/src/test/run-pass/rvalue-static-promotion.rs index e57491930a45..acf96b566df8 100644 --- a/src/test/run-pass/rvalue-static-promotion.rs +++ b/src/test/run-pass/rvalue-static-promotion.rs @@ -8,8 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[allow(unused_variables)] -fn main() { - let x: &'static u32 = &42; - let y: &'static Option = &None; +use std::cell::Cell; + +const NONE_CELL_STRING: Option> = None; + +struct Foo(T); +impl Foo { + const FOO: Option> = None; +} + +fn main() { + let _: &'static u32 = &42; + let _: &'static Option = &None; + + // We should be able to peek at consts and see they're None. + let _: &'static Option> = &NONE_CELL_STRING; + let _: &'static Option> = &Foo::FOO; }