diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index db3aa9a1efa4b..d44fdd5d9b978 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -516,6 +516,7 @@ define_dep_nodes!( <'tcx> [] UsedTraitImports(DefId), [] HasTypeckTables(DefId), [] ConstEval { param_env: ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)> }, + [] CheckMatch(DefId), [] SymbolName(DefId), [] InstanceSymbolName { instance: Instance<'tcx> }, [] SpecializationGraph(DefId), diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 45e80b58a4f0d..9609ae5a0beb8 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -371,7 +371,8 @@ for ::middle::const_val::ErrKind<'gcx> { MiscBinaryOp | MiscCatchAll | IndexOpFeatureGated | - TypeckError => { + TypeckError | + CheckMatchError => { // nothing to do } UnimplementedConstVal(s) => { diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 7b23998046730..440af39a0d469 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -106,7 +106,8 @@ pub enum ErrKind<'tcx> { ErroneousReferencedConstant(Box>), - TypeckError + TypeckError, + CheckMatchError, } impl<'tcx> From for ErrKind<'tcx> { @@ -168,6 +169,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"), TypeckError => simple!("type-checking failed"), + CheckMatchError => simple!("match-checking failed"), } } @@ -212,8 +214,9 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { primary_span: Span, primary_kind: &str) { - if let ErrKind::TypeckError = self.kind { - return; + match self.kind { + ErrKind::TypeckError | ErrKind::CheckMatchError => return, + _ => {} } self.struct_error(tcx, primary_span, primary_kind).emit(); } diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index a477e779af9a6..18c60394a8ed5 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -37,7 +37,7 @@ use ty::{self, CrateInherentImpls, Ty, TyCtxt}; use ty::steal::Steal; use ty::subst::Substs; use util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet}; -use util::common::{profq_msg, ProfileQueriesMsg}; +use util::common::{profq_msg, ErrorReported, ProfileQueriesMsg}; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_back::PanicStrategy; @@ -205,6 +205,9 @@ define_maps! { <'tcx> [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> const_val::EvalResult<'tcx>, + [] fn check_match: CheckMatch(DefId) + -> Result<(), ErrorReported>, + /// Performs the privacy check and computes "access levels". [] fn privacy_access_levels: PrivacyAccessLevels(CrateNum) -> Rc, diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 739537c7c3a71..e63365aa4a8b2 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -800,6 +800,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::SpecializationGraph => { force!(specialization_graph_of, def_id!()); } DepKind::ObjectSafety => { force!(is_object_safe, def_id!()); } DepKind::TraitImpls => { force!(trait_impls_of, def_id!()); } + DepKind::CheckMatch => { force!(check_match, def_id!()); } DepKind::ParamEnv => { force!(param_env, def_id!()); } DepKind::DescribeDef => { force!(describe_def, def_id!()); } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 83207fbe3c346..0815294237917 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -477,6 +477,7 @@ impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { } TypeckError => TypeckError, + CheckMatchError => CheckMatchError, }) } } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 98ed8d99fda70..762b9787c8d35 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -24,9 +24,11 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::lint; use rustc_errors::DiagnosticBuilder; +use rustc::util::common::ErrorReported; use rustc::hir::def::*; -use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; +use rustc::hir::def_id::DefId; +use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, Pat, PatKind}; use rustc_back::slice; @@ -42,19 +44,10 @@ impl<'a, 'tcx> Visitor<'tcx> for OuterVisitor<'a, 'tcx> { NestedVisitorMap::OnlyBodies(&self.tcx.hir) } - fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl, - b: hir::BodyId, s: Span, id: ast::NodeId) { - intravisit::walk_fn(self, fk, fd, b, s, id); - - let def_id = self.tcx.hir.local_def_id(id); - - MatchVisitor { - tcx: self.tcx, - tables: self.tcx.body_tables(b), - region_scope_tree: &self.tcx.region_scope_tree(def_id), - param_env: self.tcx.param_env(def_id), - identity_substs: Substs::identity_for_item(self.tcx, def_id), - }.visit_body(self.tcx.hir.body(b)); + fn visit_body(&mut self, body: &'tcx hir::Body) { + intravisit::walk_body(self, body); + let def_id = self.tcx.hir.body_owner_def_id(body.id()); + let _ = self.tcx.check_match(def_id); } } @@ -63,6 +56,27 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.sess.abort_if_errors(); } +pub(crate) fn check_match<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, +) -> Result<(), ErrorReported> { + let body_id = if let Some(id) = tcx.hir.as_local_node_id(def_id) { + tcx.hir.body_owned_by(id) + } else { + return Ok(()); + }; + + tcx.sess.track_errors(|| { + MatchVisitor { + tcx, + tables: tcx.body_tables(body_id), + region_scope_tree: &tcx.region_scope_tree(def_id), + param_env: tcx.param_env(def_id), + identity_substs: Substs::identity_for_item(tcx, def_id), + }.visit_body(tcx.hir.body(body_id)); + }) +} + fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> DiagnosticBuilder<'a> { struct_span_err!(sess, sp, E0004, "{}", &error_message) } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index a548c1df16e28..eb4db6365cc57 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -18,7 +18,6 @@ use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::LayoutOf; -use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; use rustc::ty::subst::{Substs, Subst}; use rustc::util::common::ErrorReported; @@ -684,14 +683,7 @@ impl<'a, 'tcx> ConstContext<'a, 'tcx> { } } -pub fn provide(providers: &mut Providers) { - *providers = Providers { - const_eval, - ..*providers - }; -} - -fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx> { let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) { @@ -705,8 +697,18 @@ fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let tables = tcx.typeck_tables_of(def_id); let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) { + let body_id = tcx.hir.body_owned_by(id); + + // Do match-check before building MIR + if tcx.check_match(def_id).is_err() { + return Err(ConstEvalErr { + span: tcx.def_span(key.value.0), + kind: CheckMatchError, + }); + } + tcx.mir_const_qualif(def_id); - tcx.hir.body(tcx.hir.body_owned_by(id)) + tcx.hir.body(body_id) } else { tcx.extern_const_body(def_id).body }; diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 7b1c33cb2710c..714cead4befda 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -48,5 +48,15 @@ pub mod pattern; pub use eval::*; +use rustc::ty::maps::Providers; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + const_eval: eval::const_eval, + check_match: check_match::check_match, + ..*providers + }; +} + // Build the diagnostics array at the end so that the metadata includes error use sites. __build_diagnostic_array! { librustc_const_eval, DIAGNOSTICS } diff --git a/src/test/compile-fail/const-match-check.rs b/src/test/compile-fail/const-match-check.rs new file mode 100644 index 0000000000000..36a6600b62d98 --- /dev/null +++ b/src/test/compile-fail/const-match-check.rs @@ -0,0 +1,44 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: matchck eval1 eval2 + +#[cfg(matchck)] +const X: i32 = { let 0 = 0; 0 }; +//[matchck]~^ ERROR refutable pattern in local binding + +#[cfg(matchck)] +static Y: i32 = { let 0 = 0; 0 }; +//[matchck]~^ ERROR refutable pattern in local binding + +#[cfg(matchck)] +trait Bar { + const X: i32 = { let 0 = 0; 0 }; + //[matchck]~^ ERROR refutable pattern in local binding +} + +#[cfg(matchck)] +impl Bar for () { + const X: i32 = { let 0 = 0; 0 }; + //[matchck]~^ ERROR refutable pattern in local binding +} + +#[cfg(eval1)] +enum Foo { + A = { let 0 = 0; 0 }, + //[eval1]~^ ERROR refutable pattern in local binding +} + +fn main() { + #[cfg(eval2)] + let x: [i32; { let 0 = 0; 0 }] = []; + //[eval2]~^ ERROR refutable pattern in local binding + //[eval2]~| ERROR constant evaluation error +}