From: Mazdak Farrokhzad Date: Sat, 21 Mar 2020 04:33:18 +0000 (+0100) Subject: Rollup merge of #70038 - DutchGhost:const-forget-tests, r=RalfJung X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=45b10f6f9876abe8b57394ef28e57936c4a07466;hp=d6f3a433d9847b2c4c6c31f9ea985625b16dded1;p=rust.git Rollup merge of #70038 - DutchGhost:const-forget-tests, r=RalfJung Remove the call that makes miri fail Fixes the concern raised in https://github.com/rust-lang/rust/pull/69645/files#r392884274 cc @RalfJung --- diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md index f93591204cd..9457cce11af 100644 --- a/.github/ISSUE_TEMPLATE/tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/tracking_issue.md @@ -36,11 +36,11 @@ for larger features an implementation could be broken up into multiple PRs. - [ ] Implement the RFC (cc @rust-lang/XXX -- can anyone write up mentoring instructions?) -- [ ] Adjust documentation ([see instructions on rustc-guide][doc-guide]) -- [ ] Stabilization PR ([see instructions on rustc-guide][stabilization-guide]) +- [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide]) +- [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide]) -[stabilization-guide]: https://rust-lang.github.io/rustc-guide/stabilization_guide.html#stabilization-pr -[doc-guide]: https://rust-lang.github.io/rustc-guide/stabilization_guide.html#documentation-prs +[stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr +[doc-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#documentation-prs ### Unresolved Questions $DIR/type-annotations-needed-expr.rs:2:39 @@ -468,7 +468,7 @@ fn annotate_method_call( &segment.args, ) { let borrow = tables.borrow(); - if let Some((DefKind::Method, did)) = borrow.type_dependent_def(e.hir_id) { + if let Some((DefKind::AssocFn, did)) = borrow.type_dependent_def(e.hir_id) { let generics = self.tcx.generics_of(did); if !generics.params.is_empty() { err.span_suggestion( diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs index 1a09729ef64..50b324c7227 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -3,6 +3,8 @@ use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::SubregionOrigin; use rustc::util::common::ErrorReported; use rustc_errors::struct_span_err; @@ -47,6 +49,15 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn try_report_anon_anon_conflict(&self) -> Option { let (span, sub, sup) = self.regions()?; + if let Some(RegionResolutionError::ConcreteFailure( + SubregionOrigin::ReferenceOutlivesReferent(..), + .., + )) = self.error + { + // This error doesn't make much sense in this case. + return None; + } + // Determine whether the sub and sup consist of both anonymous (elided) regions. let anon_reg_sup = self.tcx().is_suitable_region(sup)?; diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs index 2ae7f4cc04f..15acf632b2c 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -33,11 +33,11 @@ pub(super) fn find_anon_type( let fndecl = match self.tcx().hir().get(hir_id) { Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref m, ..), .. }) | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Method(ref m, ..), + kind: hir::TraitItemKind::Fn(ref m, ..), .. }) | Node::ImplItem(&hir::ImplItem { - kind: hir::ImplItemKind::Method(ref m, ..), + kind: hir::ImplItemKind::Fn(ref m, ..), .. }) => &m.decl, _ => return None, @@ -93,8 +93,8 @@ struct FindNestedTypeVisitor<'tcx> { impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { type Map = Map<'tcx>; - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.tcx.hir()) } fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { @@ -212,8 +212,8 @@ struct TyPathVisitor<'tcx> { impl Visitor<'tcx> for TyPathVisitor<'tcx> { type Map = Map<'tcx>; - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Map<'tcx>> { - NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap> { + NestedVisitorMap::OnlyBodies(self.tcx.hir()) } fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs index d8c314a0d2f..2357ee689d5 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/mod.rs @@ -17,12 +17,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { - if let Some(tables) = self.in_progress_tables { - let tables = tables.borrow(); - NiceRegionError::new(self, error.clone(), Some(&tables)).try_report().is_some() - } else { - NiceRegionError::new(self, error.clone(), None).try_report().is_some() - } + NiceRegionError::new(self, error.clone()).try_report().is_some() } } @@ -30,16 +25,11 @@ pub struct NiceRegionError<'cx, 'tcx> { infcx: &'cx InferCtxt<'cx, 'tcx>, error: Option>, regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>, - tables: Option<&'cx ty::TypeckTables<'tcx>>, } impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { - pub fn new( - infcx: &'cx InferCtxt<'cx, 'tcx>, - error: RegionResolutionError<'tcx>, - tables: Option<&'cx ty::TypeckTables<'tcx>>, - ) -> Self { - Self { infcx, error: Some(error), regions: None, tables } + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>, error: RegionResolutionError<'tcx>) -> Self { + Self { infcx, error: Some(error), regions: None } } pub fn new_from_span( @@ -47,9 +37,8 @@ pub fn new_from_span( span: Span, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>, - tables: Option<&'cx ty::TypeckTables<'tcx>>, ) -> Self { - Self { infcx, error: None, regions: Some((span, sub, sup)), tables } + Self { infcx, error: None, regions: Some((span, sub, sup)) } } fn tcx(&self) -> TyCtxt<'tcx> { diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs index 4dc9096533b..de72c276595 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/util.rs @@ -51,52 +51,44 @@ pub(super) fn find_param_with_region( }; let hir = &self.tcx().hir(); - if let Some(hir_id) = hir.as_local_hir_id(id) { - if let Some(body_id) = hir.maybe_body_owned_by(hir_id) { - let body = hir.body(body_id); - let owner_id = hir.body_owner(body_id); - let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); - if let Some(tables) = self.tables { - body.params - .iter() - .enumerate() - .filter_map(|(index, param)| { - // May return None; sometimes the tables are not yet populated. - let ty_hir_id = fn_decl.inputs[index].hir_id; - let param_ty_span = hir.span(ty_hir_id); - let ty = tables.node_type_opt(param.hir_id)?; - let mut found_anon_region = false; - let new_param_ty = self.tcx().fold_regions(&ty, &mut false, |r, _| { - if *r == *anon_region { - found_anon_region = true; - replace_region - } else { - r - } - }); - if found_anon_region { - let is_first = index == 0; - Some(AnonymousParamInfo { - param: param, - param_ty: new_param_ty, - param_ty_span: param_ty_span, - bound_region: bound_region, - is_first: is_first, - }) - } else { - None - } - }) - .next() + let hir_id = hir.as_local_hir_id(id)?; + let body_id = hir.maybe_body_owned_by(hir_id)?; + let body = hir.body(body_id); + let owner_id = hir.body_owner(body_id); + let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); + let poly_fn_sig = self.tcx().fn_sig(id); + let fn_sig = self.tcx().liberate_late_bound_regions(id, &poly_fn_sig); + body.params + .iter() + .enumerate() + .filter_map(|(index, param)| { + // May return None; sometimes the tables are not yet populated. + let ty = fn_sig.inputs()[index]; + let mut found_anon_region = false; + let new_param_ty = self.tcx().fold_regions(&ty, &mut false, |r, _| { + if *r == *anon_region { + found_anon_region = true; + replace_region + } else { + r + } + }); + if found_anon_region { + let ty_hir_id = fn_decl.inputs[index].hir_id; + let param_ty_span = hir.span(ty_hir_id); + let is_first = index == 0; + Some(AnonymousParamInfo { + param, + param_ty: new_param_ty, + param_ty_span, + bound_region, + is_first, + }) } else { None } - } else { - None - } - } else { - None - } + }) + .next() } // Here, we check for the case where the anonymous region diff --git a/src/librustc_infer/infer/freshen.rs b/src/librustc_infer/infer/freshen.rs index 63dded3b43d..a454feea36b 100644 --- a/src/librustc_infer/infer/freshen.rs +++ b/src/librustc_infer/infer/freshen.rs @@ -143,10 +143,7 @@ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.needs_infer() - && !t.has_erasable_regions() - && !(t.has_closure_types() && self.infcx.in_progress_tables.is_some()) - { + if !t.needs_infer() && !t.has_erasable_regions() { return t; } diff --git a/src/librustc_infer/infer/glb.rs b/src/librustc_infer/infer/glb.rs index 2634d9cac3e..8b26bcef573 100644 --- a/src/librustc_infer/infer/glb.rs +++ b/src/librustc_infer/infer/glb.rs @@ -18,7 +18,7 @@ pub fn new( fields: &'combine mut CombineFields<'infcx, 'tcx>, a_is_expected: bool, ) -> Glb<'combine, 'infcx, 'tcx> { - Glb { fields: fields, a_is_expected: a_is_expected } + Glb { fields, a_is_expected } } } diff --git a/src/librustc_infer/infer/higher_ranked/README.md b/src/librustc_infer/infer/higher_ranked/README.md index e7afaa5beb0..533d0ef7e6c 100644 --- a/src/librustc_infer/infer/higher_ranked/README.md +++ b/src/librustc_infer/infer/higher_ranked/README.md @@ -1,8 +1,8 @@ To learn more about how Higher-ranked trait bounds work in the _old_ trait -solver, see [this chapter][oldhrtb] of the rustc-guide. +solver, see [this chapter][oldhrtb] of the rustc-dev-guide. To learn more about how they work in the _new_ trait solver, see [this chapter][newhrtb]. -[oldhrtb]: https://rust-lang.github.io/rustc-guide/traits/hrtb.html -[newhrtb]: https://rust-lang.github.io/rustc-guide/borrow_check/region_inference.html#placeholders-and-universes +[oldhrtb]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html +[newhrtb]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference.html#placeholders-and-universes diff --git a/src/librustc_infer/infer/higher_ranked/mod.rs b/src/librustc_infer/infer/higher_ranked/mod.rs index 33781188a95..105b987f85e 100644 --- a/src/librustc_infer/infer/higher_ranked/mod.rs +++ b/src/librustc_infer/infer/higher_ranked/mod.rs @@ -71,9 +71,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// needed (but is also permitted). /// /// For more information about how placeholders and HRTBs work, see - /// the [rustc guide]. + /// the [rustc dev guide]. /// - /// [rustc guide]: https://rust-lang.github.io/rustc-guide/traits/hrtb.html + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html pub fn replace_bound_vars_with_placeholders( &self, binder: &ty::Binder, diff --git a/src/librustc_infer/infer/lexical_region_resolve/README.md b/src/librustc_infer/infer/lexical_region_resolve/README.md index c26b5625a90..e0b2c0bffee 100644 --- a/src/librustc_infer/infer/lexical_region_resolve/README.md +++ b/src/librustc_infer/infer/lexical_region_resolve/README.md @@ -2,6 +2,6 @@ Lexical Region Resolution was removed in https://github.com/rust-lang/rust/pull/64790. Rust now uses Non-lexical lifetimes. For more info, please see the [borrowck -chapter][bc] in the rustc-guide. +chapter][bc] in the rustc-dev-guide. -[bc]: https://rust-lang.github.io/rustc-guide/borrow_check/region_inference.html +[bc]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference.html diff --git a/src/librustc_infer/infer/lexical_region_resolve/mod.rs b/src/librustc_infer/infer/lexical_region_resolve/mod.rs index fc9f3bb0767..3af10e850d5 100644 --- a/src/librustc_infer/infer/lexical_region_resolve/mod.rs +++ b/src/librustc_infer/infer/lexical_region_resolve/mod.rs @@ -7,6 +7,7 @@ use crate::infer::region_constraints::VarInfos; use crate::infer::region_constraints::VerifyBound; use crate::infer::RegionVariableOrigin; +use crate::infer::RegionckMode; use crate::infer::SubregionOrigin; use rustc::middle::free_region::RegionRelations; use rustc::ty::fold::TypeFoldable; @@ -33,12 +34,29 @@ pub fn resolve<'tcx>( region_rels: &RegionRelations<'_, 'tcx>, var_infos: VarInfos, data: RegionConstraintData<'tcx>, + mode: RegionckMode, ) -> (LexicalRegionResolutions<'tcx>, Vec>) { debug!("RegionConstraintData: resolve_regions()"); let mut errors = vec![]; let mut resolver = LexicalResolver { region_rels, var_infos, data }; - let values = resolver.infer_variable_values(&mut errors); - (values, errors) + match mode { + RegionckMode::Solve => { + let values = resolver.infer_variable_values(&mut errors); + (values, errors) + } + RegionckMode::Erase { suppress_errors: false } => { + // Do real inference to get errors, then erase the results. + let mut values = resolver.infer_variable_values(&mut errors); + let re_erased = region_rels.tcx.lifetimes.re_erased; + + values.values.iter_mut().for_each(|v| *v = VarValue::Value(re_erased)); + (values, errors) + } + RegionckMode::Erase { suppress_errors: true } => { + // Skip region inference entirely. + (resolver.erased_data(region_rels.tcx), Vec::new()) + } + } } /// Contains the result of lexical region resolution. Offers methods @@ -163,6 +181,19 @@ fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx } } + /// An erased version of the lexical region resolutions. Used when we're + /// erasing regions and suppressing errors: in item bodies with + /// `-Zborrowck=mir`. + fn erased_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { + LexicalRegionResolutions { + error_region: tcx.lifetimes.re_static, + values: IndexVec::from_elem_n( + VarValue::Value(tcx.lifetimes.re_erased), + self.num_vars(), + ), + } + } + fn dump_constraints(&self, free_regions: &RegionRelations<'_, 'tcx>) { debug!("----() Start constraint listing (context={:?}) ()----", free_regions.context); for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { @@ -751,7 +782,7 @@ fn construct_graph(&self) -> RegionGraph<'tcx> { let dummy_source = graph.add_node(()); let dummy_sink = graph.add_node(()); - for (constraint, _) in &self.data.constraints { + for constraint in self.data.constraints.keys() { match *constraint { Constraint::VarSubVar(a_id, b_id) => { graph.add_edge( diff --git a/src/librustc_infer/infer/lub.rs b/src/librustc_infer/infer/lub.rs index b6d20ba1f3f..20ddeec6850 100644 --- a/src/librustc_infer/infer/lub.rs +++ b/src/librustc_infer/infer/lub.rs @@ -18,7 +18,7 @@ pub fn new( fields: &'combine mut CombineFields<'infcx, 'tcx>, a_is_expected: bool, ) -> Lub<'combine, 'infcx, 'tcx> { - Lub { fields: fields, a_is_expected: a_is_expected } + Lub { fields, a_is_expected } } } diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs index 11b08d468b1..391fce946bf 100644 --- a/src/librustc_infer/infer/mod.rs +++ b/src/librustc_infer/infer/mod.rs @@ -5,7 +5,6 @@ pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; -pub use rustc::ty::IntVarValue; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; @@ -13,18 +12,17 @@ use rustc::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc::middle::free_region::RegionRelations; -use rustc::middle::lang_items; use rustc::middle::region; use rustc::mir; use rustc::mir::interpret::ConstEvalResult; -use rustc::session::config::BorrowckMode; +use rustc::traits::select; use rustc::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use rustc::ty::fold::{TypeFoldable, TypeFolder}; use rustc::ty::relate::RelateResult; use rustc::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; +pub use rustc::ty::IntVarValue; use rustc::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; use rustc::ty::{ConstVid, FloatVid, IntVid, TyVid}; - use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; @@ -32,8 +30,10 @@ use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_session::config::BorrowckMode; use rustc_span::symbol::Symbol; use rustc_span::Span; + use std::cell::{Cell, Ref, RefCell}; use std::collections::BTreeMap; use std::fmt; @@ -58,7 +58,6 @@ mod lexical_region_resolve; mod lub; pub mod nll_relate; -pub mod opaque_types; pub mod outlives; pub mod region_constraints; pub mod resolve; @@ -80,31 +79,50 @@ pub struct InferOk<'tcx, T> { pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult<'tcx, T> = Result>; // "fixup result" -/// A flag that is used to suppress region errors. This is normally -/// false, but sometimes -- when we are doing region checks that the -/// NLL borrow checker will also do -- it might be set to true. -#[derive(Copy, Clone, Default, Debug)] -pub struct SuppressRegionErrors { - suppressed: bool, +/// How we should handle region solving. +/// +/// This is used so that the region values inferred by HIR region solving are +/// not exposed, and so that we can avoid doing work in HIR typeck that MIR +/// typeck will also do. +#[derive(Copy, Clone, Debug)] +pub enum RegionckMode { + /// The default mode: report region errors, don't erase regions. + Solve, + /// Erase the results of region after solving. + Erase { + /// A flag that is used to suppress region errors, when we are doing + /// region checks that the NLL borrow checker will also do -- it might + /// be set to true. + suppress_errors: bool, + }, +} + +impl Default for RegionckMode { + fn default() -> Self { + RegionckMode::Solve + } } -impl SuppressRegionErrors { +impl RegionckMode { pub fn suppressed(self) -> bool { - self.suppressed + match self { + Self::Solve => false, + Self::Erase { suppress_errors } => suppress_errors, + } } /// Indicates that the MIR borrowck will repeat these region /// checks, so we should ignore errors if NLL is (unconditionally) /// enabled. - pub fn when_nll_is_enabled(tcx: TyCtxt<'_>) -> Self { + pub fn for_item_body(tcx: TyCtxt<'_>) -> Self { // FIXME(Centril): Once we actually remove `::Migrate` also make // this always `true` and then proceed to eliminate the dead code. match tcx.borrowck_mode() { // If we're on Migrate mode, report AST region errors - BorrowckMode::Migrate => SuppressRegionErrors { suppressed: false }, + BorrowckMode::Migrate => RegionckMode::Erase { suppress_errors: false }, // If we're on MIR, don't report AST region errors as they should be reported by NLL - BorrowckMode::Mir => SuppressRegionErrors { suppressed: true }, + BorrowckMode::Mir => RegionckMode::Erase { suppress_errors: true }, } } } @@ -215,10 +233,10 @@ pub struct InferCtxt<'a, 'tcx> { /// Caches the results of trait selection. This cache is used /// for things that have to do with the parameters in scope. - pub selection_cache: traits::SelectionCache<'tcx>, + pub selection_cache: select::SelectionCache<'tcx>, /// Caches the results of trait evaluation. - pub evaluation_cache: traits::EvaluationCache<'tcx>, + pub evaluation_cache: select::EvaluationCache<'tcx>, /// the set of predicates on which errors have been reported, to /// avoid reporting the same error twice. @@ -1208,20 +1226,13 @@ pub fn resolve_regions_and_report_errors( region_context: DefId, region_map: ®ion::ScopeTree, outlives_env: &OutlivesEnvironment<'tcx>, - suppress: SuppressRegionErrors, + mode: RegionckMode, ) { assert!( self.is_tainted_by_errors() || self.inner.borrow().region_obligations.is_empty(), "region_obligations not empty: {:#?}", self.inner.borrow().region_obligations ); - - let region_rels = &RegionRelations::new( - self.tcx, - region_context, - region_map, - outlives_env.free_region_map(), - ); let (var_infos, data) = self .inner .borrow_mut() @@ -1229,8 +1240,16 @@ pub fn resolve_regions_and_report_errors( .take() .expect("regions already resolved") .into_infos_and_data(); + + let region_rels = &RegionRelations::new( + self.tcx, + region_context, + region_map, + outlives_env.free_region_map(), + ); + let (lexical_region_resolutions, errors) = - lexical_region_resolve::resolve(region_rels, var_infos, data); + lexical_region_resolve::resolve(region_rels, var_infos, data, mode); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); @@ -1241,7 +1260,7 @@ pub fn resolve_regions_and_report_errors( // this infcx was in use. This is totally hokey but // otherwise we have a hard time separating legit region // errors from silly ones. - self.report_region_errors(region_map, &errors, suppress); + self.report_region_errors(region_map, &errors); } } @@ -1474,31 +1493,6 @@ pub fn verify_generic_bound( .verify_generic_bound(origin, kind, a, bound); } - pub fn type_is_copy_modulo_regions( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span, - ) -> bool { - let ty = self.resolve_vars_if_possible(&ty); - - // Even if the type may have no inference variables, during - // type-checking closure types are in local tables only. - if !self.in_progress_tables.is_some() || !ty.has_closure_types() { - if !(param_env, ty).has_local_value() { - return ty.is_copy_modulo_regions(self.tcx, param_env, span); - } - } - - let copy_def_id = self.tcx.require_lang_item(lang_items::CopyTraitLangItem, None); - - // This can get called from typeck (by euv), and `moves_by_default` - // rightly refuses to work with inference variables, but - // moves_by_default has a cache, which we want to use in other - // cases. - traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span) - } - /// Obtains the latest type of the given closure; this may be a /// closure in the current function, in which case its /// `ClosureKind` may not yet be known. @@ -1522,30 +1516,6 @@ pub fn closure_sig(&self, def_id: DefId, substs: SubstsRef<'tcx>) -> ty::PolyFnS closure_sig_ty.fn_sig(self.tcx) } - /// Normalizes associated types in `value`, potentially returning - /// new obligations that must further be processed. - pub fn partially_normalize_associated_types_in( - &self, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: &T, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - debug!("partially_normalize_associated_types_in(value={:?})", value); - let mut selcx = traits::SelectionContext::new(self); - let cause = ObligationCause::misc(span, body_id); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, param_env, cause, value); - debug!( - "partially_normalize_associated_types_in: result={:?} predicates={:?}", - value, obligations - ); - InferOk { value, obligations } - } - /// Clears the selection, evaluation, and projection caches. This is useful when /// repeatedly attempting to select an `Obligation` while changing only /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing. @@ -1671,14 +1641,14 @@ pub fn shallow_resolve_changed(&self, infer: ty::InferTy) -> bool { ty::IntVar(v) => { // If inlined_probe_value returns a value it's always a - // `ty::Int(_)` or `ty::UInt(_)`, which nevers matches a + // `ty::Int(_)` or `ty::UInt(_)`, which never matches a // `ty::Infer(_)`. self.infcx.inner.borrow_mut().int_unification_table.inlined_probe_value(v).is_some() } ty::FloatVar(v) => { // If inlined_probe_value returns a value it's always a - // `ty::Float(_)`, which nevers matches a `ty::Infer(_)`. + // `ty::Float(_)`, which never matches a `ty::Infer(_)`. // // Not `inlined_probe_value(v)` because this call site is colder. self.infcx.inner.borrow_mut().float_unification_table.probe_value(v).is_some() diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs index e35b8f9c8ec..50bea300c50 100644 --- a/src/librustc_infer/infer/nll_relate/mod.rs +++ b/src/librustc_infer/infer/nll_relate/mod.rs @@ -340,7 +340,7 @@ fn relate_ty_var>( // In NLL, we don't have type inference variables // floating around, so we can do this rather imprecise // variant of the occurs-check. - assert!(!generalized_ty.has_infer_types()); + assert!(!generalized_ty.has_infer_types_or_consts()); } self.infcx.inner.borrow_mut().type_variables.instantiate(vid, generalized_ty); diff --git a/src/librustc_infer/infer/opaque_types/mod.rs b/src/librustc_infer/infer/opaque_types/mod.rs deleted file mode 100644 index 06d45690e41..00000000000 --- a/src/librustc_infer/infer/opaque_types/mod.rs +++ /dev/null @@ -1,1328 +0,0 @@ -use crate::infer::error_reporting::{note_and_explain_free_region, note_and_explain_region}; -use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind}; -use crate::traits::{self, PredicateObligation}; -use rustc::middle::region; -use rustc::session::config::nightly_options; -use rustc::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use rustc::ty::free_region_map::FreeRegionRelations; -use rustc::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; -use rustc::ty::{self, GenericParamDefKind, Ty, TyCtxt}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{struct_span_err, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_hir::Node; -use rustc_span::Span; - -pub type OpaqueTypeMap<'tcx> = DefIdMap>; - -/// Information about the opaque types whose values we -/// are inferring in this function (these are the `impl Trait` that -/// appear in the return type). -#[derive(Copy, Clone, Debug)] -pub struct OpaqueTypeDecl<'tcx> { - /// The opaque type (`ty::Opaque`) for this declaration. - pub opaque_type: Ty<'tcx>, - - /// The substitutions that we apply to the opaque type that this - /// `impl Trait` desugars to. e.g., if: - /// - /// fn foo<'a, 'b, T>() -> impl Trait<'a> - /// - /// winds up desugared to: - /// - /// type Foo<'x, X> = impl Trait<'x> - /// fn foo<'a, 'b, T>() -> Foo<'a, T> - /// - /// then `substs` would be `['a, T]`. - pub substs: SubstsRef<'tcx>, - - /// The span of this particular definition of the opaque type. So - /// for example: - /// - /// ``` - /// type Foo = impl Baz; - /// fn bar() -> Foo { - /// ^^^ This is the span we are looking for! - /// ``` - /// - /// In cases where the fn returns `(impl Trait, impl Trait)` or - /// other such combinations, the result is currently - /// over-approximated, but better than nothing. - pub definition_span: Span, - - /// The type variable that represents the value of the opaque type - /// that we require. In other words, after we compile this function, - /// we will be created a constraint like: - /// - /// Foo<'a, T> = ?C - /// - /// where `?C` is the value of this type variable. =) It may - /// naturally refer to the type and lifetime parameters in scope - /// in this function, though ultimately it should only reference - /// those that are arguments to `Foo` in the constraint above. (In - /// other words, `?C` should not include `'b`, even though it's a - /// lifetime parameter on `foo`.) - pub concrete_ty: Ty<'tcx>, - - /// Returns `true` if the `impl Trait` bounds include region bounds. - /// For example, this would be true for: - /// - /// fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b - /// - /// but false for: - /// - /// fn foo<'c>() -> impl Trait<'c> - /// - /// unless `Trait` was declared like: - /// - /// trait Trait<'c>: 'c - /// - /// in which case it would be true. - /// - /// This is used during regionck to decide whether we need to - /// impose any additional constraints to ensure that region - /// variables in `concrete_ty` wind up being constrained to - /// something from `substs` (or, at minimum, things that outlive - /// the fn body). (Ultimately, writeback is responsible for this - /// check.) - pub has_required_region_bounds: bool, - - /// The origin of the opaque type. - pub origin: hir::OpaqueTyOrigin, -} - -/// Whether member constraints should be generated for all opaque types -pub enum GenerateMemberConstraints { - /// The default, used by typeck - WhenRequired, - /// The borrow checker needs member constraints in any case where we don't - /// have a `'static` bound. This is because the borrow checker has more - /// flexibility in the values of regions. For example, given `f<'a, 'b>` - /// the borrow checker can have an inference variable outlive `'a` and `'b`, - /// but not be equal to `'static`. - IfNoStaticBound, -} - -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - /// Replaces all opaque types in `value` with fresh inference variables - /// and creates appropriate obligations. For example, given the input: - /// - /// impl Iterator - /// - /// this method would create two type variables, `?0` and `?1`. It would - /// return the type `?0` but also the obligations: - /// - /// ?0: Iterator - /// ?1: Debug - /// - /// Moreover, it returns a `OpaqueTypeMap` that would map `?0` to - /// info about the `impl Iterator<..>` type and `?1` to info about - /// the `impl Debug` type. - /// - /// # Parameters - /// - /// - `parent_def_id` -- the `DefId` of the function in which the opaque type - /// is defined - /// - `body_id` -- the body-id with which the resulting obligations should - /// be associated - /// - `param_env` -- the in-scope parameter environment to be used for - /// obligations - /// - `value` -- the value within which we are instantiating opaque types - /// - `value_span` -- the span where the value came from, used in error reporting - pub fn instantiate_opaque_types>( - &self, - parent_def_id: DefId, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: &T, - value_span: Span, - ) -> InferOk<'tcx, (T, OpaqueTypeMap<'tcx>)> { - debug!( - "instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \ - param_env={:?}, value_span={:?})", - value, parent_def_id, body_id, param_env, value_span, - ); - let mut instantiator = Instantiator { - infcx: self, - parent_def_id, - body_id, - param_env, - value_span, - opaque_types: Default::default(), - obligations: vec![], - }; - let value = instantiator.instantiate_opaque_types_in_map(value); - InferOk { value: (value, instantiator.opaque_types), obligations: instantiator.obligations } - } - - /// Given the map `opaque_types` containing the opaque - /// `impl Trait` types whose underlying, hidden types are being - /// inferred, this method adds constraints to the regions - /// appearing in those underlying hidden types to ensure that they - /// at least do not refer to random scopes within the current - /// function. These constraints are not (quite) sufficient to - /// guarantee that the regions are actually legal values; that - /// final condition is imposed after region inference is done. - /// - /// # The Problem - /// - /// Let's work through an example to explain how it works. Assume - /// the current function is as follows: - /// - /// ```text - /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) - /// ``` - /// - /// Here, we have two `impl Trait` types whose values are being - /// inferred (the `impl Bar<'a>` and the `impl - /// Bar<'b>`). Conceptually, this is sugar for a setup where we - /// define underlying opaque types (`Foo1`, `Foo2`) and then, in - /// the return type of `foo`, we *reference* those definitions: - /// - /// ```text - /// type Foo1<'x> = impl Bar<'x>; - /// type Foo2<'x> = impl Bar<'x>; - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// // ^^^^ ^^ - /// // | | - /// // | substs - /// // def_id - /// ``` - /// - /// As indicating in the comments above, each of those references - /// is (in the compiler) basically a substitution (`substs`) - /// applied to the type of a suitable `def_id` (which identifies - /// `Foo1` or `Foo2`). - /// - /// Now, at this point in compilation, what we have done is to - /// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with - /// fresh inference variables C1 and C2. We wish to use the values - /// of these variables to infer the underlying types of `Foo1` and - /// `Foo2`. That is, this gives rise to higher-order (pattern) unification - /// constraints like: - /// - /// ```text - /// for<'a> (Foo1<'a> = C1) - /// for<'b> (Foo1<'b> = C2) - /// ``` - /// - /// For these equation to be satisfiable, the types `C1` and `C2` - /// can only refer to a limited set of regions. For example, `C1` - /// can only refer to `'static` and `'a`, and `C2` can only refer - /// to `'static` and `'b`. The job of this function is to impose that - /// constraint. - /// - /// Up to this point, C1 and C2 are basically just random type - /// inference variables, and hence they may contain arbitrary - /// regions. In fact, it is fairly likely that they do! Consider - /// this possible definition of `foo`: - /// - /// ```text - /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { - /// (&*x, &*y) - /// } - /// ``` - /// - /// Here, the values for the concrete types of the two impl - /// traits will include inference variables: - /// - /// ```text - /// &'0 i32 - /// &'1 i32 - /// ``` - /// - /// Ordinarily, the subtyping rules would ensure that these are - /// sufficiently large. But since `impl Bar<'a>` isn't a specific - /// type per se, we don't get such constraints by default. This - /// is where this function comes into play. It adds extra - /// constraints to ensure that all the regions which appear in the - /// inferred type are regions that could validly appear. - /// - /// This is actually a bit of a tricky constraint in general. We - /// want to say that each variable (e.g., `'0`) can only take on - /// values that were supplied as arguments to the opaque type - /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in - /// scope. We don't have a constraint quite of this kind in the current - /// region checker. - /// - /// # The Solution - /// - /// We generally prefer to make `<=` constraints, since they - /// integrate best into the region solver. To do that, we find the - /// "minimum" of all the arguments that appear in the substs: that - /// is, some region which is less than all the others. In the case - /// of `Foo1<'a>`, that would be `'a` (it's the only choice, after - /// all). Then we apply that as a least bound to the variables - /// (e.g., `'a <= '0`). - /// - /// In some cases, there is no minimum. Consider this example: - /// - /// ```text - /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } - /// ``` - /// - /// Here we would report a more complex "in constraint", like `'r - /// in ['a, 'b, 'static]` (where `'r` is some regon appearing in - /// the hidden type). - /// - /// # Constrain regions, not the hidden concrete type - /// - /// Note that generating constraints on each region `Rc` is *not* - /// the same as generating an outlives constraint on `Tc` iself. - /// For example, if we had a function like this: - /// - /// ```rust - /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { - /// (x, y) - /// } - /// - /// // Equivalent to: - /// type FooReturn<'a, T> = impl Foo<'a>; - /// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. } - /// ``` - /// - /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` - /// is an inference variable). If we generated a constraint that - /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- - /// but this is not necessary, because the opaque type we - /// create will be allowed to reference `T`. So we only generate a - /// constraint that `'0: 'a`. - /// - /// # The `free_region_relations` parameter - /// - /// The `free_region_relations` argument is used to find the - /// "minimum" of the regions supplied to a given opaque type. - /// It must be a relation that can answer whether `'a <= 'b`, - /// where `'a` and `'b` are regions that appear in the "substs" - /// for the opaque type references (the `<'a>` in `Foo1<'a>`). - /// - /// Note that we do not impose the constraints based on the - /// generic regions from the `Foo1` definition (e.g., `'x`). This - /// is because the constraints we are imposing here is basically - /// the concern of the one generating the constraining type C1, - /// which is the current function. It also means that we can - /// take "implied bounds" into account in some cases: - /// - /// ```text - /// trait SomeTrait<'a, 'b> { } - /// fn foo<'a, 'b>(_: &'a &'b u32) -> impl SomeTrait<'a, 'b> { .. } - /// ``` - /// - /// Here, the fact that `'b: 'a` is known only because of the - /// implied bounds from the `&'a &'b u32` parameter, and is not - /// "inherent" to the opaque type definition. - /// - /// # Parameters - /// - /// - `opaque_types` -- the map produced by `instantiate_opaque_types` - /// - `free_region_relations` -- something that can be used to relate - /// the free regions (`'a`) that appear in the impl trait. - pub fn constrain_opaque_types>( - &self, - opaque_types: &OpaqueTypeMap<'tcx>, - free_region_relations: &FRR, - ) { - debug!("constrain_opaque_types()"); - - for (&def_id, opaque_defn) in opaque_types { - self.constrain_opaque_type( - def_id, - opaque_defn, - GenerateMemberConstraints::WhenRequired, - free_region_relations, - ); - } - } - - /// See `constrain_opaque_types` for documentation. - pub fn constrain_opaque_type>( - &self, - def_id: DefId, - opaque_defn: &OpaqueTypeDecl<'tcx>, - mode: GenerateMemberConstraints, - free_region_relations: &FRR, - ) { - debug!("constrain_opaque_type()"); - debug!("constrain_opaque_type: def_id={:?}", def_id); - debug!("constrain_opaque_type: opaque_defn={:#?}", opaque_defn); - - let tcx = self.tcx; - - let concrete_ty = self.resolve_vars_if_possible(&opaque_defn.concrete_ty); - - debug!("constrain_opaque_type: concrete_ty={:?}", concrete_ty); - - let opaque_type_generics = tcx.generics_of(def_id); - - let span = tcx.def_span(def_id); - - // If there are required region bounds, we can use them. - if opaque_defn.has_required_region_bounds { - let predicates_of = tcx.predicates_of(def_id); - debug!("constrain_opaque_type: predicates: {:#?}", predicates_of,); - let bounds = predicates_of.instantiate(tcx, opaque_defn.substs); - debug!("constrain_opaque_type: bounds={:#?}", bounds); - let opaque_type = tcx.mk_opaque(def_id, opaque_defn.substs); - - let required_region_bounds = - required_region_bounds(tcx, opaque_type, bounds.predicates); - debug_assert!(!required_region_bounds.is_empty()); - - for required_region in required_region_bounds { - concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, - op: |r| self.sub_regions(infer::CallReturn(span), required_region, r), - }); - } - if let GenerateMemberConstraints::IfNoStaticBound = mode { - self.generate_member_constraint( - concrete_ty, - opaque_type_generics, - opaque_defn, - def_id, - ); - } - return; - } - - // There were no `required_region_bounds`, - // so we have to search for a `least_region`. - // Go through all the regions used as arguments to the - // opaque type. These are the parameters to the opaque - // type; so in our example above, `substs` would contain - // `['a]` for the first impl trait and `'b` for the - // second. - let mut least_region = None; - for param in &opaque_type_generics.params { - match param.kind { - GenericParamDefKind::Lifetime => {} - _ => continue, - } - - // Get the value supplied for this region from the substs. - let subst_arg = opaque_defn.substs.region_at(param.index as usize); - - // Compute the least upper bound of it with the other regions. - debug!("constrain_opaque_types: least_region={:?}", least_region); - debug!("constrain_opaque_types: subst_arg={:?}", subst_arg); - match least_region { - None => least_region = Some(subst_arg), - Some(lr) => { - if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) { - // keep the current least region - } else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) { - // switch to `subst_arg` - least_region = Some(subst_arg); - } else { - // There are two regions (`lr` and - // `subst_arg`) which are not relatable. We - // can't find a best choice. Therefore, - // instead of creating a single bound like - // `'r: 'a` (which is our preferred choice), - // we will create a "in bound" like `'r in - // ['a, 'b, 'c]`, where `'a..'c` are the - // regions that appear in the impl trait. - - // For now, enforce a feature gate outside of async functions. - self.member_constraint_feature_gate(opaque_defn, def_id, lr, subst_arg); - - return self.generate_member_constraint( - concrete_ty, - opaque_type_generics, - opaque_defn, - def_id, - ); - } - } - } - } - - let least_region = least_region.unwrap_or(tcx.lifetimes.re_static); - debug!("constrain_opaque_types: least_region={:?}", least_region); - - if let GenerateMemberConstraints::IfNoStaticBound = mode { - if least_region != tcx.lifetimes.re_static { - self.generate_member_constraint( - concrete_ty, - opaque_type_generics, - opaque_defn, - def_id, - ); - } - } - concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, - op: |r| self.sub_regions(infer::CallReturn(span), least_region, r), - }); - } - - /// As a fallback, we sometimes generate an "in constraint". For - /// a case like `impl Foo<'a, 'b>`, where `'a` and `'b` cannot be - /// related, we would generate a constraint `'r in ['a, 'b, - /// 'static]` for each region `'r` that appears in the hidden type - /// (i.e., it must be equal to `'a`, `'b`, or `'static`). - /// - /// `conflict1` and `conflict2` are the two region bounds that we - /// detected which were unrelated. They are used for diagnostics. - fn generate_member_constraint( - &self, - concrete_ty: Ty<'tcx>, - opaque_type_generics: &ty::Generics, - opaque_defn: &OpaqueTypeDecl<'tcx>, - opaque_type_def_id: DefId, - ) { - // Create the set of choice regions: each region in the hidden - // type can be equal to any of the region parameters of the - // opaque type definition. - let choice_regions: Lrc>> = Lrc::new( - opaque_type_generics - .params - .iter() - .filter(|param| match param.kind { - GenericParamDefKind::Lifetime => true, - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => false, - }) - .map(|param| opaque_defn.substs.region_at(param.index as usize)) - .chain(std::iter::once(self.tcx.lifetimes.re_static)) - .collect(), - ); - - concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, - op: |r| { - self.member_constraint( - opaque_type_def_id, - opaque_defn.definition_span, - concrete_ty, - r, - &choice_regions, - ) - }, - }); - } - - /// Member constraints are presently feature-gated except for - /// async-await. We expect to lift this once we've had a bit more - /// time. - fn member_constraint_feature_gate( - &self, - opaque_defn: &OpaqueTypeDecl<'tcx>, - opaque_type_def_id: DefId, - conflict1: ty::Region<'tcx>, - conflict2: ty::Region<'tcx>, - ) -> bool { - // If we have `#![feature(member_constraints)]`, no problems. - if self.tcx.features().member_constraints { - return false; - } - - let span = self.tcx.def_span(opaque_type_def_id); - - // Without a feature-gate, we only generate member-constraints for async-await. - let context_name = match opaque_defn.origin { - // No feature-gate required for `async fn`. - hir::OpaqueTyOrigin::AsyncFn => return false, - - // Otherwise, generate the label we'll use in the error message. - hir::OpaqueTyOrigin::TypeAlias - | hir::OpaqueTyOrigin::FnReturn - | hir::OpaqueTyOrigin::Misc => "impl Trait", - }; - let msg = format!("ambiguous lifetime bound in `{}`", context_name); - let mut err = self.tcx.sess.struct_span_err(span, &msg); - - let conflict1_name = conflict1.to_string(); - let conflict2_name = conflict2.to_string(); - let label_owned; - let label = match (&*conflict1_name, &*conflict2_name) { - ("'_", "'_") => "the elided lifetimes here do not outlive one another", - _ => { - label_owned = format!( - "neither `{}` nor `{}` outlives the other", - conflict1_name, conflict2_name, - ); - &label_owned - } - }; - err.span_label(span, label); - - if nightly_options::is_nightly_build() { - err.help("add #![feature(member_constraints)] to the crate attributes to enable"); - } - - err.emit(); - true - } - - /// Given the fully resolved, instantiated type for an opaque - /// type, i.e., the value of an inference variable like C1 or C2 - /// (*), computes the "definition type" for an opaque type - /// definition -- that is, the inferred value of `Foo1<'x>` or - /// `Foo2<'x>` that we would conceptually use in its definition: - /// - /// type Foo1<'x> = impl Bar<'x> = AAA; <-- this type AAA - /// type Foo2<'x> = impl Bar<'x> = BBB; <-- or this type BBB - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// - /// Note that these values are defined in terms of a distinct set of - /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main - /// purpose of this function is to do that translation. - /// - /// (*) C1 and C2 were introduced in the comments on - /// `constrain_opaque_types`. Read that comment for more context. - /// - /// # Parameters - /// - /// - `def_id`, the `impl Trait` type - /// - `substs`, the substs used to instantiate this opaque type - /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of - /// `opaque_defn.concrete_ty` - pub fn infer_opaque_definition_from_instantiation( - &self, - def_id: DefId, - substs: SubstsRef<'tcx>, - instantiated_ty: Ty<'tcx>, - span: Span, - ) -> Ty<'tcx> { - debug!( - "infer_opaque_definition_from_instantiation(def_id={:?}, instantiated_ty={:?})", - def_id, instantiated_ty - ); - - // Use substs to build up a reverse map from regions to their - // identity mappings. This is necessary because of `impl - // Trait` lifetimes are computed by replacing existing - // lifetimes with 'static and remapping only those used in the - // `impl Trait` return type, resulting in the parameters - // shifting. - let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id); - let map: FxHashMap, GenericArg<'tcx>> = - substs.iter().enumerate().map(|(index, subst)| (*subst, id_substs[index])).collect(); - - // Convert the type from the function into a type valid outside - // the function, by replacing invalid regions with 'static, - // after producing an error for each of them. - let definition_ty = instantiated_ty.fold_with(&mut ReverseMapper::new( - self.tcx, - self.is_tainted_by_errors(), - def_id, - map, - instantiated_ty, - span, - )); - debug!("infer_opaque_definition_from_instantiation: definition_ty={:?}", definition_ty); - - definition_ty - } -} - -pub fn unexpected_hidden_region_diagnostic( - tcx: TyCtxt<'tcx>, - region_scope_tree: Option<®ion::ScopeTree>, - span: Span, - hidden_ty: Ty<'tcx>, - hidden_region: ty::Region<'tcx>, -) -> DiagnosticBuilder<'tcx> { - let mut err = struct_span_err!( - tcx.sess, - span, - E0700, - "hidden type for `impl Trait` captures lifetime that does not appear in bounds", - ); - - // Explain the region we are capturing. - if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) = hidden_region { - // Assuming regionck succeeded (*), we ought to always be - // capturing *some* region from the fn header, and hence it - // ought to be free. So under normal circumstances, we will go - // down this path which gives a decent human readable - // explanation. - // - // (*) if not, the `tainted_by_errors` flag would be set to - // true in any case, so we wouldn't be here at all. - note_and_explain_free_region( - tcx, - &mut err, - &format!("hidden type `{}` captures ", hidden_ty), - hidden_region, - "", - ); - } else { - // Ugh. This is a painful case: the hidden region is not one - // that we can easily summarize or explain. This can happen - // in a case like - // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: - // - // ``` - // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { - // if condition() { a } else { b } - // } - // ``` - // - // Here the captured lifetime is the intersection of `'a` and - // `'b`, which we can't quite express. - - if let Some(region_scope_tree) = region_scope_tree { - // If the `region_scope_tree` is available, this is being - // invoked from the "region inferencer error". We can at - // least report a really cryptic error for now. - note_and_explain_region( - tcx, - region_scope_tree, - &mut err, - &format!("hidden type `{}` captures ", hidden_ty), - hidden_region, - "", - ); - } else { - // If the `region_scope_tree` is *unavailable*, this is - // being invoked by the code that comes *after* region - // inferencing. This is a bug, as the region inferencer - // ought to have noticed the failed constraint and invoked - // error reporting, which in turn should have prevented us - // from getting trying to infer the hidden type - // completely. - tcx.sess.delay_span_bug( - span, - &format!( - "hidden type captures unexpected lifetime `{:?}` \ - but no region inference failure", - hidden_region, - ), - ); - } - } - - err -} - -// Visitor that requires that (almost) all regions in the type visited outlive -// `least_region`. We cannot use `push_outlives_components` because regions in -// closure signatures are not included in their outlives components. We need to -// ensure all regions outlive the given bound so that we don't end up with, -// say, `ReScope` appearing in a return type and causing ICEs when other -// functions end up with region constraints involving regions from other -// functions. -// -// We also cannot use `for_each_free_region` because for closures it includes -// the regions parameters from the enclosing item. -// -// We ignore any type parameters because impl trait values are assumed to -// capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - tcx: TyCtxt<'tcx>, - op: OP, -} - -impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - fn visit_binder>(&mut self, t: &ty::Binder) -> bool { - t.skip_binder().visit_with(self); - false // keep visiting - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - match *r { - // ignore bound regions, keep visiting - ty::ReLateBound(_, _) => false, - _ => { - (self.op)(r); - false - } - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - // We're only interested in types involving regions - if !ty.flags.intersects(ty::TypeFlags::HAS_FREE_REGIONS) { - return false; // keep visiting - } - - match ty.kind { - ty::Closure(def_id, ref substs) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar_ty in substs.as_closure().upvar_tys(def_id, self.tcx) { - upvar_ty.visit_with(self); - } - - substs.as_closure().sig_ty(def_id, self.tcx).visit_with(self); - } - - ty::Generator(def_id, ref substs, _) => { - // Skip lifetime parameters of the enclosing item(s) - // Also skip the witness type, because that has no free regions. - - for upvar_ty in substs.as_generator().upvar_tys(def_id, self.tcx) { - upvar_ty.visit_with(self); - } - - substs.as_generator().return_ty(def_id, self.tcx).visit_with(self); - substs.as_generator().yield_ty(def_id, self.tcx).visit_with(self); - substs.as_generator().resume_ty(def_id, self.tcx).visit_with(self); - } - _ => { - ty.super_visit_with(self); - } - } - - false - } -} - -struct ReverseMapper<'tcx> { - tcx: TyCtxt<'tcx>, - - /// If errors have already been reported in this fn, we suppress - /// our own errors because they are sometimes derivative. - tainted_by_errors: bool, - - opaque_type_def_id: DefId, - map: FxHashMap, GenericArg<'tcx>>, - map_missing_regions_to_empty: bool, - - /// initially `Some`, set to `None` once error has been reported - hidden_ty: Option>, - - /// Span of function being checked. - span: Span, -} - -impl ReverseMapper<'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - tainted_by_errors: bool, - opaque_type_def_id: DefId, - map: FxHashMap, GenericArg<'tcx>>, - hidden_ty: Ty<'tcx>, - span: Span, - ) -> Self { - Self { - tcx, - tainted_by_errors, - opaque_type_def_id, - map, - map_missing_regions_to_empty: false, - hidden_ty: Some(hidden_ty), - span, - } - } - - fn fold_kind_mapping_missing_regions_to_empty( - &mut self, - kind: GenericArg<'tcx>, - ) -> GenericArg<'tcx> { - assert!(!self.map_missing_regions_to_empty); - self.map_missing_regions_to_empty = true; - let kind = kind.fold_with(self); - self.map_missing_regions_to_empty = false; - kind - } - - fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { - assert!(!self.map_missing_regions_to_empty); - kind.fold_with(self) - } -} - -impl TypeFolder<'tcx> for ReverseMapper<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match r { - // Ignore bound regions and `'static` regions that appear in the - // type, we only need to remap regions that reference lifetimes - // from the function declaraion. - // This would ignore `'r` in a type like `for<'r> fn(&'r u32)`. - ty::ReLateBound(..) | ty::ReStatic => return r, - - // If regions have been erased (by writeback), don't try to unerase - // them. - ty::ReErased => return r, - - // The regions that we expect from borrow checking. - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} - - ty::ReEmpty(_) - | ty::RePlaceholder(_) - | ty::ReVar(_) - | ty::ReScope(_) - | ty::ReClosureBound(_) => { - // All of the regions in the type should either have been - // erased by writeback, or mapped back to named regions by - // borrow checking. - bug!("unexpected region kind in opaque type: {:?}", r); - } - } - - let generics = self.tcx().generics_of(self.opaque_type_def_id); - match self.map.get(&r.into()).map(|k| k.unpack()) { - Some(GenericArgKind::Lifetime(r1)) => r1, - Some(u) => panic!("region mapped to unexpected kind: {:?}", u), - None if self.map_missing_regions_to_empty || self.tainted_by_errors => { - self.tcx.lifetimes.re_root_empty - } - None if generics.parent.is_some() => { - if let Some(hidden_ty) = self.hidden_ty.take() { - unexpected_hidden_region_diagnostic( - self.tcx, - None, - self.tcx.def_span(self.opaque_type_def_id), - hidden_ty, - r, - ) - .emit(); - } - self.tcx.lifetimes.re_root_empty - } - None => { - self.tcx - .sess - .struct_span_err(self.span, "non-defining opaque type use in defining scope") - .span_label( - self.span, - format!( - "lifetime `{}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias", - r - ), - ) - .emit(); - - self.tcx().lifetimes.re_static - } - } - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { - ty::Closure(def_id, substs) => { - // I am a horrible monster and I pray for death. When - // we encounter a closure here, it is always a closure - // from within the function that we are currently - // type-checking -- one that is now being encapsulated - // in an opaque type. Ideally, we would - // go through the types/lifetimes that it references - // and treat them just like we would any other type, - // which means we would error out if we find any - // reference to a type/region that is not in the - // "reverse map". - // - // **However,** in the case of closures, there is a - // somewhat subtle (read: hacky) consideration. The - // problem is that our closure types currently include - // all the lifetime parameters declared on the - // enclosing function, even if they are unused by the - // closure itself. We can't readily filter them out, - // so here we replace those values with `'empty`. This - // can't really make a difference to the rest of the - // compiler; those regions are ignored for the - // outlives relation, and hence don't affect trait - // selection or auto traits, and they are erased - // during codegen. - - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, &kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_mapping_missing_regions_to_empty(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_closure(def_id, substs) - } - - ty::Generator(def_id, substs, movability) => { - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, &kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_mapping_missing_regions_to_empty(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_generator(def_id, substs, movability) - } - - ty::Param(..) => { - // Look it up in the substitution list. - match self.map.get(&ty.into()).map(|k| k.unpack()) { - // Found it in the substitution list; replace with the parameter from the - // opaque type. - Some(GenericArgKind::Type(t1)) => t1, - Some(u) => panic!("type mapped to unexpected kind: {:?}", u), - None => { - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "type parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ty - ), - ) - .emit(); - - self.tcx().types.err - } - } - } - - _ => ty.super_fold_with(self), - } - } - - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - trace!("checking const {:?}", ct); - // Find a const parameter - match ct.val { - ty::ConstKind::Param(..) => { - // Look it up in the substitution list. - match self.map.get(&ct.into()).map(|k| k.unpack()) { - // Found it in the substitution list, replace with the parameter from the - // opaque type. - Some(GenericArgKind::Const(c1)) => c1, - Some(u) => panic!("const mapped to unexpected kind: {:?}", u), - None => { - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "const parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ct - ), - ) - .emit(); - - self.tcx().consts.err - } - } - } - - _ => ct, - } - } -} - -struct Instantiator<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - parent_def_id: DefId, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value_span: Span, - opaque_types: OpaqueTypeMap<'tcx>, - obligations: Vec>, -} - -impl<'a, 'tcx> Instantiator<'a, 'tcx> { - fn instantiate_opaque_types_in_map>(&mut self, value: &T) -> T { - debug!("instantiate_opaque_types_in_map(value={:?})", value); - let tcx = self.infcx.tcx; - value.fold_with(&mut BottomUpFolder { - tcx, - ty_op: |ty| { - if ty.references_error() { - return tcx.types.err; - } else if let ty::Opaque(def_id, substs) = ty.kind { - // Check that this is `impl Trait` type is - // declared by `parent_def_id` -- i.e., one whose - // value we are inferring. At present, this is - // always true during the first phase of - // type-check, but not always true later on during - // NLL. Once we support named opaque types more fully, - // this same scenario will be able to arise during all phases. - // - // Here is an example using type alias `impl Trait` - // that indicates the distinction we are checking for: - // - // ```rust - // mod a { - // pub type Foo = impl Iterator; - // pub fn make_foo() -> Foo { .. } - // } - // - // mod b { - // fn foo() -> a::Foo { a::make_foo() } - // } - // ``` - // - // Here, the return type of `foo` references a - // `Opaque` indeed, but not one whose value is - // presently being inferred. You can get into a - // similar situation with closure return types - // today: - // - // ```rust - // fn foo() -> impl Iterator { .. } - // fn bar() { - // let x = || foo(); // returns the Opaque assoc with `foo` - // } - // ``` - if let Some(opaque_hir_id) = tcx.hir().as_local_hir_id(def_id) { - let parent_def_id = self.parent_def_id; - let def_scope_default = || { - let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); - parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id) - }; - let (in_definition_scope, origin) = match tcx.hir().find(opaque_hir_id) { - Some(Node::Item(item)) => match item.kind { - // Anonymous `impl Trait` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: Some(parent), - origin, - .. - }) => (parent == self.parent_def_id, origin), - // Named `type Foo = impl Bar;` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: None, - origin, - .. - }) => ( - may_define_opaque_type(tcx, self.parent_def_id, opaque_hir_id), - origin, - ), - _ => (def_scope_default(), hir::OpaqueTyOrigin::TypeAlias), - }, - Some(Node::ImplItem(item)) => match item.kind { - hir::ImplItemKind::OpaqueTy(_) => ( - may_define_opaque_type(tcx, self.parent_def_id, opaque_hir_id), - hir::OpaqueTyOrigin::TypeAlias, - ), - _ => (def_scope_default(), hir::OpaqueTyOrigin::TypeAlias), - }, - _ => bug!( - "expected (impl) item, found {}", - tcx.hir().node_to_string(opaque_hir_id), - ), - }; - if in_definition_scope { - return self.fold_opaque_ty(ty, def_id, substs, origin); - } - - debug!( - "instantiate_opaque_types_in_map: \ - encountered opaque outside its definition scope \ - def_id={:?}", - def_id, - ); - } - } - - ty - }, - lt_op: |lt| lt, - ct_op: |ct| ct, - }) - } - - fn fold_opaque_ty( - &mut self, - ty: Ty<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - origin: hir::OpaqueTyOrigin, - ) -> Ty<'tcx> { - let infcx = self.infcx; - let tcx = infcx.tcx; - - debug!("instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})", def_id, substs); - - // Use the same type variable if the exact same opaque type appears more - // than once in the return type (e.g., if it's passed to a type alias). - if let Some(opaque_defn) = self.opaque_types.get(&def_id) { - debug!("instantiate_opaque_types: returning concrete ty {:?}", opaque_defn.concrete_ty); - return opaque_defn.concrete_ty; - } - let span = tcx.def_span(def_id); - debug!("fold_opaque_ty {:?} {:?}", self.value_span, span); - let ty_var = infcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - - let predicates_of = tcx.predicates_of(def_id); - debug!("instantiate_opaque_types: predicates={:#?}", predicates_of,); - let bounds = predicates_of.instantiate(tcx, substs); - - let param_env = tcx.param_env(def_id); - let InferOk { value: bounds, obligations } = - infcx.partially_normalize_associated_types_in(span, self.body_id, param_env, &bounds); - self.obligations.extend(obligations); - - debug!("instantiate_opaque_types: bounds={:?}", bounds); - - let required_region_bounds = required_region_bounds(tcx, ty, bounds.predicates.clone()); - debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds); - - // Make sure that we are in fact defining the *entire* type - // (e.g., `type Foo = impl Bar;` needs to be - // defined by a function like `fn foo() -> Foo`). - debug!("instantiate_opaque_types: param_env={:#?}", self.param_env,); - debug!("instantiate_opaque_types: generics={:#?}", tcx.generics_of(def_id),); - - // Ideally, we'd get the span where *this specific `ty` came - // from*, but right now we just use the span from the overall - // value being folded. In simple cases like `-> impl Foo`, - // these are the same span, but not in cases like `-> (impl - // Foo, impl Bar)`. - let definition_span = self.value_span; - - self.opaque_types.insert( - def_id, - OpaqueTypeDecl { - opaque_type: ty, - substs, - definition_span, - concrete_ty: ty_var, - has_required_region_bounds: !required_region_bounds.is_empty(), - origin, - }, - ); - debug!("instantiate_opaque_types: ty_var={:?}", ty_var); - - for predicate in &bounds.predicates { - if let ty::Predicate::Projection(projection) = &predicate { - if projection.skip_binder().ty.references_error() { - // No point on adding these obligations since there's a type error involved. - return ty_var; - } - } - } - - self.obligations.reserve(bounds.predicates.len()); - for predicate in bounds.predicates { - // Change the predicate to refer to the type variable, - // which will be the concrete type instead of the opaque type. - // This also instantiates nested instances of `impl Trait`. - let predicate = self.instantiate_opaque_types_in_map(&predicate); - - let cause = traits::ObligationCause::new(span, self.body_id, traits::SizedReturnType); - - // Require that the predicate holds for the concrete type. - debug!("instantiate_opaque_types: predicate={:?}", predicate); - self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate)); - } - - ty_var - } -} - -/// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `def_id`. -/// -/// Example: -/// ```rust -/// pub mod foo { -/// pub mod bar { -/// pub trait Bar { .. } -/// -/// pub type Baz = impl Bar; -/// -/// fn f1() -> Baz { .. } -/// } -/// -/// fn f2() -> bar::Baz { .. } -/// } -/// ``` -/// -/// Here, `def_id` is the `DefId` of the defining use of the opaque type (e.g., `f1` or `f2`), -/// and `opaque_hir_id` is the `HirId` of the definition of the opaque type `Baz`. -/// For the above example, this function returns `true` for `f1` and `false` for `f2`. -pub fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: DefId, opaque_hir_id: hir::HirId) -> bool { - let mut hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - - // Named opaque types can be defined by any siblings or children of siblings. - let scope = tcx.hir().get_defining_scope(opaque_hir_id); - // We walk up the node tree until we hit the root or the scope of the opaque type. - while hir_id != scope && hir_id != hir::CRATE_HIR_ID { - hir_id = tcx.hir().get_parent_item(hir_id); - } - // Syntactically, we are allowed to define the concrete type if: - let res = hir_id == scope; - trace!( - "may_define_opaque_type(def={:?}, opaque_node={:?}) = {}", - tcx.hir().find(hir_id), - tcx.hir().get(opaque_hir_id), - res - ); - res -} - -/// Given a set of predicates that apply to an object type, returns -/// the region bounds that the (erased) `Self` type must -/// outlive. Precisely *because* the `Self` type is erased, the -/// parameter `erased_self_ty` must be supplied to indicate what type -/// has been used to represent `Self` in the predicates -/// themselves. This should really be a unique type; `FreshTy(0)` is a -/// popular choice. -/// -/// N.B., in some cases, particularly around higher-ranked bounds, -/// this function returns a kind of conservative approximation. -/// That is, all regions returned by this function are definitely -/// required, but there may be other region bounds that are not -/// returned, as well as requirements like `for<'a> T: 'a`. -/// -/// Requires that trait definitions have been processed so that we can -/// elaborate predicates and walk supertraits. -// -// FIXME: callers may only have a `&[Predicate]`, not a `Vec`, so that's -// what this code should accept. -crate fn required_region_bounds( - tcx: TyCtxt<'tcx>, - erased_self_ty: Ty<'tcx>, - predicates: Vec>, -) -> Vec> { - debug!( - "required_region_bounds(erased_self_ty={:?}, predicates={:?})", - erased_self_ty, predicates - ); - - assert!(!erased_self_ty.has_escaping_bound_vars()); - - traits::elaborate_predicates(tcx, predicates) - .filter_map(|predicate| { - match predicate { - ty::Predicate::Projection(..) - | ty::Predicate::Trait(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::RegionOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, - ty::Predicate::TypeOutlives(predicate) => { - // Search for a bound of the form `erased_self_ty - // : 'a`, but be wary of something like `for<'a> - // erased_self_ty : 'a` (we interpret a - // higher-ranked bound like that as 'static, - // though at present the code in `fulfill.rs` - // considers such bounds to be unsatisfiable, so - // it's kind of a moot point since you could never - // construct such an object, but this seems - // correct even if that code changes). - let ty::OutlivesPredicate(ref t, ref r) = predicate.skip_binder(); - if t == &erased_self_ty && !r.has_escaping_bound_vars() { - Some(*r) - } else { - None - } - } - } - }) - .collect() -} diff --git a/src/librustc_infer/infer/outlives/env.rs b/src/librustc_infer/infer/outlives/env.rs index aac6c7640ca..6c1e86bf408 100644 --- a/src/librustc_infer/infer/outlives/env.rs +++ b/src/librustc_infer/infer/outlives/env.rs @@ -1,10 +1,11 @@ use crate::infer::{GenericKind, InferCtxt}; -use crate::traits::query::outlives_bounds::{self, OutlivesBound}; +use crate::traits::query::OutlivesBound; +use rustc::ty; use rustc::ty::free_region_map::FreeRegionMap; -use rustc::ty::{self, Ty}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_span::Span; + +use super::explicit_outlives_bounds; /// The `OutlivesEnvironment` collects information about what outlives /// what in a given type-checking setting. For example, if we have a @@ -76,7 +77,7 @@ pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { region_bound_pairs_accum: vec![], }; - env.add_outlives_bounds(None, outlives_bounds::explicit_outlives_bounds(param_env)); + env.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); env } @@ -142,39 +143,6 @@ pub fn pop_snapshot_post_closure(&mut self, len: usize) { self.region_bound_pairs_accum.truncate(len); } - /// This method adds "implied bounds" into the outlives environment. - /// Implied bounds are outlives relationships that we can deduce - /// on the basis that certain types must be well-formed -- these are - /// either the types that appear in the function signature or else - /// the input types to an impl. For example, if you have a function - /// like - /// - /// ``` - /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { } - /// ``` - /// - /// we can assume in the caller's body that `'b: 'a` and that `T: - /// 'b` (and hence, transitively, that `T: 'a`). This method would - /// add those assumptions into the outlives-environment. - /// - /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` - pub fn add_implied_bounds( - &mut self, - infcx: &InferCtxt<'a, 'tcx>, - fn_sig_tys: &[Ty<'tcx>], - body_id: hir::HirId, - span: Span, - ) { - debug!("add_implied_bounds()"); - - for &ty in fn_sig_tys { - let ty = infcx.resolve_vars_if_possible(&ty); - debug!("add_implied_bounds: ty = {}", ty); - let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span); - self.add_outlives_bounds(Some(infcx), implied_bounds) - } - } - /// Save the current set of region-bound pairs under the given `body_id`. pub fn save_implied_bounds(&mut self, body_id: hir::HirId) { let old = @@ -188,8 +156,11 @@ pub fn save_implied_bounds(&mut self, body_id: hir::HirId) { /// contain inference variables, it must be supplied, in which /// case we will register "givens" on the inference context. (See /// `RegionConstraintData`.) - fn add_outlives_bounds(&mut self, infcx: Option<&InferCtxt<'a, 'tcx>>, outlives_bounds: I) - where + pub fn add_outlives_bounds( + &mut self, + infcx: Option<&InferCtxt<'a, 'tcx>>, + outlives_bounds: I, + ) where I: IntoIterator>, { // Record relationships such as `T:'x` that don't go into the diff --git a/src/librustc_infer/infer/outlives/mod.rs b/src/librustc_infer/infer/outlives/mod.rs index 6fc72470c9f..75cf742de31 100644 --- a/src/librustc_infer/infer/outlives/mod.rs +++ b/src/librustc_infer/infer/outlives/mod.rs @@ -3,3 +3,25 @@ pub mod env; pub mod obligations; pub mod verify; + +use rustc::traits::query::OutlivesBound; +use rustc::ty; + +pub fn explicit_outlives_bounds<'tcx>( + param_env: ty::ParamEnv<'tcx>, +) -> impl Iterator> + 'tcx { + debug!("explicit_outlives_bounds()"); + param_env.caller_bounds.into_iter().filter_map(move |predicate| match predicate { + ty::Predicate::Projection(..) + | ty::Predicate::Trait(..) + | ty::Predicate::Subtype(..) + | ty::Predicate::WellFormed(..) + | ty::Predicate::ObjectSafe(..) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::TypeOutlives(..) + | ty::Predicate::ConstEvaluatable(..) => None, + ty::Predicate::RegionOutlives(ref data) => data + .no_bound_vars() + .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)), + }) +} diff --git a/src/librustc_infer/infer/region_constraints/README.md b/src/librustc_infer/infer/region_constraints/README.md index d789fb0de10..0231dd06677 100644 --- a/src/librustc_infer/infer/region_constraints/README.md +++ b/src/librustc_infer/infer/region_constraints/README.md @@ -1,3 +1,3 @@ -For info on how the current borrowck works, see the [rustc guide]. +For info on how the current borrowck works, see the [rustc dev guide]. -[rustc guide]: https://rust-lang.github.io/rustc-guide/borrow_check.html +[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html diff --git a/src/librustc_infer/infer/region_constraints/leak_check.rs b/src/librustc_infer/infer/region_constraints/leak_check.rs index 74ffdc7a4f0..6ebe3f57597 100644 --- a/src/librustc_infer/infer/region_constraints/leak_check.rs +++ b/src/librustc_infer/infer/region_constraints/leak_check.rs @@ -34,7 +34,7 @@ pub fn leak_check( assert!(self.in_snapshot()); // Go through each placeholder that we created. - for (_, &placeholder_region) in placeholder_map { + for &placeholder_region in placeholder_map.values() { // Find the universe this placeholder inhabits. let placeholder = match placeholder_region { ty::RePlaceholder(p) => p, @@ -85,7 +85,7 @@ impl<'tcx> TaintSet<'tcx> { fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self { let mut regions = FxHashSet::default(); regions.insert(initial_region); - TaintSet { directions: directions, regions: regions } + TaintSet { directions, regions } } fn fixed_point( diff --git a/src/librustc_infer/infer/region_constraints/mod.rs b/src/librustc_infer/infer/region_constraints/mod.rs index 3f9fa6459b3..868b9504379 100644 --- a/src/librustc_infer/infer/region_constraints/mod.rs +++ b/src/librustc_infer/infer/region_constraints/mod.rs @@ -766,7 +766,7 @@ fn combine_vars( b: Region<'tcx>, origin: SubregionOrigin<'tcx>, ) -> Region<'tcx> { - let vars = TwoRegions { a: a, b: b }; + let vars = TwoRegions { a, b }; if let Some(&c) = self.combine_map(t).get(&vars) { return tcx.mk_region(ReVar(c)); } diff --git a/src/librustc_infer/infer/resolve.rs b/src/librustc_infer/infer/resolve.rs index 3510b927d96..ce0f2f40894 100644 --- a/src/librustc_infer/infer/resolve.rs +++ b/src/librustc_infer/infer/resolve.rs @@ -28,7 +28,7 @@ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.has_infer_types() && !t.has_infer_consts() { + if !t.has_infer_types_or_consts() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); @@ -37,7 +37,7 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { } fn fold_const(&mut self, ct: &'tcx Const<'tcx>) -> &'tcx Const<'tcx> { - if !ct.has_infer_consts() { + if !ct.has_infer_types_or_consts() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { let ct = self.infcx.shallow_resolve(ct); @@ -160,7 +160,7 @@ pub fn fully_resolve<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, value: &T) -> Fix where T: TypeFoldable<'tcx>, { - let mut full_resolver = FullTypeResolver { infcx: infcx, err: None }; + let mut full_resolver = FullTypeResolver { infcx, err: None }; let result = value.fold_with(&mut full_resolver); match full_resolver.err { None => Ok(result), @@ -169,7 +169,7 @@ pub fn fully_resolve<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, value: &T) -> Fix } // N.B. This type is not public because the protocol around checking the -// `err` field is not enforcable otherwise. +// `err` field is not enforceable otherwise. struct FullTypeResolver<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, err: Option>, diff --git a/src/librustc_infer/infer/sub.rs b/src/librustc_infer/infer/sub.rs index 2b770ced42a..f6fc38b5358 100644 --- a/src/librustc_infer/infer/sub.rs +++ b/src/librustc_infer/infer/sub.rs @@ -19,7 +19,7 @@ pub fn new( f: &'combine mut CombineFields<'infcx, 'tcx>, a_is_expected: bool, ) -> Sub<'combine, 'infcx, 'tcx> { - Sub { fields: f, a_is_expected: a_is_expected } + Sub { fields: f, a_is_expected } } fn with_expected_switched R>(&mut self, f: F) -> R { diff --git a/src/librustc_infer/lib.rs b/src/librustc_infer/lib.rs index 6c66ef47f33..cb8ae8c592b 100644 --- a/src/librustc_infer/lib.rs +++ b/src/librustc_infer/lib.rs @@ -1,13 +1,12 @@ -//! This crates defines the trait resolution method and the type inference engine. +//! This crates defines the type inference engine. //! -//! - **Traits.** Trait resolution is implemented in the `traits` module. //! - **Type inference.** The type inference code can be found in the `infer` module; //! this code handles low-level equality and subtyping operations. The //! type check pass in the compiler is found in the `librustc_typeck` crate. //! -//! For more information about how rustc works, see the [rustc guide]. +//! For more information about how rustc works, see the [rustc dev guide]. //! -//! [rustc guide]: https://rust-lang.github.io/rustc-guide/ +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ //! //! # Note //! @@ -17,12 +16,11 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] -#![feature(drain_filter)] #![feature(never_type)] #![feature(range_is_empty)] #![feature(in_band_lifetimes)] #![feature(crate_visibility_modifier)] -#![recursion_limit = "512"] +#![recursion_limit = "512"] // For rustdoc #[macro_use] extern crate rustc_macros; diff --git a/src/librustc_infer/traits/auto_trait.rs b/src/librustc_infer/traits/auto_trait.rs deleted file mode 100644 index 1a4f899ac85..00000000000 --- a/src/librustc_infer/traits/auto_trait.rs +++ /dev/null @@ -1,837 +0,0 @@ -//! Support code for rustdoc and external tools. -//! You really don't want to be using this unless you need to. - -use super::*; - -use crate::infer::region_constraints::{Constraint, RegionConstraintData}; -use crate::infer::InferCtxt; -use rustc::ty::fold::TypeFolder; -use rustc::ty::{Region, RegionVid}; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - -use std::collections::hash_map::Entry; -use std::collections::VecDeque; - -// FIXME(twk): this is obviously not nice to duplicate like that -#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] -pub enum RegionTarget<'tcx> { - Region(Region<'tcx>), - RegionVid(RegionVid), -} - -#[derive(Default, Debug, Clone)] -pub struct RegionDeps<'tcx> { - larger: FxHashSet>, - smaller: FxHashSet>, -} - -pub enum AutoTraitResult { - ExplicitImpl, - PositiveImpl(A), - NegativeImpl, -} - -impl AutoTraitResult { - fn is_auto(&self) -> bool { - match *self { - AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl => true, - _ => false, - } - } -} - -pub struct AutoTraitInfo<'cx> { - pub full_user_env: ty::ParamEnv<'cx>, - pub region_data: RegionConstraintData<'cx>, - pub vid_to_region: FxHashMap>, -} - -pub struct AutoTraitFinder<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> AutoTraitFinder<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - AutoTraitFinder { tcx } - } - - /// Makes a best effort to determine whether and under which conditions an auto trait is - /// implemented for a type. For example, if you have - /// - /// ``` - /// struct Foo { data: Box } - /// ``` - /// - /// then this might return that Foo: Send if T: Send (encoded in the AutoTraitResult type). - /// The analysis attempts to account for custom impls as well as other complex cases. This - /// result is intended for use by rustdoc and other such consumers. - /// - /// (Note that due to the coinductive nature of Send, the full and correct result is actually - /// quite simple to generate. That is, when a type has no custom impl, it is Send iff its field - /// types are all Send. So, in our example, we might have that Foo: Send if Box: Send. - /// But this is often not the best way to present to the user.) - /// - /// Warning: The API should be considered highly unstable, and it may be refactored or removed - /// in the future. - pub fn find_auto_trait_generics( - &self, - ty: Ty<'tcx>, - orig_env: ty::ParamEnv<'tcx>, - trait_did: DefId, - auto_trait_callback: impl Fn(&InferCtxt<'_, 'tcx>, AutoTraitInfo<'tcx>) -> A, - ) -> AutoTraitResult { - let tcx = self.tcx; - - let trait_ref = ty::TraitRef { def_id: trait_did, substs: tcx.mk_substs_trait(ty, &[]) }; - - let trait_pred = ty::Binder::bind(trait_ref); - - let bail_out = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::with_negative(&infcx, true); - let result = selcx.select(&Obligation::new( - ObligationCause::dummy(), - orig_env, - trait_pred.to_poly_trait_predicate(), - )); - - match result { - Ok(Some(Vtable::VtableImpl(_))) => { - debug!( - "find_auto_trait_generics({:?}): \ - manual impl found, bailing out", - trait_ref - ); - true - } - _ => false, - } - }); - - // If an explicit impl exists, it always takes priority over an auto impl - if bail_out { - return AutoTraitResult::ExplicitImpl; - } - - return tcx.infer_ctxt().enter(|mut infcx| { - let mut fresh_preds = FxHashSet::default(); - - // Due to the way projections are handled by SelectionContext, we need to run - // evaluate_predicates twice: once on the original param env, and once on the result of - // the first evaluate_predicates call. - // - // The problem is this: most of rustc, including SelectionContext and traits::project, - // are designed to work with a concrete usage of a type (e.g., Vec - // fn() { Vec }. This information will generally never change - given - // the 'T' in fn() { ... }, we'll never know anything else about 'T'. - // If we're unable to prove that 'T' implements a particular trait, we're done - - // there's nothing left to do but error out. - // - // However, synthesizing an auto trait impl works differently. Here, we start out with - // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing - // with - and progressively discover the conditions we need to fulfill for it to - // implement a certain auto trait. This ends up breaking two assumptions made by trait - // selection and projection: - // - // * We can always cache the result of a particular trait selection for the lifetime of - // an InfCtxt - // * Given a projection bound such as '::SomeItem = K', if 'T: - // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K' - // - // We fix the first assumption by manually clearing out all of the InferCtxt's caches - // in between calls to SelectionContext.select. This allows us to keep all of the - // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift - // them between calls. - // - // We fix the second assumption by reprocessing the result of our first call to - // evaluate_predicates. Using the example of '::SomeItem = K', our first - // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass, - // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing - // SelectionContext to return it back to us. - - let (new_env, user_env) = match self.evaluate_predicates( - &mut infcx, - trait_did, - ty, - orig_env, - orig_env, - &mut fresh_preds, - false, - ) { - Some(e) => e, - None => return AutoTraitResult::NegativeImpl, - }; - - let (full_env, full_user_env) = self - .evaluate_predicates( - &mut infcx, - trait_did, - ty, - new_env, - user_env, - &mut fresh_preds, - true, - ) - .unwrap_or_else(|| { - panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env) - }); - - debug!( - "find_auto_trait_generics({:?}): fulfilling \ - with {:?}", - trait_ref, full_env - ); - infcx.clear_caches(); - - // At this point, we already have all of the bounds we need. FulfillmentContext is used - // to store all of the necessary region/lifetime bounds in the InferContext, as well as - // an additional sanity check. - let mut fulfill = FulfillmentContext::new(); - fulfill.register_bound( - &infcx, - full_env, - ty, - trait_did, - ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID), - ); - fulfill.select_all_or_error(&infcx).unwrap_or_else(|e| { - panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, e) - }); - - let body_id_map: FxHashMap<_, _> = infcx - .inner - .borrow() - .region_obligations - .iter() - .map(|&(id, _)| (id, vec![])) - .collect(); - - infcx.process_registered_region_obligations(&body_id_map, None, full_env); - - let region_data = infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .region_constraint_data() - .clone(); - - let vid_to_region = self.map_vid_to_region(®ion_data); - - let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; - - return AutoTraitResult::PositiveImpl(auto_trait_callback(&infcx, info)); - }); - } -} - -impl AutoTraitFinder<'tcx> { - /// The core logic responsible for computing the bounds for our synthesized impl. - /// - /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like - /// `FulfillmentContext`, we recursively select the nested obligations of predicates we - /// encounter. However, whenever we encounter an `UnimplementedError` involving a type - /// parameter, we add it to our `ParamEnv`. Since our goal is to determine when a particular - /// type implements an auto trait, Unimplemented errors tell us what conditions need to be met. - /// - /// This method ends up working somewhat similarly to `FulfillmentContext`, but with a few key - /// differences. `FulfillmentContext` works under the assumption that it's dealing with concrete - /// user code. According, it considers all possible ways that a `Predicate` could be met, which - /// isn't always what we want for a synthesized impl. For example, given the predicate `T: - /// Iterator`, `FulfillmentContext` can end up reporting an Unimplemented error for `T: - /// IntoIterator` -- since there's an implementation of `Iterator` where `T: IntoIterator`, - /// `FulfillmentContext` will drive `SelectionContext` to consider that impl before giving up. - /// If we were to rely on `FulfillmentContext`s decision, we might end up synthesizing an impl - /// like this: - /// - /// impl Send for Foo where T: IntoIterator - /// - /// While it might be technically true that Foo implements Send where `T: IntoIterator`, - /// the bound is overly restrictive - it's really only necessary that `T: Iterator`. - /// - /// For this reason, `evaluate_predicates` handles predicates with type variables specially. - /// When we encounter an `Unimplemented` error for a bound such as `T: Iterator`, we immediately - /// add it to our `ParamEnv`, and add it to our stack for recursive evaluation. When we later - /// select it, we'll pick up any nested bounds, without ever inferring that `T: IntoIterator` - /// needs to hold. - /// - /// One additional consideration is supertrait bounds. Normally, a `ParamEnv` is only ever - /// constructed once for a given type. As part of the construction process, the `ParamEnv` will - /// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo`, the - /// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our - /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate_predicates`, or - /// else `SelectionContext` will choke on the missing predicates. However, this should never - /// show up in the final synthesized generics: we don't want our generated docs page to contain - /// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a - /// separate `user_env`, which only holds the predicates that will actually be displayed to the - /// user. - fn evaluate_predicates( - &self, - infcx: &InferCtxt<'_, 'tcx>, - trait_did: DefId, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - user_env: ty::ParamEnv<'tcx>, - fresh_preds: &mut FxHashSet>, - only_projections: bool, - ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> { - let tcx = infcx.tcx; - - let mut select = SelectionContext::with_negative(&infcx, true); - - let mut already_visited = FxHashSet::default(); - let mut predicates = VecDeque::new(); - predicates.push_back(ty::Binder::bind(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: trait_did, - substs: infcx.tcx.mk_substs_trait(ty, &[]), - }, - })); - - let mut computed_preds: FxHashSet<_> = param_env.caller_bounds.iter().cloned().collect(); - let mut user_computed_preds: FxHashSet<_> = - user_env.caller_bounds.iter().cloned().collect(); - - let mut new_env = param_env; - let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID); - - while let Some(pred) = predicates.pop_front() { - infcx.clear_caches(); - - if !already_visited.insert(pred) { - continue; - } - - // Call `infcx.resolve_vars_if_possible` to see if we can - // get rid of any inference variables. - let obligation = infcx.resolve_vars_if_possible(&Obligation::new( - dummy_cause.clone(), - new_env, - pred, - )); - let result = select.select(&obligation); - - match &result { - &Ok(Some(ref vtable)) => { - // If we see an explicit negative impl (e.g., `impl !Send for MyStruct`), - // we immediately bail out, since it's impossible for us to continue. - match vtable { - Vtable::VtableImpl(VtableImplData { impl_def_id, .. }) => { - // Blame 'tidy' for the weird bracket placement. - if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { - debug!( - "evaluate_nested_obligations: found explicit negative impl\ - {:?}, bailing out", - impl_def_id - ); - return None; - } - } - _ => {} - } - - let obligations = vtable.clone().nested_obligations().into_iter(); - - if !self.evaluate_nested_obligations( - ty, - obligations, - &mut user_computed_preds, - fresh_preds, - &mut predicates, - &mut select, - only_projections, - ) { - return None; - } - } - &Ok(None) => {} - &Err(SelectionError::Unimplemented) => { - if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) { - already_visited.remove(&pred); - self.add_user_pred( - &mut user_computed_preds, - ty::Predicate::Trait(pred, hir::Constness::NotConst), - ); - predicates.push_back(pred); - } else { - debug!( - "evaluate_nested_obligations: `Unimplemented` found, bailing: \ - {:?} {:?} {:?}", - ty, - pred, - pred.skip_binder().trait_ref.substs - ); - return None; - } - } - _ => panic!("Unexpected error for '{:?}': {:?}", ty, result), - }; - - computed_preds.extend(user_computed_preds.iter().cloned()); - let normalized_preds = - elaborate_predicates(tcx, computed_preds.iter().cloned().collect()); - new_env = - ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal, None); - } - - let final_user_env = ty::ParamEnv::new( - tcx.mk_predicates(user_computed_preds.into_iter()), - user_env.reveal, - None, - ); - debug!( - "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \ - '{:?}'", - ty, trait_did, new_env, final_user_env - ); - - return Some((new_env, final_user_env)); - } - - /// This method is designed to work around the following issue: - /// When we compute auto trait bounds, we repeatedly call `SelectionContext.select`, - /// progressively building a `ParamEnv` based on the results we get. - /// However, our usage of `SelectionContext` differs from its normal use within the compiler, - /// in that we capture and re-reprocess predicates from `Unimplemented` errors. - /// - /// This can lead to a corner case when dealing with region parameters. - /// During our selection loop in `evaluate_predicates`, we might end up with - /// two trait predicates that differ only in their region parameters: - /// one containing a HRTB lifetime parameter, and one containing a 'normal' - /// lifetime parameter. For example: - /// - /// T as MyTrait<'a> - /// T as MyTrait<'static> - /// - /// If we put both of these predicates in our computed `ParamEnv`, we'll - /// confuse `SelectionContext`, since it will (correctly) view both as being applicable. - /// - /// To solve this, we pick the 'more strict' lifetime bound -- i.e., the HRTB - /// Our end goal is to generate a user-visible description of the conditions - /// under which a type implements an auto trait. A trait predicate involving - /// a HRTB means that the type needs to work with any choice of lifetime, - /// not just one specific lifetime (e.g., `'static`). - fn add_user_pred<'c>( - &self, - user_computed_preds: &mut FxHashSet>, - new_pred: ty::Predicate<'c>, - ) { - let mut should_add_new = true; - user_computed_preds.retain(|&old_pred| { - match (&new_pred, old_pred) { - (&ty::Predicate::Trait(new_trait, _), ty::Predicate::Trait(old_trait, _)) => { - if new_trait.def_id() == old_trait.def_id() { - let new_substs = new_trait.skip_binder().trait_ref.substs; - let old_substs = old_trait.skip_binder().trait_ref.substs; - - if !new_substs.types().eq(old_substs.types()) { - // We can't compare lifetimes if the types are different, - // so skip checking `old_pred`. - return true; - } - - for (new_region, old_region) in - new_substs.regions().zip(old_substs.regions()) - { - match (new_region, old_region) { - // If both predicates have an `ReLateBound` (a HRTB) in the - // same spot, we do nothing. - ( - ty::RegionKind::ReLateBound(_, _), - ty::RegionKind::ReLateBound(_, _), - ) => {} - - (ty::RegionKind::ReLateBound(_, _), _) - | (_, ty::RegionKind::ReVar(_)) => { - // One of these is true: - // The new predicate has a HRTB in a spot where the old - // predicate does not (if they both had a HRTB, the previous - // match arm would have executed). A HRBT is a 'stricter' - // bound than anything else, so we want to keep the newer - // predicate (with the HRBT) in place of the old predicate. - // - // OR - // - // The old predicate has a region variable where the new - // predicate has some other kind of region. An region - // variable isn't something we can actually display to a user, - // so we choose their new predicate (which doesn't have a region - // varaible). - // - // In both cases, we want to remove the old predicate, - // from `user_computed_preds`, and replace it with the new - // one. Having both the old and the new - // predicate in a `ParamEnv` would confuse `SelectionContext`. - // - // We're currently in the predicate passed to 'retain', - // so we return `false` to remove the old predicate from - // `user_computed_preds`. - return false; - } - (_, ty::RegionKind::ReLateBound(_, _)) - | (ty::RegionKind::ReVar(_), _) => { - // This is the opposite situation as the previous arm. - // One of these is true: - // - // The old predicate has a HRTB lifetime in a place where the - // new predicate does not. - // - // OR - // - // The new predicate has a region variable where the old - // predicate has some other type of region. - // - // We want to leave the old - // predicate in `user_computed_preds`, and skip adding - // new_pred to `user_computed_params`. - should_add_new = false - } - _ => {} - } - } - } - } - _ => {} - } - return true; - }); - - if should_add_new { - user_computed_preds.insert(new_pred); - } - } - - /// This is very similar to `handle_lifetimes`. However, instead of matching `ty::Region`s - /// to each other, we match `ty::RegionVid`s to `ty::Region`s. - fn map_vid_to_region<'cx>( - &self, - regions: &RegionConstraintData<'cx>, - ) -> FxHashMap> { - let mut vid_map: FxHashMap, RegionDeps<'cx>> = FxHashMap::default(); - let mut finished_map = FxHashMap::default(); - - for constraint in regions.constraints.keys() { - match constraint { - &Constraint::VarSubVar(r1, r2) => { - { - let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default(); - deps1.larger.insert(RegionTarget::RegionVid(r2)); - } - - let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default(); - deps2.smaller.insert(RegionTarget::RegionVid(r1)); - } - &Constraint::RegSubVar(region, vid) => { - { - let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default(); - deps1.larger.insert(RegionTarget::RegionVid(vid)); - } - - let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); - deps2.smaller.insert(RegionTarget::Region(region)); - } - &Constraint::VarSubReg(vid, region) => { - finished_map.insert(vid, region); - } - &Constraint::RegSubReg(r1, r2) => { - { - let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default(); - deps1.larger.insert(RegionTarget::Region(r2)); - } - - let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default(); - deps2.smaller.insert(RegionTarget::Region(r1)); - } - } - } - - while !vid_map.is_empty() { - let target = *vid_map.keys().next().expect("Keys somehow empty"); - let deps = vid_map.remove(&target).expect("Entry somehow missing"); - - for smaller in deps.smaller.iter() { - for larger in deps.larger.iter() { - match (smaller, larger) { - (&RegionTarget::Region(_), &RegionTarget::Region(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let larger_deps = v.into_mut(); - larger_deps.smaller.insert(*smaller); - larger_deps.smaller.remove(&target); - } - } - (&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => { - finished_map.insert(v1, r1); - } - (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { - // Do nothing; we don't care about regions that are smaller than vids. - } - (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let larger_deps = v.into_mut(); - larger_deps.smaller.insert(*smaller); - larger_deps.smaller.remove(&target); - } - } - } - } - } - } - finished_map - } - - fn is_param_no_infer(&self, substs: SubstsRef<'_>) -> bool { - return self.is_of_param(substs.type_at(0)) && !substs.types().any(|t| t.has_infer_types()); - } - - pub fn is_of_param(&self, ty: Ty<'_>) -> bool { - return match ty.kind { - ty::Param(_) => true, - ty::Projection(p) => self.is_of_param(p.self_ty()), - _ => false, - }; - } - - fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - match p.ty().skip_binder().kind { - ty::Projection(proj) if proj == p.skip_binder().projection_ty => true, - _ => false, - } - } - - fn evaluate_nested_obligations( - &self, - ty: Ty<'_>, - nested: impl Iterator>>, - computed_preds: &mut FxHashSet>, - fresh_preds: &mut FxHashSet>, - predicates: &mut VecDeque>, - select: &mut SelectionContext<'_, 'tcx>, - only_projections: bool, - ) -> bool { - let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID); - - for (obligation, mut predicate) in nested.map(|o| (o.clone(), o.predicate)) { - let is_new_pred = fresh_preds.insert(self.clean_pred(select.infcx(), predicate)); - - // Resolve any inference variables that we can, to help selection succeed - predicate = select.infcx().resolve_vars_if_possible(&predicate); - - // We only add a predicate as a user-displayable bound if - // it involves a generic parameter, and doesn't contain - // any inference variables. - // - // Displaying a bound involving a concrete type (instead of a generic - // parameter) would be pointless, since it's always true - // (e.g. u8: Copy) - // Displaying an inference variable is impossible, since they're - // an internal compiler detail without a defined visual representation - // - // We check this by calling is_of_param on the relevant types - // from the various possible predicates - match &predicate { - &ty::Predicate::Trait(p, _) => { - if self.is_param_no_infer(p.skip_binder().trait_ref.substs) - && !only_projections - && is_new_pred - { - self.add_user_pred(computed_preds, predicate); - } - predicates.push_back(p); - } - &ty::Predicate::Projection(p) => { - debug!( - "evaluate_nested_obligations: examining projection predicate {:?}", - predicate - ); - - // As described above, we only want to display - // bounds which include a generic parameter but don't include - // an inference variable. - // Additionally, we check if we've seen this predicate before, - // to avoid rendering duplicate bounds to the user. - if self.is_param_no_infer(p.skip_binder().projection_ty.substs) - && !p.ty().skip_binder().has_infer_types() - && is_new_pred - { - debug!( - "evaluate_nested_obligations: adding projection predicate\ - to computed_preds: {:?}", - predicate - ); - - // Under unusual circumstances, we can end up with a self-refeential - // projection predicate. For example: - // ::Value == ::Value - // Not only is displaying this to the user pointless, - // having it in the ParamEnv will cause an issue if we try to call - // poly_project_and_unify_type on the predicate, since this kind of - // predicate will normally never end up in a ParamEnv. - // - // For these reasons, we ignore these weird predicates, - // ensuring that we're able to properly synthesize an auto trait impl - if self.is_self_referential_projection(p) { - debug!( - "evaluate_nested_obligations: encountered a projection - predicate equating a type with itself! Skipping" - ); - } else { - self.add_user_pred(computed_preds, predicate); - } - } - - // There are three possible cases when we project a predicate: - // - // 1. We encounter an error. This means that it's impossible for - // our current type to implement the auto trait - there's bound - // that we could add to our ParamEnv that would 'fix' this kind - // of error, as it's not caused by an unimplemented type. - // - // 2. We successfully project the predicate (Ok(Some(_))), generating - // some subobligations. We then process these subobligations - // like any other generated sub-obligations. - // - // 3. We receieve an 'ambiguous' result (Ok(None)) - // If we were actually trying to compile a crate, - // we would need to re-process this obligation later. - // However, all we care about is finding out what bounds - // are needed for our type to implement a particular auto trait. - // We've already added this obligation to our computed ParamEnv - // above (if it was necessary). Therefore, we don't need - // to do any further processing of the obligation. - // - // Note that we *must* try to project *all* projection predicates - // we encounter, even ones without inference variable. - // This ensures that we detect any projection errors, - // which indicate that our type can *never* implement the given - // auto trait. In that case, we will generate an explicit negative - // impl (e.g. 'impl !Send for MyType'). However, we don't - // try to process any of the generated subobligations - - // they contain no new information, since we already know - // that our type implements the projected-through trait, - // and can lead to weird region issues. - // - // Normally, we'll generate a negative impl as a result of encountering - // a type with an explicit negative impl of an auto trait - // (for example, raw pointers have !Send and !Sync impls) - // However, through some **interesting** manipulations of the type - // system, it's actually possible to write a type that never - // implements an auto trait due to a projection error, not a normal - // negative impl error. To properly handle this case, we need - // to ensure that we catch any potential projection errors, - // and turn them into an explicit negative impl for our type. - debug!("Projecting and unifying projection predicate {:?}", predicate); - - match poly_project_and_unify_type(select, &obligation.with(p)) { - Err(e) => { - debug!( - "evaluate_nested_obligations: Unable to unify predicate \ - '{:?}' '{:?}', bailing out", - ty, e - ); - return false; - } - Ok(Some(v)) => { - // We only care about sub-obligations - // when we started out trying to unify - // some inference variables. See the comment above - // for more infomration - if p.ty().skip_binder().has_infer_types() { - if !self.evaluate_nested_obligations( - ty, - v.clone().iter().cloned(), - computed_preds, - fresh_preds, - predicates, - select, - only_projections, - ) { - return false; - } - } - } - Ok(None) => { - // It's ok not to make progress when hvave no inference variables - - // in that case, we were only performing unifcation to check if an - // error occurred (which would indicate that it's impossible for our - // type to implement the auto trait). - // However, we should always make progress (either by generating - // subobligations or getting an error) when we started off with - // inference variables - if p.ty().skip_binder().has_infer_types() { - panic!("Unexpected result when selecting {:?} {:?}", ty, obligation) - } - } - } - } - &ty::Predicate::RegionOutlives(ref binder) => { - if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { - return false; - } - } - &ty::Predicate::TypeOutlives(ref binder) => { - match ( - binder.no_bound_vars(), - binder.map_bound_ref(|pred| pred.0).no_bound_vars(), - ) { - (None, Some(t_a)) => { - select.infcx().register_region_obligation_with_cause( - t_a, - select.infcx().tcx.lifetimes.re_static, - &dummy_cause, - ); - } - (Some(ty::OutlivesPredicate(t_a, r_b)), _) => { - select.infcx().register_region_obligation_with_cause( - t_a, - r_b, - &dummy_cause, - ); - } - _ => {} - }; - } - _ => panic!("Unexpected predicate {:?} {:?}", ty, predicate), - }; - } - return true; - } - - pub fn clean_pred( - &self, - infcx: &InferCtxt<'_, 'tcx>, - p: ty::Predicate<'tcx>, - ) -> ty::Predicate<'tcx> { - infcx.freshen(p) - } -} - -// Replaces all ReVars in a type with ty::Region's, using the provided map -pub struct RegionReplacer<'a, 'tcx> { - vid_to_region: &'a FxHashMap>, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - (match r { - &ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(), - _ => None, - }) - .unwrap_or_else(|| r.super_fold_with(self)) - } -} diff --git a/src/librustc_infer/traits/chalk_fulfill.rs b/src/librustc_infer/traits/chalk_fulfill.rs deleted file mode 100644 index 82fa683a290..00000000000 --- a/src/librustc_infer/traits/chalk_fulfill.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferCtxt; -use crate::traits::query::NoSolution; -use crate::traits::{ - Environment, FulfillmentError, FulfillmentErrorCode, InEnvironment, ObligationCause, - PredicateObligation, SelectionError, TraitEngine, -}; -use rustc::ty::{self, Ty}; -use rustc_data_structures::fx::FxHashSet; - -pub use rustc::traits::ChalkCanonicalGoal as CanonicalGoal; - -pub struct FulfillmentContext<'tcx> { - obligations: FxHashSet>>, -} - -impl FulfillmentContext<'tcx> { - crate fn new() -> Self { - FulfillmentContext { obligations: FxHashSet::default() } - } -} - -fn in_environment( - infcx: &InferCtxt<'_, 'tcx>, - obligation: PredicateObligation<'tcx>, -) -> InEnvironment<'tcx, PredicateObligation<'tcx>> { - assert!(!infcx.is_in_snapshot()); - let obligation = infcx.resolve_vars_if_possible(&obligation); - - let environment = match obligation.param_env.def_id { - Some(def_id) => infcx.tcx.environment(def_id), - None if obligation.param_env.caller_bounds.is_empty() => { - Environment { clauses: ty::List::empty() } - } - _ => bug!("non-empty `ParamEnv` with no def-id"), - }; - - InEnvironment { environment, goal: obligation } -} - -impl TraitEngine<'tcx> for FulfillmentContext<'tcx> { - fn normalize_projection_type( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - _param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - _cause: ObligationCause<'tcx>, - ) -> Ty<'tcx> { - infcx.tcx.mk_ty(ty::Projection(projection_ty)) - } - - fn register_predicate_obligation( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - obligation: PredicateObligation<'tcx>, - ) { - self.obligations.insert(in_environment(infcx, obligation)); - } - - fn select_all_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec>> { - self.select_where_possible(infcx)?; - - if self.obligations.is_empty() { - Ok(()) - } else { - let errors = self - .obligations - .iter() - .map(|obligation| FulfillmentError { - obligation: obligation.goal.clone(), - code: FulfillmentErrorCode::CodeAmbiguity, - points_at_arg_span: false, - }) - .collect(); - Err(errors) - } - } - - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec>> { - let mut errors = Vec::new(); - let mut next_round = FxHashSet::default(); - let mut making_progress; - - loop { - making_progress = false; - - // We iterate over all obligations, and record if we are able - // to unambiguously prove at least one obligation. - for obligation in self.obligations.drain() { - let mut orig_values = OriginalQueryValues::default(); - let canonical_goal = infcx.canonicalize_query( - &InEnvironment { - environment: obligation.environment, - goal: obligation.goal.predicate, - }, - &mut orig_values, - ); - - match infcx.tcx.evaluate_goal(canonical_goal) { - Ok(response) => { - if response.is_proven() { - making_progress = true; - - match infcx.instantiate_query_response_and_region_obligations( - &obligation.goal.cause, - obligation.goal.param_env, - &orig_values, - &response, - ) { - Ok(infer_ok) => next_round.extend( - infer_ok - .obligations - .into_iter() - .map(|obligation| in_environment(infcx, obligation)), - ), - - Err(_err) => errors.push(FulfillmentError { - obligation: obligation.goal, - code: FulfillmentErrorCode::CodeSelectionError( - SelectionError::Unimplemented, - ), - points_at_arg_span: false, - }), - } - } else { - // Ambiguous: retry at next round. - next_round.insert(obligation); - } - } - - Err(NoSolution) => errors.push(FulfillmentError { - obligation: obligation.goal, - code: FulfillmentErrorCode::CodeSelectionError( - SelectionError::Unimplemented, - ), - points_at_arg_span: false, - }), - } - } - next_round = std::mem::replace(&mut self.obligations, next_round); - - if !making_progress { - break; - } - } - - if errors.is_empty() { Ok(()) } else { Err(errors) } - } - - fn pending_obligations(&self) -> Vec> { - self.obligations.iter().map(|obligation| obligation.goal.clone()).collect() - } -} diff --git a/src/librustc_infer/traits/codegen/mod.rs b/src/librustc_infer/traits/codegen/mod.rs deleted file mode 100644 index bd4129a4de7..00000000000 --- a/src/librustc_infer/traits/codegen/mod.rs +++ /dev/null @@ -1,110 +0,0 @@ -// This file contains various trait resolution methods used by codegen. -// They all assume regions can be erased and monomorphic types. It -// seems likely that they should eventually be merged into more -// general routines. - -use crate::infer::{InferCtxt, TyCtxtInferExt}; -use crate::traits::{ - FulfillmentContext, Obligation, ObligationCause, SelectionContext, TraitEngine, Vtable, -}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, TyCtxt}; - -/// Attempts to resolve an obligation to a vtable. The result is -/// a shallow vtable resolution, meaning that we do not -/// (necessarily) resolve all nested obligations on the impl. Note -/// that type check should guarantee to us that all nested -/// obligations *could be* resolved if we wanted to. -/// Assumes that this is run after the entire crate has been successfully type-checked. -pub fn codegen_fulfill_obligation<'tcx>( - ty: TyCtxt<'tcx>, - (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), -) -> Vtable<'tcx, ()> { - // Remove any references to regions; this helps improve caching. - let trait_ref = ty.erase_regions(&trait_ref); - - debug!( - "codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", - (param_env, trait_ref), - trait_ref.def_id() - ); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - ty.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - - let obligation_cause = ObligationCause::dummy(); - let obligation = - Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); - - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - bug!( - "Encountered ambiguity selecting `{:?}` during codegen, \ - presuming due to overflow", - trait_ref - ) - } - Err(e) => { - bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) - } - }; - - debug!("fulfill_obligation: selection={:?}", selection); - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - let vtable = infcx.drain_fulfillment_cx_or_panic(&mut fulfill_cx, &vtable); - - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - vtable - }) -} - -// # Global Cache - -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - /// Finishes processes any obligations that remain in the - /// fulfillment context, and then returns the result with all type - /// variables removed and regions erased. Because this is intended - /// for use after type-check has completed, if any errors occur, - /// it will panic. It is used during normalization and other cases - /// where processing the obligations in `fulfill_cx` may cause - /// type inference variables that appear in `result` to be - /// unified, and hence we need to process those obligations to get - /// the complete picture of the type. - fn drain_fulfillment_cx_or_panic( - &self, - fulfill_cx: &mut FulfillmentContext<'tcx>, - result: &T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - debug!("drain_fulfillment_cx_or_panic()"); - - // In principle, we only need to do this so long as `result` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - if let Err(errors) = fulfill_cx.select_all_or_error(self) { - bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors); - } - - let result = self.resolve_vars_if_possible(result); - self.tcx.erase_regions(&result) - } -} diff --git a/src/librustc_infer/traits/coherence.rs b/src/librustc_infer/traits/coherence.rs deleted file mode 100644 index 0dec5ae6da5..00000000000 --- a/src/librustc_infer/traits/coherence.rs +++ /dev/null @@ -1,540 +0,0 @@ -//! See Rustc Guide chapters on [trait-resolution] and [trait-specialization] for more info on how -//! this works. -//! -//! [trait-resolution]: https://rust-lang.github.io/rustc-guide/traits/resolution.html -//! [trait-specialization]: https://rust-lang.github.io/rustc-guide/traits/specialization.html - -use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt}; -use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::SkipLeakCheck; -use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::symbol::sym; -use rustc_span::DUMMY_SP; - -/// Whether we do the orphan check relative to this crate or -/// to some remote crate. -#[derive(Copy, Clone, Debug)] -enum InCrate { - Local, - Remote, -} - -#[derive(Debug, Copy, Clone)] -pub enum Conflict { - Upstream, - Downstream, -} - -pub struct OverlapResult<'tcx> { - pub impl_header: ty::ImplHeader<'tcx>, - pub intercrate_ambiguity_causes: Vec, - - /// `true` if the overlap might've been permitted before the shift - /// to universes. - pub involves_placeholder: bool, -} - -pub fn add_placeholder_note(err: &mut rustc_errors::DiagnosticBuilder<'_>) { - err.note( - "this behavior recently changed as a result of a bug fix; \ - see rust-lang/rust#56105 for details", - ); -} - -/// If there are types that satisfy both impls, invokes `on_overlap` -/// with a suitably-freshened `ImplHeader` with those types -/// substituted. Otherwise, invokes `no_overlap`. -pub fn overlapping_impls( - tcx: TyCtxt<'_>, - impl1_def_id: DefId, - impl2_def_id: DefId, - skip_leak_check: SkipLeakCheck, - on_overlap: F1, - no_overlap: F2, -) -> R -where - F1: FnOnce(OverlapResult<'_>) -> R, - F2: FnOnce() -> R, -{ - debug!( - "overlapping_impls(\ - impl1_def_id={:?}, \ - impl2_def_id={:?})", - impl1_def_id, impl2_def_id, - ); - - let overlaps = tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some() - }); - - if !overlaps { - return no_overlap(); - } - - // In the case where we detect an error, run the check again, but - // this time tracking intercrate ambuiguity causes for better - // diagnostics. (These take time and can lead to false errors.) - tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - selcx.enable_tracking_intercrate_ambiguity_causes(); - on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap()) - }) -} - -fn with_fresh_ty_vars<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: DefId, -) -> ty::ImplHeader<'tcx> { - let tcx = selcx.tcx(); - let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id); - - let header = ty::ImplHeader { - impl_def_id, - self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs), - trait_ref: tcx.impl_trait_ref(impl_def_id).subst(tcx, impl_substs), - predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates, - }; - - let Normalized { value: mut header, obligations } = - traits::normalize(selcx, param_env, ObligationCause::dummy(), &header); - - header.predicates.extend(obligations.into_iter().map(|o| o.predicate)); - header -} - -/// Can both impl `a` and impl `b` be satisfied by a common type (including -/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls. -fn overlap<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - skip_leak_check: SkipLeakCheck, - a_def_id: DefId, - b_def_id: DefId, -) -> Option> { - debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id); - - selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| { - overlap_within_probe(selcx, a_def_id, b_def_id, snapshot) - }) -} - -fn overlap_within_probe( - selcx: &mut SelectionContext<'cx, 'tcx>, - a_def_id: DefId, - b_def_id: DefId, - snapshot: &CombinedSnapshot<'_, 'tcx>, -) -> Option> { - // For the purposes of this check, we don't bring any placeholder - // types into scope; instead, we replace the generic types with - // fresh type variables, and hence we do our evaluations in an - // empty environment. - let param_env = ty::ParamEnv::empty(); - - let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id); - let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id); - - debug!("overlap: a_impl_header={:?}", a_impl_header); - debug!("overlap: b_impl_header={:?}", b_impl_header); - - // Do `a` and `b` unify? If not, no overlap. - let obligations = match selcx - .infcx() - .at(&ObligationCause::dummy(), param_env) - .eq_impl_headers(&a_impl_header, &b_impl_header) - { - Ok(InferOk { obligations, value: () }) => obligations, - Err(_) => { - return None; - } - }; - - debug!("overlap: unification check succeeded"); - - // Are any of the obligations unsatisfiable? If so, no overlap. - let infcx = selcx.infcx(); - let opt_failing_obligation = a_impl_header - .predicates - .iter() - .chain(&b_impl_header.predicates) - .map(|p| infcx.resolve_vars_if_possible(p)) - .map(|p| Obligation { - cause: ObligationCause::dummy(), - param_env, - recursion_depth: 0, - predicate: p, - }) - .chain(obligations) - .find(|o| !selcx.predicate_may_hold_fatal(o)); - // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported - // to the canonical trait query form, `infcx.predicate_may_hold`, once - // the new system supports intercrate mode (which coherence needs). - - if let Some(failing_obligation) = opt_failing_obligation { - debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); - return None; - } - - let impl_header = selcx.infcx().resolve_vars_if_possible(&a_impl_header); - let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); - debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); - - let involves_placeholder = match selcx.infcx().region_constraints_added_in_snapshot(snapshot) { - Some(true) => true, - _ => false, - }; - - Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) -} - -pub fn trait_ref_is_knowable<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, -) -> Option { - debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref); - if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() { - // A downstream or cousin crate is allowed to implement some - // substitution of this trait-ref. - return Some(Conflict::Downstream); - } - - if trait_ref_is_local_or_fundamental(tcx, trait_ref) { - // This is a local or fundamental trait, so future-compatibility - // is no concern. We know that downstream/cousin crates are not - // allowed to implement a substitution of this trait ref, which - // means impls could only come from dependencies of this crate, - // which we already know about. - return None; - } - - // This is a remote non-fundamental trait, so if another crate - // can be the "final owner" of a substitution of this trait-ref, - // they are allowed to implement it future-compatibly. - // - // However, if we are a final owner, then nobody else can be, - // and if we are an intermediate owner, then we don't care - // about future-compatibility, which means that we're OK if - // we are an owner. - if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() { - debug!("trait_ref_is_knowable: orphan check passed"); - return None; - } else { - debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned"); - return Some(Conflict::Upstream); - } -} - -pub fn trait_ref_is_local_or_fundamental<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, -) -> bool { - trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental) -} - -pub enum OrphanCheckErr<'tcx> { - NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>), - UncoveredTy(Ty<'tcx>, Option>), -} - -/// Checks the coherence orphan rules. `impl_def_id` should be the -/// `DefId` of a trait impl. To pass, either the trait must be local, or else -/// two conditions must be satisfied: -/// -/// 1. All type parameters in `Self` must be "covered" by some local type constructor. -/// 2. Some local type must appear in `Self`. -pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> { - debug!("orphan_check({:?})", impl_def_id); - - // We only except this routine to be invoked on implementations - // of a trait, not inherent implementations. - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - debug!("orphan_check: trait_ref={:?}", trait_ref); - - // If the *trait* is local to the crate, ok. - if trait_ref.def_id.is_local() { - debug!("trait {:?} is local to current crate", trait_ref.def_id); - return Ok(()); - } - - orphan_check_trait_ref(tcx, trait_ref, InCrate::Local) -} - -/// Checks whether a trait-ref is potentially implementable by a crate. -/// -/// The current rule is that a trait-ref orphan checks in a crate C: -/// -/// 1. Order the parameters in the trait-ref in subst order - Self first, -/// others linearly (e.g., `>` is U < V < W). -/// 2. Of these type parameters, there is at least one type parameter -/// in which, walking the type as a tree, you can reach a type local -/// to C where all types in-between are fundamental types. Call the -/// first such parameter the "local key parameter". -/// - e.g., `Box` is OK, because you can visit LocalType -/// going through `Box`, which is fundamental. -/// - similarly, `FundamentalPair, Box>` is OK for -/// the same reason. -/// - but (knowing that `Vec` is non-fundamental, and assuming it's -/// not local), `Vec` is bad, because `Vec<->` is between -/// the local type and the type parameter. -/// 3. Every type parameter before the local key parameter is fully known in C. -/// - e.g., `impl T: Trait` is bad, because `T` might be -/// an unknown type. -/// - but `impl LocalType: Trait` is OK, because `LocalType` -/// occurs before `T`. -/// 4. Every type in the local key parameter not known in C, going -/// through the parameter's type tree, must appear only as a subtree of -/// a type local to C, with only fundamental types between the type -/// local to C and the local key parameter. -/// - e.g., `Vec>>` (or equivalently `Box>>`) -/// is bad, because the only local type with `T` as a subtree is -/// `LocalType`, and `Vec<->` is between it and the type parameter. -/// - similarly, `FundamentalPair, T>` is bad, because -/// the second occurrence of `T` is not a subtree of *any* local type. -/// - however, `LocalType>` is OK, because `T` is a subtree of -/// `LocalType>`, which is local and has no types between it and -/// the type parameter. -/// -/// The orphan rules actually serve several different purposes: -/// -/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where -/// every type local to one crate is unknown in the other) can't implement -/// the same trait-ref. This follows because it can be seen that no such -/// type can orphan-check in 2 such crates. -/// -/// To check that a local impl follows the orphan rules, we check it in -/// InCrate::Local mode, using type parameters for the "generic" types. -/// -/// 2. They ground negative reasoning for coherence. If a user wants to -/// write both a conditional blanket impl and a specific impl, we need to -/// make sure they do not overlap. For example, if we write -/// ``` -/// impl IntoIterator for Vec -/// impl IntoIterator for T -/// ``` -/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. -/// We can observe that this holds in the current crate, but we need to make -/// sure this will also hold in all unknown crates (both "independent" crates, -/// which we need for link-safety, and also child crates, because we don't want -/// child crates to get error for impl conflicts in a *dependency*). -/// -/// For that, we only allow negative reasoning if, for every assignment to the -/// inference variables, every unknown crate would get an orphan error if they -/// try to implement this trait-ref. To check for this, we use InCrate::Remote -/// mode. That is sound because we already know all the impls from known crates. -/// -/// 3. For non-#[fundamental] traits, they guarantee that parent crates can -/// add "non-blanket" impls without breaking negative reasoning in dependent -/// crates. This is the "rebalancing coherence" (RFC 1023) restriction. -/// -/// For that, we only a allow crate to perform negative reasoning on -/// non-local-non-#[fundamental] only if there's a local key parameter as per (2). -/// -/// Because we never perform negative reasoning generically (coherence does -/// not involve type parameters), this can be interpreted as doing the full -/// orphan check (using InCrate::Local mode), substituting non-local known -/// types for all inference variables. -/// -/// This allows for crates to future-compatibly add impls as long as they -/// can't apply to types with a key parameter in a child crate - applying -/// the rules, this basically means that every type parameter in the impl -/// must appear behind a non-fundamental type (because this is not a -/// type-system requirement, crate owners might also go for "semantic -/// future-compatibility" involving things such as sealed traits, but -/// the above requirement is sufficient, and is necessary in "open world" -/// cases). -/// -/// Note that this function is never called for types that have both type -/// parameters and inference variables. -fn orphan_check_trait_ref<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - in_crate: InCrate, -) -> Result<(), OrphanCheckErr<'tcx>> { - debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})", trait_ref, in_crate); - - if trait_ref.needs_infer() && trait_ref.needs_subst() { - bug!( - "can't orphan check a trait ref with both params and inference variables {:?}", - trait_ref - ); - } - - // Given impl Trait for T0, an impl is valid only - // if at least one of the following is true: - // - // - Trait is a local trait - // (already checked in orphan_check prior to calling this function) - // - All of - // - At least one of the types T0..=Tn must be a local type. - // Let Ti be the first such type. - // - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti) - // - fn uncover_fundamental_ty<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - in_crate: InCrate, - ) -> Vec> { - if fundamental_ty(ty) && ty_is_non_local(ty, in_crate).is_some() { - ty.walk_shallow().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).collect() - } else { - vec![ty] - } - } - - let mut non_local_spans = vec![]; - for (i, input_ty) in - trait_ref.input_types().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).enumerate() - { - debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty); - let non_local_tys = ty_is_non_local(input_ty, in_crate); - if non_local_tys.is_none() { - debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); - return Ok(()); - } else if let ty::Param(_) = input_ty.kind { - debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); - let local_type = trait_ref - .input_types() - .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .find(|ty| ty_is_non_local_constructor(ty, in_crate).is_none()); - - debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type); - - return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type)); - } - if let Some(non_local_tys) = non_local_tys { - for input_ty in non_local_tys { - non_local_spans.push((input_ty, i == 0)); - } - } - } - // If we exit above loop, never found a local type. - debug!("orphan_check_trait_ref: no local type"); - Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) -} - -fn ty_is_non_local<'t>(ty: Ty<'t>, in_crate: InCrate) -> Option>> { - match ty_is_non_local_constructor(ty, in_crate) { - Some(ty) => { - if !fundamental_ty(ty) { - Some(vec![ty]) - } else { - let tys: Vec<_> = ty - .walk_shallow() - .filter_map(|t| ty_is_non_local(t, in_crate)) - .flat_map(|i| i) - .collect(); - if tys.is_empty() { None } else { Some(tys) } - } - } - None => None, - } -} - -fn fundamental_ty(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Ref(..) => true, - ty::Adt(def, _) => def.is_fundamental(), - _ => false, - } -} - -fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { - match in_crate { - // The type is local to *this* crate - it will not be - // local in any other crate. - InCrate::Remote => false, - InCrate::Local => def_id.is_local(), - } -} - -fn ty_is_non_local_constructor<'tcx>(ty: Ty<'tcx>, in_crate: InCrate) -> Option> { - debug!("ty_is_non_local_constructor({:?})", ty); - - match ty.kind { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::Never - | ty::Tuple(..) - | ty::Param(..) - | ty::Projection(..) => Some(ty), - - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate { - InCrate::Local => Some(ty), - // The inference variable might be unified with a local - // type in that remote crate. - InCrate::Remote => None, - }, - - ty::Adt(def, _) => { - if def_id_is_local(def.did, in_crate) { - None - } else { - Some(ty) - } - } - ty::Foreign(did) => { - if def_id_is_local(did, in_crate) { - None - } else { - Some(ty) - } - } - ty::Opaque(..) => { - // This merits some explanation. - // Normally, opaque types are not involed when performing - // coherence checking, since it is illegal to directly - // implement a trait on an opaque type. However, we might - // end up looking at an opaque type during coherence checking - // if an opaque type gets used within another type (e.g. as - // a type parameter). This requires us to decide whether or - // not an opaque type should be considered 'local' or not. - // - // We choose to treat all opaque types as non-local, even - // those that appear within the same crate. This seems - // somewhat suprising at first, but makes sense when - // you consider that opaque types are supposed to hide - // the underlying type *within the same crate*. When an - // opaque type is used from outside the module - // where it is declared, it should be impossible to observe - // anyything about it other than the traits that it implements. - // - // The alternative would be to look at the underlying type - // to determine whether or not the opaque type itself should - // be considered local. However, this could make it a breaking change - // to switch the underlying ('defining') type from a local type - // to a remote type. This would violate the rule that opaque - // types should be completely opaque apart from the traits - // that they implement, so we don't use this behavior. - Some(ty) - } - - ty::Dynamic(ref tt, ..) => { - if let Some(principal) = tt.principal() { - if def_id_is_local(principal.def_id(), in_crate) { None } else { Some(ty) } - } else { - Some(ty) - } - } - - ty::Error => None, - - ty::UnnormalizedProjection(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) => bug!("ty_is_local invoked on unexpected type: {:?}", ty), - } -} diff --git a/src/librustc_infer/traits/engine.rs b/src/librustc_infer/traits/engine.rs index ba144379616..9ad722342a1 100644 --- a/src/librustc_infer/traits/engine.rs +++ b/src/librustc_infer/traits/engine.rs @@ -1,9 +1,9 @@ use crate::infer::InferCtxt; use crate::traits::Obligation; -use rustc::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc::ty::{self, ToPredicate, Ty, WithConstness}; use rustc_hir::def_id::DefId; -use super::{ChalkFulfillmentContext, FulfillmentContext, FulfillmentError}; +use super::FulfillmentError; use super::{ObligationCause, PredicateObligation}; pub trait TraitEngine<'tcx>: 'tcx { @@ -76,13 +76,3 @@ fn register_predicate_obligations( } } } - -impl dyn TraitEngine<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Box { - if tcx.sess.opts.debugging_opts.chalk { - Box::new(ChalkFulfillmentContext::new()) - } else { - Box::new(FulfillmentContext::new()) - } - } -} diff --git a/src/librustc_infer/traits/error_reporting/mod.rs b/src/librustc_infer/traits/error_reporting/mod.rs index 471f388f0bb..8943ce4e6c5 100644 --- a/src/librustc_infer/traits/error_reporting/mod.rs +++ b/src/librustc_infer/traits/error_reporting/mod.rs @@ -1,452 +1,16 @@ -pub mod on_unimplemented; -pub mod suggestions; +use super::ObjectSafetyViolation; -use super::{ - ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, - MismatchedProjectionTypes, ObjectSafetyViolation, Obligation, ObligationCause, - ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, - OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError, - TraitNotObjectSafe, -}; - -use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc::mir::interpret::ErrorHandled; -use rustc::session::DiagnosticMessageId; -use rustc::ty::error::ExpectedFound; -use rustc::ty::fast_reject; -use rustc::ty::fold::TypeFolder; -use rustc::ty::SubtypePredicate; -use rustc::ty::{ - self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; +use crate::infer::InferCtxt; +use rustc::ty::TyCtxt; use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; -use rustc_span::source_map::SourceMap; -use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use rustc_hir::def_id::DefId; +use rustc_span::Span; use std::fmt; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - pub fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option, - fallback_has_occurred: bool, - ) { - #[derive(Debug)] - struct ErrorDescriptor<'tcx> { - predicate: ty::Predicate<'tcx>, - index: Option, // None if this is an old error - } - - let mut error_map: FxHashMap<_, Vec<_>> = self - .reported_trait_errors - .borrow() - .iter() - .map(|(&span, predicates)| { - ( - span, - predicates - .iter() - .map(|&predicate| ErrorDescriptor { predicate, index: None }) - .collect(), - ) - }) - .collect(); - - for (index, error) in errors.iter().enumerate() { - // We want to ignore desugarings here: spans are equivalent even - // if one is the result of a desugaring and the other is not. - let mut span = error.obligation.cause.span; - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(_) = expn_data.kind { - span = expn_data.call_site; - } - - error_map.entry(span).or_default().push(ErrorDescriptor { - predicate: error.obligation.predicate, - index: Some(index), - }); - - self.reported_trait_errors - .borrow_mut() - .entry(span) - .or_default() - .push(error.obligation.predicate.clone()); - } - - // We do this in 2 passes because we want to display errors in order, though - // maybe it *is* better to sort errors by span or something. - let mut is_suppressed = vec![false; errors.len()]; - for (_, error_set) in error_map.iter() { - // We want to suppress "duplicate" errors with the same span. - for error in error_set { - if let Some(index) = error.index { - // Suppress errors that are either: - // 1) strictly implied by another error. - // 2) implied by an error with a smaller index. - for error2 in error_set { - if error2.index.map_or(false, |index2| is_suppressed[index2]) { - // Avoid errors being suppressed by already-suppressed - // errors, to prevent all errors from being suppressed - // at once. - continue; - } - - if self.error_implies(&error2.predicate, &error.predicate) - && !(error2.index >= error.index - && self.error_implies(&error.predicate, &error2.predicate)) - { - info!("skipping {:?} (implied by {:?})", error, error2); - is_suppressed[index] = true; - break; - } - } - } - } - } - - for (error, suppressed) in errors.iter().zip(is_suppressed) { - if !suppressed { - self.report_fulfillment_error(error, body_id, fallback_has_occurred); - } - } - } - - // returns if `cond` not occurring implies that `error` does not occur - i.e., that - // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) -> bool { - if cond == error { - return true; - } - - let (cond, error) = match (cond, error) { - (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error, _)) => (cond, error), - _ => { - // FIXME: make this work in other cases too. - return false; - } - }; - - for implication in super::elaborate_predicates(self.tcx, vec![*cond]) { - if let ty::Predicate::Trait(implication, _) = implication { - let error = error.to_poly_trait_ref(); - let implication = implication.to_poly_trait_ref(); - // FIXME: I'm just not taking associated types at all here. - // Eventually I'll need to implement param-env-aware - // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. - let param_env = ty::ParamEnv::empty(); - if self.can_sub(param_env, error, implication).is_ok() { - debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); - return true; - } - } - } - - false - } - - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option, - fallback_has_occurred: bool, - ) { - debug!("report_fulfillment_error({:?})", error); - match error.code { - FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { - self.report_selection_error( - &error.obligation, - selection_error, - fallback_has_occurred, - error.points_at_arg_span, - ); - } - FulfillmentErrorCode::CodeProjectionError(ref e) => { - self.report_projection_error(&error.obligation, e); - } - FulfillmentErrorCode::CodeAmbiguity => { - self.maybe_report_ambiguity(&error.obligation, body_id); - } - FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { - self.report_mismatched_types( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - err.clone(), - ) - .emit(); - } - } - } - - fn report_projection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &MismatchedProjectionTypes<'tcx>, - ) { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - - if predicate.references_error() { - return; - } - - self.probe(|_| { - let err_buf; - let mut err = &error.err; - let mut values = None; - - // try to find the mismatched types to report the error with. - // - // this can fail if the problem was higher-ranked, in which - // cause I have no idea for a good error message. - if let ty::Predicate::Projection(ref data) = predicate { - let mut selcx = SelectionContext::new(self); - let (data, _) = self.replace_bound_vars_with_fresh_vars( - obligation.cause.span, - infer::LateBoundRegionConversionTime::HigherRankedType, - data, - ); - let mut obligations = vec![]; - let normalized_ty = super::normalize_projection_type( - &mut selcx, - obligation.param_env, - data.projection_ty, - obligation.cause.clone(), - 0, - &mut obligations, - ); - - debug!( - "report_projection_error obligation.cause={:?} obligation.param_env={:?}", - obligation.cause, obligation.param_env - ); - - debug!( - "report_projection_error normalized_ty={:?} data.ty={:?}", - normalized_ty, data.ty - ); - - let is_normalized_ty_expected = match &obligation.cause.code { - ObligationCauseCode::ItemObligation(_) - | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ObjectCastObligation(_) => false, - _ => true, - }; - - if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( - is_normalized_ty_expected, - normalized_ty, - data.ty, - ) { - values = Some(infer::ValuePairs::Types(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - data.ty, - ))); - - err_buf = error; - err = &err_buf; - } - } - - let msg = format!("type mismatch resolving `{}`", predicate); - let error_id = (DiagnosticMessageId::ErrorId(271), Some(obligation.cause.span), msg); - let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - let mut diag = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0271, - "type mismatch resolving `{}`", - predicate - ); - self.note_type_err(&mut diag, &obligation.cause, None, values, err); - self.note_obligation_cause(&mut diag, obligation); - diag.emit(); - } - }); - } - - fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - /// returns the fuzzy category of a given type, or None - /// if the type can be equated to any type. - fn type_category(t: Ty<'_>) -> Option { - match t.kind { - ty::Bool => Some(0), - ty::Char => Some(1), - ty::Str => Some(2), - ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3), - ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4), - ty::Ref(..) | ty::RawPtr(..) => Some(5), - ty::Array(..) | ty::Slice(..) => Some(6), - ty::FnDef(..) | ty::FnPtr(..) => Some(7), - ty::Dynamic(..) => Some(8), - ty::Closure(..) => Some(9), - ty::Tuple(..) => Some(10), - ty::Projection(..) => Some(11), - ty::Param(..) => Some(12), - ty::Opaque(..) => Some(13), - ty::Never => Some(14), - ty::Adt(adt, ..) => match adt.adt_kind() { - AdtKind::Struct => Some(15), - AdtKind::Union => Some(16), - AdtKind::Enum => Some(17), - }, - ty::Generator(..) => Some(18), - ty::Foreign(..) => Some(19), - ty::GeneratorWitness(..) => Some(20), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => None, - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - } - } - - match (type_category(a), type_category(b)) { - (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) { - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, - _ => cat_a == cat_b, - }, - // infer and error can be equated to all types - _ => true, - } - } - - fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { - self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { - hir::GeneratorKind::Gen => "a generator", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", - }) - } - - fn find_similar_impl_candidates( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Vec> { - let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true); - let all_impls = self.tcx.all_impls(trait_ref.def_id()); - - match simp { - Some(simp) => all_impls - .iter() - .filter_map(|&def_id| { - let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); - if let Some(imp_simp) = imp_simp { - if simp != imp_simp { - return None; - } - } - - Some(imp) - }) - .collect(), - None => { - all_impls.iter().map(|&def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect() - } - } - } - - fn report_similar_impl_candidates( - &self, - impl_candidates: Vec>, - err: &mut DiagnosticBuilder<'_>, - ) { - if impl_candidates.is_empty() { - return; - } - - let len = impl_candidates.len(); - let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 }; - - let normalize = |candidate| { - self.tcx.infer_ctxt().enter(|ref infcx| { - let normalized = infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .normalize(candidate) - .ok(); - match normalized { - Some(normalized) => format!("\n {:?}", normalized.value), - None => format!("\n {:?}", candidate), - } - }) - }; - - // Sort impl candidates so that ordering is consistent for UI tests. - let mut normalized_impl_candidates = - impl_candidates.iter().map(normalize).collect::>(); - - // Sort before taking the `..end` range, - // because the ordering of `impl_candidates` may not be deterministic: - // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 - normalized_impl_candidates.sort(); - - err.help(&format!( - "the following implementations were found:{}{}", - normalized_impl_candidates[..end].join(""), - if len > 5 { format!("\nand {} others", len - 4) } else { String::new() } - )); - } - - /// Reports that an overflow has occurred and halts compilation. We - /// halt compilation unconditionally because it is important that - /// overflows never be masked -- they basically represent computations - /// whose result could not be truly determined and thus we can't say - /// if the program type checks or not -- and they are unusual - /// occurrences in any case. - pub fn report_overflow_error( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: fmt::Display + TypeFoldable<'tcx>, - { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let mut err = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0275, - "overflow evaluating the requirement `{}`", - predicate - ); - - if suggest_increasing_limit { - self.suggest_new_overflow_limit(&mut err); - } - - self.note_obligation_cause_code( - &mut err, - &obligation.predicate, - &obligation.cause.code, - &mut vec![], - ); - - err.emit(); - self.tcx.sess.abort_if_errors(); - bug!(); - } - - /// Reports that a cycle was detected which led to overflow and halts - /// compilation. This is equivalent to `report_overflow_error` except - /// that we can give a more helpful error message (and, in particular, - /// we do not suggest increasing the overflow limit, which is not - /// going to help). - pub fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { - let cycle = self.resolve_vars_if_possible(&cycle.to_owned()); - assert!(!cycle.is_empty()); - - debug!("report_overflow_error_cycle: cycle={:?}", cycle); - - self.report_overflow_error(&cycle[0], false); - } - pub fn report_extra_impl_obligation( &self, error_span: Span, @@ -469,559 +33,6 @@ pub fn report_extra_impl_obligation( err } - - /// Gets the parent trait chain start - fn get_parent_trait_ref( - &self, - code: &ObligationCauseCode<'tcx>, - ) -> Option<(String, Option)> { - match code { - &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - match self.get_parent_trait_ref(&data.parent_code) { - Some(t) => Some(t), - None => { - let ty = parent_trait_ref.skip_binder().self_ty(); - let span = - TyCategory::from_ty(ty).map(|(_, def_id)| self.tcx.def_span(def_id)); - Some((ty.to_string(), span)) - } - } - } - _ => None, - } - } - - pub fn report_selection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &SelectionError<'tcx>, - fallback_has_occurred: bool, - points_at_arg: bool, - ) { - let tcx = self.tcx; - let span = obligation.cause.span; - - let mut err = match *error { - SelectionError::Unimplemented => { - if let ObligationCauseCode::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } - | ObligationCauseCode::CompareImplTypeObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } = obligation.cause.code - { - self.report_extra_impl_obligation( - span, - item_name, - impl_item_def_id, - trait_item_def_id, - &format!("`{}`", obligation.predicate), - ) - .emit(); - return; - } - match obligation.predicate { - ty::Predicate::Trait(ref trait_predicate, _) => { - let trait_predicate = self.resolve_vars_if_possible(trait_predicate); - - if self.tcx.sess.has_errors() && trait_predicate.references_error() { - return; - } - let trait_ref = trait_predicate.to_poly_trait_ref(); - let (post_message, pre_message, type_def) = self - .get_parent_trait_ref(&obligation.cause.code) - .map(|(t, s)| { - ( - format!(" in `{}`", t), - format!("within `{}`, ", t), - s.map(|s| (format!("within this `{}`", t), s)), - ) - }) - .unwrap_or_default(); - - let OnUnimplementedNote { message, label, note, enclosing_scope } = - self.on_unimplemented_note(trait_ref, obligation); - let have_alt_message = message.is_some() || label.is_some(); - let is_try = self - .tcx - .sess - .source_map() - .span_to_snippet(span) - .map(|s| &s == "?") - .unwrap_or(false); - let is_from = format!("{}", trait_ref.print_only_trait_path()) - .starts_with("std::convert::From<"); - let (message, note) = if is_try && is_from { - ( - Some(format!( - "`?` couldn't convert the error to `{}`", - trait_ref.self_ty(), - )), - Some( - "the question mark operation (`?`) implicitly performs a \ - conversion on the error value using the `From` trait" - .to_owned(), - ), - ) - } else { - (message, note) - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0277, - "{}", - message.unwrap_or_else(|| format!( - "the trait bound `{}` is not satisfied{}", - trait_ref.without_const().to_predicate(), - post_message, - )) - ); - - let explanation = - if obligation.cause.code == ObligationCauseCode::MainFunctionType { - "consider using `()`, or a `Result`".to_owned() - } else { - format!( - "{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_ref.print_only_trait_path(), - trait_ref.self_ty(), - ) - }; - - if self.suggest_add_reference_to_arg( - &obligation, - &mut err, - &trait_ref, - points_at_arg, - have_alt_message, - ) { - self.note_obligation_cause(&mut err, obligation); - err.emit(); - return; - } - if let Some(ref s) = label { - // If it has a custom `#[rustc_on_unimplemented]` - // error message, let's display it as the label! - err.span_label(span, s.as_str()); - err.help(&explanation); - } else { - err.span_label(span, explanation); - } - if let Some((msg, span)) = type_def { - err.span_label(span, &msg); - } - if let Some(ref s) = note { - // If it has a custom `#[rustc_on_unimplemented]` note, let's display it - err.note(s.as_str()); - } - if let Some(ref s) = enclosing_scope { - let enclosing_scope_span = tcx.def_span( - tcx.hir() - .opt_local_def_id(obligation.cause.body_id) - .unwrap_or_else(|| { - tcx.hir().body_owner_def_id(hir::BodyId { - hir_id: obligation.cause.body_id, - }) - }), - ); - - err.span_label(enclosing_scope_span, s.as_str()); - } - - self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); - self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); - self.suggest_remove_reference(&obligation, &mut err, &trait_ref); - self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); - self.note_version_mismatch(&mut err, &trait_ref); - if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { - err.emit(); - return; - } - - // Try to report a help message - if !trait_ref.has_infer_types() - && self.predicate_can_apply(obligation.param_env, trait_ref) - { - // If a where-clause may be useful, remind the - // user that they can add it. - // - // don't display an on-unimplemented note, as - // these notes will often be of the form - // "the type `T` can't be frobnicated" - // which is somewhat confusing. - self.suggest_restricting_param_bound( - &mut err, - &trait_ref, - obligation.cause.body_id, - ); - } else { - if !have_alt_message { - // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(trait_ref); - self.report_similar_impl_candidates(impl_candidates, &mut err); - } - self.suggest_change_mut( - &obligation, - &mut err, - &trait_ref, - points_at_arg, - ); - } - - // If this error is due to `!: Trait` not implemented but `(): Trait` is - // implemented, and fallback has occurred, then it could be due to a - // variable that used to fallback to `()` now falling back to `!`. Issue a - // note informing about the change in behaviour. - if trait_predicate.skip_binder().self_ty().is_never() - && fallback_has_occurred - { - let predicate = trait_predicate.map_bound(|mut trait_pred| { - trait_pred.trait_ref.substs = self.tcx.mk_substs_trait( - self.tcx.mk_unit(), - &trait_pred.trait_ref.substs[1..], - ); - trait_pred - }); - let unit_obligation = Obligation { - predicate: ty::Predicate::Trait( - predicate, - hir::Constness::NotConst, - ), - ..obligation.clone() - }; - if self.predicate_may_hold(&unit_obligation) { - err.note( - "the trait is implemented for `()`. \ - Possibly this error has been caused by changes to \ - Rust's type-inference algorithm (see issue #48950 \ - \ - for more information). Consider whether you meant to use \ - the type `()` here instead.", - ); - } - } - - err - } - - ty::Predicate::Subtype(ref predicate) => { - // Errors for Subtype predicates show up as - // `FulfillmentErrorCode::CodeSubtypeError`, - // not selection error. - span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) - } - - ty::Predicate::RegionOutlives(ref predicate) => { - let predicate = self.resolve_vars_if_possible(predicate); - let err = self - .region_outlives_predicate(&obligation.cause, &predicate) - .err() - .unwrap(); - struct_span_err!( - self.tcx.sess, - span, - E0279, - "the requirement `{}` is not satisfied (`{}`)", - predicate, - err, - ) - } - - ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - struct_span_err!( - self.tcx.sess, - span, - E0280, - "the requirement `{}` is not satisfied", - predicate - ) - } - - ty::Predicate::ObjectSafe(trait_def_id) => { - let violations = self.tcx.object_safety_violations(trait_def_id); - report_object_safety_error(self.tcx, span, trait_def_id, violations) - } - - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - let found_kind = self.closure_kind(closure_def_id, closure_substs).unwrap(); - let closure_span = self - .tcx - .sess - .source_map() - .def_span(self.tcx.hir().span_if_local(closure_def_id).unwrap()); - let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id).unwrap(); - let mut err = struct_span_err!( - self.tcx.sess, - closure_span, - E0525, - "expected a closure that implements the `{}` trait, \ - but this closure only implements `{}`", - kind, - found_kind - ); - - err.span_label( - closure_span, - format!("this closure implements `{}`, not `{}`", found_kind, kind), - ); - err.span_label( - obligation.cause.span, - format!("the requirement to implement `{}` derives from here", kind), - ); - - // Additional context information explaining why the closure only implements - // a particular trait. - if let Some(tables) = self.in_progress_tables { - let tables = tables.borrow(); - match (found_kind, tables.closure_kind_origins().get(hir_id)) { - (ty::ClosureKind::FnOnce, Some((span, name))) => { - err.span_label( - *span, - format!( - "closure is `FnOnce` because it moves the \ - variable `{}` out of its environment", - name - ), - ); - } - (ty::ClosureKind::FnMut, Some((span, name))) => { - err.span_label( - *span, - format!( - "closure is `FnMut` because it mutates the \ - variable `{}` here", - name - ), - ); - } - _ => {} - } - } - - err.emit(); - return; - } - - ty::Predicate::WellFormed(ty) => { - if !self.tcx.sess.opts.debugging_opts.chalk { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); - } else { - // FIXME: we'll need a better message which takes into account - // which bounds actually failed to hold. - self.tcx.sess.struct_span_err( - span, - &format!("the type `{}` is not well-formed (chalk)", ty), - ) - } - } - - ty::Predicate::ConstEvaluatable(..) => { - // Errors for `ConstEvaluatable` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. - span_bug!( - span, - "const-evaluatable requirement gave wrong error: `{:?}`", - obligation - ) - } - } - } - - OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { - let found_trait_ref = self.resolve_vars_if_possible(&*found_trait_ref); - let expected_trait_ref = self.resolve_vars_if_possible(&*expected_trait_ref); - - if expected_trait_ref.self_ty().references_error() { - return; - } - - let found_trait_ty = found_trait_ref.self_ty(); - - let found_did = match found_trait_ty.kind { - ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), - ty::Adt(def, _) => Some(def.did), - _ => None, - }; - - let found_span = found_did - .and_then(|did| self.tcx.hir().span_if_local(did)) - .map(|sp| self.tcx.sess.source_map().def_span(sp)); // the sp could be an fn def - - if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { - // We check closures twice, with obligations flowing in different directions, - // but we want to complain about them only once. - return; - } - - self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); - - let found = match found_trait_ref.skip_binder().substs.type_at(1).kind { - ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], - _ => vec![ArgKind::empty()], - }; - - let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); - let expected = match expected_ty.kind { - ty::Tuple(ref tys) => tys - .iter() - .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))) - .collect(), - _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], - }; - - if found.len() == expected.len() { - self.report_closure_arg_mismatch( - span, - found_span, - found_trait_ref, - expected_trait_ref, - ) - } else { - let (closure_span, found) = found_did - .and_then(|did| self.tcx.hir().get_if_local(did)) - .map(|node| { - let (found_span, found) = self.get_fn_like_arguments(node); - (Some(found_span), found) - }) - .unwrap_or((found_span, found)); - - self.report_arg_count_mismatch( - span, - closure_span, - expected, - found, - found_trait_ty.is_closure(), - ) - } - } - - TraitNotObjectSafe(did) => { - let violations = self.tcx.object_safety_violations(did); - report_object_safety_error(self.tcx, span, did, violations) - } - - ConstEvalFailure(ErrorHandled::TooGeneric) => { - // In this instance, we have a const expression containing an unevaluated - // generic parameter. We have no idea whether this expression is valid or - // not (e.g. it might result in an error), but we don't want to just assume - // that it's okay, because that might result in post-monomorphisation time - // errors. The onus is really on the caller to provide values that it can - // prove are well-formed. - let mut err = self - .tcx - .sess - .struct_span_err(span, "constant expression depends on a generic parameter"); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - err.note("this may fail depending on what value the parameter takes"); - err - } - - // Already reported in the query. - ConstEvalFailure(ErrorHandled::Reported) => { - self.tcx.sess.delay_span_bug(span, "constant in type had an ignored error"); - return; - } - - Overflow => { - bug!("overflow should be handled before the `report_selection_error` path"); - } - }; - - self.note_obligation_cause(&mut err, obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); - - err.emit(); - } - - /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait - /// with the same path as `trait_ref`, a help message about - /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) { - let get_trait_impl = |trait_def_id| { - let mut trait_impl = None; - self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| { - if trait_impl.is_none() { - trait_impl = Some(impl_def_id); - } - }); - trait_impl - }; - let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); - let all_traits = self.tcx.all_traits(LOCAL_CRATE); - let traits_with_same_path: std::collections::BTreeSet<_> = all_traits - .iter() - .filter(|trait_def_id| **trait_def_id != trait_ref.def_id()) - .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path) - .collect(); - for trait_with_same_path in traits_with_same_path { - if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) { - let impl_span = self.tcx.def_span(impl_def_id); - err.span_help(impl_span, "trait impl with same name found"); - let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); - let crate_msg = format!( - "perhaps two different versions of crate `{}` are being used?", - trait_crate - ); - err.note(&crate_msg); - } - } - } - - fn mk_obligation_for_def_id( - &self, - def_id: DefId, - output_ty: Ty<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> PredicateObligation<'tcx> { - let new_trait_ref = - ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) }; - Obligation::new(cause, param_env, new_trait_ref.without_const().to_predicate()) - } -} - -pub fn recursive_type_with_infinite_size_error( - tcx: TyCtxt<'tcx>, - type_def_id: DefId, -) -> DiagnosticBuilder<'tcx> { - assert!(type_def_id.is_local()); - let span = tcx.hir().span_if_local(type_def_id).unwrap(); - let span = tcx.sess.source_map().def_span(span); - let mut err = struct_span_err!( - tcx.sess, - span, - E0072, - "recursive type `{}` has infinite size", - tcx.def_path_str(type_def_id) - ); - err.span_label(span, "recursive type has infinite size"); - err.help(&format!( - "insert indirection (e.g., a `Box`, `Rc`, or `&`) \ - at some point to make `{}` representable", - tcx.def_path_str(type_def_id) - )); - err } pub fn report_object_safety_error( @@ -1093,560 +104,3 @@ pub fn report_object_safety_error( err } - -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option, - ) { - // Unable to successfully determine, probably means - // insufficient type information, but could mean - // ambiguous impls. The latter *ought* to be a - // coherence violation, so we don't report it here. - - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let span = obligation.cause.span; - - debug!( - "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})", - predicate, obligation, body_id, obligation.cause.code, - ); - - // Ambiguity errors are often caused as fallout from earlier - // errors. So just ignore them if this infcx is tainted. - if self.is_tainted_by_errors() { - return; - } - - let mut err = match predicate { - ty::Predicate::Trait(ref data, _) => { - let trait_ref = data.to_poly_trait_ref(); - let self_ty = trait_ref.self_ty(); - debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); - - if predicate.references_error() { - return; - } - // Typically, this ambiguity should only happen if - // there are unresolved type inference variables - // (otherwise it would suggest a coherence - // failure). But given #21974 that is not necessarily - // the case -- we can have multiple where clauses that - // are only distinguished by a region, which results - // in an ambiguity even when all types are fully - // known, since we don't dispatch based on region - // relationships. - - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. We used to check for `self.tcx.sess.has_errors()` to - // avoid inundating the user with unnecessary errors, but we now - // check upstream for type errors and dont add the obligations to - // begin with in those cases. - if self - .tcx - .lang_items() - .sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) - { - self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit(); - return; - } - let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283); - err.note(&format!("cannot resolve `{}`", predicate)); - if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { - self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } else if let ( - Ok(ref snippet), - ObligationCauseCode::BindingObligation(ref def_id, _), - ) = - (self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code) - { - let generics = self.tcx.generics_of(*def_id); - if !generics.params.is_empty() && !snippet.ends_with('>') { - // FIXME: To avoid spurious suggestions in functions where type arguments - // where already supplied, we check the snippet to make sure it doesn't - // end with a turbofish. Ideally we would have access to a `PathSegment` - // instead. Otherwise we would produce the following output: - // - // error[E0283]: type annotations needed - // --> $DIR/issue-54954.rs:3:24 - // | - // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); - // | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - // | | - // | cannot infer type - // | help: consider specifying the type argument - // | in the function call: - // | `Tt::const_val::<[i8; 123]>::` - // ... - // LL | const fn const_val() -> usize { - // | --------- - required by this bound in `Tt::const_val` - // | - // = note: cannot resolve `_: Tt` - - err.span_suggestion( - span, - &format!( - "consider specifying the type argument{} in the function call", - if generics.params.len() > 1 { "s" } else { "" }, - ), - format!( - "{}::<{}>", - snippet, - generics - .params - .iter() - .map(|p| p.name.to_string()) - .collect::>() - .join(", ") - ), - Applicability::HasPlaceholders, - ); - } - } - err - } - - ty::Predicate::WellFormed(ty) => { - // Same hacky approach as above to avoid deluging user - // with error messages. - if ty.references_error() || self.tcx.sess.has_errors() { - return; - } - self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) - } - - ty::Predicate::Subtype(ref data) => { - if data.references_error() || self.tcx.sess.has_errors() { - // no need to overload user in such cases - return; - } - let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); - // both must be type variables, or the other would've been instantiated - assert!(a.is_ty_var() && b.is_ty_var()); - self.need_type_info_err(body_id, span, a, ErrorCode::E0282) - } - ty::Predicate::Projection(ref data) => { - let trait_ref = data.to_poly_trait_ref(self.tcx); - let self_ty = trait_ref.self_ty(); - if predicate.references_error() { - return; - } - let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); - err.note(&format!("cannot resolve `{}`", predicate)); - err - } - - _ => { - if self.tcx.sess.has_errors() { - return; - } - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0284, - "type annotations needed: cannot resolve `{}`", - predicate, - ); - err.span_label(span, &format!("cannot resolve `{}`", predicate)); - err - } - }; - self.note_obligation_cause(&mut err, obligation); - err.emit(); - } - - /// Returns `true` if the trait predicate may apply for *some* assignment - /// to the type parameters. - fn predicate_can_apply( - &self, - param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, - ) -> bool { - struct ParamToVarFolder<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - var_map: FxHashMap, Ty<'tcx>>, - } - - impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(ty::ParamTy { name, .. }) = ty.kind { - let infcx = self.infcx; - self.var_map.entry(ty).or_insert_with(|| { - infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), - span: DUMMY_SP, - }) - }) - } else { - ty.super_fold_with(self) - } - } - } - - self.probe(|_| { - let mut selcx = SelectionContext::new(self); - - let cleaned_pred = - pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); - - let cleaned_pred = super::project::normalize( - &mut selcx, - param_env, - ObligationCause::dummy(), - &cleaned_pred, - ) - .value; - - let obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - cleaned_pred.without_const().to_predicate(), - ); - - self.predicate_may_hold(&obligation) - }) - } - - fn note_obligation_cause( - &self, - err: &mut DiagnosticBuilder<'_>, - obligation: &PredicateObligation<'tcx>, - ) { - // First, attempt to add note to this error with an async-await-specific - // message, and fall back to regular note otherwise. - if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { - self.note_obligation_cause_code( - err, - &obligation.predicate, - &obligation.cause.code, - &mut vec![], - ); - self.suggest_unsized_bound_if_applicable(err, obligation); - } - } - - fn suggest_unsized_bound_if_applicable( - &self, - err: &mut DiagnosticBuilder<'_>, - obligation: &PredicateObligation<'tcx>, - ) { - if let ( - ty::Predicate::Trait(pred, _), - ObligationCauseCode::BindingObligation(item_def_id, span), - ) = (&obligation.predicate, &obligation.cause.code) - { - if let (Some(generics), true) = ( - self.tcx.hir().get_if_local(*item_def_id).as_ref().and_then(|n| n.generics()), - Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), - ) { - for param in generics.params { - if param.span == *span - && !param.bounds.iter().any(|bound| { - bound.trait_def_id() == self.tcx.lang_items().sized_trait() - }) - { - let (span, separator) = match param.bounds { - [] => (span.shrink_to_hi(), ":"), - [.., bound] => (bound.span().shrink_to_hi(), " + "), - }; - err.span_suggestion( - span, - "consider relaxing the implicit `Sized` restriction", - format!("{} ?Sized", separator), - Applicability::MachineApplicable, - ); - return; - } - } - } - } - } - - fn is_recursive_obligation( - &self, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, - cause_code: &ObligationCauseCode<'tcx>, - ) -> bool { - if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - - if obligated_types.iter().any(|ot| ot == &parent_trait_ref.skip_binder().self_ty()) { - return true; - } - } - false - } -} - -/// Summarizes information -#[derive(Clone)] -pub enum ArgKind { - /// An argument of non-tuple type. Parameters are (name, ty) - Arg(String, String), - - /// An argument of tuple type. For a "found" argument, the span is - /// the locationo in the source of the pattern. For a "expected" - /// argument, it will be None. The vector is a list of (name, ty) - /// strings for the components of the tuple. - Tuple(Option, Vec<(String, String)>), -} - -impl ArgKind { - fn empty() -> ArgKind { - ArgKind::Arg("_".to_owned(), "_".to_owned()) - } - - /// Creates an `ArgKind` from the expected type of an - /// argument. It has no name (`_`) and an optional source span. - pub fn from_expected_ty(t: Ty<'_>, span: Option) -> ArgKind { - match t.kind { - ty::Tuple(ref tys) => ArgKind::Tuple( - span, - tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::>(), - ), - _ => ArgKind::Arg("_".to_owned(), t.to_string()), - } - } -} - -/// Suggest restricting a type param with a new bound. -pub fn suggest_constraining_type_param( - tcx: TyCtxt<'_>, - generics: &hir::Generics<'_>, - err: &mut DiagnosticBuilder<'_>, - param_name: &str, - constraint: &str, - source_map: &SourceMap, - span: Span, - def_id: Option, -) -> bool { - const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound with"; - const MSG_RESTRICT_TYPE: &str = "consider restricting this type parameter with"; - const MSG_RESTRICT_TYPE_FURTHER: &str = "consider further restricting this type parameter with"; - - let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); - - let param = if let Some(param) = param { - param - } else { - return false; - }; - - if def_id == tcx.lang_items().sized_trait() { - // Type parameters are already `Sized` by default. - err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); - return true; - } - - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - err.span_help(param.span, &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint)); - - err.tool_only_span_suggestion( - param.span, - MSG_RESTRICT_BOUND_FURTHER, - format!("{} + {}", param_name, constraint), - Applicability::MachineApplicable, - ); - - return true; - } - - if generics.where_clause.predicates.is_empty() { - if let Some(bounds_span) = param.bounds_span() { - // If user has provided some bounds, suggest restricting them: - // - // fn foo(t: T) { ... } - // --- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - - err.span_help( - bounds_span, - &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint), - ); - - let span_hi = param.span.with_hi(span.hi()); - let span_with_colon = source_map.span_through_char(span_hi, ':'); - - if span_hi != param.span && span_with_colon != span_hi { - err.tool_only_span_suggestion( - span_with_colon, - MSG_RESTRICT_BOUND_FURTHER, - format!("{}: {} + ", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } else { - // If user hasn't provided any bounds, suggest adding a new one: - // - // fn foo(t: T) { ... } - // - help: consider restricting this type parameter with `T: Foo` - - err.span_help( - param.span, - &format!("{} `{}: {}`", MSG_RESTRICT_TYPE, param_name, constraint), - ); - - err.tool_only_span_suggestion( - param.span, - MSG_RESTRICT_TYPE, - format!("{}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - - true - } else { - // This part is a bit tricky, because using the `where` clause user can - // provide zero, one or many bounds for the same type parameter, so we - // have following cases to consider: - // - // 1) When the type parameter has been provided zero bounds - // - // Message: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - help: consider restricting this type parameter with `where X: Bar` - // - // Suggestion: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - insert: `, T: Zar` - - let mut param_spans = Vec::new(); - - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, bounded_ty, .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); - } - } - } - } - } - - let where_clause_span = - generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(); - - match ¶m_spans[..] { - &[] => { - err.span_help( - param.span, - &format!("{} `where {}: {}`", MSG_RESTRICT_TYPE, param_name, constraint), - ); - - err.tool_only_span_suggestion( - where_clause_span, - MSG_RESTRICT_TYPE, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - - &[¶m_span] => { - err.span_help( - param_span, - &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint), - ); - - let span_hi = param_span.with_hi(span.hi()); - let span_with_colon = source_map.span_through_char(span_hi, ':'); - - if span_hi != param_span && span_with_colon != span_hi { - err.tool_only_span_suggestion( - span_with_colon, - MSG_RESTRICT_BOUND_FURTHER, - format!("{}: {} +", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } - - _ => { - err.span_help( - param.span, - &format!( - "{} `where {}: {}`", - MSG_RESTRICT_TYPE_FURTHER, param_name, constraint, - ), - ); - - err.tool_only_span_suggestion( - where_clause_span, - MSG_RESTRICT_BOUND_FURTHER, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } - - true - } -} diff --git a/src/librustc_infer/traits/error_reporting/on_unimplemented.rs b/src/librustc_infer/traits/error_reporting/on_unimplemented.rs deleted file mode 100644 index 87c1107bd42..00000000000 --- a/src/librustc_infer/traits/error_reporting/on_unimplemented.rs +++ /dev/null @@ -1,223 +0,0 @@ -use super::{ - ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, -}; -use crate::infer::InferCtxt; -use rustc::ty::subst::Subst; -use rustc::ty::{self, GenericParamDefKind}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::symbol::sym; - -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - fn impl_similar_to( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> Option { - let tcx = self.tcx; - let param_env = obligation.param_env; - let trait_ref = tcx.erase_late_bound_regions(&trait_ref); - let trait_self_ty = trait_ref.self_ty(); - - let mut self_match_impls = vec![]; - let mut fuzzy_match_impls = vec![]; - - self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { - let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); - let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); - - let impl_self_ty = impl_trait_ref.self_ty(); - - if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { - self_match_impls.push(def_id); - - if trait_ref - .substs - .types() - .skip(1) - .zip(impl_trait_ref.substs.types().skip(1)) - .all(|(u, v)| self.fuzzy_match_tys(u, v)) - { - fuzzy_match_impls.push(def_id); - } - } - }); - - let impl_def_id = if self_match_impls.len() == 1 { - self_match_impls[0] - } else if fuzzy_match_impls.len() == 1 { - fuzzy_match_impls[0] - } else { - return None; - }; - - tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) - } - - /// Used to set on_unimplemented's `ItemContext` - /// to be the enclosing (async) block/function/closure - fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { - let hir = &self.tcx.hir(); - let node = hir.find(hir_id)?; - match &node { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { - self.describe_generator(*body_id).or_else(|| { - Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { - "an async function" - } else { - "a function" - }) - }) - } - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(body_id)), - .. - }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")), - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Method(sig, body_id), - .. - }) => self.describe_generator(*body_id).or_else(|| { - Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { - "an async method" - } else { - "a method" - }) - }), - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), - .. - }) => self.describe_generator(*body_id).or_else(|| { - Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) - }), - hir::Node::Expr(hir::Expr { .. }) => { - let parent_hid = hir.get_parent_node(hir_id); - if parent_hid != hir_id { - return self.describe_enclosure(parent_hid); - } else { - None - } - } - _ => None, - } - } - - crate fn on_unimplemented_note( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> OnUnimplementedNote { - let def_id = - self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); - let trait_ref = *trait_ref.skip_binder(); - - let mut flags = vec![]; - flags.push(( - sym::item_context, - self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), - )); - - match obligation.cause.code { - ObligationCauseCode::BuiltinDerivedObligation(..) - | ObligationCauseCode::ImplDerivedObligation(..) => {} - _ => { - // this is a "direct", user-specified, rather than derived, - // obligation. - flags.push((sym::direct, None)); - } - } - - if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code { - // FIXME: maybe also have some way of handling methods - // from other traits? That would require name resolution, - // which we might want to be some sort of hygienic. - // - // Currently I'm leaving it for what I need for `try`. - if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { - let method = self.tcx.item_name(item); - flags.push((sym::from_method, None)); - flags.push((sym::from_method, Some(method.to_string()))); - } - } - if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { - flags.push((sym::parent_trait, Some(t))); - } - - if let Some(k) = obligation.cause.span.desugaring_kind() { - flags.push((sym::from_desugaring, None)); - flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); - } - let generics = self.tcx.generics_of(def_id); - let self_ty = trait_ref.self_ty(); - // This is also included through the generics list as `Self`, - // but the parser won't allow you to use it - flags.push((sym::_Self, Some(self_ty.to_string()))); - if let Some(def) = self_ty.ty_adt_def() { - // We also want to be able to select self's original - // signature with no type arguments resolved - flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); - } - - for param in generics.params.iter() { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize].to_string() - } - GenericParamDefKind::Lifetime => continue, - }; - let name = param.name; - flags.push((name, Some(value))); - } - - if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { - flags.push((sym::crate_local, None)); - } - - // Allow targeting all integers using `{integral}`, even if the exact type was resolved - if self_ty.is_integral() { - flags.push((sym::_Self, Some("{integral}".to_owned()))); - } - - if let ty::Array(aty, len) = self_ty.kind { - flags.push((sym::_Self, Some("[]".to_owned()))); - flags.push((sym::_Self, Some(format!("[{}]", aty)))); - if let Some(def) = aty.ty_adt_def() { - // We also want to be able to select the array's type's original - // signature with no type arguments resolved - flags.push(( - sym::_Self, - Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), - )); - let tcx = self.tcx; - if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { - flags.push(( - sym::_Self, - Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), - )); - } else { - flags.push(( - sym::_Self, - Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), - )); - } - } - } - if let ty::Dynamic(traits, _) = self_ty.kind { - for t in *traits.skip_binder() { - match t { - ty::ExistentialPredicate::Trait(trait_ref) => { - flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) - } - _ => {} - } - } - } - - if let Ok(Some(command)) = - OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) - { - command.evaluate(self.tcx, trait_ref, &flags[..]) - } else { - OnUnimplementedNote::default() - } - } -} diff --git a/src/librustc_infer/traits/error_reporting/suggestions.rs b/src/librustc_infer/traits/error_reporting/suggestions.rs deleted file mode 100644 index f1206ddf909..00000000000 --- a/src/librustc_infer/traits/error_reporting/suggestions.rs +++ /dev/null @@ -1,1711 +0,0 @@ -use super::{ - ArgKind, EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, - PredicateObligation, -}; - -use crate::infer::InferCtxt; -use crate::traits::error_reporting::suggest_constraining_type_param; - -use rustc::ty::TypeckTables; -use rustc::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc_errors::{ - error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style, -}; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; -use rustc_span::symbol::{kw, sym}; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; -use std::fmt; - -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - crate fn suggest_restricting_param_bound( - &self, - mut err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::PolyTraitRef<'_>, - body_id: hir::HirId, - ) { - let self_ty = trait_ref.self_ty(); - let (param_ty, projection) = match &self_ty.kind { - ty::Param(_) => (true, None), - ty::Projection(projection) => (false, Some(projection)), - _ => return, - }; - - let suggest_restriction = - |generics: &hir::Generics<'_>, msg, err: &mut DiagnosticBuilder<'_>| { - let span = generics.where_clause.span_for_predicates_or_empty_place(); - if !span.from_expansion() && span.desugaring_kind().is_none() { - err.span_suggestion( - generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(), - &format!("consider further restricting {}", msg), - format!( - "{} {} ", - if !generics.where_clause.predicates.is_empty() { - "," - } else { - " where" - }, - trait_ref.without_const().to_predicate(), - ), - Applicability::MachineApplicable, - ); - } - }; - - // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we - // don't suggest `T: Sized + ?Sized`. - let mut hir_id = body_id; - while let Some(node) = self.tcx.hir().find(hir_id) { - match node { - hir::Node::TraitItem(hir::TraitItem { - generics, - kind: hir::TraitItemKind::Method(..), - .. - }) if param_ty && self_ty == self.tcx.types.self_param => { - // Restricting `Self` for a single method. - suggest_restriction(&generics, "`Self`", err); - return; - } - - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) - | hir::Node::TraitItem(hir::TraitItem { - generics, - kind: hir::TraitItemKind::Method(..), - .. - }) - | hir::Node::ImplItem(hir::ImplItem { - generics, - kind: hir::ImplItemKind::Method(..), - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, generics, _, _), - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { generics, .. }, .. - }) if projection.is_some() => { - // Missing associated type bound. - suggest_restriction(&generics, "the associated type", err); - return; - } - - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, generics), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Enum(_, generics), span, .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Union(_, generics), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, generics, ..), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { generics, .. }, - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(_, generics, _), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::TyAlias(_, generics), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::TraitAlias(generics, _), - span, - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), - span, - .. - }) - | hir::Node::TraitItem(hir::TraitItem { generics, span, .. }) - | hir::Node::ImplItem(hir::ImplItem { generics, span, .. }) - if param_ty => - { - // Missing generic type parameter bound. - let param_name = self_ty.to_string(); - let constraint = trait_ref.print_only_trait_path().to_string(); - if suggest_constraining_type_param( - self.tcx, - generics, - &mut err, - ¶m_name, - &constraint, - self.tcx.sess.source_map(), - *span, - Some(trait_ref.def_id()), - ) { - return; - } - } - - hir::Node::Crate => return, - - _ => {} - } - - hir_id = self.tcx.hir().get_parent_item(hir_id); - } - } - - /// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a - /// suggestion to borrow the initializer in order to use have a slice instead. - crate fn suggest_borrow_on_unsized_slice( - &self, - code: &ObligationCauseCode<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, - ) { - if let &ObligationCauseCode::VariableType(hir_id) = code { - let parent_node = self.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(ref local)) = self.tcx.hir().find(parent_node) { - if let Some(ref expr) = local.init { - if let hir::ExprKind::Index(_, _) = expr.kind { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(expr.span) { - err.span_suggestion( - expr.span, - "consider borrowing here", - format!("&{}", snippet), - Applicability::MachineApplicable, - ); - } - } - } - } - } - } - - /// Given a closure's `DefId`, return the given name of the closure. - /// - /// This doesn't account for reassignments, but it's only used for suggestions. - crate fn get_closure_name( - &self, - def_id: DefId, - err: &mut DiagnosticBuilder<'_>, - msg: &str, - ) -> Option { - let get_name = - |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind<'_>| -> Option { - // Get the local name of this closure. This can be inaccurate because - // of the possibility of reassignment, but this should be good enough. - match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { - Some(format!("{}", name)) - } - _ => { - err.note(&msg); - None - } - } - }; - - let hir = self.tcx.hir(); - let hir_id = hir.as_local_hir_id(def_id)?; - let parent_node = hir.get_parent_node(hir_id); - match hir.find(parent_node) { - Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { - get_name(err, &local.pat.kind) - } - // Different to previous arm because one is `&hir::Local` and the other - // is `P`. - Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind), - _ => return None, - } - } - - /// We tried to apply the bound to an `fn` or closure. Check whether calling it would - /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling - /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. - crate fn suggest_fn_call( - &self, - obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::Binder>, - points_at_arg: bool, - ) { - let self_ty = trait_ref.self_ty(); - let (def_id, output_ty, callable) = match self_ty.kind { - ty::Closure(def_id, substs) => { - (def_id, self.closure_sig(def_id, substs).output(), "closure") - } - ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), - _ => return, - }; - let msg = format!("use parentheses to call the {}", callable); - - let obligation = self.mk_obligation_for_def_id( - trait_ref.def_id(), - output_ty.skip_binder(), - obligation.cause.clone(), - obligation.param_env, - ); - - match self.evaluate_obligation(&obligation) { - Ok(EvaluationResult::EvaluatedToOk) - | Ok(EvaluationResult::EvaluatedToOkModuloRegions) - | Ok(EvaluationResult::EvaluatedToAmbig) => {} - _ => return, - } - let hir = self.tcx.hir(); - // Get the name of the callable and the arguments to be used in the suggestion. - let snippet = match hir.get_if_local(def_id) { - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(_, decl, _, span, ..), - .. - })) => { - err.span_label(*span, "consider calling this closure"); - let name = match self.get_closure_name(def_id, err, &msg) { - Some(name) => name, - None => return, - }; - let args = decl.inputs.iter().map(|_| "_").collect::>().join(", "); - format!("{}({})", name, args) - } - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(.., body_id), - .. - })) => { - err.span_label(ident.span, "consider calling this function"); - let body = hir.body(*body_id); - let args = body - .params - .iter() - .map(|arg| match &arg.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - // FIXME: provide a better suggestion when encountering `SelfLower`, it - // should suggest a method call. - if ident.name != kw::SelfLower => ident.to_string(), - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - format!("{}({})", ident, args) - } - _ => return, - }; - if points_at_arg { - // When the obligation error has been ensured to have been caused by - // an argument, the `obligation.cause.span` points at the expression - // of the argument, so we can provide a suggestion. This is signaled - // by `points_at_arg`. Otherwise, we give a more general note. - err.span_suggestion( - obligation.cause.span, - &msg, - snippet, - Applicability::HasPlaceholders, - ); - } else { - err.help(&format!("{}: `{}`", msg, snippet)); - } - } - - crate fn suggest_add_reference_to_arg( - &self, - obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, - trait_ref: &ty::Binder>, - points_at_arg: bool, - has_custom_message: bool, - ) -> bool { - if !points_at_arg { - return false; - } - - let span = obligation.cause.span; - let param_env = obligation.param_env; - let trait_ref = trait_ref.skip_binder(); - - if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code { - // Try to apply the original trait binding obligation by borrowing. - let self_ty = trait_ref.self_ty(); - let found = self_ty.to_string(); - let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty); - let substs = self.tcx.mk_substs_trait(new_self_ty, &[]); - let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs); - let new_obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - new_trait_ref.without_const().to_predicate(), - ); - if self.predicate_must_hold_modulo_regions(&new_obligation) { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - // We have a very specific type of error, where just borrowing this argument - // might solve the problem. In cases like this, the important part is the - // original type obligation, not the last one that failed, which is arbitrary. - // Because of this, we modify the error to refer to the original obligation and - // return early in the caller. - let msg = format!( - "the trait bound `{}: {}` is not satisfied", - found, - obligation.parent_trait_ref.skip_binder().print_only_trait_path(), - ); - if has_custom_message { - err.note(&msg); - } else { - err.message = vec![(msg, Style::NoStyle)]; - } - if snippet.starts_with('&') { - // This is already a literal borrow and the obligation is failing - // somewhere else in the obligation chain. Do not suggest non-sense. - return false; - } - err.span_label( - span, - &format!( - "expected an implementor of trait `{}`", - obligation.parent_trait_ref.skip_binder().print_only_trait_path(), - ), - ); - err.span_suggestion( - span, - "consider borrowing here", - format!("&{}", snippet), - Applicability::MaybeIncorrect, - ); - return true; - } - } - } - false - } - - /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, - /// suggest removing these references until we reach a type that implements the trait. - crate fn suggest_remove_reference( - &self, - obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, - trait_ref: &ty::Binder>, - ) { - let trait_ref = trait_ref.skip_binder(); - let span = obligation.cause.span; - - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let refs_number = - snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count(); - if let Some('\'') = - snippet.chars().filter(|c| !c.is_whitespace()).skip(refs_number).next() - { - // Do not suggest removal of borrow from type arguments. - return; - } - - let mut trait_type = trait_ref.self_ty(); - - for refs_remaining in 0..refs_number { - if let ty::Ref(_, t_type, _) = trait_type.kind { - trait_type = t_type; - - let new_obligation = self.mk_obligation_for_def_id( - trait_ref.def_id, - trait_type, - ObligationCause::dummy(), - obligation.param_env, - ); - - if self.predicate_may_hold(&new_obligation) { - let sp = self - .tcx - .sess - .source_map() - .span_take_while(span, |c| c.is_whitespace() || *c == '&'); - - let remove_refs = refs_remaining + 1; - - let msg = if remove_refs == 1 { - "consider removing the leading `&`-reference".to_string() - } else { - format!("consider removing {} leading `&`-references", remove_refs) - }; - - err.span_suggestion_short( - sp, - &msg, - String::new(), - Applicability::MachineApplicable, - ); - break; - } - } else { - break; - } - } - } - } - - /// Check if the trait bound is implemented for a different mutability and note it in the - /// final error. - crate fn suggest_change_mut( - &self, - obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, - trait_ref: &ty::Binder>, - points_at_arg: bool, - ) { - let span = obligation.cause.span; - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let refs_number = - snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count(); - if let Some('\'') = - snippet.chars().filter(|c| !c.is_whitespace()).skip(refs_number).next() - { - // Do not suggest removal of borrow from type arguments. - return; - } - let trait_ref = self.resolve_vars_if_possible(trait_ref); - if trait_ref.has_infer_types() { - // Do not ICE while trying to find if a reborrow would succeed on a trait with - // unresolved bindings. - return; - } - - if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { - let trait_type = match mutability { - hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type), - hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type), - }; - - let new_obligation = self.mk_obligation_for_def_id( - trait_ref.skip_binder().def_id, - trait_type, - ObligationCause::dummy(), - obligation.param_env, - ); - - if self.evaluate_obligation_no_overflow(&new_obligation).must_apply_modulo_regions() - { - let sp = self - .tcx - .sess - .source_map() - .span_take_while(span, |c| c.is_whitespace() || *c == '&'); - if points_at_arg && mutability == hir::Mutability::Not && refs_number > 0 { - err.span_suggestion( - sp, - "consider changing this borrow's mutability", - "&mut ".to_string(), - Applicability::MachineApplicable, - ); - } else { - err.note(&format!( - "`{}` is implemented for `{:?}`, but not for `{:?}`", - trait_ref.print_only_trait_path(), - trait_type, - trait_ref.skip_binder().self_ty(), - )); - } - } - } - } - } - - crate fn suggest_semicolon_removal( - &self, - obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, - span: Span, - trait_ref: &ty::Binder>, - ) { - let hir = self.tcx.hir(); - let parent_node = hir.get_parent_node(obligation.cause.body_id); - let node = hir.find(parent_node); - if let Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(sig, _, body_id), .. - })) = node - { - let body = hir.body(*body_id); - if let hir::ExprKind::Block(blk, _) = &body.value.kind { - if sig.decl.output.span().overlaps(span) - && blk.expr.is_none() - && "()" == &trait_ref.self_ty().to_string() - { - // FIXME(estebank): When encountering a method with a trait - // bound not satisfied in the return type with a body that has - // no return, suggest removal of semicolon on last statement. - // Once that is added, close #54771. - if let Some(ref stmt) = blk.stmts.last() { - let sp = self.tcx.sess.source_map().end_point(stmt.span); - err.span_label(sp, "consider removing this semicolon"); - } - } - } - } - } - - /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if - /// applicable and signal that the error has been expanded appropriately and needs to be - /// emitted. - crate fn suggest_impl_trait( - &self, - err: &mut DiagnosticBuilder<'tcx>, - span: Span, - obligation: &PredicateObligation<'tcx>, - trait_ref: &ty::Binder>, - ) -> bool { - match obligation.cause.code.peel_derives() { - // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`. - ObligationCauseCode::SizedReturnType => {} - _ => return false, - } - - let hir = self.tcx.hir(); - let parent_node = hir.get_parent_node(obligation.cause.body_id); - let node = hir.find(parent_node); - let (sig, body_id) = if let Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(sig, _, body_id), - .. - })) = node - { - (sig, body_id) - } else { - return false; - }; - let body = hir.body(*body_id); - let trait_ref = self.resolve_vars_if_possible(trait_ref); - let ty = trait_ref.skip_binder().self_ty(); - let is_object_safe = match ty.kind { - ty::Dynamic(predicates, _) => { - // If the `dyn Trait` is not object safe, do not suggest `Box`. - predicates - .principal_def_id() - .map_or(true, |def_id| self.tcx.object_safety_violations(def_id).is_empty()) - } - // We only want to suggest `impl Trait` to `dyn Trait`s. - // For example, `fn foo() -> str` needs to be filtered out. - _ => return false, - }; - - let ret_ty = if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { - ret_ty - } else { - return false; - }; - - // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for - // cases like `fn foo() -> (dyn Trait, i32) {}`. - // Recursively look for `TraitObject` types and if there's only one, use that span to - // suggest `impl Trait`. - - // Visit to make sure there's a single `return` type to suggest `impl Trait`, - // otherwise suggest using `Box` or an enum. - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); - - let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); - - let mut ret_types = visitor - .returns - .iter() - .filter_map(|expr| tables.node_type_opt(expr.hir_id)) - .map(|ty| self.resolve_vars_if_possible(&ty)); - let (last_ty, all_returns_have_same_type) = ret_types.clone().fold( - (None, true), - |(last_ty, mut same): (std::option::Option>, bool), ty| { - let ty = self.resolve_vars_if_possible(&ty); - same &= last_ty.map_or(true, |last_ty| last_ty == ty) && ty.kind != ty::Error; - (Some(ty), same) - }, - ); - let all_returns_conform_to_trait = - if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { - match ty_ret_ty.kind { - ty::Dynamic(predicates, _) => { - let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); - let param_env = ty::ParamEnv::empty(); - ret_types.all(|returned_ty| { - predicates.iter().all(|predicate| { - let pred = predicate.with_self_ty(self.tcx, returned_ty); - let obl = Obligation::new(cause.clone(), param_env, pred); - self.predicate_may_hold(&obl) - }) - }) - } - _ => false, - } - } else { - true - }; - - let (snippet, last_ty) = - if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( - // Verify that we're dealing with a return `dyn Trait` - ret_ty.span.overlaps(span), - &ret_ty.kind, - self.tcx.sess.source_map().span_to_snippet(ret_ty.span), - // If any of the return types does not conform to the trait, then we can't - // suggest `impl Trait` nor trait objects, it is a type mismatch error. - all_returns_conform_to_trait, - last_ty, - ) { - (snippet, last_ty) - } else { - return false; - }; - err.code(error_code!(E0746)); - err.set_primary_message("return type cannot have an unboxed trait object"); - err.children.clear(); - let impl_trait_msg = "for information on `impl Trait`, see \ - "; - let trait_obj_msg = "for information on trait objects, see \ - "; - let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); - let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; - if all_returns_have_same_type { - // Suggest `-> impl Trait`. - err.span_suggestion( - ret_ty.span, - &format!( - "return `impl {1}` instead, as all return paths are of type `{}`, \ - which implements `{1}`", - last_ty, trait_obj, - ), - format!("impl {}", trait_obj), - Applicability::MachineApplicable, - ); - err.note(impl_trait_msg); - } else { - if is_object_safe { - // Suggest `-> Box` and `Box::new(returned_value)`. - // Get all the return values and collect their span and suggestion. - let mut suggestions = visitor - .returns - .iter() - .map(|expr| { - ( - expr.span, - format!( - "Box::new({})", - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap() - ), - ) - }) - .collect::>(); - // Add the suggestion for the return type. - suggestions.push((ret_ty.span, format!("Box", trait_obj))); - err.multipart_suggestion( - "return a boxed trait object instead", - suggestions, - Applicability::MaybeIncorrect, - ); - } else { - // This is currently not possible to trigger because E0038 takes precedence, but - // leave it in for completeness in case anything changes in an earlier stage. - err.note(&format!( - "if trait `{}` was object safe, you could return a trait object", - trait_obj, - )); - } - err.note(trait_obj_msg); - err.note(&format!( - "if all the returned values were of the same type you could use \ - `impl {}` as the return type", - trait_obj, - )); - err.note(impl_trait_msg); - err.note("you can create a new `enum` with a variant for each returned type"); - } - true - } - - crate fn point_at_returns_when_relevant( - &self, - err: &mut DiagnosticBuilder<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) { - match obligation.cause.code.peel_derives() { - ObligationCauseCode::SizedReturnType => {} - _ => return, - } - - let hir = self.tcx.hir(); - let parent_node = hir.get_parent_node(obligation.cause.body_id); - let node = hir.find(parent_node); - if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) = - node - { - let body = hir.body(*body_id); - // Point at all the `return`s in the function as they have failed trait bounds. - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); - let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); - for expr in &visitor.returns { - if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { - let ty = self.resolve_vars_if_possible(&returned_ty); - err.span_label(expr.span, &format!("this returned value is of type `{}`", ty)); - } - } - } - } - - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - pub fn get_fn_like_arguments(&self, node: Node<'_>) -> (Span, Vec) { - match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(_, ref _decl, id, span, _), - .. - }) => ( - self.tcx.sess.source_map().def_span(span), - self.tcx - .hir() - .body(id) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = - *arg.pat - { - ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - let snippet = self - .tcx - .sess - .source_map() - .span_to_snippet(pat.span) - .unwrap(); - (snippet, "_".to_owned()) - }) - .collect::>(), - ) - } else { - let name = - self.tcx.sess.source_map().span_to_snippet(arg.pat.span).unwrap(); - ArgKind::Arg(name, "_".to_owned()) - } - }) - .collect::>(), - ), - Node::Item(&hir::Item { span, kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { - span, - kind: hir::ImplItemKind::Method(ref sig, _), - .. - }) - | Node::TraitItem(&hir::TraitItem { - span, - kind: hir::TraitItemKind::Method(ref sig, _), - .. - }) => ( - self.tcx.sess.source_map().def_span(span), - sig.decl - .inputs - .iter() - .map(|arg| match arg.clone().kind { - hir::TyKind::Tup(ref tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::>(), - ), - Node::Ctor(ref variant_data) => { - let span = variant_data - .ctor_hir_id() - .map(|hir_id| self.tcx.hir().span(hir_id)) - .unwrap_or(DUMMY_SP); - let span = self.tcx.sess.source_map().def_span(span); - - (span, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {:?}", node), - } - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - pub fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option, - expected_args: Vec, - found_args: Vec, - is_closure: bool, - ) -> DiagnosticBuilder<'tcx> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = match &other[..] { - &[ArgKind::Tuple(..)] => true, - _ => false, - }; - match (arg_length, arguments.get(0)) { - (1, Some(&ArgKind::Tuple(_, ref fields))) => { - format!("a single {}-tuple as argument", fields.len()) - } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), - } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - - err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {}", found_str)); - - // move |_| { ... } - // ^^^^^^^^-- def_span - // - // move |_| { ... } - // ^^^^^-- prefix - let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); - // move |_| { ... } - // ^^^-- pipe_span - let pipe_span = - if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion( - pipe_span, - &format!( - "consider changing the closure to take and ignore the expected argument{}", - if expected_args.len() < 2 { "" } else { "s" } - ), - format!("|{}|", underscores), - Applicability::MachineApplicable, - ); - } - - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::>() - .join(", "); - err.span_suggestion( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{}|", sugg), - Applicability::MachineApplicable, - ); - } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] { - if fields.len() == found_args.len() && is_closure { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::>() - .join(", ") - ) - } else { - String::new() - }, - ); - err.span_suggestion( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); - } - } - } - - err - } - - crate fn report_closure_arg_mismatch( - &self, - span: Span, - found_span: Option, - expected_ref: ty::PolyTraitRef<'tcx>, - found: ty::PolyTraitRef<'tcx>, - ) -> DiagnosticBuilder<'tcx> { - crate fn build_fn_sig_string<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: &ty::TraitRef<'tcx>, - ) -> String { - let inputs = trait_ref.substs.type_at(1); - let sig = if let ty::Tuple(inputs) = inputs.kind { - tcx.mk_fn_sig( - inputs.iter().map(|k| k.expect_ty()), - tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), - false, - hir::Unsafety::Normal, - ::rustc_target::spec::abi::Abi::Rust, - ) - } else { - tcx.mk_fn_sig( - ::std::iter::once(inputs), - tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), - false, - hir::Unsafety::Normal, - ::rustc_target::spec::abi::Abi::Rust, - ) - }; - ty::Binder::bind(sig).to_string() - } - - let argument_is_closure = expected_ref.skip_binder().substs.type_at(0).is_closure(); - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0631, - "type mismatch in {} arguments", - if argument_is_closure { "closure" } else { "function" } - ); - - let found_str = format!( - "expected signature of `{}`", - build_fn_sig_string(self.tcx, found.skip_binder()) - ); - err.span_label(span, found_str); - - let found_span = found_span.unwrap_or(span); - let expected_str = format!( - "found signature of `{}`", - build_fn_sig_string(self.tcx, expected_ref.skip_binder()) - ); - err.span_label(found_span, expected_str); - - err - } -} - -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - crate fn suggest_fully_qualified_path( - &self, - err: &mut DiagnosticBuilder<'_>, - def_id: DefId, - span: Span, - trait_ref: DefId, - ) { - if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) { - if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind { - err.note(&format!( - "{}s cannot be accessed directly on a `trait`, they can only be \ - accessed through a specific `impl`", - assoc_item.kind.suggestion_descr(), - )); - err.span_suggestion( - span, - "use the fully qualified path to an implementation", - format!("::{}", self.tcx.def_path_str(trait_ref), assoc_item.ident), - Applicability::HasPlaceholders, - ); - } - } - } - - /// Adds an async-await specific note to the diagnostic when the future does not implement - /// an auto trait because of a captured type. - /// - /// ```ignore (diagnostic) - /// note: future does not implement `Qux` as this value is used across an await - /// --> $DIR/issue-64130-3-other.rs:17:5 - /// | - /// LL | let x = Foo; - /// | - has type `Foo` - /// LL | baz().await; - /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later - /// LL | } - /// | - `x` is later dropped here - /// ``` - /// - /// When the diagnostic does not implement `Send` or `Sync` specifically, then the diagnostic - /// is "replaced" with a different message and a more specific error. - /// - /// ```ignore (diagnostic) - /// error: future cannot be sent between threads safely - /// --> $DIR/issue-64130-2-send.rs:21:5 - /// | - /// LL | fn is_send(t: T) { } - /// | ------- ---- required by this bound in `is_send` - /// ... - /// LL | is_send(bar()); - /// | ^^^^^^^ future returned by `bar` is not send - /// | - /// = help: within `impl std::future::Future`, the trait `std::marker::Send` is not - /// implemented for `Foo` - /// note: future is not send as this value is used across an await - /// --> $DIR/issue-64130-2-send.rs:15:5 - /// | - /// LL | let x = Foo; - /// | - has type `Foo` - /// LL | baz().await; - /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later - /// LL | } - /// | - `x` is later dropped here - /// ``` - /// - /// Returns `true` if an async-await specific note was added to the diagnostic. - crate fn maybe_note_obligation_cause_for_async_await( - &self, - err: &mut DiagnosticBuilder<'_>, - obligation: &PredicateObligation<'tcx>, - ) -> bool { - debug!( - "maybe_note_obligation_cause_for_async_await: obligation.predicate={:?} \ - obligation.cause.span={:?}", - obligation.predicate, obligation.cause.span - ); - let source_map = self.tcx.sess.source_map(); - - // Attempt to detect an async-await error by looking at the obligation causes, looking - // for a generator to be present. - // - // When a future does not implement a trait because of a captured type in one of the - // generators somewhere in the call stack, then the result is a chain of obligations. - // - // Given a `async fn` A that calls a `async fn` B which captures a non-send type and that - // future is passed as an argument to a function C which requires a `Send` type, then the - // chain looks something like this: - // - // - `BuiltinDerivedObligation` with a generator witness (B) - // - `BuiltinDerivedObligation` with a generator (B) - // - `BuiltinDerivedObligation` with `std::future::GenFuture` (B) - // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) - // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) - // - `BuiltinDerivedObligation` with a generator witness (A) - // - `BuiltinDerivedObligation` with a generator (A) - // - `BuiltinDerivedObligation` with `std::future::GenFuture` (A) - // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) - // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) - // - `BindingObligation` with `impl_send (Send requirement) - // - // The first obligation in the chain is the most useful and has the generator that captured - // the type. The last generator has information about where the bound was introduced. At - // least one generator should be present for this diagnostic to be modified. - let (mut trait_ref, mut target_ty) = match obligation.predicate { - ty::Predicate::Trait(p, _) => { - (Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty())) - } - _ => (None, None), - }; - let mut generator = None; - let mut last_generator = None; - let mut next_code = Some(&obligation.cause.code); - while let Some(code) = next_code { - debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code); - match code { - ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) - | ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { - let ty = derived_obligation.parent_trait_ref.self_ty(); - debug!( - "maybe_note_obligation_cause_for_async_await: \ - parent_trait_ref={:?} self_ty.kind={:?}", - derived_obligation.parent_trait_ref, ty.kind - ); - - match ty.kind { - ty::Generator(did, ..) => { - generator = generator.or(Some(did)); - last_generator = Some(did); - } - ty::GeneratorWitness(..) => {} - _ if generator.is_none() => { - trait_ref = Some(*derived_obligation.parent_trait_ref.skip_binder()); - target_ty = Some(ty); - } - _ => {} - } - - next_code = Some(derived_obligation.parent_code.as_ref()); - } - _ => break, - } - } - - // Only continue if a generator was found. - debug!( - "maybe_note_obligation_cause_for_async_await: generator={:?} trait_ref={:?} \ - target_ty={:?}", - generator, trait_ref, target_ty - ); - let (generator_did, trait_ref, target_ty) = match (generator, trait_ref, target_ty) { - (Some(generator_did), Some(trait_ref), Some(target_ty)) => { - (generator_did, trait_ref, target_ty) - } - _ => return false, - }; - - let span = self.tcx.def_span(generator_did); - - // Do not ICE on closure typeck (#66868). - if self.tcx.hir().as_local_hir_id(generator_did).is_none() { - return false; - } - - // Get the tables from the infcx if the generator is the function we are - // currently type-checking; otherwise, get them by performing a query. - // This is needed to avoid cycles. - let in_progress_tables = self.in_progress_tables.map(|t| t.borrow()); - let generator_did_root = self.tcx.closure_base_def_id(generator_did); - debug!( - "maybe_note_obligation_cause_for_async_await: generator_did={:?} \ - generator_did_root={:?} in_progress_tables.local_id_root={:?} span={:?}", - generator_did, - generator_did_root, - in_progress_tables.as_ref().map(|t| t.local_id_root), - span - ); - let query_tables; - let tables: &TypeckTables<'tcx> = match &in_progress_tables { - Some(t) if t.local_id_root == Some(generator_did_root) => t, - _ => { - query_tables = self.tcx.typeck_tables_of(generator_did); - &query_tables - } - }; - - // Look for a type inside the generator interior that matches the target type to get - // a span. - let target_ty_erased = self.tcx.erase_regions(&target_ty); - let target_span = tables - .generator_interior_types - .iter() - .find(|ty::GeneratorInteriorTypeCause { ty, .. }| { - // Careful: the regions for types that appear in the - // generator interior are not generally known, so we - // want to erase them when comparing (and anyway, - // `Send` and other bounds are generally unaffected by - // the choice of region). When erasing regions, we - // also have to erase late-bound regions. This is - // because the types that appear in the generator - // interior generally contain "bound regions" to - // represent regions that are part of the suspended - // generator frame. Bound regions are preserved by - // `erase_regions` and so we must also call - // `erase_late_bound_regions`. - let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(*ty)); - let ty_erased = self.tcx.erase_regions(&ty_erased); - let eq = ty::TyS::same_type(ty_erased, target_ty_erased); - debug!( - "maybe_note_obligation_cause_for_async_await: ty_erased={:?} \ - target_ty_erased={:?} eq={:?}", - ty_erased, target_ty_erased, eq - ); - eq - }) - .map(|ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. }| { - (span, source_map.span_to_snippet(*span), scope_span, expr) - }); - - debug!( - "maybe_note_obligation_cause_for_async_await: target_ty={:?} \ - generator_interior_types={:?} target_span={:?}", - target_ty, tables.generator_interior_types, target_span - ); - if let Some((target_span, Ok(snippet), scope_span, expr)) = target_span { - self.note_obligation_cause_for_async_await( - err, - *target_span, - scope_span, - *expr, - snippet, - generator_did, - last_generator, - trait_ref, - target_ty, - tables, - obligation, - next_code, - ); - true - } else { - false - } - } - - /// Unconditionally adds the diagnostic note described in - /// `maybe_note_obligation_cause_for_async_await`'s documentation comment. - crate fn note_obligation_cause_for_async_await( - &self, - err: &mut DiagnosticBuilder<'_>, - target_span: Span, - scope_span: &Option, - expr: Option, - snippet: String, - first_generator: DefId, - last_generator: Option, - trait_ref: ty::TraitRef<'_>, - target_ty: Ty<'tcx>, - tables: &ty::TypeckTables<'_>, - obligation: &PredicateObligation<'tcx>, - next_code: Option<&ObligationCauseCode<'tcx>>, - ) { - let source_map = self.tcx.sess.source_map(); - - let is_async_fn = self - .tcx - .parent(first_generator) - .map(|parent_did| self.tcx.asyncness(parent_did)) - .map(|parent_asyncness| parent_asyncness == hir::IsAsync::Async) - .unwrap_or(false); - let is_async_move = self - .tcx - .hir() - .as_local_hir_id(first_generator) - .and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id)) - .map(|body_id| self.tcx.hir().body(body_id)) - .and_then(|body| body.generator_kind()) - .map(|generator_kind| match generator_kind { - hir::GeneratorKind::Async(..) => true, - _ => false, - }) - .unwrap_or(false); - let await_or_yield = if is_async_fn || is_async_move { "await" } else { "yield" }; - - // Special case the primary error message when send or sync is the trait that was - // not implemented. - let is_send = self.tcx.is_diagnostic_item(sym::send_trait, trait_ref.def_id); - let is_sync = self.tcx.is_diagnostic_item(sym::sync_trait, trait_ref.def_id); - let hir = self.tcx.hir(); - let trait_explanation = if is_send || is_sync { - let (trait_name, trait_verb) = - if is_send { ("`Send`", "sent") } else { ("`Sync`", "shared") }; - - err.clear_code(); - err.set_primary_message(format!( - "future cannot be {} between threads safely", - trait_verb - )); - - let original_span = err.span.primary_span().unwrap(); - let mut span = MultiSpan::from_span(original_span); - - let message = if let Some(name) = last_generator - .and_then(|generator_did| self.tcx.parent(generator_did)) - .and_then(|parent_did| hir.as_local_hir_id(parent_did)) - .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) - { - format!("future returned by `{}` is not {}", name, trait_name) - } else { - format!("future is not {}", trait_name) - }; - - span.push_span_label(original_span, message); - err.set_span(span); - - format!("is not {}", trait_name) - } else { - format!("does not implement `{}`", trait_ref.print_only_trait_path()) - }; - - // Look at the last interior type to get a span for the `.await`. - let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap(); - let mut span = MultiSpan::from_span(await_span); - span.push_span_label( - await_span, - format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet), - ); - - span.push_span_label(target_span, format!("has type `{}`", target_ty)); - - // If available, use the scope span to annotate the drop location. - if let Some(scope_span) = scope_span { - span.push_span_label( - source_map.end_point(*scope_span), - format!("`{}` is later dropped here", snippet), - ); - } - - err.span_note( - span, - &format!( - "future {} as this value is used across an {}", - trait_explanation, await_or_yield, - ), - ); - - if let Some(expr_id) = expr { - let expr = hir.expect_expr(expr_id); - debug!("target_ty evaluated from {:?}", expr); - - let parent = hir.get_parent_node(expr_id); - if let Some(hir::Node::Expr(e)) = hir.find(parent) { - let parent_span = hir.span(parent); - let parent_did = parent.owner_def_id(); - // ```rust - // impl T { - // fn foo(&self) -> i32 {} - // } - // T.foo(); - // ^^^^^^^ a temporary `&T` created inside this method call due to `&self` - // ``` - // - let is_region_borrow = - tables.expr_adjustments(expr).iter().any(|adj| adj.is_region_borrow()); - - // ```rust - // struct Foo(*const u8); - // bar(Foo(std::ptr::null())).await; - // ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor. - // ``` - debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did)); - let is_raw_borrow_inside_fn_like_call = match self.tcx.def_kind(parent_did) { - Some(DefKind::Fn) | Some(DefKind::Ctor(..)) => target_ty.is_unsafe_ptr(), - _ => false, - }; - - if (tables.is_method_call(e) && is_region_borrow) - || is_raw_borrow_inside_fn_like_call - { - err.span_help( - parent_span, - "consider moving this into a `let` \ - binding to create a shorter lived borrow", - ); - } - } - } - - // Add a note for the item obligation that remains - normally a note pointing to the - // bound that introduced the obligation (e.g. `T: Send`). - debug!("note_obligation_cause_for_async_await: next_code={:?}", next_code); - self.note_obligation_cause_code( - err, - &obligation.predicate, - next_code.unwrap(), - &mut Vec::new(), - ); - } - - crate fn note_obligation_cause_code( - &self, - err: &mut DiagnosticBuilder<'_>, - predicate: &T, - cause_code: &ObligationCauseCode<'tcx>, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, - ) where - T: fmt::Display, - { - let tcx = self.tcx; - match *cause_code { - ObligationCauseCode::ExprAssignable - | ObligationCauseCode::MatchExpressionArm { .. } - | ObligationCauseCode::Pattern { .. } - | ObligationCauseCode::IfExpression { .. } - | ObligationCauseCode::IfExpressionWithNoElse - | ObligationCauseCode::MainFunctionType - | ObligationCauseCode::StartFunctionType - | ObligationCauseCode::IntrinsicType - | ObligationCauseCode::MethodReceiver - | ObligationCauseCode::ReturnNoExpression - | ObligationCauseCode::MiscObligation => {} - ObligationCauseCode::SliceOrArrayElem => { - err.note("slice and array elements must have `Sized` type"); - } - ObligationCauseCode::TupleElem => { - err.note("only the last element of a tuple may have a dynamically sized type"); - } - ObligationCauseCode::ProjectionWf(data) => { - err.note(&format!("required so that the projection `{}` is well-formed", data,)); - } - ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => { - err.note(&format!( - "required so that reference `{}` does not outlive its referent", - ref_ty, - )); - } - ObligationCauseCode::ObjectTypeBound(object_ty, region) => { - err.note(&format!( - "required so that the lifetime bound of `{}` for `{}` is satisfied", - region, object_ty, - )); - } - ObligationCauseCode::ItemObligation(item_def_id) => { - let item_name = tcx.def_path_str(item_def_id); - let msg = format!("required by `{}`", item_name); - - if let Some(sp) = tcx.hir().span_if_local(item_def_id) { - let sp = tcx.sess.source_map().def_span(sp); - err.span_label(sp, &msg); - } else { - err.note(&msg); - } - } - ObligationCauseCode::BindingObligation(item_def_id, span) => { - let item_name = tcx.def_path_str(item_def_id); - let msg = format!("required by this bound in `{}`", item_name); - if let Some(ident) = tcx.opt_item_name(item_def_id) { - err.span_label(ident.span, ""); - } - if span != DUMMY_SP { - err.span_label(span, &msg); - } else { - err.note(&msg); - } - } - ObligationCauseCode::ObjectCastObligation(object_ty) => { - err.note(&format!( - "required for the cast to the object type `{}`", - self.ty_to_string(object_ty) - )); - } - ObligationCauseCode::Coercion { source: _, target } => { - err.note(&format!("required by cast to type `{}`", self.ty_to_string(target))); - } - ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => { - err.note( - "the `Copy` trait is required because the repeated element will be copied", - ); - if suggest_const_in_array_repeat_expressions { - err.note( - "this array initializer can be evaluated at compile-time, see issue \ - #48147 \ - for more information", - ); - if tcx.sess.opts.unstable_features.is_nightly_build() { - err.help( - "add `#![feature(const_in_array_repeat_expressions)]` to the \ - crate attributes to enable", - ); - } - } - } - ObligationCauseCode::VariableType(_) => { - err.note("all local variables must have a statically known size"); - if !self.tcx.features().unsized_locals { - err.help("unsized locals are gated as an unstable feature"); - } - } - ObligationCauseCode::SizedArgumentType => { - err.note("all function arguments must have a statically known size"); - if !self.tcx.features().unsized_locals { - err.help("unsized locals are gated as an unstable feature"); - } - } - ObligationCauseCode::SizedReturnType => { - err.note("the return type of a function must have a statically known size"); - } - ObligationCauseCode::SizedYieldType => { - err.note("the yield type of a generator must have a statically known size"); - } - ObligationCauseCode::AssignmentLhsSized => { - err.note("the left-hand-side of an assignment must have a statically known size"); - } - ObligationCauseCode::TupleInitializerSized => { - err.note("tuples must have a statically known size to be initialized"); - } - ObligationCauseCode::StructInitializerSized => { - err.note("structs must have a statically known size to be initialized"); - } - ObligationCauseCode::FieldSized { adt_kind: ref item, last } => match *item { - AdtKind::Struct => { - if last { - err.note( - "the last field of a packed struct may only have a \ - dynamically sized type if it does not need drop to be run", - ); - } else { - err.note( - "only the last field of a struct may have a dynamically sized type", - ); - } - } - AdtKind::Union => { - err.note("no field of a union may have a dynamically sized type"); - } - AdtKind::Enum => { - err.note("no field of an enum variant may have a dynamically sized type"); - } - }, - ObligationCauseCode::ConstSized => { - err.note("constant expressions must have a statically known size"); - } - ObligationCauseCode::ConstPatternStructural => { - err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`"); - } - ObligationCauseCode::SharedStatic => { - err.note("shared static variables must have a type that implements `Sync`"); - } - ObligationCauseCode::BuiltinDerivedObligation(ref data) => { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - let ty = parent_trait_ref.skip_binder().self_ty(); - err.note(&format!("required because it appears within the type `{}`", ty)); - obligated_types.push(ty); - - let parent_predicate = parent_trait_ref.without_const().to_predicate(); - if !self.is_recursive_obligation(obligated_types, &data.parent_code) { - self.note_obligation_cause_code( - err, - &parent_predicate, - &data.parent_code, - obligated_types, - ); - } - } - ObligationCauseCode::ImplDerivedObligation(ref data) => { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - err.note(&format!( - "required because of the requirements on the impl of `{}` for `{}`", - parent_trait_ref.print_only_trait_path(), - parent_trait_ref.skip_binder().self_ty() - )); - let parent_predicate = parent_trait_ref.without_const().to_predicate(); - self.note_obligation_cause_code( - err, - &parent_predicate, - &data.parent_code, - obligated_types, - ); - } - ObligationCauseCode::CompareImplMethodObligation { .. } => { - err.note(&format!( - "the requirement `{}` appears on the impl method \ - but not on the corresponding trait method", - predicate - )); - } - ObligationCauseCode::CompareImplTypeObligation { .. } => { - err.note(&format!( - "the requirement `{}` appears on the associated impl type \ - but not on the corresponding associated trait type", - predicate - )); - } - ObligationCauseCode::ReturnType - | ObligationCauseCode::ReturnValue(_) - | ObligationCauseCode::BlockTailExpression(_) => (), - ObligationCauseCode::TrivialBound => { - err.help("see issue #48214"); - if tcx.sess.opts.unstable_features.is_nightly_build() { - err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable"); - } - } - ObligationCauseCode::AssocTypeBound(ref data) => { - err.span_label(data.original, "associated type defined here"); - if let Some(sp) = data.impl_span { - err.span_label(sp, "in this `impl` item"); - } - for sp in &data.bounds { - err.span_label(*sp, "restricted in this bound"); - } - } - } - } - - crate fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) { - let current_limit = self.tcx.sess.recursion_limit.get(); - let suggested_limit = current_limit * 2; - err.help(&format!( - "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", - suggested_limit, self.tcx.crate_name, - )); - } -} - -/// Collect all the returned expressions within the input expression. -/// Used to point at the return spans when we want to suggest some change to them. -#[derive(Default)] -struct ReturnsVisitor<'v> { - returns: Vec<&'v hir::Expr<'v>>, - in_block_tail: bool, -} - -impl<'v> Visitor<'v> for ReturnsVisitor<'v> { - type Map = rustc::hir::map::Map<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Map> { - hir::intravisit::NestedVisitorMap::None - } - - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - // Visit every expression to detect `return` paths, either through the function's tail - // expression or `return` statements. We walk all nodes to find `return` statements, but - // we only care about tail expressions when `in_block_tail` is `true`, which means that - // they're in the return path of the function body. - match ex.kind { - hir::ExprKind::Ret(Some(ex)) => { - self.returns.push(ex); - } - hir::ExprKind::Block(block, _) if self.in_block_tail => { - self.in_block_tail = false; - for stmt in block.stmts { - hir::intravisit::walk_stmt(self, stmt); - } - self.in_block_tail = true; - if let Some(expr) = block.expr { - self.visit_expr(expr); - } - } - hir::ExprKind::Match(_, arms, _) if self.in_block_tail => { - for arm in arms { - self.visit_expr(arm.body); - } - } - // We need to walk to find `return`s in the entire body. - _ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex), - _ => self.returns.push(ex), - } - } - - fn visit_body(&mut self, body: &'v hir::Body<'v>) { - assert!(!self.in_block_tail); - if body.generator_kind().is_none() { - if let hir::ExprKind::Block(block, None) = body.value.kind { - if block.expr.is_some() { - self.in_block_tail = true; - } - } - } - hir::intravisit::walk_body(self, body); - } -} diff --git a/src/librustc_infer/traits/fulfill.rs b/src/librustc_infer/traits/fulfill.rs deleted file mode 100644 index 28d3f269180..00000000000 --- a/src/librustc_infer/traits/fulfill.rs +++ /dev/null @@ -1,565 +0,0 @@ -use crate::infer::{InferCtxt, ShallowResolver}; -use rustc::ty::error::ExpectedFound; -use rustc::ty::{self, ToPolyTraitRef, Ty, TypeFoldable}; -use rustc_data_structures::obligation_forest::ProcessResult; -use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; -use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; -use std::marker::PhantomData; - -use super::engine::{TraitEngine, TraitEngineExt}; -use super::project; -use super::select::SelectionContext; -use super::wf; -use super::CodeAmbiguity; -use super::CodeProjectionError; -use super::CodeSelectionError; -use super::{ConstEvalFailure, Unimplemented}; -use super::{FulfillmentError, FulfillmentErrorCode}; -use super::{ObligationCause, PredicateObligation}; - -impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { - /// Note that we include both the `ParamEnv` and the `Predicate`, - /// as the `ParamEnv` can influence whether fulfillment succeeds - /// or fails. - type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>; - - fn as_cache_key(&self) -> Self::CacheKey { - self.obligation.param_env.and(self.obligation.predicate) - } -} - -/// The fulfillment context is used to drive trait resolution. It -/// consists of a list of obligations that must be (eventually) -/// satisfied. The job is to track which are satisfied, which yielded -/// errors, and which are still pending. At any point, users can call -/// `select_where_possible`, and the fulfillment context will try to do -/// selection, retaining only those obligations that remain -/// ambiguous. This may be helpful in pushing type inference -/// along. Once all type inference constraints have been generated, the -/// method `select_all_or_error` can be used to report any remaining -/// ambiguous cases as errors. -pub struct FulfillmentContext<'tcx> { - // A list of all obligations that have been registered with this - // fulfillment context. - predicates: ObligationForest>, - // Should this fulfillment context register type-lives-for-region - // obligations on its parent infcx? In some cases, region - // obligations are either already known to hold (normalization) or - // hopefully verifed elsewhere (type-impls-bound), and therefore - // should not be checked. - // - // Note that if we are normalizing a type that we already - // know is well-formed, there should be no harm setting this - // to true - all the region variables should be determinable - // using the RFC 447 rules, which don't depend on - // type-lives-for-region constraints, and because the type - // is well-formed, the constraints should hold. - register_region_obligations: bool, - // Is it OK to register obligations into this infcx inside - // an infcx snapshot? - // - // The "primary fulfillment" in many cases in typeck lives - // outside of any snapshot, so any use of it inside a snapshot - // will lead to trouble and therefore is checked against, but - // other fulfillment contexts sometimes do live inside of - // a snapshot (they don't *straddle* a snapshot, so there - // is no trouble there). - usable_in_snapshot: bool, -} - -#[derive(Clone, Debug)] -pub struct PendingPredicateObligation<'tcx> { - pub obligation: PredicateObligation<'tcx>, - pub stalled_on: Vec, -} - -// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(PendingPredicateObligation<'_>, 136); - -impl<'a, 'tcx> FulfillmentContext<'tcx> { - /// Creates a new fulfillment context. - pub fn new() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - register_region_obligations: true, - usable_in_snapshot: false, - } - } - - pub fn new_in_snapshot() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - register_region_obligations: true, - usable_in_snapshot: true, - } - } - - pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - register_region_obligations: false, - usable_in_snapshot: false, - } - } - - /// Attempts to select obligations using `selcx`. - fn select( - &mut self, - selcx: &mut SelectionContext<'a, 'tcx>, - ) -> Result<(), Vec>> { - debug!("select(obligation-forest-size={})", self.predicates.len()); - - let mut errors = Vec::new(); - - loop { - debug!("select: starting another iteration"); - - // Process pending obligations. - let outcome = self.predicates.process_obligations( - &mut FulfillProcessor { - selcx, - register_region_obligations: self.register_region_obligations, - }, - DoCompleted::No, - ); - debug!("select: outcome={:#?}", outcome); - - // FIXME: if we kept the original cache key, we could mark projection - // obligations as complete for the projection cache here. - - errors.extend(outcome.errors.into_iter().map(|e| to_fulfillment_error(e))); - - // If nothing new was added, no need to keep looping. - if outcome.stalled { - break; - } - } - - debug!( - "select({} predicates remaining, {} errors) done", - self.predicates.len(), - errors.len() - ); - - if errors.is_empty() { Ok(()) } else { Err(errors) } - } -} - -impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { - /// "Normalize" a projection type `::X` by - /// creating a fresh type variable `$0` as well as a projection - /// predicate `::X == $0`. When the - /// inference engine runs, it will attempt to find an impl of - /// `SomeTrait` or a where-clause that lets us unify `$0` with - /// something concrete. If this fails, we'll unify `$0` with - /// `projection_ty` again. - fn normalize_projection_type( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - ) -> Ty<'tcx> { - debug!("normalize_projection_type(projection_ty={:?})", projection_ty); - - debug_assert!(!projection_ty.has_escaping_bound_vars()); - - // FIXME(#20304) -- cache - - let mut selcx = SelectionContext::new(infcx); - let mut obligations = vec![]; - let normalized_ty = project::normalize_projection_type( - &mut selcx, - param_env, - projection_ty, - cause, - 0, - &mut obligations, - ); - self.register_predicate_obligations(infcx, obligations); - - debug!("normalize_projection_type: result={:?}", normalized_ty); - - normalized_ty - } - - fn register_predicate_obligation( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - obligation: PredicateObligation<'tcx>, - ) { - // this helps to reduce duplicate errors, as well as making - // debug output much nicer to read and so on. - let obligation = infcx.resolve_vars_if_possible(&obligation); - - debug!("register_predicate_obligation(obligation={:?})", obligation); - - assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot); - - self.predicates - .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); - } - - fn select_all_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec>> { - self.select_where_possible(infcx)?; - - let errors: Vec<_> = self - .predicates - .to_errors(CodeAmbiguity) - .into_iter() - .map(|e| to_fulfillment_error(e)) - .collect(); - if errors.is_empty() { Ok(()) } else { Err(errors) } - } - - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec>> { - let mut selcx = SelectionContext::new(infcx); - self.select(&mut selcx) - } - - fn pending_obligations(&self) -> Vec> { - self.predicates.map_pending_obligations(|o| o.obligation.clone()) - } -} - -struct FulfillProcessor<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, - register_region_obligations: bool, -} - -fn mk_pending(os: Vec>) -> Vec> { - os.into_iter() - .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) - .collect() -} - -impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { - type Obligation = PendingPredicateObligation<'tcx>; - type Error = FulfillmentErrorCode<'tcx>; - - /// Processes a predicate obligation and returns either: - /// - `Changed(v)` if the predicate is true, presuming that `v` are also true - /// - `Unchanged` if we don't have enough info to be sure - /// - `Error(e)` if the predicate does not hold - /// - /// This is always inlined, despite its size, because it has a single - /// callsite and it is called *very* frequently. - #[inline(always)] - fn process_obligation( - &mut self, - pending_obligation: &mut Self::Obligation, - ) -> ProcessResult { - // If we were stalled on some unresolved variables, first check whether - // any of them have been resolved; if not, don't bother doing more work - // yet. - let change = match pending_obligation.stalled_on.len() { - // Match arms are in order of frequency, which matters because this - // code is so hot. 1 and 0 dominate; 2+ is fairly rare. - 1 => { - let infer = pending_obligation.stalled_on[0]; - ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(infer) - } - 0 => { - // In this case we haven't changed, but wish to make a change. - true - } - _ => { - // This `for` loop was once a call to `all()`, but this lower-level - // form was a perf win. See #64545 for details. - (|| { - for &infer in &pending_obligation.stalled_on { - if ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(infer) { - return true; - } - } - false - })() - } - }; - - if !change { - debug!( - "process_predicate: pending obligation {:?} still stalled on {:?}", - self.selcx.infcx().resolve_vars_if_possible(&pending_obligation.obligation), - pending_obligation.stalled_on - ); - return ProcessResult::Unchanged; - } - - // This part of the code is much colder. - - pending_obligation.stalled_on.truncate(0); - - let obligation = &mut pending_obligation.obligation; - - if obligation.predicate.has_infer_types() { - obligation.predicate = - self.selcx.infcx().resolve_vars_if_possible(&obligation.predicate); - } - - debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); - - fn infer_ty(ty: Ty<'tcx>) -> ty::InferTy { - match ty.kind { - ty::Infer(infer) => infer, - _ => panic!(), - } - } - - match obligation.predicate { - ty::Predicate::Trait(ref data, _) => { - let trait_obligation = obligation.with(data.clone()); - - if data.is_global() { - // no type variables present, can use evaluation for better caching. - // FIXME: consider caching errors too. - if self.selcx.infcx().predicate_must_hold_considering_regions(&obligation) { - debug!( - "selecting trait `{:?}` at depth {} evaluated to holds", - data, obligation.recursion_depth - ); - return ProcessResult::Changed(vec![]); - } - } - - match self.selcx.select(&trait_obligation) { - Ok(Some(vtable)) => { - debug!( - "selecting trait `{:?}` at depth {} yielded Ok(Some)", - data, obligation.recursion_depth - ); - ProcessResult::Changed(mk_pending(vtable.nested_obligations())) - } - Ok(None) => { - debug!( - "selecting trait `{:?}` at depth {} yielded Ok(None)", - data, obligation.recursion_depth - ); - - // This is a bit subtle: for the most part, the - // only reason we can fail to make progress on - // trait selection is because we don't have enough - // information about the types in the trait. One - // exception is that we sometimes haven't decided - // what kind of closure a closure is. *But*, in - // that case, it turns out, the type of the - // closure will also change, because the closure - // also includes references to its upvars as part - // of its type, and those types are resolved at - // the same time. - // - // FIXME(#32286) logic seems false if no upvars - pending_obligation.stalled_on = - trait_ref_type_vars(self.selcx, data.to_poly_trait_ref()); - - debug!( - "process_predicate: pending obligation {:?} now stalled on {:?}", - self.selcx.infcx().resolve_vars_if_possible(obligation), - pending_obligation.stalled_on - ); - - ProcessResult::Unchanged - } - Err(selection_err) => { - info!( - "selecting trait `{:?}` at depth {} yielded Err", - data, obligation.recursion_depth - ); - - ProcessResult::Error(CodeSelectionError(selection_err)) - } - } - } - - ty::Predicate::RegionOutlives(ref binder) => { - match self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder) { - Ok(()) => ProcessResult::Changed(vec![]), - Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), - } - } - - ty::Predicate::TypeOutlives(ref binder) => { - // Check if there are higher-ranked vars. - match binder.no_bound_vars() { - // If there are, inspect the underlying type further. - None => { - // Convert from `Binder>` to `Binder`. - let binder = binder.map_bound_ref(|pred| pred.0); - - // Check if the type has any bound vars. - match binder.no_bound_vars() { - // If so, this obligation is an error (for now). Eventually we should be - // able to support additional cases here, like `for<'a> &'a str: 'a`. - // NOTE: this is duplicate-implemented between here and fulfillment. - None => ProcessResult::Error(CodeSelectionError(Unimplemented)), - // Otherwise, we have something of the form - // `for<'a> T: 'a where 'a not in T`, which we can treat as - // `T: 'static`. - Some(t_a) => { - let r_static = self.selcx.tcx().lifetimes.re_static; - if self.register_region_obligations { - self.selcx.infcx().register_region_obligation_with_cause( - t_a, - r_static, - &obligation.cause, - ); - } - ProcessResult::Changed(vec![]) - } - } - } - // If there aren't, register the obligation. - Some(ty::OutlivesPredicate(t_a, r_b)) => { - if self.register_region_obligations { - self.selcx.infcx().register_region_obligation_with_cause( - t_a, - r_b, - &obligation.cause, - ); - } - ProcessResult::Changed(vec![]) - } - } - } - - ty::Predicate::Projection(ref data) => { - let project_obligation = obligation.with(data.clone()); - match project::poly_project_and_unify_type(self.selcx, &project_obligation) { - Ok(None) => { - let tcx = self.selcx.tcx(); - pending_obligation.stalled_on = - trait_ref_type_vars(self.selcx, data.to_poly_trait_ref(tcx)); - ProcessResult::Unchanged - } - Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)), - Err(e) => ProcessResult::Error(CodeProjectionError(e)), - } - } - - ty::Predicate::ObjectSafe(trait_def_id) => { - if !self.selcx.tcx().is_object_safe(trait_def_id) { - ProcessResult::Error(CodeSelectionError(Unimplemented)) - } else { - ProcessResult::Changed(vec![]) - } - } - - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - match self.selcx.infcx().closure_kind(closure_def_id, closure_substs) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - ProcessResult::Changed(vec![]) - } else { - ProcessResult::Error(CodeSelectionError(Unimplemented)) - } - } - None => ProcessResult::Unchanged, - } - } - - ty::Predicate::WellFormed(ty) => { - match wf::obligations( - self.selcx.infcx(), - obligation.param_env, - obligation.cause.body_id, - ty, - obligation.cause.span, - ) { - None => { - pending_obligation.stalled_on = vec![infer_ty(ty)]; - ProcessResult::Unchanged - } - Some(os) => ProcessResult::Changed(mk_pending(os)), - } - } - - ty::Predicate::Subtype(ref subtype) => { - match self.selcx.infcx().subtype_predicate( - &obligation.cause, - obligation.param_env, - subtype, - ) { - None => { - // None means that both are unresolved. - pending_obligation.stalled_on = vec![ - infer_ty(subtype.skip_binder().a), - infer_ty(subtype.skip_binder().b), - ]; - ProcessResult::Unchanged - } - Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), - Some(Err(err)) => { - let expected_found = ExpectedFound::new( - subtype.skip_binder().a_is_expected, - subtype.skip_binder().a, - subtype.skip_binder().b, - ); - ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( - expected_found, - err, - )) - } - } - } - - ty::Predicate::ConstEvaluatable(def_id, substs) => { - match self.selcx.infcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - Some(obligation.cause.span), - ) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), - } - } - } - } - - fn process_backedge<'c, I>( - &mut self, - cycle: I, - _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, - ) where - I: Clone + Iterator>, - { - if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { - debug!("process_child_obligations: coinductive match"); - } else { - let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - self.selcx.infcx().report_overflow_error_cycle(&cycle); - } - } -} - -/// Returns the set of type variables contained in a trait ref -fn trait_ref_type_vars<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, - t: ty::PolyTraitRef<'tcx>, -) -> Vec { - t.skip_binder() // ok b/c this check doesn't care about regions - .input_types() - .map(|t| selcx.infcx().resolve_vars_if_possible(&t)) - .filter(|t| t.has_infer_types()) - .flat_map(|t| t.walk()) - .filter_map(|t| match t.kind { - ty::Infer(infer) => Some(infer), - _ => None, - }) - .collect() -} - -fn to_fulfillment_error<'tcx>( - error: Error, FulfillmentErrorCode<'tcx>>, -) -> FulfillmentError<'tcx> { - let obligation = error.backtrace.into_iter().next().unwrap().obligation; - FulfillmentError::new(obligation, error.error) -} diff --git a/src/librustc_infer/traits/misc.rs b/src/librustc_infer/traits/misc.rs deleted file mode 100644 index 7ab918c159e..00000000000 --- a/src/librustc_infer/traits/misc.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Miscellaneous type-system utilities that are too small to deserve their own modules. - -use crate::infer::TyCtxtInferExt; -use crate::traits::{self, ObligationCause}; - -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_hir as hir; - -#[derive(Clone)] -pub enum CopyImplementationError<'tcx> { - InfrigingFields(Vec<&'tcx ty::FieldDef>), - NotAnAdt, - HasDestructor, -} - -pub fn can_type_implement_copy( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - self_type: Ty<'tcx>, -) -> Result<(), CopyImplementationError<'tcx>> { - // FIXME: (@jroesch) float this code up - tcx.infer_ctxt().enter(|infcx| { - let (adt, substs) = match self_type.kind { - // These types used to have a builtin impl. - // Now libcore provides that impl. - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()), - - ty::Adt(adt, substs) => (adt, substs), - - _ => return Err(CopyImplementationError::NotAnAdt), - }; - - let mut infringing = Vec::new(); - for variant in &adt.variants { - for field in &variant.fields { - let ty = field.ty(tcx, substs); - if ty.references_error() { - continue; - } - let span = tcx.def_span(field.did); - let cause = ObligationCause { span, ..ObligationCause::dummy() }; - let ctx = traits::FulfillmentContext::new(); - match traits::fully_normalize(&infcx, ctx, cause, param_env, &ty) { - Ok(ty) => { - if !infcx.type_is_copy_modulo_regions(param_env, ty, span) { - infringing.push(field); - } - } - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); - } - }; - } - } - if !infringing.is_empty() { - return Err(CopyImplementationError::InfrigingFields(infringing)); - } - if adt.has_dtor(tcx) { - return Err(CopyImplementationError::HasDestructor); - } - - Ok(()) - }) -} diff --git a/src/librustc_infer/traits/mod.rs b/src/librustc_infer/traits/mod.rs index fcaab093ee2..1c0785497be 100644 --- a/src/librustc_infer/traits/mod.rs +++ b/src/librustc_infer/traits/mod.rs @@ -2,119 +2,32 @@ //! //! [rustc guide]: https://rust-lang.github.io/rustc-guide/traits/resolution.html -#[allow(dead_code)] -pub mod auto_trait; -mod chalk_fulfill; -pub mod codegen; -mod coherence; mod engine; pub mod error_reporting; -mod fulfill; -pub mod misc; -mod object_safety; -mod on_unimplemented; mod project; -pub mod query; -mod select; -mod specialize; mod structural_impls; -mod structural_match; mod util; -pub mod wf; -use crate::infer::outlives::env::OutlivesEnvironment; -use crate::infer::{InferCtxt, SuppressRegionErrors, TyCtxtInferExt}; -use rustc::middle::region; use rustc::ty::error::{ExpectedFound, TypeError}; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::subst::{InternalSubsts, SubstsRef}; -use rustc::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, WithConstness}; -use rustc::util::common::ErrorReported; +use rustc::ty::{self, Ty}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::{Span, DUMMY_SP}; - -use std::fmt::Debug; +use rustc_span::Span; pub use self::FulfillmentErrorCode::*; pub use self::ObligationCauseCode::*; pub use self::SelectionError::*; pub use self::Vtable::*; -pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; -pub use self::coherence::{OrphanCheckErr, OverlapResult}; pub use self::engine::{TraitEngine, TraitEngineExt}; -pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; -pub use self::object_safety::astconv_object_safety_violations; -pub use self::object_safety::is_vtable_safe_method; -pub use self::object_safety::MethodViolationCode; -pub use self::object_safety::ObjectSafetyViolation; -pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote}; pub use self::project::MismatchedProjectionTypes; pub use self::project::{ - normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type, -}; -pub use self::project::{Normalized, ProjectionCache, ProjectionCacheSnapshot, Reveal}; -pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; -pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; -pub use self::specialize::find_associated_item; -pub use self::specialize::specialization_graph::FutureCompatOverlapError; -pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; -pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; -pub use self::structural_match::search_for_structural_match_violation; -pub use self::structural_match::type_marked_structural; -pub use self::structural_match::NonStructuralMatchTy; -pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; -pub use self::util::{expand_trait_aliases, TraitAliasExpander}; -pub use self::util::{ - get_vtable_index_of_object_method, impl_is_default, impl_item_is_final, - predicate_for_trait_def, upcast_choices, -}; -pub use self::util::{ - supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, -}; - -pub use self::chalk_fulfill::{ - CanonicalGoal as ChalkCanonicalGoal, FulfillmentContext as ChalkFulfillmentContext, + Normalized, NormalizedTy, ProjectionCache, ProjectionCacheEntry, ProjectionCacheKey, + ProjectionCacheSnapshot, Reveal, }; +crate use self::util::elaborate_predicates; pub use rustc::traits::*; -/// Whether to skip the leak check, as part of a future compatibility warning step. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum SkipLeakCheck { - Yes, - No, -} - -impl SkipLeakCheck { - fn is_yes(self) -> bool { - self == SkipLeakCheck::Yes - } -} - -/// The "default" for skip-leak-check corresponds to the current -/// behavior (do not skip the leak check) -- not the behavior we are -/// transitioning into. -impl Default for SkipLeakCheck { - fn default() -> Self { - SkipLeakCheck::No - } -} - -/// The mode that trait queries run in. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum TraitQueryMode { - // Standard/un-canonicalized queries get accurate - // spans etc. passed in and hence can do reasonable - // error reporting on their own. - Standard, - // Canonicalized queries get dummy spans and hence - // must generally propagate errors to - // pre-canonicalization callsites. - Canonical, -} - /// An `Obligation` represents some trait reference (e.g., `int: Eq`) for /// which the vtable must be found. The process of finding a vtable is /// called "resolving" the `Obligation`. This process consists of @@ -170,418 +83,6 @@ pub enum FulfillmentErrorCode<'tcx> { CodeAmbiguity, } -/// Creates predicate obligations from the generic bounds. -pub fn predicates_for_generics<'tcx>( - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - generic_bounds: &ty::InstantiatedPredicates<'tcx>, -) -> PredicateObligations<'tcx> { - util::predicates_for_generics(cause, 0, param_env, generic_bounds) -} - -/// Determines whether the type `ty` is known to meet `bound` and -/// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound (note that this is -/// conservative towards *no impl*, which is the opposite of the -/// `evaluate` methods). -pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - def_id: DefId, - span: Span, -) -> bool { - debug!( - "type_known_to_meet_bound_modulo_regions(ty={:?}, bound={:?})", - ty, - infcx.tcx.def_path_str(def_id) - ); - - let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) }; - let obligation = Obligation { - param_env, - cause: ObligationCause::misc(span, hir::DUMMY_HIR_ID), - recursion_depth: 0, - predicate: trait_ref.without_const().to_predicate(), - }; - - let result = infcx.predicate_must_hold_modulo_regions(&obligation); - debug!( - "type_known_to_meet_ty={:?} bound={} => {:?}", - ty, - infcx.tcx.def_path_str(def_id), - result - ); - - if result && (ty.has_infer_types() || ty.has_closure_types()) { - // Because of inference "guessing", selection can sometimes claim - // to succeed while the success requires a guess. To ensure - // this function's result remains infallible, we must confirm - // that guess. While imperfect, I believe this is sound. - - // The handling of regions in this area of the code is terrible, - // see issue #29149. We should be able to improve on this with - // NLL. - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); - - // We can use a dummy node-id here because we won't pay any mind - // to region obligations that arise (there shouldn't really be any - // anyhow). - let cause = ObligationCause::misc(span, hir::DUMMY_HIR_ID); - - fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause); - - // Note: we only assume something is `Copy` if we can - // *definitively* show that it implements `Copy`. Otherwise, - // assume it is move; linear is always ok. - match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => { - debug!( - "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success", - ty, - infcx.tcx.def_path_str(def_id) - ); - true - } - Err(e) => { - debug!( - "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} errors={:?}", - ty, - infcx.tcx.def_path_str(def_id), - e - ); - false - } - } - } else { - result - } -} - -fn do_normalize_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - region_context: DefId, - cause: ObligationCause<'tcx>, - elaborated_env: ty::ParamEnv<'tcx>, - predicates: Vec>, -) -> Result>, ErrorReported> { - debug!( - "do_normalize_predicates(predicates={:?}, region_context={:?}, cause={:?})", - predicates, region_context, cause, - ); - let span = cause.span; - tcx.infer_ctxt().enter(|infcx| { - // FIXME. We should really... do something with these region - // obligations. But this call just continues the older - // behavior (i.e., doesn't cause any new bugs), and it would - // take some further refactoring to actually solve them. In - // particular, we would have to handle implied bounds - // properly, and that code is currently largely confined to - // regionck (though I made some efforts to extract it - // out). -nmatsakis - // - // @arielby: In any case, these obligations are checked - // by wfcheck anyway, so I'm not sure we have to check - // them here too, and we will remove this function when - // we move over to lazy normalization *anyway*. - let fulfill_cx = FulfillmentContext::new_ignoring_regions(); - let predicates = - match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, &predicates) { - Ok(predicates) => predicates, - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); - return Err(ErrorReported); - } - }; - - debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); - - let region_scope_tree = region::ScopeTree::default(); - - // We can use the `elaborated_env` here; the region code only - // cares about declarations like `'a: 'b`. - let outlives_env = OutlivesEnvironment::new(elaborated_env); - - infcx.resolve_regions_and_report_errors( - region_context, - ®ion_scope_tree, - &outlives_env, - SuppressRegionErrors::default(), - ); - - let predicates = match infcx.fully_resolve(&predicates) { - Ok(predicates) => predicates, - Err(fixup_err) => { - // If we encounter a fixup error, it means that some type - // variable wound up unconstrained. I actually don't know - // if this can happen, and I certainly don't expect it to - // happen often, but if it did happen it probably - // represents a legitimate failure due to some kind of - // unconstrained variable, and it seems better not to ICE, - // all things considered. - tcx.sess.span_err(span, &fixup_err.to_string()); - return Err(ErrorReported); - } - }; - if predicates.has_local_value() { - // FIXME: shouldn't we, you know, actually report an error here? or an ICE? - Err(ErrorReported) - } else { - Ok(predicates) - } - }) -} - -// FIXME: this is gonna need to be removed ... -/// Normalizes the parameter environment, reporting errors if they occur. -pub fn normalize_param_env_or_error<'tcx>( - tcx: TyCtxt<'tcx>, - region_context: DefId, - unnormalized_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, -) -> ty::ParamEnv<'tcx> { - // I'm not wild about reporting errors here; I'd prefer to - // have the errors get reported at a defined place (e.g., - // during typeck). Instead I have all parameter - // environments, in effect, going through this function - // and hence potentially reporting errors. This ensures of - // course that we never forget to normalize (the - // alternative seemed like it would involve a lot of - // manual invocations of this fn -- and then we'd have to - // deal with the errors at each of those sites). - // - // In any case, in practice, typeck constructs all the - // parameter environments once for every fn as it goes, - // and errors will get reported then; so after typeck we - // can be sure that no errors should occur. - - debug!( - "normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})", - region_context, unnormalized_env, cause - ); - - let mut predicates: Vec<_> = - util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec()).collect(); - - debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - - let elaborated_env = ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - unnormalized_env.reveal, - unnormalized_env.def_id, - ); - - // HACK: we are trying to normalize the param-env inside *itself*. The problem is that - // normalization expects its param-env to be already normalized, which means we have - // a circularity. - // - // The way we handle this is by normalizing the param-env inside an unnormalized version - // of the param-env, which means that if the param-env contains unnormalized projections, - // we'll have some normalization failures. This is unfortunate. - // - // Lazy normalization would basically handle this by treating just the - // normalizing-a-trait-ref-requires-itself cycles as evaluation failures. - // - // Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated - // types, so to make the situation less bad, we normalize all the predicates *but* - // the `TypeOutlives` predicates first inside the unnormalized parameter environment, and - // then we normalize the `TypeOutlives` bounds inside the normalized parameter environment. - // - // This works fairly well because trait matching does not actually care about param-env - // TypeOutlives predicates - these are normally used by regionck. - let outlives_predicates: Vec<_> = predicates - .drain_filter(|predicate| match predicate { - ty::Predicate::TypeOutlives(..) => true, - _ => false, - }) - .collect(); - - debug!( - "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})", - predicates, outlives_predicates - ); - let non_outlives_predicates = match do_normalize_predicates( - tcx, - region_context, - cause.clone(), - elaborated_env, - predicates, - ) { - Ok(predicates) => predicates, - // An unnormalized env is better than nothing. - Err(ErrorReported) => { - debug!("normalize_param_env_or_error: errored resolving non-outlives predicates"); - return elaborated_env; - } - }; - - debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates); - - // Not sure whether it is better to include the unnormalized TypeOutlives predicates - // here. I believe they should not matter, because we are ignoring TypeOutlives param-env - // predicates here anyway. Keeping them here anyway because it seems safer. - let outlives_env: Vec<_> = - non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect(); - let outlives_env = - ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal, None); - let outlives_predicates = match do_normalize_predicates( - tcx, - region_context, - cause, - outlives_env, - outlives_predicates, - ) { - Ok(predicates) => predicates, - // An unnormalized env is better than nothing. - Err(ErrorReported) => { - debug!("normalize_param_env_or_error: errored resolving outlives predicates"); - return elaborated_env; - } - }; - debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates); - - let mut predicates = non_outlives_predicates; - predicates.extend(outlives_predicates); - debug!("normalize_param_env_or_error: final predicates={:?}", predicates); - ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - unnormalized_env.reveal, - unnormalized_env.def_id, - ) -} - -pub fn fully_normalize<'a, 'tcx, T>( - infcx: &InferCtxt<'a, 'tcx>, - mut fulfill_cx: FulfillmentContext<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &T, -) -> Result>> -where - T: TypeFoldable<'tcx>, -{ - debug!("fully_normalize_with_fulfillcx(value={:?})", value); - let selcx = &mut SelectionContext::new(infcx); - let Normalized { value: normalized_value, obligations } = - project::normalize(selcx, param_env, cause, value); - debug!( - "fully_normalize: normalized_value={:?} obligations={:?}", - normalized_value, obligations - ); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation); - } - - debug!("fully_normalize: select_all_or_error start"); - fulfill_cx.select_all_or_error(infcx)?; - debug!("fully_normalize: select_all_or_error complete"); - let resolved_value = infcx.resolve_vars_if_possible(&normalized_value); - debug!("fully_normalize: resolved_value={:?}", resolved_value); - Ok(resolved_value) -} - -/// Normalizes the predicates and checks whether they hold in an empty -/// environment. If this returns false, then either normalize -/// encountered an error or one of the predicates did not hold. Used -/// when creating vtables to check for unsatisfiable methods. -pub fn normalize_and_test_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: Vec>, -) -> bool { - debug!("normalize_and_test_predicates(predicates={:?})", predicates); - - let result = tcx.infer_ctxt().enter(|infcx| { - let param_env = ty::ParamEnv::reveal_all(); - let mut selcx = SelectionContext::new(&infcx); - let mut fulfill_cx = FulfillmentContext::new(); - let cause = ObligationCause::dummy(); - let Normalized { value: predicates, obligations } = - normalize(&mut selcx, param_env, cause.clone(), &predicates); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - for predicate in predicates { - let obligation = Obligation::new(cause.clone(), param_env, predicate); - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - - fulfill_cx.select_all_or_error(&infcx).is_ok() - }); - debug!("normalize_and_test_predicates(predicates={:?}) = {:?}", predicates, result); - result -} - -fn substitute_normalize_and_test_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - key: (DefId, SubstsRef<'tcx>), -) -> bool { - debug!("substitute_normalize_and_test_predicates(key={:?})", key); - - let predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; - let result = normalize_and_test_predicates(tcx, predicates); - - debug!("substitute_normalize_and_test_predicates(key={:?}) = {:?}", key, result); - result -} - -/// Given a trait `trait_ref`, iterates the vtable entries -/// that come from `trait_ref`, including its supertraits. -#[inline] // FIXME(#35870): avoid closures being unexported due to `impl Trait`. -fn vtable_methods<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { - debug!("vtable_methods({:?})", trait_ref); - - tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| { - let trait_methods = tcx - .associated_items(trait_ref.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Method); - - // Now list each method's DefId and InternalSubsts (for within its trait). - // If the method can never be called from this object, produce None. - trait_methods.map(move |trait_method| { - debug!("vtable_methods: trait_method={:?}", trait_method); - let def_id = trait_method.def_id; - - // Some methods cannot be called on an object; skip those. - if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { - debug!("vtable_methods: not vtable safe"); - return None; - } - - // The method may have some early-bound lifetimes; add regions for those. - let substs = trait_ref.map_bound(|trait_ref| { - InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize] - } - }) - }); - - // The trait type may have higher-ranked lifetimes in it; - // erase them if they appear, so that we get the type - // at some particular call site. - let substs = - tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); - - // It's possible that the method relies on where-clauses that - // do not hold for this particular set of type parameters. - // Note that this method could then never be called, so we - // do not want to try and codegen it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - if !normalize_and_test_predicates(tcx, predicates.predicates) { - debug!("vtable_methods: predicates do not hold"); - return None; - } - - Some((def_id, substs)) - }) - })) -} - impl<'tcx, O> Obligation<'tcx, O> { pub fn new( cause: ObligationCause<'tcx>, @@ -591,7 +92,7 @@ pub fn new( Obligation { cause, param_env, recursion_depth: 0, predicate } } - fn with_depth( + pub fn with_depth( cause: ObligationCause<'tcx>, recursion_depth: usize, param_env: ty::ParamEnv<'tcx>, @@ -620,28 +121,16 @@ pub fn with

Version {}

\ \

Back to index

", - crate_name, version + crate_name, + Escape(version), ) } else { String::new() @@ -1541,7 +1594,7 @@ fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap Option { let mut path = String::new(); - // We can safely ignore macros from other libraries + // We can safely ignore synthetic `SourceFile`s. let file = match item.source.filename { FileName::Real(ref path) => path, _ => return None, @@ -2324,7 +2377,7 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func f.generics.print() ) .len(); - write!(w, "{}
", render_spotlight_traits(it));
+    write!(w, "
");
     render_attributes(w, it, false);
     write!(
         w,
@@ -2527,13 +2580,7 @@ fn trait_item(w: &mut Buffer, cx: &Context, m: &clean::Item, t: &clean::Item) {
         let item_type = m.type_();
         let id = cx.derive_id(format!("{}.{}", item_type, name));
         let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space()));
-        write!(
-            w,
-            "

{extra}", - extra = render_spotlight_traits(m), - id = id, - ns_id = ns_id - ); + write!(w, "

", id = id, ns_id = ns_id); render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl); write!(w, ""); render_stability_since(w, m, t); @@ -2726,7 +2773,7 @@ fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>) -> String { let name = it.name.as_ref().unwrap(); let ty = match it.type_() { Typedef | AssocType => AssocType, - s @ _ => s, + s => s, }; let anchor = format!("#{}.{}", ty, name); @@ -3130,26 +3177,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) { render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) } -fn render_attribute(attr: &ast::MetaItem) -> Option { - let path = pprust::path_to_string(&attr.path); - - if attr.is_word() { - Some(path) - } else if let Some(v) = attr.value_str() { - Some(format!("{} = {:?}", path, v)) - } else if let Some(values) = attr.meta_item_list() { - let display: Vec<_> = values - .iter() - .filter_map(|attr| attr.meta_item().and_then(|mi| render_attribute(mi))) - .collect(); - - if !display.is_empty() { Some(format!("{}({})", path, display.join(", "))) } else { None } - } else { - None - } -} - -const ATTRIBUTE_WHITELIST: &'static [Symbol] = &[ +const ATTRIBUTE_WHITELIST: &[Symbol] = &[ sym::export_name, sym::lang, sym::link_section, @@ -3174,9 +3202,8 @@ fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) { if !ATTRIBUTE_WHITELIST.contains(&attr.name_or_empty()) { continue; } - if let Some(s) = render_attribute(&attr.meta().unwrap()) { - attrs.push_str(&format!("#[{}]\n", s)); - } + + attrs.push_str(&pprust::attribute_to_string(&attr)); } if !attrs.is_empty() { write!( @@ -3400,10 +3427,8 @@ fn render_assoc_items( let deref_impl = traits.iter().find(|t| t.inner_impl().trait_.def_id() == c.deref_trait_did); if let Some(impl_) = deref_impl { - let has_deref_mut = traits - .iter() - .find(|t| t.inner_impl().trait_.def_id() == c.deref_mut_trait_did) - .is_some(); + let has_deref_mut = + traits.iter().any(|t| t.inner_impl().trait_.def_id() == c.deref_mut_trait_did); render_deref_methods(w, cx, impl_, containing_item, has_deref_mut); } @@ -3519,76 +3544,6 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool { } } -fn render_spotlight_traits(item: &clean::Item) -> String { - match item.inner { - clean::FunctionItem(clean::Function { ref decl, .. }) - | clean::TyMethodItem(clean::TyMethod { ref decl, .. }) - | clean::MethodItem(clean::Method { ref decl, .. }) - | clean::ForeignFunctionItem(clean::Function { ref decl, .. }) => spotlight_decl(decl), - _ => String::new(), - } -} - -fn spotlight_decl(decl: &clean::FnDecl) -> String { - let mut out = Buffer::html(); - let mut trait_ = String::new(); - - if let Some(did) = decl.output.def_id() { - let c = cache(); - if let Some(impls) = c.impls.get(&did) { - for i in impls { - let impl_ = i.inner_impl(); - if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) { - if out.is_empty() { - out.push_str(&format!( - "

Important traits for {}

\ - ", - impl_.for_.print() - )); - trait_.push_str(&impl_.for_.print().to_string()); - } - - //use the "where" class here to make it small - out.push_str(&format!( - "{}", - impl_.print() - )); - let t_did = impl_.trait_.def_id().unwrap(); - for it in &impl_.items { - if let clean::TypedefItem(ref tydef, _) = it.inner { - out.push_str(" "); - assoc_type( - &mut out, - it, - &[], - Some(&tydef.type_), - AssocItemLink::GotoSource(t_did, &FxHashSet::default()), - "", - ); - out.push_str(";"); - } - } - } - } - } - } - - if !out.is_empty() { - out.insert_str( - 0, - &format!( - "
ⓘ\ - Important traits for {}
\ -
", - trait_ - ), - ); - out.push_str("
"); - } - - out.into_inner() -} - fn render_impl( w: &mut Buffer, cx: &Context, @@ -3600,7 +3555,7 @@ fn render_impl( use_absolute: Option, is_on_foreign_type: bool, show_default_items: bool, - // This argument is used to reference same type with different pathes to avoid duplication + // This argument is used to reference same type with different paths to avoid duplication // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], ) { @@ -3695,14 +3650,13 @@ fn doc_impl_item( (true, " hidden") }; match item.inner { - clean::MethodItem(clean::Method { ref decl, .. }) - | clean::TyMethodItem(clean::TyMethod { ref decl, .. }) => { + clean::MethodItem(clean::Method { .. }) + | clean::TyMethodItem(clean::TyMethod { .. }) => { // Only render when the method is not static or we allow static methods if render_method_item { let id = cx.derive_id(format!("{}.{}", item_type, name)); let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space())); write!(w, "

", id, item_type, extra_class); - write!(w, "{}", spotlight_decl(decl)); write!(w, "", ns_id); render_assoc_item(w, item, link.anchor(&id), ItemType::Impl); write!(w, ""); @@ -3815,7 +3769,7 @@ fn render_default_items( ) { for trait_item in &t.items { let n = trait_item.name.clone(); - if i.items.iter().find(|m| m.name == n).is_some() { + if i.items.iter().any(|m| m.name == n) { continue; } let did = i.trait_.as_ref().unwrap().def_id().unwrap(); @@ -3974,7 +3928,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer) { "
\

Version {}

\
", - version + Escape(version) ); } } @@ -4609,7 +4563,7 @@ fn item_keyword(w: &mut Buffer, cx: &Context, it: &clean::Item) { document(w, cx, it) } -crate const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang"; +crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; fn make_item_keywords(it: &clean::Item) -> String { format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index a0a35e4ce4b..ed0de2b3119 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -12,7 +12,7 @@ use serde::Serialize; use super::{plain_summary_line, shorten, Impl, IndexItem, IndexItemFunctionType, ItemType}; -use super::{RenderInfo, Type}; +use super::{Generic, RenderInfo, RenderType, TypeWithKind}; /// Indicates where an external crate can be found. pub enum ExternalLocation { @@ -139,6 +139,7 @@ pub fn from_krate( deref_trait_did, deref_mut_trait_did, owned_box_did, + .. } = renderinfo; let external_paths = @@ -587,17 +588,20 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { let mut lastpathid = 0usize; for item in search_index { - item.parent_idx = item.parent.map(|defid| { + item.parent_idx = item.parent.and_then(|defid| { if defid_to_pathid.contains_key(&defid) { - *defid_to_pathid.get(&defid).expect("no pathid") + defid_to_pathid.get(&defid).map(|x| *x) } else { let pathid = lastpathid; defid_to_pathid.insert(defid, pathid); lastpathid += 1; - let &(ref fqp, short) = paths.get(&defid).unwrap(); - crate_paths.push((short, fqp.last().unwrap().clone())); - pathid + if let Some(&(ref fqp, short)) = paths.get(&defid) { + crate_paths.push((short, fqp.last().unwrap().clone())); + Some(pathid) + } else { + None + } } }); @@ -646,20 +650,25 @@ fn get_index_search_type(item: &clean::Item) -> Option { _ => return None, }; - let inputs = - all_types.iter().map(|arg| get_index_type(&arg)).filter(|a| a.name.is_some()).collect(); + let inputs = all_types + .iter() + .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) + .filter(|a| a.ty.name.is_some()) + .collect(); let output = ret_types .iter() - .map(|arg| get_index_type(&arg)) - .filter(|a| a.name.is_some()) + .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) + .filter(|a| a.ty.name.is_some()) .collect::>(); let output = if output.is_empty() { None } else { Some(output) }; Some(IndexItemFunctionType { inputs, output }) } -fn get_index_type(clean_type: &clean::Type) -> Type { - let t = Type { +fn get_index_type(clean_type: &clean::Type) -> RenderType { + let t = RenderType { + ty: clean_type.def_id(), + idx: None, name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()), generics: get_generics(clean_type), }; @@ -684,12 +693,17 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option } } -fn get_generics(clean_type: &clean::Type) -> Option> { +fn get_generics(clean_type: &clean::Type) -> Option> { clean_type.generics().and_then(|types| { let r = types .iter() - .filter_map(|t| get_index_type_name(t, false)) - .map(|s| s.to_ascii_lowercase()) + .filter_map(|t| { + if let Some(name) = get_index_type_name(t, false) { + Some(Generic { name: name.to_ascii_lowercase(), defid: t.def_id(), idx: None }) + } else { + None + } + }) .collect::>(); if r.is_empty() { None } else { Some(r) } }) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 79e7d3d783b..86f46b2d7e1 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -35,7 +35,7 @@ fn fold_item(&mut self, item: clean::Item) -> Option { // If we're including source files, and we haven't seen this file yet, // then we need to render it out to the filesystem. if self.scx.include_sources - // skip all invalid or macro spans + // skip all synthetic "files" && item.source.filename.is_real() // skip non-local items && item.def_id.is_local() diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 2870c6e0a61..3f12fb893a4 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -341,7 +341,6 @@ function getSearchElement() { function handleEscape(ev) { var help = getHelpElement(); var search = getSearchElement(); - hideModal(); if (hasClass(help, "hidden") === false) { displayHelp(false, ev, help); } else if (hasClass(search, "hidden") === false) { @@ -373,7 +372,6 @@ function getSearchElement() { case "s": case "S": displayHelp(false, ev); - hideModal(); ev.preventDefault(); focusSearchBar(); break; @@ -386,7 +384,6 @@ function getSearchElement() { case "?": if (ev.shiftKey) { - hideModal(); displayHelp(true, ev); } break; @@ -526,13 +523,14 @@ function getSearchElement() { } function initSearch(rawSearchIndex) { - var currentResults, index, searchIndex; var MAX_LEV_DISTANCE = 3; var MAX_RESULTS = 200; var GENERICS_DATA = 1; var NAME = 0; var INPUTS_DATA = 0; var OUTPUT_DATA = 1; + var NO_TYPE_FILTER = -1; + var currentResults, index, searchIndex; var params = getQueryStringParams(); // Populate search bar with query string search term when provided, @@ -559,7 +557,7 @@ function getSearchElement() { return i; } } - return -1; + return NO_TYPE_FILTER; } var valLower = query.query.toLowerCase(), @@ -722,6 +720,13 @@ function getSearchElement() { }; } + function getObjectFromId(id) { + if (typeof id === "number") { + return searchIndex[id]; + } + return {'name': id}; + } + function checkGenerics(obj, val) { // The names match, but we need to be sure that all generics kinda // match as well. @@ -738,8 +743,10 @@ function getSearchElement() { for (var y = 0; y < vlength; ++y) { var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1}; var elength = elems.length; + var firstGeneric = getObjectFromId(val.generics[y]).name; for (var x = 0; x < elength; ++x) { - var tmp_lev = levenshtein(elems[x], val.generics[y]); + var tmp_lev = levenshtein(getObjectFromId(elems[x]).name, + firstGeneric); if (tmp_lev < lev.lev) { lev.lev = tmp_lev; lev.pos = x; @@ -774,8 +781,9 @@ function getSearchElement() { for (var y = 0; allFound === true && y < val.generics.length; ++y) { allFound = false; + var firstGeneric = getObjectFromId(val.generics[y]).name; for (x = 0; allFound === false && x < elems.length; ++x) { - allFound = elems[x] === val.generics[y]; + allFound = getObjectFromId(elems[x]).name === firstGeneric; } if (allFound === true) { elems.splice(x - 1, 1); @@ -832,16 +840,22 @@ function getSearchElement() { return lev_distance + 1; } - function findArg(obj, val, literalSearch) { + function findArg(obj, val, literalSearch, typeFilter) { var lev_distance = MAX_LEV_DISTANCE + 1; - if (obj && obj.type && obj.type[INPUTS_DATA] && - obj.type[INPUTS_DATA].length > 0) { + if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { var length = obj.type[INPUTS_DATA].length; for (var i = 0; i < length; i++) { - var tmp = checkType(obj.type[INPUTS_DATA][i], val, literalSearch); - if (literalSearch === true && tmp === true) { - return true; + var tmp = obj.type[INPUTS_DATA][i]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch === true) { + if (tmp === true) { + return true; + } + continue; } lev_distance = Math.min(tmp, lev_distance); if (lev_distance === 0) { @@ -852,20 +866,20 @@ function getSearchElement() { return literalSearch === true ? false : lev_distance; } - function checkReturned(obj, val, literalSearch) { + function checkReturned(obj, val, literalSearch, typeFilter) { var lev_distance = MAX_LEV_DISTANCE + 1; if (obj && obj.type && obj.type.length > OUTPUT_DATA) { var ret = obj.type[OUTPUT_DATA]; - if (!obj.type[OUTPUT_DATA].length) { + if (typeof ret[0] === "string") { ret = [ret]; } for (var x = 0; x < ret.length; ++x) { - var r = ret[x]; - if (typeof r === "string") { - r = [r]; + var tmp = ret[x]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; } - var tmp = checkType(r, val, literalSearch); + tmp = checkType(tmp, val, literalSearch); if (literalSearch === true) { if (tmp === true) { return true; @@ -920,7 +934,7 @@ function getSearchElement() { function typePassesFilter(filter, type) { // No filter - if (filter < 0) return true; + if (filter <= NO_TYPE_FILTER) return true; // Exact match if (filter === type) return true; @@ -929,11 +943,13 @@ function getSearchElement() { var name = itemTypes[type]; switch (itemTypes[filter]) { case "constant": - return (name == "associatedconstant"); + return name === "associatedconstant"; case "fn": - return (name == "method" || name == "tymethod"); + return name === "method" || name === "tymethod"; case "type": - return (name == "primitive" || name == "keyword"); + return name === "primitive" || name === "associatedtype"; + case "trait": + return name === "traitalias"; } // No match @@ -962,42 +978,33 @@ function getSearchElement() { if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { continue; } - in_args = findArg(searchIndex[i], val, true); - returned = checkReturned(searchIndex[i], val, true); + in_args = findArg(searchIndex[i], val, true, typeFilter); + returned = checkReturned(searchIndex[i], val, true, typeFilter); ty = searchIndex[i]; fullId = generateId(ty); - if (searchWords[i] === val.name) { - // filter type: ... queries - if (typePassesFilter(typeFilter, searchIndex[i].ty) && - results[fullId] === undefined) - { - results[fullId] = {id: i, index: -1}; - } - } else if ((in_args === true || returned === true) && - typePassesFilter(typeFilter, searchIndex[i].ty)) { - if (in_args === true || returned === true) { - if (in_args === true) { - results_in_args[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (returned === true) { - results_returned[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - } else { - results[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } + if (searchWords[i] === val.name + && typePassesFilter(typeFilter, searchIndex[i].ty) + && results[fullId] === undefined) { + results[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (in_args === true && results_in_args[fullId] === undefined) { + results_in_args[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (returned === true && results_returned[fullId] === undefined) { + results_returned[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; } } query.inputs = [val]; @@ -1028,7 +1035,7 @@ function getSearchElement() { // allow searching for void (no output) functions as well var typeOutput = type.length > OUTPUT_DATA ? type[OUTPUT_DATA].name : ""; - returned = checkReturned(ty, output, true); + returned = checkReturned(ty, output, true, NO_TYPE_FILTER); if (output.name === "*" || returned === true) { in_args = false; var is_module = false; @@ -1129,16 +1136,8 @@ function getSearchElement() { lev += 1; } } - if ((in_args = findArg(ty, valGenerics)) <= MAX_LEV_DISTANCE) { - if (typePassesFilter(typeFilter, ty.ty) === false) { - in_args = MAX_LEV_DISTANCE + 1; - } - } - if ((returned = checkReturned(ty, valGenerics)) <= MAX_LEV_DISTANCE) { - if (typePassesFilter(typeFilter, ty.ty) === false) { - returned = MAX_LEV_DISTANCE + 1; - } - } + in_args = findArg(ty, valGenerics, false, typeFilter); + returned = checkReturned(ty, valGenerics, false, typeFilter); lev += lev_add; if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) { @@ -2504,31 +2503,6 @@ function getSearchElement() { lineNumbersFunc(e); }); - function showModal(content) { - var modal = document.createElement("div"); - modal.id = "important"; - addClass(modal, "modal"); - modal.innerHTML = "
✕" + - "
" + content + - "
"; - document.getElementsByTagName("body")[0].appendChild(modal); - document.getElementById("modal-close").onclick = hideModal; - modal.onclick = hideModal; - } - - function hideModal() { - var modal = document.getElementById("important"); - if (modal) { - modal.parentNode.removeChild(modal); - } - } - - onEachLazy(document.getElementsByClassName("important-traits"), function(e) { - e.onclick = function() { - showModal(e.lastElementChild.innerHTML); - }; - }); - // In the search display, allows to switch between tabs. function printTab(nb) { if (nb === 0 || nb === 1 || nb === 2) { diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 0dfe82c5014..8887bca3c59 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -143,12 +143,9 @@ code, pre, a.test-arrow { border-radius: 3px; padding: 0 0.1em; } -.docblock pre code, .docblock-short pre code, .docblock code.spotlight { +.docblock pre code, .docblock-short pre code { padding: 0; } -.docblock code.spotlight :last-child { - padding-bottom: 0.6em; -} pre { padding: 14px; } @@ -503,7 +500,7 @@ h4 > code, h3 > code, .invisible > code { font-size: 0.8em; } -.content .methods > div:not(.important-traits) { +.content .methods > div { margin-left: 40px; margin-bottom: 15px; } @@ -1035,7 +1032,7 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { .information { position: absolute; - left: -20px; + left: -25px; margin-top: 7px; z-index: 1; } @@ -1050,12 +1047,13 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { width: 120px; display: none; text-align: center; - padding: 5px 3px; + padding: 5px 3px 3px 3px; border-radius: 6px; margin-left: 5px; top: -5px; left: 105%; z-index: 10; + font-size: 16px; } .tooltip:hover .tooltiptext { @@ -1066,14 +1064,20 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { content: " "; position: absolute; top: 50%; - left: 11px; + left: 16px; margin-top: -5px; border-width: 5px; border-style: solid; } -.important-traits .tooltip .tooltiptext { +.tooltip.compile_fail, .tooltip.ignore { + font-weight: bold; + font-size: 20px; +} + +.tooltip .tooltiptext { border: 1px solid; + font-weight: normal; } pre.rust { @@ -1117,17 +1121,6 @@ pre.rust { font-size: 16px; } -.important-traits { - cursor: pointer; - z-index: 2; -} - -h4 > .important-traits { - position: absolute; - left: -44px; - top: 2px; -} - #all-types { text-align: center; border: 1px solid; @@ -1354,12 +1347,6 @@ h4 > .important-traits { z-index: 1; } - h4 > .important-traits { - position: absolute; - left: -22px; - top: 24px; - } - #titles > div > div.count { float: left; width: 100%; @@ -1462,82 +1449,12 @@ h4 > .important-traits { } } -.modal { - position: fixed; - width: 100vw; - height: 100vh; - z-index: 10000; - top: 0; - left: 0; -} - -.modal-content { - display: block; - max-width: 60%; - min-width: 200px; - padding: 8px; - top: 40%; - position: absolute; - left: 50%; - transform: translate(-50%, -40%); - border: 1px solid; - border-radius: 4px; - border-top-right-radius: 0; -} - -.modal-content > .docblock { - margin: 0; -} - h3.important { margin: 0; margin-bottom: 13px; font-size: 19px; } -.modal-content > .docblock > code.content { - margin: 0; - padding: 0; - font-size: 20px; -} - -.modal-content > .close { - position: absolute; - font-weight: 900; - right: -25px; - top: -1px; - font-size: 18px; - width: 25px; - padding-right: 2px; - border-top-right-radius: 5px; - border-bottom-right-radius: 5px; - text-align: center; - border: 1px solid; - border-right: 0; - cursor: pointer; -} - -.modal-content > .whiter { - height: 25px; - position: absolute; - width: 3px; - right: -2px; - top: 0px; -} - -#main > div.important-traits { - position: absolute; - left: -24px; - margin-top: 16px; -} - -.content > .methods > .method > div.important-traits { - position: absolute; - font-weight: 400; - left: -42px; - margin-top: 2px; -} - kbd { display: inline-block; padding: 3px 5px; diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index 9a0e7bbabcb..ff32a0fa09e 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -254,7 +254,7 @@ a.test-arrow:hover{ } pre.compile_fail { - border-left: 2px solid rgba(255,0,0,.6); + border-left: 2px solid rgba(255,0,0,.8); } pre.compile_fail:hover, .information:hover + pre.compile_fail { @@ -270,7 +270,7 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip.compile_fail { - color: rgba(255,0,0,.6); + color: rgba(255,0,0,.8); } .information > .compile_fail:hover { @@ -282,7 +282,7 @@ pre.ignore:hover, .information:hover + pre.ignore { } .information > .ignore:hover { - color: rgba(255,142,0,1); + color: #ff9200; } .search-failed a { @@ -290,20 +290,15 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip .tooltiptext { - background-color: black; + background-color: #000; color: #fff; + border-color: #000; } .tooltip .tooltiptext::after { border-color: transparent black transparent transparent; } -.important-traits .tooltip .tooltiptext { - background-color: white; - color: black; - border-color: black; -} - #titles > div:not(.selected) { background-color: #252525; border-top-color: #252525; @@ -317,33 +312,6 @@ pre.ignore:hover, .information:hover + pre.ignore { color: #888; } -.modal { - background-color: rgba(0,0,0,0.3); -} - -.modal-content { - background-color: #272727; - border-color: #999; -} - -.modal-content > .close { - background-color: #272727; - border-color: #999; -} - -.modal-content > .close:hover { - background-color: #ff1f1f; - color: white; -} - -.modal-content > .whiter { - background-color: #272727; -} - -.modal-content > .close:hover + .whiter { - background-color: #ff1f1f; -} - @media (max-width: 700px) { .sidebar-menu { background-color: #505050; diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index ca8ea1c456a..2b2819f7126 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -248,7 +248,7 @@ a.test-arrow:hover{ } pre.compile_fail { - border-left: 2px solid rgba(255,0,0,.4); + border-left: 2px solid rgba(255,0,0,.5); } pre.compile_fail:hover, .information:hover + pre.compile_fail { @@ -256,7 +256,7 @@ pre.compile_fail:hover, .information:hover + pre.compile_fail { } pre.ignore { - border-left: 2px solid rgba(255,142,0,.4); + border-left: 2px solid rgba(255,142,0,.6); } pre.ignore:hover, .information:hover + pre.ignore { @@ -264,7 +264,7 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip.compile_fail { - color: rgba(255,0,0,.3); + color: rgba(255,0,0,.5); } .information > .compile_fail:hover { @@ -272,11 +272,11 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip.ignore { - color: rgba(255,142,0,.3); + color: rgba(255,142,0,.6); } .information > .ignore:hover { - color: rgba(255,142,0,1); + color: #ff9200; } .search-failed a { @@ -284,7 +284,7 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip .tooltiptext { - background-color: black; + background-color: #000; color: #fff; } @@ -292,12 +292,6 @@ pre.ignore:hover, .information:hover + pre.ignore { border-color: transparent black transparent transparent; } -.important-traits .tooltip .tooltiptext { - background-color: white; - color: black; - border-color: black; -} - #titles > div:not(.selected) { background-color: #e6e6e6; border-top-color: #e6e6e6; @@ -311,33 +305,6 @@ pre.ignore:hover, .information:hover + pre.ignore { color: #888; } -.modal { - background-color: rgba(0,0,0,0.3); -} - -.modal-content { - background-color: #eee; - border-color: #999; -} - -.modal-content > .close { - background-color: #eee; - border-color: #999; -} - -.modal-content > .close:hover { - background-color: #ff1f1f; - color: white; -} - -.modal-content > .whiter { - background-color: #eee; -} - -.modal-content > .close:hover + .whiter { - background-color: #ff1f1f; -} - @media (max-width: 700px) { .sidebar-menu { background-color: #F1F1F1; diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 9fc1d76185f..6790f3bd5d0 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -8,106 +8,106 @@ //! directly written to a `Write` handle. /// The file contents of the main `rustdoc.css` file, responsible for the core layout of the page. -pub static RUSTDOC_CSS: &'static str = include_str!("static/rustdoc.css"); +pub static RUSTDOC_CSS: &str = include_str!("static/rustdoc.css"); /// The file contents of `settings.css`, responsible for the items on the settings page. -pub static SETTINGS_CSS: &'static str = include_str!("static/settings.css"); +pub static SETTINGS_CSS: &str = include_str!("static/settings.css"); /// The file contents of the `noscript.css` file, used in case JS isn't supported or is disabled. -pub static NOSCRIPT_CSS: &'static str = include_str!("static/noscript.css"); +pub static NOSCRIPT_CSS: &str = include_str!("static/noscript.css"); /// The file contents of `normalize.css`, included to even out standard elements between browser /// implementations. -pub static NORMALIZE_CSS: &'static str = include_str!("static/normalize.css"); +pub static NORMALIZE_CSS: &str = include_str!("static/normalize.css"); /// The file contents of `main.js`, which contains the core JavaScript used on documentation pages, /// including search behavior and docblock folding, among others. -pub static MAIN_JS: &'static str = include_str!("static/main.js"); +pub static MAIN_JS: &str = include_str!("static/main.js"); /// The file contents of `settings.js`, which contains the JavaScript used to handle the settings /// page. -pub static SETTINGS_JS: &'static str = include_str!("static/settings.js"); +pub static SETTINGS_JS: &str = include_str!("static/settings.js"); /// The file contents of `storage.js`, which contains functionality related to browser Local /// Storage, used to store documentation settings. -pub static STORAGE_JS: &'static str = include_str!("static/storage.js"); +pub static STORAGE_JS: &str = include_str!("static/storage.js"); /// The file contents of `brush.svg`, the icon used for the theme-switch button. -pub static BRUSH_SVG: &'static [u8] = include_bytes!("static/brush.svg"); +pub static BRUSH_SVG: &[u8] = include_bytes!("static/brush.svg"); /// The file contents of `wheel.svg`, the icon used for the settings button. -pub static WHEEL_SVG: &'static [u8] = include_bytes!("static/wheel.svg"); +pub static WHEEL_SVG: &[u8] = include_bytes!("static/wheel.svg"); /// The file contents of `down-arrow.svg`, the icon used for the crate choice combobox. -pub static DOWN_ARROW_SVG: &'static [u8] = include_bytes!("static/down-arrow.svg"); +pub static DOWN_ARROW_SVG: &[u8] = include_bytes!("static/down-arrow.svg"); /// The contents of `COPYRIGHT.txt`, the license listing for files distributed with documentation /// output. -pub static COPYRIGHT: &'static [u8] = include_bytes!("static/COPYRIGHT.txt"); +pub static COPYRIGHT: &[u8] = include_bytes!("static/COPYRIGHT.txt"); /// The contents of `LICENSE-APACHE.txt`, the text of the Apache License, version 2.0. -pub static LICENSE_APACHE: &'static [u8] = include_bytes!("static/LICENSE-APACHE.txt"); +pub static LICENSE_APACHE: &[u8] = include_bytes!("static/LICENSE-APACHE.txt"); /// The contents of `LICENSE-MIT.txt`, the text of the MIT License. -pub static LICENSE_MIT: &'static [u8] = include_bytes!("static/LICENSE-MIT.txt"); +pub static LICENSE_MIT: &[u8] = include_bytes!("static/LICENSE-MIT.txt"); /// The contents of `rust-logo.png`, the default icon of the documentation. -pub static RUST_LOGO: &'static [u8] = include_bytes!("static/rust-logo.png"); +pub static RUST_LOGO: &[u8] = include_bytes!("static/rust-logo.png"); /// The contents of `favicon.ico`, the default favicon of the documentation. -pub static RUST_FAVICON: &'static [u8] = include_bytes!("static/favicon.ico"); +pub static RUST_FAVICON: &[u8] = include_bytes!("static/favicon.ico"); /// The built-in themes given to every documentation site. pub mod themes { /// The "light" theme, selected by default when no setting is available. Used as the basis for /// the `--check-theme` functionality. - pub static LIGHT: &'static str = include_str!("static/themes/light.css"); + pub static LIGHT: &str = include_str!("static/themes/light.css"); /// The "dark" theme. - pub static DARK: &'static str = include_str!("static/themes/dark.css"); + pub static DARK: &str = include_str!("static/themes/dark.css"); } /// Files related to the Fira Sans font. pub mod fira_sans { /// The file `FiraSans-Regular.woff`, the Regular variant of the Fira Sans font. - pub static REGULAR: &'static [u8] = include_bytes!("static/FiraSans-Regular.woff"); + pub static REGULAR: &[u8] = include_bytes!("static/FiraSans-Regular.woff"); /// The file `FiraSans-Medium.woff`, the Medium variant of the Fira Sans font. - pub static MEDIUM: &'static [u8] = include_bytes!("static/FiraSans-Medium.woff"); + pub static MEDIUM: &[u8] = include_bytes!("static/FiraSans-Medium.woff"); /// The file `FiraSans-LICENSE.txt`, the license text for the Fira Sans font. - pub static LICENSE: &'static [u8] = include_bytes!("static/FiraSans-LICENSE.txt"); + pub static LICENSE: &[u8] = include_bytes!("static/FiraSans-LICENSE.txt"); } /// Files related to the Source Serif Pro font. pub mod source_serif_pro { /// The file `SourceSerifPro-Regular.ttf.woff`, the Regular variant of the Source Serif Pro /// font. - pub static REGULAR: &'static [u8] = include_bytes!("static/SourceSerifPro-Regular.ttf.woff"); + pub static REGULAR: &[u8] = include_bytes!("static/SourceSerifPro-Regular.ttf.woff"); /// The file `SourceSerifPro-Bold.ttf.woff`, the Bold variant of the Source Serif Pro font. - pub static BOLD: &'static [u8] = include_bytes!("static/SourceSerifPro-Bold.ttf.woff"); + pub static BOLD: &[u8] = include_bytes!("static/SourceSerifPro-Bold.ttf.woff"); /// The file `SourceSerifPro-It.ttf.woff`, the Italic variant of the Source Serif Pro font. - pub static ITALIC: &'static [u8] = include_bytes!("static/SourceSerifPro-It.ttf.woff"); + pub static ITALIC: &[u8] = include_bytes!("static/SourceSerifPro-It.ttf.woff"); /// The file `SourceSerifPro-LICENSE.txt`, the license text for the Source Serif Pro font. - pub static LICENSE: &'static [u8] = include_bytes!("static/SourceSerifPro-LICENSE.md"); + pub static LICENSE: &[u8] = include_bytes!("static/SourceSerifPro-LICENSE.md"); } /// Files related to the Source Code Pro font. pub mod source_code_pro { /// The file `SourceCodePro-Regular.woff`, the Regular variant of the Source Code Pro font. - pub static REGULAR: &'static [u8] = include_bytes!("static/SourceCodePro-Regular.woff"); + pub static REGULAR: &[u8] = include_bytes!("static/SourceCodePro-Regular.woff"); /// The file `SourceCodePro-Semibold.woff`, the Semibold variant of the Source Code Pro font. - pub static SEMIBOLD: &'static [u8] = include_bytes!("static/SourceCodePro-Semibold.woff"); + pub static SEMIBOLD: &[u8] = include_bytes!("static/SourceCodePro-Semibold.woff"); /// The file `SourceCodePro-LICENSE.txt`, the license text of the Source Code Pro font. - pub static LICENSE: &'static [u8] = include_bytes!("static/SourceCodePro-LICENSE.txt"); + pub static LICENSE: &[u8] = include_bytes!("static/SourceCodePro-LICENSE.txt"); } /// Files related to the sidebar in rustdoc sources. pub mod sidebar { /// File script to handle sidebar. - pub static SOURCE_SCRIPT: &'static str = include_str!("static/source-script.js"); + pub static SOURCE_SCRIPT: &str = include_str!("static/source-script.js"); } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 4ea14ab9077..3c1f0509bba 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -38,6 +38,7 @@ extern crate rustc_session; extern crate rustc_span as rustc_span; extern crate rustc_target; +extern crate rustc_trait_selection; extern crate rustc_typeck; extern crate test as testing; #[macro_use] @@ -48,8 +49,8 @@ use std::panic; use std::process; -use rustc::session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; -use rustc::session::{early_error, early_warn}; +use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; +use rustc_session::{early_error, early_warn}; #[macro_use] mod externalfiles; diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index d4a7f3313a4..f48224512ba 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -1,4 +1,5 @@ use crate::clean; +use crate::config::OutputFormat; use crate::core::DocContext; use crate::fold::{self, DocFolder}; use crate::passes::Pass; @@ -6,6 +7,8 @@ use rustc_ast::attr; use rustc_span::symbol::sym; use rustc_span::FileName; +use serde::Serialize; +use serde_json; use std::collections::BTreeMap; use std::ops; @@ -16,16 +19,16 @@ description: "counts the number of items with and without documentation", }; -fn calculate_doc_coverage(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate { - let mut calc = CoverageCalculator::default(); +fn calculate_doc_coverage(krate: clean::Crate, ctx: &DocContext<'_>) -> clean::Crate { + let mut calc = CoverageCalculator::new(); let krate = calc.fold_crate(krate); - calc.print_results(); + calc.print_results(ctx.renderinfo.borrow().output_format); krate } -#[derive(Default, Copy, Clone)] +#[derive(Default, Copy, Clone, Serialize)] struct ItemCount { total: u64, with_docs: u64, @@ -64,13 +67,41 @@ fn add_assign(&mut self, rhs: Self) { } } -#[derive(Default)] struct CoverageCalculator { items: BTreeMap, } +fn limit_filename_len(filename: String) -> String { + let nb_chars = filename.chars().count(); + if nb_chars > 35 { + "...".to_string() + + &filename[filename.char_indices().nth(nb_chars - 32).map(|x| x.0).unwrap_or(0)..] + } else { + filename + } +} + impl CoverageCalculator { - fn print_results(&self) { + fn new() -> CoverageCalculator { + CoverageCalculator { items: Default::default() } + } + + fn to_json(&self) -> String { + serde_json::to_string( + &self + .items + .iter() + .map(|(k, v)| (k.to_string(), v)) + .collect::>(), + ) + .expect("failed to convert JSON data to string") + } + + fn print_results(&self, output_format: Option) { + if output_format.map(|o| o.is_json()).unwrap_or_else(|| false) { + println!("{}", self.to_json()); + return; + } let mut total = ItemCount::default(); fn print_table_line() { @@ -93,15 +124,7 @@ fn print_table_record(name: &str, count: ItemCount, percentage: f64) { for (file, &count) in &self.items { if let Some(percentage) = count.percentage() { - let mut name = file.to_string(); - // if a filename is too long, shorten it so we don't blow out the table - // FIXME(misdreavus): this needs to count graphemes, and probably also track - // double-wide characters... - if name.len() > 35 { - name = "...".to_string() + &name[name.len() - 32..]; - } - - print_table_record(&name, count, percentage); + print_table_record(&limit_filename_len(file.to_string()), count, percentage); total += count; } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 7aa90d66781..113c781e332 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1,4 +1,3 @@ -use rustc::lint; use rustc::ty; use rustc_ast::ast::{self, Ident}; use rustc_errors::Applicability; @@ -12,6 +11,7 @@ }; use rustc_hir::def_id::DefId; use rustc_resolve::ParentScope; +use rustc_session::lint; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; @@ -149,7 +149,7 @@ fn resolve( // In case this is a trait item, skip the // early return and try looking for the trait. let value = match res { - Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true, + Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::AssocConst, _) => true, Res::Def(DefKind::AssocTy, _) => false, Res::Def(DefKind::Variant, _) => { return handle_variant(cx, res, extra_fragment); @@ -348,7 +348,7 @@ fn fold_item(&mut self, mut item: Item) -> Option { let parent_node = self.cx.as_local_hir_id(item.def_id).and_then(|hir_id| { // FIXME: this fails hard for impls in non-module scope, but is necessary for the // current `resolve()` implementation. - match self.cx.as_local_hir_id(self.cx.tcx.parent_module(hir_id)).unwrap() { + match self.cx.as_local_hir_id(self.cx.tcx.parent_module(hir_id).to_def_id()).unwrap() { id if id != hir_id => Some(id), _ => None, } @@ -813,7 +813,7 @@ fn ambiguity_error( for (res, ns) in candidates { let (action, mut suggestion) = match res { - Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => { + Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::Fn, _) => { ("add parentheses", format!("{}()", path_str)) } Res::Def(DefKind::Macro(..), _) => { diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 71cff637c12..38f371783e9 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -1,9 +1,9 @@ //! Contains information about "passes", used to modify crate information during the documentation //! process. -use rustc::lint; use rustc::middle::privacy::AccessLevels; use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_session::lint; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use std::mem; use std::ops::Range; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index b63dbbf80d8..c5aa4677d56 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -1,5 +1,4 @@ use rustc::hir::map::Map; -use rustc::session::{self, config, DiagnosticOutput}; use rustc::util::common::ErrorReported; use rustc_ast::ast; use rustc_ast::with_globals; @@ -8,6 +7,7 @@ use rustc_hir as hir; use rustc_hir::intravisit; use rustc_interface::interface; +use rustc_session::{self, config, DiagnosticOutput, Session}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; @@ -52,7 +52,7 @@ pub fn run(options: Options) -> i32 { cg: options.codegen_options.clone(), externs: options.externs.clone(), unstable_features: UnstableFeatures::from_environment(), - lint_cap: Some(::rustc::lint::Level::Allow), + lint_cap: Some(rustc_session::lint::Level::Allow), actually_rustdoc: true, debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() }, edition: options.edition, @@ -107,12 +107,12 @@ pub fn run(options: Options) -> i32 { let mut hir_collector = HirCollector { sess: compiler.session(), collector: &mut collector, - map: *tcx.hir(), + map: tcx.hir(), codes: ErrorCodes::from( compiler.session().opts.unstable_features.is_nightly_build(), ), }; - hir_collector.visit_testable("".to_string(), &krate.attrs, |this| { + hir_collector.visit_testable("".to_string(), &krate.item.attrs, |this| { intravisit::walk_crate(this, krate); }); }); @@ -146,6 +146,7 @@ fn scrape_test_config(krate: &::rustc_hir::Crate) -> TestOptions { TestOptions { no_crate_inject: false, display_warnings: false, attrs: Vec::new() }; let test_attrs: Vec<_> = krate + .item .attrs .iter() .filter(|a| a.check_name(sym::doc)) @@ -449,7 +450,7 @@ pub fn make_test( } if !found_macro { - if let ast::ItemKind::Mac(..) = item.kind { + if let ast::ItemKind::MacCall(..) = item.kind { found_macro = true; } } @@ -853,9 +854,9 @@ fn register_header(&mut self, name: &str, level: u32) { } struct HirCollector<'a, 'hir> { - sess: &'a session::Session, + sess: &'a Session, collector: &'a mut Collector, - map: &'a Map<'hir>, + map: Map<'hir>, codes: ErrorCodes, } @@ -903,8 +904,8 @@ fn visit_testable( impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { type Map = Map<'hir>; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { - intravisit::NestedVisitorMap::All(&self.map) + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.map) } fn visit_item(&mut self, item: &'hir hir::Item) { @@ -955,7 +956,7 @@ fn visit_struct_field(&mut self, f: &'hir hir::StructField) { } fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef) { - self.visit_testable(macro_def.name.to_string(), ¯o_def.attrs, |_| ()); + self.visit_testable(macro_def.ident.to_string(), ¯o_def.attrs, |_| ()); } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 8f2f88d08bf..feaf391c95a 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -64,11 +64,11 @@ fn store_path(&mut self, did: DefId) { pub fn visit(mut self, krate: &'tcx hir::Crate) -> Module<'tcx> { let mut module = self.visit_mod_contents( - krate.span, - krate.attrs, + krate.item.span, + krate.item.attrs, &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public }, hir::CRATE_HIR_ID, - &krate.module, + &krate.item.module, None, ); // Attach the crate's exported macros to the top-level module: @@ -620,8 +620,8 @@ fn visit_local_macro( def: &'tcx hir::MacroDef, renamed: Option, ) -> Macro<'tcx> { - debug!("visit_local_macro: {}", def.name); - let tts = def.body.trees().collect::>(); + debug!("visit_local_macro: {}", def.ident); + let tts = def.ast.body.inner_tokens().trees().collect::>(); // Extract the spans of all matchers. They represent the "interface" of the macro. let matchers = tts.chunks(4).map(|arm| arm[0].span()).collect(); @@ -629,7 +629,7 @@ fn visit_local_macro( hid: def.hir_id, def_id: self.cx.tcx.hir().local_def_id(def.hir_id), attrs: &def.attrs, - name: renamed.unwrap_or(def.name), + name: renamed.unwrap_or(def.ident.name), whence: def.span, matchers, imported_from: None, diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 8965c6860c4..25f3ddcbeba 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -133,22 +133,41 @@ #[derive(Debug, Default, Copy, Clone)] pub struct System; -// The AllocRef impl just forwards to the GlobalAlloc impl, which is in `std::sys::*::alloc`. +// The AllocRef impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl, +// which is in `std::sys::*::alloc`. #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) + fn alloc(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { + if layout.size() == 0 { + Ok((layout.dangling(), 0)) + } else { + unsafe { + NonNull::new(GlobalAlloc::alloc(self, layout)) + .ok_or(AllocErr) + .map(|p| (p, layout.size())) + } + } } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) + fn alloc_zeroed(&mut self, layout: Layout) -> Result<(NonNull, usize), AllocErr> { + if layout.size() == 0 { + Ok((layout.dangling(), 0)) + } else { + unsafe { + NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)) + .ok_or(AllocErr) + .map(|p| (p, layout.size())) + } + } } #[inline] unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) + if layout.size() != 0 { + GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) + } } #[inline] @@ -157,8 +176,18 @@ unsafe fn realloc( ptr: NonNull, layout: Layout, new_size: usize, - ) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) + ) -> Result<(NonNull, usize), AllocErr> { + match (layout.size(), new_size) { + (0, 0) => Ok((layout.dangling(), 0)), + (0, _) => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())), + (_, 0) => { + self.dealloc(ptr, layout); + Ok((layout.dangling(), 0)) + } + (_, _) => NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)) + .ok_or(AllocErr) + .map(|p| (p, new_size)), + } } } diff --git a/src/libstd/backtrace.rs b/src/libstd/backtrace.rs index 97db0ff3791..34317c7a2ee 100644 --- a/src/libstd/backtrace.rs +++ b/src/libstd/backtrace.rs @@ -92,6 +92,7 @@ // a backtrace or actually symbolizing it. use crate::env; +use crate::ffi::c_void; use crate::fmt; use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sync::Mutex; @@ -144,10 +145,16 @@ fn _assert() {} } struct BacktraceFrame { - frame: backtrace::Frame, + frame: RawFrame, symbols: Vec, } +enum RawFrame { + Actual(backtrace::Frame), + #[cfg(test)] + Fake, +} + struct BacktraceSymbol { name: Option>, filename: Option, @@ -162,8 +169,8 @@ enum BytesOrWide { impl fmt::Debug for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut capture = match &self.inner { - Inner::Unsupported => return fmt.write_str("unsupported backtrace"), - Inner::Disabled => return fmt.write_str("disabled backtrace"), + Inner::Unsupported => return fmt.write_str(""), + Inner::Disabled => return fmt.write_str(""), Inner::Captured(c) => c.lock().unwrap(), }; capture.resolve(); @@ -193,11 +200,11 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(fn_name) = self.name.as_ref().map(|b| backtrace::SymbolName::new(b)) { write!(fmt, "fn: \"{:#}\"", fn_name)?; } else { - write!(fmt, "fn: \"\"")?; + write!(fmt, "fn: ")?; } if let Some(fname) = self.filename.as_ref() { - write!(fmt, ", file: {:?}", fname)?; + write!(fmt, ", file: \"{:?}\"", fname)?; } if let Some(line) = self.lineno.as_ref() { @@ -293,7 +300,10 @@ fn create(ip: usize) -> Backtrace { let mut actual_start = None; unsafe { backtrace::trace_unsynchronized(|frame| { - frames.push(BacktraceFrame { frame: frame.clone(), symbols: Vec::new() }); + frames.push(BacktraceFrame { + frame: RawFrame::Actual(frame.clone()), + symbols: Vec::new(), + }); if frame.symbol_address() as usize == ip && actual_start.is_none() { actual_start = Some(frames.len()); } @@ -393,8 +403,13 @@ fn resolve(&mut self) { let _lock = lock(); for frame in self.frames.iter_mut() { let symbols = &mut frame.symbols; + let frame = match &frame.frame { + RawFrame::Actual(frame) => frame, + #[cfg(test)] + RawFrame::Fake => unimplemented!(), + }; unsafe { - backtrace::resolve_frame_unsynchronized(&frame.frame, |symbol| { + backtrace::resolve_frame_unsynchronized(frame, |symbol| { symbols.push(BacktraceSymbol { name: symbol.name().map(|m| m.as_bytes().to_vec()), filename: symbol.filename_raw().map(|b| match b { @@ -408,3 +423,65 @@ fn resolve(&mut self) { } } } + +impl RawFrame { + fn ip(&self) -> *mut c_void { + match self { + RawFrame::Actual(frame) => frame.ip(), + #[cfg(test)] + RawFrame::Fake => 1 as *mut c_void, + } + } +} + +#[test] +fn test_debug() { + let backtrace = Backtrace { + inner: Inner::Captured(Mutex::new(Capture { + actual_start: 1, + resolved: true, + frames: vec![ + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"std::backtrace::Backtrace::create".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), + lineno: Some(100), + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"__rust_maybe_catch_panic".to_vec()), + filename: None, + lineno: None, + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![ + BacktraceSymbol { + name: Some(b"std::rt::lang_start_internal".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(300), + }, + BacktraceSymbol { + name: Some(b"std::rt::lang_start".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(400), + }, + ], + }, + ], + })), + }; + + #[rustfmt::skip] + let expected = "Backtrace [\ + \n { fn: \"__rust_maybe_catch_panic\" },\ + \n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\ + \n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\ + \n]"; + + assert_eq!(format!("{:#?}", backtrace), expected); +} diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index d0e1a01b006..44f8e8bd171 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2461,7 +2461,7 @@ pub fn new() -> RandomState { KEYS.with(|keys| { let (k0, k1) = keys.get(); keys.set((k0.wrapping_add(1), k1)); - RandomState { k0: k0, k1: k1 } + RandomState { k0, k1 } }) } } diff --git a/src/libstd/env.rs b/src/libstd/env.rs index af35a5d9b7c..6aad082a97f 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -723,8 +723,8 @@ pub struct ArgsOs { /// (such as `*` and `?`). On Windows this is not done, and such arguments are /// passed as-is. /// -/// On glibc Linux, arguments are retrieved by placing a function in .init_array. -/// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. +/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array". +/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension. /// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS /// and Windows. /// @@ -758,8 +758,8 @@ pub fn args() -> Args { /// set to arbitrary text, and it may not even exist, so this property should /// not be relied upon for security purposes. /// -/// On glibc Linux, arguments are retrieved by placing a function in .init_array. -/// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. +/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array". +/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension. /// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS /// and Windows. /// diff --git a/src/libstd/error.rs b/src/libstd/error.rs index b480581e21b..2a370f19296 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -135,7 +135,7 @@ fn backtrace(&self) -> Option<&Backtrace> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.41.0", reason = "use the Display impl or to_string()")] + #[rustc_deprecated(since = "1.42.0", reason = "use the Display impl or to_string()")] fn description(&self) -> &str { "description() is deprecated; use Display" } @@ -552,6 +552,9 @@ fn description(&self) -> &str { } } +#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] +impl Error for alloc::collections::TryReserveError {} + // Copied from `any.rs`. impl dyn Error + 'static { /// Returns `true` if the boxed type is the same as `T` diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 941ea6a767c..20425aea8d5 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -4,6 +4,9 @@ //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! Although using these constants won’t cause compilation warnings, +//! new code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index d89b38e1a00..a1128a589a6 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -4,6 +4,9 @@ //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! Although using these constants won’t cause compilation warnings, +//! new code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 09be3f13050..e20fcfafa22 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -407,7 +407,7 @@ pub fn create>(path: P) -> io::Result { /// /// It is equivalent to `OpenOptions::new()` but allows you to write more /// readable code. Instead of `OpenOptions::new().read(true).open("foo.txt")` - /// you can write `File::with_options().read(true).open("foo.txt"). This + /// you can write `File::with_options().read(true).open("foo.txt")`. This /// also avoids the need to import `OpenOptions`. /// /// See the [`OpenOptions::new`] function for more details. diff --git a/src/libstd/future.rs b/src/libstd/future.rs index 7b1beb1ecda..c0675eeba98 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -1,12 +1,14 @@ //! Asynchronous values. -use core::cell::Cell; -use core::marker::Unpin; -use core::ops::{Drop, Generator, GeneratorState}; -use core::option::Option; -use core::pin::Pin; -use core::ptr::NonNull; -use core::task::{Context, Poll}; +#[cfg(bootstrap)] +use core::{ + cell::Cell, + marker::Unpin, + ops::{Drop, Generator, GeneratorState}, + pin::Pin, + ptr::NonNull, + task::{Context, Poll}, +}; #[doc(inline)] #[stable(feature = "futures_api", since = "1.36.0")] @@ -17,6 +19,7 @@ /// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give /// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). // This is `const` to avoid extra errors after we recover from `const async fn` +#[cfg(bootstrap)] #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] pub const fn from_generator>(x: T) -> impl Future { @@ -24,6 +27,7 @@ pub const fn from_generator>(x: T) -> impl Future>(x: T) -> impl Future> !Unpin for GenFuture {} +#[cfg(bootstrap)] #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] impl> Future for GenFuture { @@ -41,22 +47,22 @@ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // Safe because we're !Unpin + !Drop mapping to a ?Unpin value let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; let _guard = unsafe { set_task_context(cx) }; - match gen.resume( - #[cfg(not(bootstrap))] - (), - ) { + match gen.resume(()) { GeneratorState::Yielded(()) => Poll::Pending, GeneratorState::Complete(x) => Poll::Ready(x), } } } +#[cfg(bootstrap)] thread_local! { static TLS_CX: Cell>>> = Cell::new(None); } +#[cfg(bootstrap)] struct SetOnDrop(Option>>); +#[cfg(bootstrap)] impl Drop for SetOnDrop { fn drop(&mut self) { TLS_CX.with(|tls_cx| { @@ -67,6 +73,7 @@ fn drop(&mut self) { // Safety: the returned guard must drop before `cx` is dropped and before // any previous guard is dropped. +#[cfg(bootstrap)] unsafe fn set_task_context(cx: &mut Context<'_>) -> SetOnDrop { // transmute the context's lifetime to 'static so we can store it. let cx = core::mem::transmute::<&mut Context<'_>, &mut Context<'static>>(cx); @@ -74,6 +81,7 @@ unsafe fn set_task_context(cx: &mut Context<'_>) -> SetOnDrop { SetOnDrop(old_cx) } +#[cfg(bootstrap)] #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] /// Polls a future in the current thread-local task waker. diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 9787cbb556b..f36aa1846a1 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -96,7 +96,7 @@ impl Cursor { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new(inner: T) -> Cursor { - Cursor { pos: 0, inner: inner } + Cursor { pos: 0, inner } } /// Consumes this cursor, returning the underlying value. diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index a50dd9575de..dc831432c17 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -371,7 +371,7 @@ fn read_to_end_with_reservation( F: FnMut(&R) -> usize, { let start_len = buf.len(); - let mut g = Guard { len: buf.len(), buf: buf }; + let mut g = Guard { len: buf.len(), buf }; let ret; loop { if g.len == g.buf.len() { @@ -497,7 +497,6 @@ pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Resul /// [`&str`]: ../../std/primitive.str.html /// [slice]: ../../std/primitive.slice.html #[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] pub trait Read { /// Pull some bytes from this source into the specified buffer, returning /// how many bytes were read. @@ -939,7 +938,7 @@ fn take(self, limit: u64) -> Take where Self: Sized, { - Take { inner: self, limit: limit } + Take { inner: self, limit } } } @@ -1051,6 +1050,7 @@ fn deref_mut(&mut self) -> &mut [u8] { /// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on /// Windows. #[stable(feature = "iovec", since = "1.36.0")] +#[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a>(sys::io::IoSlice<'a>); @@ -1228,7 +1228,6 @@ pub fn initialize(&self, buf: &mut [u8]) { /// /// [`write_all`]: #method.write_all #[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] pub trait Write { /// Write a buffer into this writer, returning how many bytes were written. /// diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index d410faca30d..0fb0757792e 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -792,10 +792,14 @@ fn print_to( { let result = local_s .try_with(|s| { - if let Ok(mut borrowed) = s.try_borrow_mut() { - if let Some(w) = borrowed.as_mut() { - return w.write_fmt(args); - } + // Note that we completely remove a local sink to write to in case + // our printing recursively panics/prints, so the recursive + // panic/print goes to the global sink instead of our local sink. + let prev = s.borrow_mut().take(); + if let Some(mut w) = prev { + let result = w.write_fmt(args); + *s.borrow_mut() = Some(w); + return result; } global_s().write_fmt(args) }) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index 3c69c1160d5..314424631fc 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -234,12 +234,55 @@ mod crate_keyword {} #[doc(keyword = "else")] // -/// What to do when an [`if`] condition does not hold. +/// What expression to evaluate when an [`if`] condition evaluates to [`false`]. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// `else` expressions are optional. When no else expressions are supplied it is assumed to evaluate +/// to the unit type `()`. +/// +/// The type that the `else` blocks evaluate to must be compatible with the type that the `if` block +/// evaluates to. +/// +/// As can be seen below, `else` must be followed by either: `if`, `if let`, or a block `{}` and it +/// will return the value of that expression. +/// +/// ```rust +/// let result = if true == false { +/// "oh no" +/// } else if "something" == "other thing" { +/// "oh dear" +/// } else if let Some(200) = "blarg".parse::().ok() { +/// "uh oh" +/// } else { +/// println!("Sneaky side effect."); +/// "phew, nothing's broken" +/// }; +/// ``` +/// +/// Here's another example but here we do not try and return an expression: +/// +/// ```rust +/// if true == false { +/// println!("oh no"); +/// } else if "something" == "other thing" { +/// println!("oh dear"); +/// } else if let Some(200) = "blarg".parse::().ok() { +/// println!("uh oh"); +/// } else { +/// println!("phew, nothing's broken"); +/// } +/// ``` /// +/// The above is _still_ an expression but it will always evaluate to `()`. +/// +/// There is possibly no limit to the number of `else` blocks that could follow an `if` expression +/// however if you have several then a [`match`] expression might be preferable. +/// +/// Read more about control flow in the [Rust Book]. +/// +/// [Rust Book]: ../book/ch03-05-control-flow.html#handling-multiple-conditions-with-else-if +/// [`match`]: keyword.match.html +/// [`false`]: keyword.false.html /// [`if`]: keyword.if.html -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 mod else_keyword {} #[doc(keyword = "enum")] @@ -637,10 +680,18 @@ mod impl_keyword {} // /// Iterate over a series of values with [`for`]. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// The expression immediately following `in` must implement the [`Iterator`] trait. +/// +/// ## Literal Examples: +/// +/// * `for _ **in** 1..3 {}` - Iterate over an exclusive range up to but excluding 3. +/// * `for _ **in** 1..=3 {}` - Iterate over an inclusive range up to and includeing 3. +/// +/// (Read more about [range patterns]) /// +/// [`Iterator`]: ../book/ch13-04-performance.html +/// [`range patterns`]: ../reference/patterns.html?highlight=range#range-patterns /// [`for`]: keyword.for.html -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 mod in_keyword {} #[doc(keyword = "let")] @@ -925,9 +976,15 @@ mod mut_keyword {} // /// Make an item visible to others. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// The keyword `pub` makes any module, function, or data structure accessible from inside +/// of external modules. The `pub` keyword may also be used in a `use` declaration to re-export +/// an identifier from a namespace. /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// For more information on the `pub` keyword, please see the visibility section +/// of the [reference] and for some examples, see [Rust by Example]. +/// +/// [reference]:../reference/visibility-and-privacy.html?highlight=pub#visibility-and-privacy +/// [Rust by Example]:../rust-by-example/mod/visibility.html mod pub_keyword {} #[doc(keyword = "ref")] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 7b3c702b929..1f122b02b6a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -236,11 +236,11 @@ #![feature(arbitrary_self_types)] #![feature(array_error_internals)] #![feature(asm)] -#![feature(assoc_int_consts)] #![feature(associated_type_bounds)] #![feature(atomic_mut_ptr)] #![feature(box_syntax)] #![feature(c_variadic)] +#![cfg_attr(not(bootstrap), feature(cfg_accessible))] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] @@ -256,7 +256,6 @@ #![feature(doc_cfg)] #![feature(doc_keyword)] #![feature(doc_masked)] -#![feature(doc_spotlight)] #![feature(dropck_eyepatch)] #![feature(duration_constants)] #![feature(exact_size_is_empty)] @@ -294,7 +293,8 @@ #![feature(shrink_to)] #![feature(slice_concat_ext)] #![feature(slice_internals)] -#![feature(specialization)] +#![cfg_attr(bootstrap, feature(specialization))] +#![cfg_attr(not(bootstrap), feature(min_specialization))] #![feature(staged_api)] #![feature(std_internals)] #![feature(stdsimd)] diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 9e1ac8754d9..7fd7de56f46 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -185,7 +185,7 @@ macro_rules! eprintln { /// builds or when debugging in release mode is significantly faster. /// /// Note that the macro is intended as a debugging tool and therefore you -/// should avoid having uses of it in version control for longer periods. +/// should avoid having uses of it in version control for long periods. /// Use cases involving debug output that should be added to version control /// are better served by macros such as [`debug!`] from the [`log`] crate. /// diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index a59d7f0263b..57cba6b1f7a 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -901,7 +901,7 @@ impl ToSocketAddrs for str { type Iter = vec::IntoIter; fn to_socket_addrs(&self) -> io::Result> { // try to parse as a regular SocketAddr first - if let Some(addr) = self.parse().ok() { + if let Ok(addr) = self.parse() { return Ok(vec![addr].into_iter()); } diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 200b00b1195..edc28033c9b 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -651,7 +651,7 @@ pub fn is_benchmarking(&self) -> bool { /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitely excludes it, since + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since /// it is obviously not reserved for future use. /// /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs index 868a7e261c4..3f38ee54710 100644 --- a/src/libstd/net/parser.rs +++ b/src/libstd/net/parser.rs @@ -206,7 +206,7 @@ fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> Ipv6Addr { } // read `::` if previous code parsed less than 8 groups - if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() { + if self.read_given_char(':').is_none() || self.read_given_char(':').is_none() { return None; } diff --git a/src/libstd/os/linux/fs.rs b/src/libstd/os/linux/fs.rs index dd71201b50b..d22b44a0666 100644 --- a/src/libstd/os/linux/fs.rs +++ b/src/libstd/os/linux/fs.rs @@ -34,7 +34,7 @@ pub trait MetadataExt { /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated(since = "1.8.0", reason = "other methods of this trait are now prefered")] + #[rustc_deprecated(since = "1.8.0", reason = "other methods of this trait are now preferred")] #[allow(deprecated)] fn as_raw_stat(&self) -> &raw::stat; diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 8b12aaaa7e2..10078bd4aee 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -14,7 +14,6 @@ use crate::intrinsics; use crate::mem::{self, ManuallyDrop}; use crate::process; -use crate::raw; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::stdio::panic_output; use crate::sys_common::backtrace::{self, RustBacktrace}; @@ -41,12 +40,7 @@ // hook up these functions, but it is not this day! #[allow(improper_ctypes)] extern "C" { - fn __rust_maybe_catch_panic( - f: fn(*mut u8), - data: *mut u8, - data_ptr: *mut usize, - vtable_ptr: *mut usize, - ) -> u32; + fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); /// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings. /// It cannot be `Box` because the other end of this call does not depend @@ -247,54 +241,88 @@ pub unsafe fn r#try R>(f: F) -> Result> union Data { f: ManuallyDrop, r: ManuallyDrop, + p: ManuallyDrop>, } // We do some sketchy operations with ownership here for the sake of - // performance. We can only pass pointers down to - // `__rust_maybe_catch_panic` (can't pass objects by value), so we do all - // the ownership tracking here manually using a union. + // performance. We can only pass pointers down to `do_call` (can't pass + // objects by value), so we do all the ownership tracking here manually + // using a union. // // We go through a transition where: // - // * First, we set the data to be the closure that we're going to call. + // * First, we set the data field `f` to be the argumentless closure that we're going to call. // * When we make the function call, the `do_call` function below, we take - // ownership of the function pointer. At this point the `Data` union is + // ownership of the function pointer. At this point the `data` union is // entirely uninitialized. // * If the closure successfully returns, we write the return value into the - // data's return slot. Note that `ptr::write` is used as it's overwriting - // uninitialized data. - // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're + // data's return slot (field `r`). + // * If the closure panics (`do_catch` below), we write the panic payload into field `p`. + // * Finally, when we come back out of the `try` intrinsic we're // in one of two states: // // 1. The closure didn't panic, in which case the return value was - // filled in. We move it out of `data` and return it. - // 2. The closure panicked, in which case the return value wasn't - // filled in. In this case the entire `data` union is invalid, so - // there is no need to drop anything. + // filled in. We move it out of `data.r` and return it. + // 2. The closure panicked, in which case the panic payload was + // filled in. We move it out of `data.p` and return it. // // Once we stack all that together we should have the "most efficient' // method of calling a catch panic whilst juggling ownership. - let mut any_data = 0; - let mut any_vtable = 0; let mut data = Data { f: ManuallyDrop::new(f) }; - let r = __rust_maybe_catch_panic( - do_call::, - &mut data as *mut _ as *mut u8, - &mut any_data, - &mut any_vtable, - ); - - return if r == 0 { + let data_ptr = &mut data as *mut _ as *mut u8; + return if do_try(do_call::, data_ptr, do_catch::) == 0 { Ok(ManuallyDrop::into_inner(data.r)) } else { - update_panic_count(-1); - Err(mem::transmute(raw::TraitObject { - data: any_data as *mut _, - vtable: any_vtable as *mut _, - })) + Err(ManuallyDrop::into_inner(data.p)) }; + // Compatibility wrapper around the try intrinsic for bootstrap. + // + // We also need to mark it #[inline(never)] to work around a bug on MinGW + // targets: the unwinding implementation was relying on UB, but this only + // becomes a problem in practice if inlining is involved. + #[cfg(not(bootstrap))] + use intrinsics::r#try as do_try; + #[cfg(bootstrap)] + #[inline(never)] + unsafe fn do_try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32 { + use crate::mem::MaybeUninit; + #[cfg(target_env = "msvc")] + type TryPayload = [u64; 2]; + #[cfg(not(target_env = "msvc"))] + type TryPayload = *mut u8; + + let mut payload: MaybeUninit = MaybeUninit::uninit(); + let payload_ptr = payload.as_mut_ptr() as *mut u8; + let r = intrinsics::r#try(try_fn, data, payload_ptr); + if r != 0 { + #[cfg(target_env = "msvc")] + { + catch_fn(data, payload_ptr) + } + #[cfg(not(target_env = "msvc"))] + { + catch_fn(data, payload.assume_init()) + } + } + r + } + + // We consider unwinding to be rare, so mark this function as cold. However, + // do not mark it no-inline -- that decision is best to leave to the + // optimizer (in most cases this function is not inlined even as a normal, + // non-cold function, though, as of the writing of this comment). + #[cold] + unsafe fn cleanup(payload: *mut u8) -> Box { + let obj = Box::from_raw(__rust_panic_cleanup(payload)); + update_panic_count(-1); + obj + } + + // See comment on do_try above for why #[inline(never)] is needed on bootstrap. + #[cfg_attr(bootstrap, inline(never))] + #[cfg_attr(not(bootstrap), inline)] fn do_call R, R>(data: *mut u8) { unsafe { let data = data as *mut Data; @@ -303,6 +331,19 @@ fn do_call R, R>(data: *mut u8) { data.r = ManuallyDrop::new(f()); } } + + // We *do* want this part of the catch to be inlined: this allows the + // compiler to properly track accesses to the Data union and optimize it + // away most of the time. + #[inline] + fn do_catch R, R>(data: *mut u8, payload: *mut u8) { + unsafe { + let data = data as *mut Data; + let data = &mut (*data); + let obj = cleanup(payload); + data.p = ManuallyDrop::new(obj); + } + } } /// Determines whether the current thread is unwinding because of panic. diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index 7c0efe828c2..6712f5ba580 100644 --- a/src/libstd/prelude/v1.rs +++ b/src/libstd/prelude/v1.rs @@ -53,6 +53,15 @@ PartialEq, PartialOrd, RustcDecodable, RustcEncodable, }; +#[cfg(not(bootstrap))] +#[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" +)] +#[doc(hidden)] +pub use core::prelude::v1::cfg_accessible; + // The file so far is equivalent to src/libcore/prelude/v1.rs, // and below to src/liballoc/prelude.rs. // Those files are duplicated rather than using glob imports diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index d4b41e11c0e..adad90f56d1 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -771,7 +771,7 @@ mod prim_tuple {} #[doc(primitive = "f32")] /// The 32-bit floating point type. /// -/// *[See also the `std::f32` module](f32/index.html).* +/// *[See also the `std::f32::consts` module](f32/consts/index.html).* /// #[stable(feature = "rust1", since = "1.0.0")] mod prim_f32 {} @@ -780,7 +780,7 @@ mod prim_f32 {} // /// The 64-bit floating point type. /// -/// *[See also the `std::f64` module](f64/index.html).* +/// *[See also the `std::f64::consts` module](f64/consts/index.html).* /// #[stable(feature = "rust1", since = "1.0.0")] mod prim_f64 {} @@ -788,80 +788,60 @@ mod prim_f64 {} #[doc(primitive = "i8")] // /// The 8-bit signed integer type. -/// -/// *[See also the `std::i8` module](i8/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_i8 {} #[doc(primitive = "i16")] // /// The 16-bit signed integer type. -/// -/// *[See also the `std::i16` module](i16/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_i16 {} #[doc(primitive = "i32")] // /// The 32-bit signed integer type. -/// -/// *[See also the `std::i32` module](i32/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_i32 {} #[doc(primitive = "i64")] // /// The 64-bit signed integer type. -/// -/// *[See also the `std::i64` module](i64/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_i64 {} #[doc(primitive = "i128")] // /// The 128-bit signed integer type. -/// -/// *[See also the `std::i128` module](i128/index.html).* #[stable(feature = "i128", since = "1.26.0")] mod prim_i128 {} #[doc(primitive = "u8")] // /// The 8-bit unsigned integer type. -/// -/// *[See also the `std::u8` module](u8/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_u8 {} #[doc(primitive = "u16")] // /// The 16-bit unsigned integer type. -/// -/// *[See also the `std::u16` module](u16/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_u16 {} #[doc(primitive = "u32")] // /// The 32-bit unsigned integer type. -/// -/// *[See also the `std::u32` module](u32/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_u32 {} #[doc(primitive = "u64")] // /// The 64-bit unsigned integer type. -/// -/// *[See also the `std::u64` module](u64/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_u64 {} #[doc(primitive = "u128")] // /// The 128-bit unsigned integer type. -/// -/// *[See also the `std::u128` module](u128/index.html).* #[stable(feature = "i128", since = "1.26.0")] mod prim_u128 {} @@ -869,8 +849,6 @@ mod prim_u128 {} // /// The pointer-sized signed integer type. /// -/// *[See also the `std::isize` module](isize/index.html).* -/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. @@ -881,8 +859,6 @@ mod prim_isize {} // /// The pointer-sized unsigned integer type. /// -/// *[See also the `std::usize` module](usize/index.html).* -/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/src/libstd/sync/mpsc/spsc_queue.rs index c51aa7619db..0274268f69f 100644 --- a/src/libstd/sync/mpsc/spsc_queue.rs +++ b/src/libstd/sync/mpsc/spsc_queue.rs @@ -40,7 +40,7 @@ struct Consumer { tail: UnsafeCell<*mut Node>, // where to pop from tail_prev: AtomicPtr>, // where to pop from cache_bound: usize, // maximum cache size - cached_nodes: AtomicUsize, // number of nodes marked as cachable + cached_nodes: AtomicUsize, // number of nodes marked as cacheable addition: Addition, } diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 6eeddc28512..0cb16b19d73 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -416,7 +416,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { unsafe fn new(lock: &'mutex Mutex) -> LockResult> { - poison::map_result(lock.poison.borrow(), |guard| MutexGuard { lock: lock, poison: guard }) + poison::map_result(lock.poison.borrow(), |guard| MutexGuard { lock, poison: guard }) } } diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index b99b4d8d9fd..1e6b6c430be 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -363,7 +363,7 @@ pub fn call_once_force(&self, f: F) /// assert!(handle.join().is_err()); /// assert_eq!(INIT.is_completed(), false); /// ``` - #[stable(feature = "once_is_completed", since = "1.44.0")] + #[stable(feature = "once_is_completed", since = "1.43.0")] #[inline] pub fn is_completed(&self) -> bool { // An `Acquire` load is enough because that makes all the initialization diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index fdd29af8581..50f54dbf143 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -465,16 +465,13 @@ fn from(t: T) -> Self { impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { - poison::map_result(lock.poison.borrow(), |_| RwLockReadGuard { lock: lock }) + poison::map_result(lock.poison.borrow(), |_| RwLockReadGuard { lock }) } } impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { - poison::map_result(lock.poison.borrow(), |guard| RwLockWriteGuard { - lock: lock, - poison: guard, - }) + poison::map_result(lock.poison.borrow(), |guard| RwLockWriteGuard { lock, poison: guard }) } } diff --git a/src/libstd/sys/cloudabi/io.rs b/src/libstd/sys/cloudabi/io.rs index 976e122463d..d5f475b4310 100644 --- a/src/libstd/sys/cloudabi/io.rs +++ b/src/libstd/sys/cloudabi/io.rs @@ -1,5 +1,6 @@ use crate::mem; +#[derive(Copy, Clone)] pub struct IoSlice<'a>(&'a [u8]); impl<'a> IoSlice<'a> { diff --git a/src/libstd/sys/hermit/io.rs b/src/libstd/sys/hermit/io.rs index 976e122463d..d5f475b4310 100644 --- a/src/libstd/sys/hermit/io.rs +++ b/src/libstd/sys/hermit/io.rs @@ -1,5 +1,6 @@ use crate::mem; +#[derive(Copy, Clone)] pub struct IoSlice<'a>(&'a [u8]); impl<'a> IoSlice<'a> { diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/src/libstd/sys/sgx/abi/usercalls/alloc.rs index b54c115a2b6..76a9b427b39 100644 --- a/src/libstd/sys/sgx/abi/usercalls/alloc.rs +++ b/src/libstd/sys/sgx/abi/usercalls/alloc.rs @@ -151,7 +151,7 @@ unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self { /// It is also possible to obtain a mutable reference `&mut UserRef`. Unlike /// regular mutable references, these are not exclusive. Userspace may always /// write to the backing memory at any time, so it can't be assumed that there -/// the pointed-to memory is uniquely borrowed. The two different refence types +/// the pointed-to memory is uniquely borrowed. The two different reference types /// are used solely to indicate intent: a mutable reference is for writing to /// user memory, an immutable reference for reading from user memory. #[unstable(feature = "sgx_platform", issue = "56975")] diff --git a/src/libstd/sys/sgx/io.rs b/src/libstd/sys/sgx/io.rs index 976e122463d..d5f475b4310 100644 --- a/src/libstd/sys/sgx/io.rs +++ b/src/libstd/sys/sgx/io.rs @@ -1,5 +1,6 @@ use crate::mem; +#[derive(Copy, Clone)] pub struct IoSlice<'a>(&'a [u8]); impl<'a> IoSlice<'a> { diff --git a/src/libstd/sys/unix/io.rs b/src/libstd/sys/unix/io.rs index b4a64e93c84..deb5ee76bd0 100644 --- a/src/libstd/sys/unix/io.rs +++ b/src/libstd/sys/unix/io.rs @@ -3,6 +3,7 @@ use libc::{c_void, iovec}; +#[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { vec: iovec, diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 06876cb0614..fbcb006ecdf 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -156,7 +156,7 @@ pub fn cvt_r(mut f: F) -> crate::io::Result // On Unix-like platforms, libc::abort will unregister signal handlers // including the SIGABRT handler, preventing the abort from being blocked, and -// fclose streams, with the side effect of flushing them so libc bufferred +// fclose streams, with the side effect of flushing them so libc buffered // output will be printed. Additionally the shell will generally print a more // understandable error message like "Abort trap" rather than "Illegal // instruction" that intrinsics::abort would cause, as intrinsics::abort is diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 79b0dc02978..b37675e0a0a 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -280,7 +280,7 @@ pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Resul }; let mut timeout = libc::timeval { tv_sec: secs, - tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, + tv_usec: dur.subsec_micros() as libc::suseconds_t, }; if timeout.tv_sec == 0 && timeout.tv_usec == 0 { timeout.tv_usec = 1; diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index 83f052c898b..859da691ad2 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -19,9 +19,9 @@ if #[cfg(target_os = "fuchsia")] { // fuchsia doesn't have /dev/null } else if #[cfg(target_os = "redox")] { - const DEV_NULL: &'static str = "null:\0"; + const DEV_NULL: &str = "null:\0"; } else { - const DEV_NULL: &'static str = "/dev/null\0"; + const DEV_NULL: &str = "/dev/null\0"; } } diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 528fe321efb..2626ca37cf8 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -13,6 +13,10 @@ impl Handler { pub unsafe fn new() -> Handler { make_handler() } + + fn null() -> Handler { + Handler { _data: crate::ptr::null_mut() } + } } impl Drop for Handler { @@ -41,8 +45,9 @@ mod imp { use libc::{mmap, munmap}; use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE}; - use libc::{MAP_ANON, MAP_PRIVATE, PROT_READ, PROT_WRITE, SIGSEGV}; + use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV}; + use crate::sys::unix::os::page_size; use crate::sys_common::thread_info; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -108,13 +113,20 @@ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { } static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut(); + static mut NEED_ALTSTACK: bool = false; pub unsafe fn init() { let mut action: sigaction = mem::zeroed(); - action.sa_flags = SA_SIGINFO | SA_ONSTACK; - action.sa_sigaction = signal_handler as sighandler_t; - sigaction(SIGSEGV, &action, ptr::null_mut()); - sigaction(SIGBUS, &action, ptr::null_mut()); + for &signal in &[SIGSEGV, SIGBUS] { + sigaction(signal, ptr::null_mut(), &mut action); + // Configure our signal handler if one is not already set. + if action.sa_sigaction == SIG_DFL { + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_sigaction = signal_handler as sighandler_t; + sigaction(signal, &action, ptr::null_mut()); + NEED_ALTSTACK = true; + } + } let handler = make_handler(); MAIN_ALTSTACK = handler._data; @@ -126,12 +138,22 @@ pub unsafe fn cleanup() { } unsafe fn get_stackp() -> *mut libc::c_void { - let stackp = - mmap(ptr::null_mut(), SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + let stackp = mmap( + ptr::null_mut(), + SIGSTKSZ + page_size(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0, + ); if stackp == MAP_FAILED { panic!("failed to allocate an alternative stack"); } - stackp + let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE); + if guard_result != 0 { + panic!("failed to set up alternative stack guard page"); + } + stackp.add(page_size()) } #[cfg(any( @@ -152,6 +174,9 @@ unsafe fn get_stack() -> libc::stack_t { } pub unsafe fn make_handler() -> Handler { + if !NEED_ALTSTACK { + return Handler::null(); + } let mut stack = mem::zeroed(); sigaltstack(ptr::null(), &mut stack); // Configure alternate signal stack, if one is not already set. @@ -160,7 +185,7 @@ pub unsafe fn make_handler() -> Handler { sigaltstack(&stack, ptr::null_mut()); Handler { _data: stack.ss_sp as *mut libc::c_void } } else { - Handler { _data: ptr::null_mut() } + Handler::null() } } @@ -176,7 +201,9 @@ pub unsafe fn drop_handler(handler: &mut Handler) { ss_size: SIGSTKSZ, }; sigaltstack(&stack, ptr::null_mut()); - munmap(handler._data, SIGSTKSZ); + // We know from `get_stackp` that the alternate stack we installed is part of a mapping + // that started one page earlier, so walk back a page and unmap from there. + munmap(handler._data.sub(page_size()), SIGSTKSZ + page_size()); } } } @@ -191,14 +218,12 @@ pub unsafe fn drop_handler(handler: &mut Handler) { target_os = "openbsd" )))] mod imp { - use crate::ptr; - pub unsafe fn init() {} pub unsafe fn cleanup() {} pub unsafe fn make_handler() -> super::Handler { - super::Handler { _data: ptr::null_mut() } + super::Handler::null() } pub unsafe fn drop_handler(_handler: &mut super::Handler) {} diff --git a/src/libstd/sys/vxworks/io.rs b/src/libstd/sys/vxworks/io.rs index f1a2c8446ff..0f68ebf8da9 100644 --- a/src/libstd/sys/vxworks/io.rs +++ b/src/libstd/sys/vxworks/io.rs @@ -3,6 +3,7 @@ use libc::{c_void, iovec}; +#[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { vec: iovec, diff --git a/src/libstd/sys/vxworks/mod.rs b/src/libstd/sys/vxworks/mod.rs index 12bbfa1d4e1..e23191c9431 100644 --- a/src/libstd/sys/vxworks/mod.rs +++ b/src/libstd/sys/vxworks/mod.rs @@ -103,7 +103,7 @@ pub fn cvt_r(mut f: F) -> crate::io::Result // On Unix-like platforms, libc::abort will unregister signal handlers // including the SIGABRT handler, preventing the abort from being blocked, and -// fclose streams, with the side effect of flushing them so libc bufferred +// fclose streams, with the side effect of flushing them so libc buffered // output will be printed. Additionally the shell will generally print a more // understandable error message like "Abort trap" rather than "Illegal // instruction" that intrinsics::abort would cause, as intrinsics::abort is diff --git a/src/libstd/sys/wasi/io.rs b/src/libstd/sys/wasi/io.rs index ee20ea6dab8..0ad2e152855 100644 --- a/src/libstd/sys/wasi/io.rs +++ b/src/libstd/sys/wasi/io.rs @@ -1,6 +1,7 @@ use crate::marker::PhantomData; use crate::slice; +#[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { vec: wasi::Ciovec, diff --git a/src/libstd/sys/wasm/io.rs b/src/libstd/sys/wasm/io.rs index 976e122463d..d5f475b4310 100644 --- a/src/libstd/sys/wasm/io.rs +++ b/src/libstd/sys/wasm/io.rs @@ -1,5 +1,6 @@ use crate::mem; +#[derive(Copy, Clone)] pub struct IoSlice<'a>(&'a [u8]); impl<'a> IoSlice<'a> { diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 4d377341be3..7f93ef87953 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -295,6 +295,7 @@ pub struct WSADATA { pub szSystemStatus: [u8; WSASYS_STATUS_LEN + 1], } +#[derive(Copy, Clone)] #[repr(C)] pub struct WSABUF { pub len: ULONG, @@ -1044,6 +1045,10 @@ pub fn SetFileInformationByHandle(_hFile: HANDLE, _dwBufferSize: DWORD) -> BOOL { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 } + pub fn GetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime: LPFILETIME) + -> () { + GetSystemTimeAsFileTime(lpSystemTimeAsFileTime) + } pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE, SRWLock: PSRWLOCK, dwMilliseconds: DWORD, diff --git a/src/libstd/sys/windows/io.rs b/src/libstd/sys/windows/io.rs index 9d8018fd5e8..5525d283252 100644 --- a/src/libstd/sys/windows/io.rs +++ b/src/libstd/sys/windows/io.rs @@ -2,6 +2,7 @@ use crate::slice; use crate::sys::c; +#[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { vec: c::WSABUF, diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index 86667ca7ab2..900260169c7 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -74,7 +74,7 @@ impl SystemTime { pub fn now() -> SystemTime { unsafe { let mut t: SystemTime = mem::zeroed(); - c::GetSystemTimeAsFileTime(&mut t.t); + c::GetSystemTimePreciseAsFileTime(&mut t.t); t } } diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 289ee07babf..2c7ba8f8ea1 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -70,7 +70,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| { output_filename(fmt, bows, print_fmt, cwd.as_ref()) }; - write!(fmt, "stack backtrace:\n")?; + writeln!(fmt, "stack backtrace:")?; let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path); bt_fmt.add_context()?; let mut idx = 0; diff --git a/src/libstd/sys_common/process.rs b/src/libstd/sys_common/process.rs index 042641852b3..f3a2962098b 100644 --- a/src/libstd/sys_common/process.rs +++ b/src/libstd/sys_common/process.rs @@ -47,7 +47,7 @@ pub fn apply(&self) { } } for (key, maybe_val) in self.vars.iter() { - if let &Some(ref val) = maybe_val { + if let Some(ref val) = maybe_val { env::set_var(key, val); } else { env::remove_var(key); diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index 1d96cdfe460..498950e6821 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -599,24 +599,16 @@ fn next_surrogate(&self, mut pos: usize) -> Option<(usize, u16)> { #[inline] fn final_lead_surrogate(&self) -> Option { - let len = self.len(); - if len < 3 { - return None; - } - match &self.bytes[(len - 3)..] { - &[0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)), + match self.bytes { + [.., 0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)), _ => None, } } #[inline] fn initial_trail_surrogate(&self) -> Option { - let len = self.len(); - if len < 3 { - return None; - } - match &self.bytes[..3] { - &[0xED, b2 @ 0xB0..=0xBF, b3] => Some(decode_surrogate(b2, b3)), + match self.bytes { + [0xED, b2 @ 0xB0..=0xBF, b3, ..] => Some(decode_surrogate(b2, b3)), _ => None, } } diff --git a/src/libtest/cli.rs b/src/libtest/cli.rs index a800bd17c50..5317063b80d 100644 --- a/src/libtest/cli.rs +++ b/src/libtest/cli.rs @@ -283,7 +283,7 @@ fn is_nightly() -> bool { bootstrap || !disable_unstable_features } -// Gets the CLI options assotiated with `report-time` feature. +// Gets the CLI options associated with `report-time` feature. fn get_time_options( matches: &getopts::Matches, allow_unstable: bool, diff --git a/src/libtest/console.rs b/src/libtest/console.rs index 149c9202e6e..ff741e3bd53 100644 --- a/src/libtest/console.rs +++ b/src/libtest/console.rs @@ -169,7 +169,7 @@ fn plural(count: u32, s: &str) -> String { if !quiet { if ntest != 0 || nbench != 0 { - writeln!(output, "")?; + writeln!(output)?; } writeln!(output, "{}, {}", plural(ntest, "test"), plural(nbench, "benchmark"))?; diff --git a/src/libtest/formatters/mod.rs b/src/libtest/formatters/mod.rs index a64c0fc263d..1fb840520a6 100644 --- a/src/libtest/formatters/mod.rs +++ b/src/libtest/formatters/mod.rs @@ -36,5 +36,5 @@ pub(crate) fn write_stderr_delimiter(test_output: &mut Vec, test_name: &Test Some(_) => test_output.push(b'\n'), None => (), } - write!(test_output, "---- {} stderr ----\n", test_name).unwrap(); + writeln!(test_output, "---- {} stderr ----", test_name).unwrap(); } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index b42e41cbfe6..55f9df9caaf 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -96,7 +96,7 @@ pub mod test { // Process exit code to be used to indicate test failures. const ERROR_EXIT_CODE: i32 = 101; -const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE"; +const SECONDARY_TEST_INVOKER_VAR: &str = "__RUST_TEST_INVOKE"; // The default console test runner. It accepts the command line // arguments and a vector of test_descs. @@ -158,7 +158,7 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { .filter(|test| test.desc.name.as_slice() == name) .map(make_owned_test) .next() - .expect(&format!("couldn't find a test with the provided name '{}'", name)); + .unwrap_or_else(|| panic!("couldn't find a test with the provided name '{}'", name)); let TestDescAndFn { desc, testfn } = test; let testfn = match testfn { StaticTestFn(f) => f, diff --git a/src/libtest/options.rs b/src/libtest/options.rs index 7db164c269a..8e7bd8de924 100644 --- a/src/libtest/options.rs +++ b/src/libtest/options.rs @@ -41,7 +41,7 @@ pub enum OutputFormat { Json, } -/// Whether ignored test should be runned or not +/// Whether ignored test should be run or not #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum RunIgnored { Yes, diff --git a/src/libtest/stats.rs b/src/libtest/stats.rs index aab8d012fdf..077005371c0 100644 --- a/src/libtest/stats.rs +++ b/src/libtest/stats.rs @@ -204,7 +204,7 @@ fn mean(&self) -> f64 { } fn median(&self) -> f64 { - self.percentile(50 as f64) + self.percentile(50_f64) } fn var(&self) -> f64 { @@ -230,7 +230,7 @@ fn std_dev(&self) -> f64 { } fn std_dev_pct(&self) -> f64 { - let hundred = 100 as f64; + let hundred = 100_f64; (self.std_dev() / self.mean()) * hundred } @@ -244,7 +244,7 @@ fn median_abs_dev(&self) -> f64 { } fn median_abs_dev_pct(&self) -> f64 { - let hundred = 100 as f64; + let hundred = 100_f64; (self.median_abs_dev() / self.median()) * hundred } @@ -257,11 +257,11 @@ fn percentile(&self, pct: f64) -> f64 { fn quartiles(&self) -> (f64, f64, f64) { let mut tmp = self.to_vec(); local_sort(&mut tmp); - let first = 25f64; + let first = 25_f64; let a = percentile_of_sorted(&tmp, first); - let second = 50f64; + let second = 50_f64; let b = percentile_of_sorted(&tmp, second); - let third = 75f64; + let third = 75_f64; let c = percentile_of_sorted(&tmp, third); (a, b, c) } @@ -281,7 +281,7 @@ fn percentile_of_sorted(sorted_samples: &[f64], pct: f64) -> f64 { } let zero: f64 = 0.0; assert!(zero <= pct); - let hundred = 100f64; + let hundred = 100_f64; assert!(pct <= hundred); if pct == hundred { return sorted_samples[sorted_samples.len() - 1]; @@ -307,7 +307,7 @@ pub fn winsorize(samples: &mut [f64], pct: f64) { let mut tmp = samples.to_vec(); local_sort(&mut tmp); let lo = percentile_of_sorted(&tmp, pct); - let hundred = 100 as f64; + let hundred = 100_f64; let hi = percentile_of_sorted(&tmp, hundred - pct); for samp in samples { if *samp > hi { diff --git a/src/libtest/test_result.rs b/src/libtest/test_result.rs index 5c975c6272b..465f3f8f994 100644 --- a/src/libtest/test_result.rs +++ b/src/libtest/test_result.rs @@ -27,7 +27,7 @@ pub enum TestResult { unsafe impl Send for TestResult {} /// Creates a `TestResult` depending on the raw result of test execution -/// and assotiated data. +/// and associated data. pub fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, diff --git a/src/libtest/types.rs b/src/libtest/types.rs index 2619f99592a..5b75d2f367f 100644 --- a/src/libtest/types.rs +++ b/src/libtest/types.rs @@ -59,10 +59,10 @@ pub fn padding(&self) -> NamePadding { } pub fn with_padding(&self, padding: NamePadding) -> TestName { - let name = match self { - &TestName::StaticTestName(name) => Cow::Borrowed(name), - &TestName::DynTestName(ref name) => Cow::Owned(name.clone()), - &TestName::AlignedTestName(ref name, _) => name.clone(), + let name = match *self { + TestName::StaticTestName(name) => Cow::Borrowed(name), + TestName::DynTestName(ref name) => Cow::Owned(name.clone()), + TestName::AlignedTestName(ref name, _) => name.clone(), }; TestName::AlignedTestName(name, padding) diff --git a/src/libunwind/build.rs b/src/libunwind/build.rs index a24808b3250..0628e5d2fc0 100644 --- a/src/libunwind/build.rs +++ b/src/libunwind/build.rs @@ -33,8 +33,13 @@ fn main() { } else if target.contains("dragonfly") { println!("cargo:rustc-link-lib=gcc_pic"); } else if target.contains("pc-windows-gnu") { - println!("cargo:rustc-link-lib=static-nobundle=gcc_eh"); - println!("cargo:rustc-link-lib=static-nobundle=pthread"); + // This is handled in the target spec with late_link_args_[static|dynamic] + + // cfg!(bootstrap) doesn't work in build scripts + if env::var("RUSTC_STAGE").ok() == Some("0".to_string()) { + println!("cargo:rustc-link-lib=static-nobundle=gcc_eh"); + println!("cargo:rustc-link-lib=static-nobundle=pthread"); + } } else if target.contains("uwp-windows-gnu") { println!("cargo:rustc-link-lib=unwind"); } else if target.contains("fuchsia") { diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index f92ff9f071a..bd1946133e8 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -60,37 +60,36 @@ impl ::Copy for $t {} } // Unwind info registration/deregistration routines. - // See the docs of `unwind` module in libstd. + // See the docs of libpanic_unwind. extern "C" { fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8); fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8); } - unsafe fn init() { + unsafe extern "C" fn init() { // register unwind info on module startup rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8); } - unsafe fn uninit() { + unsafe extern "C" fn uninit() { // unregister on shutdown rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8); } - // MSVC-specific init/uninit routine registration - pub mod ms_init { - // .CRT$X?? sections are roughly analogous to ELF's .init_array and .fini_array, - // except that they exploit the fact that linker will sort them alphabitically, - // so e.g., sections with names between .CRT$XIA and .CRT$XIZ are guaranteed to be - // placed between those two, without requiring any ordering of objects on the linker - // command line. - // Note that ordering of same-named sections from different objects is not guaranteed. - // Since .CRT$XIA contains init array's header symbol, which must always come first, - // we place our initialization callback into .CRT$XIB. + // MinGW-specific init/uninit routine registration + pub mod mingw_init { + // MinGW's startup objects (crt0.o / dllcrt0.o) will invoke global constructors in the + // .ctors and .dtors sections on startup and exit. In the case of DLLs, this is done when + // the DLL is loaded and unloaded. + // + // The linker will sort the sections, which ensures that our callbacks are located at the + // end of the list. Since constructors are run in reverse order, this ensures that our + // callbacks are the first and last ones executed. - #[link_section = ".CRT$XIB"] // .CRT$XI? : C initialization callbacks - pub static P_INIT: unsafe fn() = super::init; + #[link_section = ".ctors.65535"] // .ctors.* : C initialization callbacks + pub static P_INIT: unsafe extern "C" fn() = super::init; - #[link_section = ".CRT$XTY"] // .CRT$XT? : C termination callbacks - pub static P_UNINIT: unsafe fn() = super::uninit; + #[link_section = ".dtors.65535"] // .dtors.* : C termination callbacks + pub static P_UNINIT: unsafe extern "C" fn() = super::uninit; } } diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 65071c3ed86..90d24d20737 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -868,8 +868,10 @@ LLVMRustOptimizeWithNewPassManager( } else { for (const auto &C : PipelineStartEPCallbacks) PB.registerPipelineStartEPCallback(C); - for (const auto &C : OptimizerLastEPCallbacks) - PB.registerOptimizerLastEPCallback(C); + if (OptStage != LLVMRustOptStage::PreLinkThinLTO) { + for (const auto &C : OptimizerLastEPCallbacks) + PB.registerOptimizerLastEPCallback(C); + } switch (OptStage) { case LLVMRustOptStage::PreLinkNoLTO: @@ -877,6 +879,12 @@ LLVMRustOptimizeWithNewPassManager( break; case LLVMRustOptStage::PreLinkThinLTO: MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); + if (!OptimizerLastEPCallbacks.empty()) { + FunctionPassManager FPM(DebugPassManager); + for (const auto &C : OptimizerLastEPCallbacks) + C(FPM, OptLevel); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + } break; case LLVMRustOptStage::PreLinkFatLTO: MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 49b6e1bfec3..25cfee3373d 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -112,20 +112,22 @@ extern "C" void LLVMRustPrintPassTimings() { TimerGroup::printAll(OS); } -extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, - const char *Name) { - return wrap(unwrap(M)->getNamedValue(Name)); +extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, + size_t NameLen) { + return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); } extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, const char *Name, + size_t NameLen, LLVMTypeRef FunctionTy) { - return wrap( - unwrap(M)->getOrInsertFunction(Name, unwrap(FunctionTy)) + return wrap(unwrap(M) + ->getOrInsertFunction(StringRef(Name, NameLen), + unwrap(FunctionTy)) #if LLVM_VERSION_GE(9, 0) - .getCallee() + .getCallee() #endif - ); + ); } extern "C" LLVMValueRef @@ -395,22 +397,26 @@ static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { } } -extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, - char *Constraints, - LLVMBool HasSideEffects, - LLVMBool IsAlignStack, - LLVMRustAsmDialect Dialect) { - return wrap(InlineAsm::get(unwrap(Ty), AsmString, Constraints, +extern "C" LLVMValueRef +LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, + char *Constraints, size_t ConstraintsLen, + LLVMBool HasSideEffects, LLVMBool IsAlignStack, + LLVMRustAsmDialect Dialect) { + return wrap(InlineAsm::get(unwrap(Ty), + StringRef(AsmString, AsmStringLen), + StringRef(Constraints, ConstraintsLen), HasSideEffects, IsAlignStack, fromRust(Dialect))); } -extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, - char *Constraints) { - return InlineAsm::Verify(unwrap(Ty), Constraints); +extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, + size_t ConstraintsLen) { + return InlineAsm::Verify(unwrap(Ty), + StringRef(Constraints, ConstraintsLen)); } -extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) { - unwrap(M)->appendModuleInlineAsm(StringRef(Asm)); +extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, + size_t AsmLen) { + unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen)); } typedef DIBuilder *LLVMRustDIBuilderRef; @@ -665,20 +671,24 @@ extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) { extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, - const char *Producer, bool isOptimized, const char *Flags, - unsigned RuntimeVer, const char *SplitName, + const char *Producer, size_t ProducerLen, bool isOptimized, + const char *Flags, unsigned RuntimeVer, + const char *SplitName, size_t SplitNameLen, LLVMRustDebugEmissionKind Kind) { auto *File = unwrapDI(FileRef); - return wrap(Builder->createCompileUnit(Lang, File, Producer, isOptimized, - Flags, RuntimeVer, SplitName, + return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen), + isOptimized, Flags, RuntimeVer, + StringRef(SplitName, SplitNameLen), fromRust(Kind))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateFile(LLVMRustDIBuilderRef Builder, const char *Filename, - const char *Directory) { - return wrap(Builder->createFile(Filename, Directory)); +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( + LLVMRustDIBuilderRef Builder, + const char *Filename, size_t FilenameLen, + const char *Directory, size_t DirectoryLen) { + return wrap(Builder->createFile(StringRef(Filename, FilenameLen), + StringRef(Directory, DirectoryLen))); } extern "C" LLVMMetadataRef @@ -690,8 +700,10 @@ LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - const char *LinkageName, LLVMMetadataRef File, unsigned LineNo, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags, LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam, LLVMMetadataRef Decl) { @@ -705,8 +717,11 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( llvmFlags |= DINode::DIFlags::FlagMainSubprogram; #endif DISubprogram *Sub = Builder->createFunction( - unwrapDI(Scope), Name, LinkageName, unwrapDI(File), - LineNo, unwrapDI(Ty), ScopeLine, llvmFlags, + unwrapDI(Scope), + StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI(File), LineNo, + unwrapDI(Ty), ScopeLine, llvmFlags, llvmSPFlags, TParams, unwrapDIPtr(Decl)); #else bool IsLocalToUnit = isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit); @@ -716,8 +731,11 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) llvmFlags |= DINode::DIFlags::FlagMainSubprogram; DISubprogram *Sub = Builder->createFunction( - unwrapDI(Scope), Name, LinkageName, unwrapDI(File), - LineNo, unwrapDI(Ty), IsLocalToUnit, IsDefinition, + unwrapDI(Scope), + StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI(File), LineNo, + unwrapDI(Ty), IsLocalToUnit, IsDefinition, ScopeLine, llvmFlags, IsOptimized, TParams, unwrapDIPtr(Decl)); #endif @@ -725,53 +743,59 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( return wrap(Sub); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateBasicType(LLVMRustDIBuilderRef Builder, const char *Name, - uint64_t SizeInBits, uint32_t AlignInBits, - unsigned Encoding) { - return wrap(Builder->createBasicType(Name, SizeInBits, Encoding)); +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + uint64_t SizeInBits, uint32_t AlignInBits, unsigned Encoding) { + return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding)); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy, - uint64_t SizeInBits, uint32_t AlignInBits, const char *Name) { + uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace, + const char *Name, size_t NameLen) { return wrap(Builder->createPointerType(unwrapDI(PointeeTy), SizeInBits, AlignInBits, - /* DWARFAddressSpace */ None, - Name)); + AddressSpace, + StringRef(Name, NameLen))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, unsigned RunTimeLang, LLVMMetadataRef VTableHolder, - const char *UniqueId) { + const char *UniqueId, size_t UniqueIdLen) { return wrap(Builder->createStructType( - unwrapDI(Scope), Name, unwrapDI(File), LineNumber, + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNumber, SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(DerivedFrom), DINodeArray(unwrapDI(Elements)), RunTimeLang, - unwrapDI(VTableHolder), UniqueId)); + unwrapDI(VTableHolder), StringRef(UniqueId, UniqueIdLen))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Discriminator, - LLVMMetadataRef Elements, const char *UniqueId) { + LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) { return wrap(Builder->createVariantPart( - unwrapDI(Scope), Name, unwrapDI(File), LineNumber, + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNumber, SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(Discriminator), - DINodeArray(unwrapDI(Elements)), UniqueId)); + DINodeArray(unwrapDI(Elements)), StringRef(UniqueId, UniqueIdLen))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Ty) { - return wrap(Builder->createMemberType(unwrapDI(Scope), Name, + return wrap(Builder->createMemberType(unwrapDI(Scope), + StringRef(Name, NameLen), unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, fromRust(Flags), unwrapDI(Ty))); @@ -779,14 +803,15 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, - uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, + uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant, LLVMRustDIFlags Flags, LLVMMetadataRef Ty) { llvm::ConstantInt* D = nullptr; if (Discriminant) { D = unwrap(Discriminant); } - return wrap(Builder->createVariantMemberType(unwrapDI(Scope), Name, + return wrap(Builder->createVariantMemberType(unwrapDI(Scope), + StringRef(Name, NameLen), unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, D, fromRust(Flags), unwrapDI(Ty))); @@ -808,8 +833,10 @@ LLVMRustDIBuilderCreateLexicalBlockFile(LLVMRustDIBuilderRef Builder, } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, - const char *LinkageName, LLVMMetadataRef File, unsigned LineNo, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V, LLVMMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) { llvm::GlobalVariable *InitVal = cast(unwrap(V)); @@ -825,7 +852,8 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( } llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression( - unwrapDI(Context), Name, LinkageName, + unwrapDI(Context), StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, #if LLVM_VERSION_GE(10, 0) /* isDefined */ true, @@ -843,17 +871,20 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, - const char *Name, LLVMMetadataRef File, unsigned LineNo, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, unsigned ArgNo, uint32_t AlignInBits) { if (Tag == 0x100) { // DW_TAG_auto_variable return wrap(Builder->createAutoVariable( - unwrapDI(Scope), Name, unwrapDI(File), LineNo, + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits)); } else { return wrap(Builder->createParameterVariable( - unwrapDI(Scope), Name, ArgNo, unwrapDI(File), - LineNo, unwrapDI(Ty), AlwaysPreserve, fromRust(Flags))); + unwrapDI(Scope), StringRef(Name, NameLen), ArgNo, + unwrapDI(File), LineNo, + unwrapDI(Ty), AlwaysPreserve, fromRust(Flags))); } } @@ -891,50 +922,53 @@ extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd( unwrap(InsertAtEnd))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateEnumerator(LLVMRustDIBuilderRef Builder, - const char *Name, uint64_t Val) { - return wrap(Builder->createEnumerator(Name, Val)); +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + int64_t Value, bool IsUnsigned) { + return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned)); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, uint32_t AlignInBits, LLVMMetadataRef Elements, LLVMMetadataRef ClassTy, bool IsScoped) { return wrap(Builder->createEnumerationType( - unwrapDI(Scope), Name, unwrapDI(File), LineNumber, + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNumber, SizeInBits, AlignInBits, DINodeArray(unwrapDI(Elements)), unwrapDI(ClassTy), "", IsScoped)); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Elements, - unsigned RunTimeLang, const char *UniqueId) { + unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) { return wrap(Builder->createUnionType( - unwrapDI(Scope), Name, unwrapDI(File), LineNumber, - SizeInBits, AlignInBits, fromRust(Flags), - DINodeArray(unwrapDI(Elements)), RunTimeLang, UniqueId)); + unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), + LineNumber, SizeInBits, AlignInBits, fromRust(Flags), + DINodeArray(unwrapDI(Elements)), RunTimeLang, + StringRef(UniqueId, UniqueIdLen))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef Ty, LLVMMetadataRef File, unsigned LineNo, unsigned ColumnNo) { return wrap(Builder->createTemplateTypeParameter( - unwrapDI(Scope), Name, unwrapDI(Ty))); + unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(Ty))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateNameSpace(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef Scope, const char *Name, - LLVMMetadataRef File, unsigned LineNo) { +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateNameSpace( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, bool ExportSymbols) { return wrap(Builder->createNameSpace( - unwrapDI(Scope), Name, - false // ExportSymbols (only relevant for C++ anonymous namespaces) - )); + unwrapDI(Scope), StringRef(Name, NameLen), ExportSymbols + )); } extern "C" void @@ -1254,12 +1288,11 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, - OperandBundleDef *Bundle, - const char *Name) { + OperandBundleDef *Bundle) { unsigned Len = Bundle ? 1 : 0; ArrayRef Bundles = makeArrayRef(Bundle, Len); return wrap(unwrap(B)->CreateCall( - unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles, Name)); + unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles)); } extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, diff --git a/src/stage0.txt b/src/stage0.txt index 14bffbadc06..4d9a91e38b3 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.x.0` for Cargo where they were released on `date`. -date: 2020-01-30 +date: 2020-03-12 rustc: beta cargo: beta diff --git a/src/test/COMPILER_TESTS.md b/src/test/COMPILER_TESTS.md index 3f47ca834cd..ea540bd0b18 100644 --- a/src/test/COMPILER_TESTS.md +++ b/src/test/COMPILER_TESTS.md @@ -1,4 +1,4 @@ # Compiler Test Documentation Documentation for the compiler testing framework can be found in -[the rustc guide](https://rust-lang.github.io/rustc-guide/tests/intro.html). +[the rustc dev guide](https://rustc-dev-guide.rust-lang.org/tests/intro.html). diff --git a/src/test/codegen/catch-unwind.rs b/src/test/codegen/catch-unwind.rs new file mode 100644 index 00000000000..3c9bc35d1c8 --- /dev/null +++ b/src/test/codegen/catch-unwind.rs @@ -0,0 +1,19 @@ +// compile-flags: -O + +#![crate_type = "lib"] + +extern "C" { + fn bar(); +} + +// CHECK-LABEL: @foo +#[no_mangle] +pub unsafe fn foo() -> i32 { + // CHECK: call void @bar + // CHECK: ret i32 0 + std::panic::catch_unwind(|| { + bar(); + 0 + }) + .unwrap() +} diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index a89ecdfd3a9..e53e75b339b 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -10,11 +10,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @{{[0-9]+}} = {{.*}}, align 2 +// CHECK: @alloc5 = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @2, i32 0, i32 0, i32 0), {{.*}}, +// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @alloc15, i32 0, i32 0, i32 0), {{.*}} #[derive(Copy, Clone)] // repr(i16) is required for the {low,high}_align_const test diff --git a/src/test/codegen/debug-column-msvc.rs b/src/test/codegen/debug-column-msvc.rs new file mode 100644 index 00000000000..aad8b372a8a --- /dev/null +++ b/src/test/codegen/debug-column-msvc.rs @@ -0,0 +1,16 @@ +// Verify that no column information is emitted for MSVC targets +// +// only-msvc +// compile-flags: -C debuginfo=2 + +// CHECK-NOT: !DILexicalBlock({{.*}}column: {{.*}}) +// CHECK-NOT: !DILocation({{.*}}column: {{.*}}) + +pub fn add(a: u32, b: u32) -> u32 { + a + b +} + +fn main() { + let c = add(1, 2); + println!("{}", c); +} diff --git a/src/test/codegen/debug-column.rs b/src/test/codegen/debug-column.rs new file mode 100644 index 00000000000..f348c48566d --- /dev/null +++ b/src/test/codegen/debug-column.rs @@ -0,0 +1,24 @@ +// Verify that debuginfo column nubmers are 1-based byte offsets. +// +// ignore-windows +// compile-flags: -C debuginfo=2 + +fn main() { + unsafe { + // Column numbers are 1-based. Regression test for #65437. + // CHECK: call void @giraffe(), !dbg [[A:!.*]] + giraffe(); + + // Column numbers use byte offests. Regression test for #67360 + // CHECK: call void @turtle(), !dbg [[B:!.*]] +/* ż */ turtle(); + + // CHECK: [[A]] = !DILocation(line: 10, column: 9, + // CHECK: [[B]] = !DILocation(line: 14, column: 10, + } +} + +extern { + fn giraffe(); + fn turtle(); +} diff --git a/src/test/codegen/enum-discriminant-value.rs b/src/test/codegen/enum-discriminant-value.rs new file mode 100644 index 00000000000..f9da987765f --- /dev/null +++ b/src/test/codegen/enum-discriminant-value.rs @@ -0,0 +1,27 @@ +// Verify that DIEnumerator uses isUnsigned flag when appropriate. +// +// compile-flags: -g -C no-prepopulate-passes + +#[repr(i64)] +pub enum I64 { + I64Min = std::i64::MIN, + I64Max = std::i64::MAX, +} + +#[repr(u64)] +pub enum U64 { + U64Min = std::u64::MIN, + U64Max = std::u64::MAX, +} + +fn main() { + let _a = I64::I64Min; + let _b = I64::I64Max; + let _c = U64::U64Min; + let _d = U64::U64Max; +} + +// CHECK: !DIEnumerator(name: "I64Min", value: -9223372036854775808) +// CHECK: !DIEnumerator(name: "I64Max", value: 9223372036854775807) +// CHECK: !DIEnumerator(name: "U64Min", value: 0, isUnsigned: true) +// CHECK: !DIEnumerator(name: "U64Max", value: 18446744073709551615, isUnsigned: true) diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index 52ffb97a5b2..4724dc3c3e5 100644 --- a/src/test/codegen/remap_path_prefix/main.rs +++ b/src/test/codegen/remap_path_prefix/main.rs @@ -12,7 +12,7 @@ include!("aux_mod.rs"); // Here we check that the expansion of the file!() macro is mapped. -// CHECK: @0 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>, align 1 +// CHECK: @alloc1 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>, align 1 pub static FILE_PATH: &'static str = file!(); fn main() { diff --git a/src/test/codegen/repeat-trusted-len.rs b/src/test/codegen/repeat-trusted-len.rs index 8fbe712065b..8e08b78ad1e 100644 --- a/src/test/codegen/repeat-trusted-len.rs +++ b/src/test/codegen/repeat-trusted-len.rs @@ -5,14 +5,9 @@ use std::iter; -// CHECK: @helper([[USIZE:i[0-9]+]] %_1) -#[no_mangle] -pub fn helper(_: usize) { -} - // CHECK-LABEL: @repeat_take_collect #[no_mangle] pub fn repeat_take_collect() -> Vec { -// CHECK: call void @llvm.memset.p0i8.[[USIZE]](i8* {{(nonnull )?}}align 1{{.*}} %{{[0-9]+}}, i8 42, [[USIZE]] 100000, i1 false) +// CHECK: call void @llvm.memset.p0i8.i{{[0-9]+}}(i8* {{(nonnull )?}}align 1{{.*}} %{{[0-9]+}}, i8 42, i{{[0-9]+}} 100000, i1 false) iter::repeat(42).take(100000).collect() } diff --git a/src/test/codegen/sanitizer-recover.rs b/src/test/codegen/sanitizer-recover.rs index 9a583725b0b..05b4ab5653c 100644 --- a/src/test/codegen/sanitizer-recover.rs +++ b/src/test/codegen/sanitizer-recover.rs @@ -14,8 +14,8 @@ //[MSAN-RECOVER-LTO] compile-flags: -Zsanitizer=memory -Zsanitizer-recover=memory -C lto=fat // // MSAN-NOT: @__msan_keep_going -// MSAN-RECOVER: @__msan_keep_going = weak_odr {{.*}} constant i32 1 -// MSAN-RECOVER-LTO: @__msan_keep_going = weak_odr {{.*}} constant i32 1 +// MSAN-RECOVER: @__msan_keep_going = weak_odr {{.*}}constant i32 1 +// MSAN-RECOVER-LTO: @__msan_keep_going = weak_odr {{.*}}constant i32 1 // ASAN-LABEL: define i32 @penguin( // ASAN: call void @__asan_report_load4(i64 %0) diff --git a/src/test/codegen/try-panic-abort.rs b/src/test/codegen/try-panic-abort.rs new file mode 100644 index 00000000000..166d2bb9942 --- /dev/null +++ b/src/test/codegen/try-panic-abort.rs @@ -0,0 +1,20 @@ +// compile-flags: -C panic=abort -O + +#![crate_type = "lib"] +#![feature(unwind_attributes, core_intrinsics)] + +extern "C" { + #[unwind(allow)] + fn bar(data: *mut u8); +} +extern "Rust" { + fn catch(data: *mut u8, exception: *mut u8); +} + +// CHECK-LABEL: @foo +#[no_mangle] +pub unsafe fn foo() -> i32 { + // CHECK: call void @bar + // CHECK: ret i32 0 + std::intrinsics::r#try(|x| bar(x), 0 as *mut u8, |x, y| catch(x, y)) +} diff --git a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs index abe34a39caf..3e5cdad7ab9 100644 --- a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs +++ b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs @@ -11,5 +11,3 @@ fn panic_impl(info: &PanicInfo) -> ! { loop {} } #[lang = "eh_personality"] fn eh_personality() {} -#[lang = "eh_unwind_resume"] -fn eh_unwind_resume() {} diff --git a/src/test/compile-fail/chalkify/chalk_initial_program.rs b/src/test/compile-fail/chalkify/chalk_initial_program.rs deleted file mode 100644 index df25bad622b..00000000000 --- a/src/test/compile-fail/chalkify/chalk_initial_program.rs +++ /dev/null @@ -1,16 +0,0 @@ -// compile-flags: -Z chalk - -trait Foo { } - -impl Foo for i32 { } - -impl Foo for u32 { } - -fn gimme() { } - -// Note: this also tests that `std::process::Termination` is implemented for `()`. -fn main() { - gimme::(); - gimme::(); - gimme::(); //~ERROR the trait bound `f32: Foo` is not satisfied -} diff --git a/src/test/compile-fail/chalkify/generic_impls.rs b/src/test/compile-fail/chalkify/generic_impls.rs deleted file mode 100644 index d70c6f8055d..00000000000 --- a/src/test/compile-fail/chalkify/generic_impls.rs +++ /dev/null @@ -1,18 +0,0 @@ -// compile-flags: -Z chalk - -trait Foo { } - -impl Foo for (T, u32) { } - -fn gimme() { } - -fn foo() { - gimme::<(T, u32)>(); - gimme::<(Option, u32)>(); - gimme::<(Option, f32)>(); //~ ERROR -} - -fn main() { - gimme::<(i32, u32)>(); - gimme::<(i32, f32)>(); //~ ERROR -} diff --git a/src/test/compile-fail/chalkify/impl_wf.rs b/src/test/compile-fail/chalkify/impl_wf.rs deleted file mode 100644 index 6bb4cf86e79..00000000000 --- a/src/test/compile-fail/chalkify/impl_wf.rs +++ /dev/null @@ -1,39 +0,0 @@ -// compile-flags: -Z chalk - -trait Foo: Sized { } - -trait Bar { - type Item: Foo; -} - -impl Foo for i32 { } - -impl Foo for str { } -//~^ ERROR the size for values of type `str` cannot be known at compilation time - -// Implicit `T: Sized` bound. -impl Foo for Option { } - -impl Bar for () { - type Item = i32; -} - -impl Bar for Option { - type Item = Option; -} - -impl Bar for f32 { -//~^ ERROR the trait bound `f32: Foo` is not satisfied - type Item = f32; - //~^ ERROR the trait bound `f32: Foo` is not satisfied -} - -trait Baz where U: Foo { } - -impl Baz for i32 { } - -impl Baz for f32 { } -//~^ ERROR the trait bound `f32: Foo` is not satisfied - -fn main() { -} diff --git a/src/test/compile-fail/chalkify/recursive_where_clause_on_type.rs b/src/test/compile-fail/chalkify/recursive_where_clause_on_type.rs deleted file mode 100644 index 861f86e6165..00000000000 --- a/src/test/compile-fail/chalkify/recursive_where_clause_on_type.rs +++ /dev/null @@ -1,28 +0,0 @@ -// compile-flags: -Z chalk - -#![feature(trivial_bounds)] - -trait Bar { - fn foo(); -} -trait Foo: Bar { } - -struct S where S: Foo; - -impl Foo for S { -} - -fn bar() { - T::foo(); -} - -fn foo() { - bar::() -} - -fn main() { - // For some reason, the error is duplicated... - - foo::() //~ ERROR the type `S` is not well-formed (chalk) - //~^ ERROR the type `S` is not well-formed (chalk) -} diff --git a/src/test/compile-fail/chalkify/type_wf.rs b/src/test/compile-fail/chalkify/type_wf.rs deleted file mode 100644 index d1aa975ddc2..00000000000 --- a/src/test/compile-fail/chalkify/type_wf.rs +++ /dev/null @@ -1,24 +0,0 @@ -// compile-flags: -Z chalk - -trait Foo { } - -struct S { - x: T, -} - -impl Foo for i32 { } -impl Foo for Option { } - -fn main() { - let s = S { - x: 5, - }; - - let s = S { //~ ERROR the trait bound `{float}: Foo` is not satisfied - x: 5.0, - }; - - let s = S { - x: Some(5.0), - }; -} diff --git a/src/test/debuginfo/no-debug-attribute.rs b/src/test/debuginfo/no-debug-attribute.rs deleted file mode 100644 index db9ac4af629..00000000000 --- a/src/test/debuginfo/no-debug-attribute.rs +++ /dev/null @@ -1,37 +0,0 @@ -// ignore-lldb - -// compile-flags:-g - -// gdb-command:run - -// gdb-command:info locals -// gdb-check:No locals. -// gdb-command:continue - -// gdb-command:info locals -// gdb-check:abc = 10 -// gdb-command:continue - -#![allow(unused_variables)] -#![feature(no_debug)] -#![feature(omit_gdb_pretty_printer_section)] -#![omit_gdb_pretty_printer_section] - -#[inline(never)] -fn id(x: T) -> T {x} - -fn function_with_debuginfo() { - let abc = 10_usize; - id(abc); // #break -} - -#[no_debug] -fn function_without_debuginfo() { - let abc = -57i32; - id(abc); // #break -} - -fn main() { - function_without_debuginfo(); - function_with_debuginfo(); -} diff --git a/src/test/incremental/change_symbol_export_status.rs b/src/test/incremental/change_symbol_export_status.rs index f3de46d99dd..9b3b381d621 100644 --- a/src/test/incremental/change_symbol_export_status.rs +++ b/src/test/incremental/change_symbol_export_status.rs @@ -2,10 +2,8 @@ // compile-flags: -Zquery-dep-graph #![feature(rustc_attrs)] -#![allow(private_no_mangle_fns)] - -#![rustc_partition_codegened(module="change_symbol_export_status-mod1", cfg="rpass2")] -#![rustc_partition_reused(module="change_symbol_export_status-mod2", cfg="rpass2")] +#![rustc_partition_codegened(module = "change_symbol_export_status-mod1", cfg = "rpass2")] +#![rustc_partition_reused(module = "change_symbol_export_status-mod2", cfg = "rpass2")] // This test case makes sure that a change in symbol visibility is detected by // our dependency tracking. We do this by changing a module's visibility to diff --git a/src/test/incremental/cyclic-trait-hierarchy.rs b/src/test/incremental/cyclic-trait-hierarchy.rs index 27287d06d54..03bb5eea765 100644 --- a/src/test/incremental/cyclic-trait-hierarchy.rs +++ b/src/test/incremental/cyclic-trait-hierarchy.rs @@ -1,4 +1,4 @@ -// Adapated from rust-lang/rust#58813 +// Adapted from rust-lang/rust#58813 // revisions: rpass1 cfail2 diff --git a/src/test/incremental/hashes/call_expressions.rs b/src/test/incremental/hashes/call_expressions.rs index 50d3657d417..87f108abadd 100644 --- a/src/test/incremental/hashes/call_expressions.rs +++ b/src/test/incremental/hashes/call_expressions.rs @@ -25,7 +25,7 @@ pub fn change_callee_function() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_callee_function() { callee2(1, 2) @@ -40,7 +40,7 @@ pub fn change_argument_function() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_argument_function() { callee1(1, 3) @@ -55,10 +55,10 @@ mod change_callee_indirectly_function { #[cfg(not(cfail1))] use super::callee2 as callee; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] pub fn change_callee_indirectly_function() { @@ -81,7 +81,7 @@ pub fn change_callee_method() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_callee_method() { let s = Struct; @@ -98,7 +98,7 @@ pub fn change_argument_method() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_argument_method() { let s = Struct; @@ -115,7 +115,7 @@ pub fn change_ufcs_callee_method() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_ufcs_callee_method() { let s = Struct; @@ -132,7 +132,7 @@ pub fn change_argument_method_ufcs() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_argument_method_ufcs() { let s = Struct; @@ -149,10 +149,10 @@ pub fn change_to_ufcs() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] -// One might think this would be expanded in the HirBody/Mir, but it actually -// results in slightly different Hir/Mir. +// One might think this would be expanded in the hir_owner_items/Mir, but it actually +// results in slightly different hir_owner/Mir. pub fn change_to_ufcs() { let s = Struct; Struct::method1(&s, 'x', true); @@ -171,7 +171,7 @@ pub mod change_ufcs_callee_indirectly { #[cfg(not(cfail1))] use super::Struct2 as Struct; - #[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] diff --git a/src/test/incremental/hashes/closure_expressions.rs b/src/test/incremental/hashes/closure_expressions.rs index 08693560d0b..8edece2c8d3 100644 --- a/src/test/incremental/hashes/closure_expressions.rs +++ b/src/test/incremental/hashes/closure_expressions.rs @@ -21,7 +21,7 @@ pub fn change_closure_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn change_closure_body() { let _ = || 3u32; @@ -37,7 +37,7 @@ pub fn add_parameter() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_parameter() { let x = 0u32; @@ -53,7 +53,7 @@ pub fn change_parameter_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_parameter_pattern() { let _ = |(x,): (u32,)| x; @@ -68,7 +68,7 @@ pub fn add_move() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_move() { let _ = move || 1; @@ -84,8 +84,8 @@ pub fn add_type_ascription_to_parameter() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, typeck_tables_of")] -#[rustc_clean(cfg="cfail3")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner_items, typeck_tables_of")] +#[rustc_clean(cfg = "cfail3")] pub fn add_type_ascription_to_parameter() { let closure = |x: u32| x + 1u32; let _: u32 = closure(1); @@ -101,7 +101,7 @@ pub fn change_parameter_type() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_parameter_type() { let closure = |x: u16| (x as u64) + 1; diff --git a/src/test/incremental/hashes/consts.rs b/src/test/incremental/hashes/consts.rs index 3d2eed89636..8f77bb24f87 100644 --- a/src/test/incremental/hashes/consts.rs +++ b/src/test/incremental/hashes/consts.rs @@ -19,7 +19,7 @@ const CONST_VISIBILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub const CONST_VISIBILITY: u8 = 0; @@ -29,7 +29,7 @@ const CONST_CHANGE_TYPE_1: i32 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_1: u32 = 0; @@ -39,13 +39,13 @@ const CONST_CHANGE_TYPE_2: Option = None; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_2: Option = None; // Change value between simple literals -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_1: i16 = { #[cfg(cfail1)] @@ -57,7 +57,7 @@ // Change value between expressions -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_2: i16 = { #[cfg(cfail1)] @@ -67,7 +67,7 @@ { 1 + 2 } }; -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_3: i16 = { #[cfg(cfail1)] @@ -77,7 +77,7 @@ { 2 * 3 } }; -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_4: i16 = { #[cfg(cfail1)] @@ -99,11 +99,11 @@ mod const_change_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as Type; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_INDIRECTLY_1: Type = Type; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_INDIRECTLY_2: Option = None; } diff --git a/src/test/incremental/hashes/enum_constructors.rs b/src/test/incremental/hashes/enum_constructors.rs index 575b2e92966..5ad6eeafc7f 100644 --- a/src/test/incremental/hashes/enum_constructors.rs +++ b/src/test/incremental/hashes/enum_constructors.rs @@ -34,7 +34,7 @@ pub fn change_field_value_struct_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_struct_like() -> Enum { Enum::Struct { @@ -57,7 +57,7 @@ pub fn change_field_order_struct_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] // FIXME(michaelwoerister):Interesting. I would have thought that that changes the MIR. And it // would if it were not all constants @@ -96,7 +96,7 @@ pub fn change_constructor_path_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_struct_like() { let _ = Enum2::Struct { @@ -119,7 +119,7 @@ pub fn change_constructor_variant_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_struct_like() { let _ = Enum2::Struct2 { @@ -139,7 +139,7 @@ pub mod change_constructor_path_indirectly_struct_like { #[rustc_clean( cfg="cfail2", - except="fn_sig,Hir,HirBody,optimized_mir,mir_built,\ + except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,\ typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] @@ -161,7 +161,7 @@ pub mod change_constructor_variant_indirectly_struct_like { #[cfg(not(cfail1))] use super::Enum2::Struct2 as Variant; - #[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn function() -> Enum2 { Variant { @@ -180,7 +180,7 @@ pub fn change_field_value_tuple_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_tuple_like() -> Enum { Enum::Tuple(0, 1, 3) @@ -197,7 +197,7 @@ pub fn change_constructor_path_tuple_like() { #[cfg(not(cfail1))] #[rustc_clean( cfg="cfail2", - except="HirBody,optimized_mir,mir_built,typeck_tables_of" + except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_tuple_like() { @@ -215,7 +215,7 @@ pub fn change_constructor_variant_tuple_like() { #[cfg(not(cfail1))] #[rustc_clean( cfg="cfail2", - except="HirBody,optimized_mir,mir_built,typeck_tables_of" + except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_tuple_like() { @@ -232,7 +232,7 @@ pub mod change_constructor_path_indirectly_tuple_like { #[rustc_clean( cfg="cfail2", - except="fn_sig,Hir,HirBody,optimized_mir,mir_built,\ + except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,\ typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] @@ -251,7 +251,7 @@ pub mod change_constructor_variant_indirectly_tuple_like { #[cfg(not(cfail1))] use super::Enum2::Tuple2 as Variant; - #[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn function() -> Enum2 { Variant(0, 1, 2) @@ -278,7 +278,7 @@ pub fn change_constructor_path_c_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_c_like() { let _ = Clike2::B; @@ -293,7 +293,7 @@ pub fn change_constructor_variant_c_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_c_like() { let _ = Clike::C; @@ -309,7 +309,7 @@ pub mod change_constructor_path_indirectly_c_like { #[rustc_clean( cfg="cfail2", - except="fn_sig,Hir,HirBody,optimized_mir,mir_built,\ + except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,\ typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] @@ -328,7 +328,7 @@ pub mod change_constructor_variant_indirectly_c_like { #[cfg(not(cfail1))] use super::Clike::B as Variant; - #[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn function() -> Clike { Variant diff --git a/src/test/incremental/hashes/enum_defs.rs b/src/test/incremental/hashes/enum_defs.rs index 7256c1aa153..7be15b4bb15 100644 --- a/src/test/incremental/hashes/enum_defs.rs +++ b/src/test/incremental/hashes/enum_defs.rs @@ -26,7 +26,7 @@ enum EnumVisibility { A } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub enum EnumVisibility { A @@ -42,7 +42,7 @@ enum EnumChangeNameCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeNameCStyleVariant { Variant1, @@ -59,7 +59,7 @@ enum EnumChangeNameTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeNameTupleStyleVariant { Variant1, @@ -76,7 +76,7 @@ enum EnumChangeNameStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeNameStructStyleVariant { Variant1, @@ -93,7 +93,7 @@ enum EnumChangeValueCStyleVariant0 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum EnumChangeValueCStyleVariant0 { Variant1, @@ -109,7 +109,7 @@ enum EnumChangeValueCStyleVariant1 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeValueCStyleVariant1 { Variant1, @@ -125,7 +125,7 @@ enum EnumAddCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddCStyleVariant { Variant1, @@ -142,7 +142,7 @@ enum EnumRemoveCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumRemoveCStyleVariant { Variant1, @@ -157,7 +157,7 @@ enum EnumAddTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddTupleStyleVariant { Variant1, @@ -174,7 +174,7 @@ enum EnumRemoveTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumRemoveTupleStyleVariant { Variant1, @@ -189,7 +189,7 @@ enum EnumAddStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddStructStyleVariant { Variant1, @@ -206,7 +206,7 @@ enum EnumRemoveStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumRemoveStructStyleVariant { Variant1, @@ -221,7 +221,7 @@ enum EnumChangeFieldTypeTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldTypeTupleStyleVariant { Variant1(u32, @@ -238,7 +238,7 @@ enum EnumChangeFieldTypeStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldTypeStructStyleVariant { Variant1, @@ -257,7 +257,7 @@ enum EnumChangeFieldNameStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldNameStructStyleVariant { Variant1 { a: u32, c: u32 }, @@ -272,7 +272,7 @@ enum EnumChangeOrderTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum EnumChangeOrderTupleStyleVariant { Variant1( @@ -289,7 +289,7 @@ enum EnumChangeFieldOrderStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldOrderStructStyleVariant { Variant1 { b: f32, a: u32 }, @@ -304,7 +304,7 @@ enum EnumAddFieldTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddFieldTupleStyleVariant { Variant1(u32, u32, u32), @@ -319,7 +319,7 @@ enum EnumAddFieldStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddFieldStructStyleVariant { Variant1 { a: u32, b: u32, c: u32 }, @@ -335,7 +335,7 @@ enum EnumAddMustUse { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] #[must_use] enum EnumAddMustUse { @@ -353,7 +353,7 @@ enum EnumAddReprC { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] #[repr(C)] enum EnumAddReprC { @@ -531,7 +531,7 @@ enum EnumSwapUsageTypeParameters { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum EnumSwapUsageTypeParameters { Variant1 { @@ -552,7 +552,7 @@ enum EnumSwapUsageLifetimeParameters<'a, 'b> { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum EnumSwapUsageLifetimeParameters<'a, 'b> { Variant1 { @@ -577,7 +577,7 @@ mod change_field_type_indirectly_tuple_style { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum TupleStyle { Variant1( @@ -595,7 +595,7 @@ mod change_field_type_indirectly_struct_style { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] enum StructStyle { Variant1 { @@ -618,7 +618,7 @@ mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,predicates_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,predicates_of")] #[rustc_clean(cfg="cfail3")] enum Enum { Variant1(T) @@ -634,7 +634,7 @@ mod change_trait_bound_indirectly_where { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,predicates_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,predicates_of")] #[rustc_clean(cfg="cfail3")] enum Enum where T: Trait { Variant1(T) diff --git a/src/test/incremental/hashes/exported_vs_not.rs b/src/test/incremental/hashes/exported_vs_not.rs index ef275cabeaf..b546930ea8f 100644 --- a/src/test/incremental/hashes/exported_vs_not.rs +++ b/src/test/incremental/hashes/exported_vs_not.rs @@ -7,8 +7,8 @@ #![crate_type="rlib"] // Case 1: The function body is not exported to metadata. If the body changes, -// the hash of the HirBody node should change, but not the hash of -// either the Hir or the Metadata node. +// the hash of the hir_owner_items node should change, but not the hash of +// either the hir_owner or the Metadata node. #[cfg(cfail1)] pub fn body_not_exported_to_metadata() -> u32 { @@ -16,7 +16,7 @@ pub fn body_not_exported_to_metadata() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn body_not_exported_to_metadata() -> u32 { 2 @@ -25,7 +25,7 @@ pub fn body_not_exported_to_metadata() -> u32 { // Case 2: The function body *is* exported to metadata because the function is -// marked as #[inline]. Only the hash of the Hir depnode should be +// marked as #[inline]. Only the hash of the hir_owner depnode should be // unaffected by a change to the body. #[cfg(cfail1)] @@ -35,7 +35,7 @@ pub fn body_exported_to_metadata_because_of_inline() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] #[inline] pub fn body_exported_to_metadata_because_of_inline() -> u32 { @@ -45,7 +45,7 @@ pub fn body_exported_to_metadata_because_of_inline() -> u32 { // Case 2: The function body *is* exported to metadata because the function is -// generic. Only the hash of the Hir depnode should be +// generic. Only the hash of the hir_owner depnode should be // unaffected by a change to the body. #[cfg(cfail1)] @@ -55,7 +55,7 @@ pub fn body_exported_to_metadata_because_of_generic() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] #[inline] pub fn body_exported_to_metadata_because_of_generic() -> u32 { diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 70820dfaea4..3e54dafd9ac 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -48,7 +48,7 @@ pub fn change_iteration_variable_name() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_iteration_variable_name() { let mut _x = 0; @@ -71,7 +71,7 @@ pub fn change_iteration_variable_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_iteration_variable_pattern() { let mut _x = 0; @@ -94,7 +94,7 @@ pub fn change_iterable() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, promoted_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, promoted_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_iterable() { let mut _x = 0; @@ -116,7 +116,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -139,7 +139,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -162,7 +162,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -187,7 +187,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -212,7 +212,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -237,7 +237,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -262,7 +262,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/hashes/function_interfaces.rs b/src/test/incremental/hashes/function_interfaces.rs index 9cc2d3bcf60..bfd53628466 100644 --- a/src/test/incremental/hashes/function_interfaces.rs +++ b/src/test/incremental/hashes/function_interfaces.rs @@ -9,198 +9,196 @@ // revisions: cfail1 cfail2 cfail3 // compile-flags: -Z query-dep-graph -Zincremental-ignore-spans - #![allow(warnings)] #![feature(linkage)] #![feature(rustc_attrs)] #![crate_type = "rlib"] - // Add Parameter --------------------------------------------------------------- #[cfg(cfail1)] pub fn add_parameter() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, mir_built, optimized_mir, typeck_tables_of, fn_sig")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" +)] #[rustc_clean(cfg = "cfail3")] pub fn add_parameter(p: i32) {} - // Add Return Type ------------------------------------------------------------- #[cfg(cfail1)] pub fn add_return_type() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] #[rustc_clean(cfg = "cfail3")] pub fn add_return_type() -> () {} - // Change Parameter Type ------------------------------------------------------- #[cfg(cfail1)] pub fn type_of_parameter(p: i32) {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, mir_built, optimized_mir, typeck_tables_of, fn_sig")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" +)] #[rustc_clean(cfg = "cfail3")] pub fn type_of_parameter(p: i64) {} - // Change Parameter Type Reference --------------------------------------------- #[cfg(cfail1)] pub fn type_of_parameter_ref(p: &i32) {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, mir_built, optimized_mir, typeck_tables_of, fn_sig")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" +)] #[rustc_clean(cfg = "cfail3")] pub fn type_of_parameter_ref(p: &mut i32) {} - // Change Parameter Order ------------------------------------------------------ #[cfg(cfail1)] pub fn order_of_parameters(p1: i32, p2: i64) {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, mir_built, optimized_mir, typeck_tables_of, fn_sig")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" +)] #[rustc_clean(cfg = "cfail3")] pub fn order_of_parameters(p2: i64, p1: i32) {} - // Unsafe ---------------------------------------------------------------------- #[cfg(cfail1)] pub fn make_unsafe() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, mir_built, optimized_mir, typeck_tables_of, fn_sig")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" +)] #[rustc_clean(cfg = "cfail3")] pub unsafe fn make_unsafe() {} - // Extern ---------------------------------------------------------------------- #[cfg(cfail1)] pub fn make_extern() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, typeck_tables_of, fn_sig")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, typeck_tables_of, fn_sig")] #[rustc_clean(cfg = "cfail3")] pub extern "C" fn make_extern() {} - // Type Parameter -------------------------------------------------------------- #[cfg(cfail1)] pub fn type_parameter() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, generics_of, type_of, predicates_of")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, generics_of, type_of, predicates_of" +)] #[rustc_clean(cfg = "cfail3")] pub fn type_parameter() {} - // Lifetime Parameter ---------------------------------------------------------- #[cfg(cfail1)] pub fn lifetime_parameter() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, generics_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, generics_of")] #[rustc_clean(cfg = "cfail3")] pub fn lifetime_parameter<'a>() {} - // Trait Bound ----------------------------------------------------------------- #[cfg(cfail1)] pub fn trait_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn trait_bound() {} - // Builtin Bound --------------------------------------------------------------- #[cfg(cfail1)] pub fn builtin_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn builtin_bound() {} - // Lifetime Bound -------------------------------------------------------------- #[cfg(cfail1)] pub fn lifetime_bound<'a, T>() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, generics_of, type_of, predicates_of")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, generics_of, type_of, predicates_of" +)] #[rustc_clean(cfg = "cfail3")] pub fn lifetime_bound<'a, T: 'a>() {} - // Second Trait Bound ---------------------------------------------------------- #[cfg(cfail1)] pub fn second_trait_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn second_trait_bound() {} - // Second Builtin Bound -------------------------------------------------------- #[cfg(cfail1)] pub fn second_builtin_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn second_builtin_bound() {} - // Second Lifetime Bound ------------------------------------------------------- #[cfg(cfail1)] pub fn second_lifetime_bound<'a, 'b, T: 'a>() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, generics_of, type_of, predicates_of")] +#[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, generics_of, type_of, predicates_of" +)] #[rustc_clean(cfg = "cfail3")] pub fn second_lifetime_bound<'a, 'b, T: 'a + 'b>() {} - // Inline ---------------------------------------------------------------------- #[cfg(cfail1)] pub fn inline() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] #[rustc_clean(cfg = "cfail3")] #[inline] pub fn inline() {} - // Inline Never ---------------------------------------------------------------- #[cfg(cfail1)] @@ -208,36 +206,33 @@ pub fn inline() {} pub fn inline_never() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] #[rustc_clean(cfg = "cfail3")] #[inline(never)] pub fn inline_never() {} - // No Mangle ------------------------------------------------------------------- #[cfg(cfail1)] pub fn no_mangle() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] #[rustc_clean(cfg = "cfail3")] #[no_mangle] pub fn no_mangle() {} - // Linkage --------------------------------------------------------------------- #[cfg(cfail1)] pub fn linkage() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] #[rustc_clean(cfg = "cfail3")] #[linkage = "weak_odr"] pub fn linkage() {} - // Return Impl Trait ----------------------------------------------------------- #[cfg(cfail1)] @@ -246,13 +241,12 @@ pub fn return_impl_trait() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, typeck_tables_of, fn_sig")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, typeck_tables_of, fn_sig")] #[rustc_clean(cfg = "cfail3")] pub fn return_impl_trait() -> impl Clone { 0 } - // Change Return Impl Trait ---------------------------------------------------- #[cfg(cfail1)] @@ -267,7 +261,6 @@ pub fn change_return_impl_trait() -> impl Copy { 0u32 } - // Change Return Type Indirectly ----------------------------------------------- pub struct ReferencedType1; @@ -279,15 +272,16 @@ pub mod change_return_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as ReturnType; - #[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, mir_built, optimized_mir, typeck_tables_of, fn_sig")] + #[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + )] #[rustc_clean(cfg = "cfail3")] pub fn indirect_return_type() -> ReturnType { ReturnType {} } } - // Change Parameter Type Indirectly -------------------------------------------- pub mod change_parameter_type_indirectly { @@ -296,13 +290,14 @@ pub mod change_parameter_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as ParameterType; - #[rustc_clean(cfg = "cfail2", - except = "Hir, HirBody, mir_built, optimized_mir, typeck_tables_of, fn_sig")] + #[rustc_clean( + cfg = "cfail2", + except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + )] #[rustc_clean(cfg = "cfail3")] pub fn indirect_parameter_type(p: ParameterType) {} } - // Change Trait Bound Indirectly ----------------------------------------------- pub trait ReferencedTrait1 {} @@ -314,12 +309,11 @@ pub mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, predicates_of")] + #[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn indirect_trait_bound(p: T) {} } - // Change Trait Bound Indirectly In Where Clause ------------------------------- pub mod change_trait_bound_indirectly_in_where_clause { @@ -328,7 +322,7 @@ pub mod change_trait_bound_indirectly_in_where_clause { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg = "cfail2", except = "Hir, HirBody, predicates_of")] + #[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn indirect_trait_bound_where(p: T) where diff --git a/src/test/incremental/hashes/if_expressions.rs b/src/test/incremental/hashes/if_expressions.rs index 4b73f1371f8..93bdc0322bb 100644 --- a/src/test/incremental/hashes/if_expressions.rs +++ b/src/test/incremental/hashes/if_expressions.rs @@ -25,7 +25,7 @@ pub fn change_condition(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_condition(x: bool) -> u32 { if !x { @@ -46,7 +46,7 @@ pub fn change_then_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_then_branch(x: bool) -> u32 { if x { @@ -69,7 +69,7 @@ pub fn change_else_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_else_branch(x: bool) -> u32 { if x { @@ -94,7 +94,7 @@ pub fn add_else_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_else_branch(x: bool) -> u32 { let mut ret = 1; @@ -120,7 +120,7 @@ pub fn change_condition_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_condition_if_let(x: Option) -> u32 { if let Some(_) = x { @@ -143,7 +143,7 @@ pub fn change_then_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_then_branch_if_let(x: Option) -> u32 { if let Some(x) = x { @@ -166,7 +166,7 @@ pub fn change_else_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_else_branch_if_let(x: Option) -> u32 { if let Some(x) = x { @@ -191,7 +191,7 @@ pub fn add_else_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_else_branch_if_let(x: Option) -> u32 { let mut ret = 1; diff --git a/src/test/incremental/hashes/indexing_expressions.rs b/src/test/incremental/hashes/indexing_expressions.rs index 08cf19d7760..84c0298918e 100644 --- a/src/test/incremental/hashes/indexing_expressions.rs +++ b/src/test/incremental/hashes/indexing_expressions.rs @@ -20,10 +20,10 @@ fn change_simple_index(slice: &[u32]) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn change_simple_index(slice: &[u32]) -> u32 { slice[4] } @@ -37,10 +37,10 @@ fn change_lower_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn change_lower_bound(slice: &[u32]) -> &[u32] { &slice[2..5] } @@ -54,10 +54,10 @@ fn change_upper_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn change_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] } @@ -71,10 +71,10 @@ fn add_lower_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn add_lower_bound(slice: &[u32]) -> &[u32] { &slice[3..4] } @@ -88,10 +88,10 @@ fn add_upper_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn add_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] } @@ -105,10 +105,10 @@ fn change_mutability(slice: &mut [u32]) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn change_mutability(slice: &mut [u32]) -> u32 { (&slice[3..5])[0] } @@ -122,10 +122,10 @@ fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { &slice[3..=7] } diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index 24d436f5f97..139c265164b 100644 --- a/src/test/incremental/hashes/inherent_impls.rs +++ b/src/test/incremental/hashes/inherent_impls.rs @@ -23,7 +23,7 @@ pub fn method_name() { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,associated_item_def_ids")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,associated_item_def_ids")] #[rustc_clean(cfg="cfail3")] impl Foo { #[rustc_clean(cfg="cfail3")] @@ -44,7 +44,7 @@ pub fn method_body() { } impl Foo { #[rustc_clean( cfg="cfail2", - except="HirBody,optimized_mir,promoted_mir,mir_built,typeck_tables_of" + except="hir_owner_items,optimized_mir,promoted_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn method_body() { @@ -68,7 +68,7 @@ pub fn method_body_inlined() { } impl Foo { #[rustc_clean( cfg="cfail2", - except="HirBody,optimized_mir,promoted_mir,mir_built,typeck_tables_of" + except="hir_owner_items,optimized_mir,promoted_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] #[inline] @@ -85,10 +85,10 @@ pub fn method_privacy() { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="associated_item,Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="associated_item,hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] fn method_privacy() { } } @@ -100,7 +100,7 @@ pub fn method_selfness() { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] impl Foo { #[rustc_dirty(cfg="cfail2", except="type_of,predicates_of,promoted_mir")] @@ -120,7 +120,7 @@ pub fn method_selfmutness(&self) { } impl Foo { #[rustc_clean( cfg="cfail2", - except="Hir,HirBody,fn_sig,typeck_tables_of,optimized_mir,mir_built" + except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of,optimized_mir,mir_built" )] #[rustc_clean(cfg="cfail3")] pub fn method_selfmutness(&mut self) { } @@ -135,7 +135,7 @@ pub fn add_method_to_impl1(&self) { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,associated_item_def_ids")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,associated_item_def_ids")] #[rustc_clean(cfg="cfail3")] impl Foo { #[rustc_clean(cfg="cfail2")] @@ -160,7 +160,7 @@ pub fn add_method_parameter(&self) { } impl Foo { #[rustc_clean( cfg="cfail2", - except="Hir,HirBody,fn_sig,typeck_tables_of,optimized_mir,mir_built" + except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of,optimized_mir,mir_built" )] #[rustc_clean(cfg="cfail3")] pub fn add_method_parameter(&self, _: i32) { } @@ -178,7 +178,7 @@ pub fn change_method_parameter_name(&self, a: i64) { } #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_method_parameter_name(&self, b: i64) { } } @@ -197,7 +197,7 @@ pub fn change_method_return_type(&self) -> u16 { 0 } impl Foo { #[rustc_clean( cfg="cfail2", - except="Hir,HirBody,fn_sig,optimized_mir,mir_built,typeck_tables_of")] + except="hir_owner,hir_owner_items,fn_sig,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_method_return_type(&self) -> u8 { 0 } } @@ -214,7 +214,7 @@ pub fn make_method_inline(&self) -> u8 { 0 } #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] #[inline] pub fn make_method_inline(&self) -> u8 { 0 } @@ -232,7 +232,7 @@ pub fn change_method_parameter_order(&self, a: i64, b: i64) { } #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_method_parameter_order(&self, b: i64, a: i64) { } } @@ -251,7 +251,7 @@ pub fn make_method_unsafe(&self) { } impl Foo { #[rustc_clean( cfg="cfail2", - except="Hir,HirBody,fn_sig,typeck_tables_of,optimized_mir,mir_built" + except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of,optimized_mir,mir_built" )] #[rustc_clean(cfg="cfail3")] pub unsafe fn make_method_unsafe(&self) { } @@ -269,7 +269,7 @@ pub fn make_method_extern(&self) { } #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,fn_sig,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub extern fn make_method_extern(&self) { } } @@ -286,7 +286,7 @@ pub extern "C" fn change_method_calling_convention(&self) { } #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,fn_sig,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub extern "system" fn change_method_calling_convention(&self) { } } @@ -312,7 +312,7 @@ impl Foo { // if we lower generics before the body, then the `HirId` for // things in the body will be affected. So if you start to see // `typeck_tables_of` appear dirty, that might be the cause. -nmatsakis - #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_parameter_to_method<'a>(&self) { } } @@ -340,7 +340,7 @@ impl Foo { // appear dirty, that might be the cause. -nmatsakis #[rustc_clean( cfg="cfail2", - except="Hir,HirBody,generics_of,predicates_of,type_of", + except="hir_owner,hir_owner_items,generics_of,predicates_of,type_of", )] #[rustc_clean(cfg="cfail3")] pub fn add_type_parameter_to_method(&self) { } @@ -360,7 +360,7 @@ pub fn add_lifetime_bound_to_lifetime_param_of_method<'a, 'b>(&self) { } impl Foo { #[rustc_clean( cfg="cfail2", - except="Hir,HirBody,generics_of,predicates_of,type_of,typeck_tables_of" + except="hir_owner,hir_owner_items,generics_of,predicates_of,type_of" )] #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_bound_to_lifetime_param_of_method<'a, 'b: 'a>(&self) { } @@ -387,7 +387,7 @@ impl Foo { // generics before the body, then the `HirId` for things in the // body will be affected. So if you start to see `typeck_tables_of` // appear dirty, that might be the cause. -nmatsakis - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,generics_of,predicates_of,\ + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,generics_of,predicates_of,\ type_of")] #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_bound_to_type_param_of_method<'a, T: 'a>(&self) { } @@ -414,7 +414,7 @@ impl Foo { // generics before the body, then the `HirId` for things in the // body will be affected. So if you start to see `typeck_tables_of` // appear dirty, that might be the cause. -nmatsakis - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,predicates_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,predicates_of")] #[rustc_clean(cfg="cfail3")] pub fn add_trait_bound_to_type_param_of_method(&self) { } } @@ -431,7 +431,7 @@ pub fn add_no_mangle_to_method(&self) { } #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] #[no_mangle] pub fn add_no_mangle_to_method(&self) { } @@ -448,7 +448,7 @@ pub fn add_type_parameter_to_impl(&self) { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,generics_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,generics_of")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean( @@ -468,7 +468,7 @@ pub fn change_impl_self_type(&self) { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean(cfg="cfail2", except="fn_sig,optimized_mir,mir_built,typeck_tables_of")] @@ -485,7 +485,7 @@ pub fn add_lifetime_bound_to_impl_parameter(&self) { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean(cfg="cfail2")] @@ -502,7 +502,7 @@ pub fn add_trait_bound_to_impl_parameter(&self) { } } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean(cfg="cfail2")] diff --git a/src/test/incremental/hashes/inline_asm.rs b/src/test/incremental/hashes/inline_asm.rs index c50ee73d714..a77123110ae 100644 --- a/src/test/incremental/hashes/inline_asm.rs +++ b/src/test/incremental/hashes/inline_asm.rs @@ -33,7 +33,7 @@ pub fn change_template(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn change_template(a: i32) -> i32 { @@ -69,7 +69,7 @@ pub fn change_output(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn change_output(a: i32) -> i32 { @@ -105,7 +105,7 @@ pub fn change_input(_a: i32, _b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn change_input(_a: i32, _b: i32) -> i32 { @@ -140,7 +140,7 @@ pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { @@ -175,7 +175,7 @@ pub fn change_clobber(_a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn change_clobber(_a: i32) -> i32 { @@ -210,7 +210,7 @@ pub fn change_options(_a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn change_options(_a: i32) -> i32 { diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 68545b7daaa..2d9cf4203dc 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -22,7 +22,7 @@ pub fn change_name() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name() { let _y = 2u64; @@ -38,7 +38,7 @@ pub fn add_type() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn add_type() { let _x: u32 = 2u32; @@ -54,7 +54,7 @@ pub fn change_type() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_type() { let _x: u8 = 2; @@ -70,7 +70,7 @@ pub fn change_mutability_of_reference_type() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_reference_type() { let _x: &mut u64; @@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_slot() { let _x: u64 = 0; @@ -102,7 +102,7 @@ pub fn change_simple_binding_to_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_simple_binding_to_pattern() { let (_a, _b) = (0u8, 'x'); @@ -118,7 +118,7 @@ pub fn change_name_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name_in_pattern() { let (_a, _c) = (1u8, 'y'); @@ -134,7 +134,7 @@ pub fn add_ref_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn add_ref_in_pattern() { let (ref _a, _b) = (1u8, 'y'); @@ -150,7 +150,7 @@ pub fn add_amp_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn add_amp_in_pattern() { let (&_a, _b) = (&1u8, 'y'); @@ -166,7 +166,7 @@ pub fn change_mutability_of_binding_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern() { let (mut _a, _b) = (99u8, 'q'); @@ -182,7 +182,7 @@ pub fn add_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn add_initializer() { let _x: i16 = 3i16; @@ -198,7 +198,7 @@ pub fn change_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_initializer() { let _x = 5u16; diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs index a2222db4c59..7ce43559cff 100644 --- a/src/test/incremental/hashes/loop_expressions.rs +++ b/src/test/incremental/hashes/loop_expressions.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -47,7 +47,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -70,7 +70,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -93,7 +93,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -118,7 +118,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -143,7 +143,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -168,7 +168,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -193,7 +193,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/hashes/match_expressions.rs b/src/test/incremental/hashes/match_expressions.rs index 840b2222d90..30934c7c1d1 100644 --- a/src/test/incremental/hashes/match_expressions.rs +++ b/src/test/incremental/hashes/match_expressions.rs @@ -26,7 +26,7 @@ pub fn add_arm(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_arm(x: u32) -> u32 { match x { @@ -51,7 +51,7 @@ pub fn change_order_of_arms(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_order_of_arms(x: u32) -> u32 { match x { @@ -75,7 +75,7 @@ pub fn add_guard_clause(x: u32, y: bool) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_guard_clause(x: u32, y: bool) -> u32 { match x { @@ -99,7 +99,7 @@ pub fn change_guard_clause(x: u32, y: bool) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_guard_clause(x: u32, y: bool) -> u32 { match x { @@ -123,7 +123,7 @@ pub fn add_at_binding(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_at_binding(x: u32) -> u32 { match x { @@ -147,7 +147,7 @@ pub fn change_name_of_at_binding(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name_of_at_binding(x: u32) -> u32 { match x { @@ -170,7 +170,7 @@ pub fn change_simple_name_to_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_simple_name_to_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -193,7 +193,7 @@ pub fn change_name_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name_in_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -216,7 +216,7 @@ pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -238,7 +238,7 @@ pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -260,7 +260,7 @@ pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", -except="HirBody,mir_built,optimized_mir,typeck_tables_of")] +except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 { match (&x, x & 1) { @@ -283,7 +283,7 @@ pub fn change_rhs_of_arm(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_rhs_of_arm(x: u32) -> u32 { match x { @@ -307,7 +307,7 @@ pub fn add_alternative_to_arm(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_alternative_to_arm(x: u32) -> u32 { match x { diff --git a/src/test/incremental/hashes/panic_exprs.rs b/src/test/incremental/hashes/panic_exprs.rs index 70b0a5ab78c..b46d1fac124 100644 --- a/src/test/incremental/hashes/panic_exprs.rs +++ b/src/test/incremental/hashes/panic_exprs.rs @@ -18,7 +18,7 @@ // Indexing expression -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn indexing(slice: &[u8]) -> u8 { #[cfg(cfail1)] @@ -33,7 +33,7 @@ pub fn indexing(slice: &[u8]) -> u8 { // Arithmetic overflow plus -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_plus(val: i32) -> i32 { #[cfg(cfail1)] @@ -48,7 +48,7 @@ pub fn arithmetic_overflow_plus(val: i32) -> i32 { // Arithmetic overflow minus -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_minus(val: i32) -> i32 { #[cfg(cfail1)] @@ -63,7 +63,7 @@ pub fn arithmetic_overflow_minus(val: i32) -> i32 { // Arithmetic overflow mult -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_mult(val: i32) -> i32 { #[cfg(cfail1)] @@ -78,7 +78,7 @@ pub fn arithmetic_overflow_mult(val: i32) -> i32 { // Arithmetic overflow negation -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_negation(val: i32) -> i32 { #[cfg(cfail1)] @@ -93,7 +93,7 @@ pub fn arithmetic_overflow_negation(val: i32) -> i32 { // Division by zero -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn division_by_zero(val: i32) -> i32 { #[cfg(cfail1)] @@ -107,7 +107,7 @@ pub fn division_by_zero(val: i32) -> i32 { } // Division by zero -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn mod_by_zero(val: i32) -> i32 { #[cfg(cfail1)] @@ -122,7 +122,7 @@ pub fn mod_by_zero(val: i32) -> i32 { // shift left -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn shift_left(val: i32, shift: usize) -> i32 { #[cfg(cfail1)] @@ -137,7 +137,7 @@ pub fn shift_left(val: i32, shift: usize) -> i32 { // shift right -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn shift_right(val: i32, shift: usize) -> i32 { #[cfg(cfail1)] diff --git a/src/test/incremental/hashes/statics.rs b/src/test/incremental/hashes/statics.rs index d70ebb08b71..536b7932473 100644 --- a/src/test/incremental/hashes/statics.rs +++ b/src/test/incremental/hashes/statics.rs @@ -21,7 +21,7 @@ static STATIC_VISIBILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub static STATIC_VISIBILITY: u8 = 0; @@ -31,7 +31,7 @@ static STATIC_MUTABILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] static mut STATIC_MUTABILITY: u8 = 0; @@ -41,7 +41,7 @@ static STATIC_LINKAGE: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] #[linkage="weak_odr"] static STATIC_LINKAGE: u8 = 0; @@ -52,7 +52,7 @@ static STATIC_NO_MANGLE: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] #[no_mangle] static STATIC_NO_MANGLE: u8 = 0; @@ -63,7 +63,7 @@ static STATIC_THREAD_LOCAL: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] #[thread_local] static STATIC_THREAD_LOCAL: u8 = 0; @@ -74,7 +74,7 @@ static STATIC_CHANGE_TYPE_1: i16 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_1: u64 = 0; @@ -84,13 +84,13 @@ static STATIC_CHANGE_TYPE_2: Option = None; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_2: Option = None; // Change value between simple literals -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_1: i16 = { #[cfg(cfail1)] @@ -102,7 +102,7 @@ // Change value between expressions -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_2: i16 = { #[cfg(cfail1)] @@ -112,7 +112,7 @@ { 1 + 2 } }; -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_3: i16 = { #[cfg(cfail1)] @@ -122,7 +122,7 @@ { 2 * 3 } }; -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_4: i16 = { #[cfg(cfail1)] @@ -144,11 +144,11 @@ mod static_change_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as Type; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_INDIRECTLY_1: Type = Type; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_INDIRECTLY_2: Option = None; } diff --git a/src/test/incremental/hashes/struct_constructors.rs b/src/test/incremental/hashes/struct_constructors.rs index 7ae1798c7a2..89b18eefd06 100644 --- a/src/test/incremental/hashes/struct_constructors.rs +++ b/src/test/incremental/hashes/struct_constructors.rs @@ -31,7 +31,7 @@ pub fn change_field_value_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_regular_struct() -> RegularStruct { RegularStruct { @@ -54,7 +54,7 @@ pub fn change_field_order_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_field_order_regular_struct() -> RegularStruct { RegularStruct { @@ -82,7 +82,7 @@ pub fn add_field_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_field_regular_struct() -> RegularStruct { let struct1 = RegularStruct { @@ -117,7 +117,7 @@ pub fn change_field_label_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_field_label_regular_struct() -> RegularStruct { let struct1 = RegularStruct { @@ -152,7 +152,7 @@ pub fn change_constructor_path_regular_struct() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_regular_struct() { let _ = RegularStruct2 { @@ -173,7 +173,7 @@ pub mod change_constructor_path_indirectly_regular_struct { #[rustc_clean( cfg="cfail2", - except="fn_sig,Hir,HirBody,optimized_mir,mir_built,typeck_tables_of" + except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn function() -> Struct { @@ -196,7 +196,7 @@ pub fn change_field_value_tuple_struct() -> TupleStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_tuple_struct() -> TupleStruct { TupleStruct(0, 1, 3) @@ -213,7 +213,7 @@ pub fn change_constructor_path_tuple_struct() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_tuple_struct() { let _ = TupleStruct2(0, 1, 2); @@ -230,7 +230,7 @@ pub mod change_constructor_path_indirectly_tuple_struct { #[rustc_clean( cfg="cfail2", - except="fn_sig,Hir,HirBody,optimized_mir,mir_built,typeck_tables_of" + except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn function() -> Struct { diff --git a/src/test/incremental/hashes/struct_defs.rs b/src/test/incremental/hashes/struct_defs.rs index e0c56964831..fa08b7ec1ed 100644 --- a/src/test/incremental/hashes/struct_defs.rs +++ b/src/test/incremental/hashes/struct_defs.rs @@ -24,13 +24,13 @@ pub struct LayoutPacked; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -41,13 +41,13 @@ struct LayoutC; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -61,13 +61,13 @@ struct TupleStructFieldType(i32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -84,13 +84,13 @@ struct TupleStructFieldType( struct TupleStructAddField(i32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -106,13 +106,13 @@ struct TupleStructAddField( struct TupleStructFieldVisibility(char); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -125,13 +125,13 @@ struct TupleStructAddField( struct RecordStructFieldType { x: f32 } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -148,13 +148,13 @@ struct RecordStructFieldType { struct RecordStructFieldName { x: f32 } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -167,13 +167,13 @@ struct RecordStructFieldName { y: f32 } struct RecordStructAddField { x: f32 } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -188,13 +188,13 @@ struct RecordStructAddField { struct RecordStructFieldVisibility { x: f32 } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -209,13 +209,13 @@ struct RecordStructFieldVisibility { struct AddLifetimeParameter<'a>(&'a f32, &'a f64); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_dirty(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -228,13 +228,13 @@ struct RecordStructFieldVisibility { struct AddLifetimeParameterBound<'a, 'b>(&'a f32, &'b f64); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -247,13 +247,13 @@ struct AddLifetimeParameterBound<'a, 'b: 'a>( struct AddLifetimeParameterBoundWhereClause<'a, 'b>(&'a f32, &'b f64); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -269,13 +269,13 @@ struct AddLifetimeParameterBoundWhereClause<'a, 'b>( struct AddTypeParameter(T1, T1); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_dirty(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -293,13 +293,13 @@ struct AddTypeParameter( struct AddTypeParameterBound(T); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -312,13 +312,13 @@ struct AddTypeParameterBound( struct AddTypeParameterBoundWhereClause(T); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -332,13 +332,13 @@ struct AddTypeParameterBoundWhereClause( // fingerprint is stable (i.e., that there are no random influences like memory // addresses taken into account by the hashing algorithm). // Note: there is no #[cfg(...)], so this is ALWAYS compiled -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -351,13 +351,13 @@ struct AddTypeParameterBoundWhereClause( struct Visibility; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] +#[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -373,13 +373,13 @@ mod tuple_struct_change_field_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -396,13 +396,13 @@ mod record_struct_change_field_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -424,13 +424,13 @@ mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -444,13 +444,13 @@ mod change_trait_bound_indirectly_in_where_clause { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] diff --git a/src/test/incremental/hashes/trait_defs.rs b/src/test/incremental/hashes/trait_defs.rs index 3006cdccfbb..df41b73f92c 100644 --- a/src/test/incremental/hashes/trait_defs.rs +++ b/src/test/incremental/hashes/trait_defs.rs @@ -25,8 +25,8 @@ trait TraitVisibility { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] pub trait TraitVisibility { } @@ -36,8 +36,8 @@ pub trait TraitVisibility { } trait TraitUnsafety { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] unsafe trait TraitUnsafety { } @@ -48,8 +48,8 @@ trait TraitAddMethod { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] pub trait TraitAddMethod { fn method(); } @@ -63,8 +63,8 @@ trait TraitChangeMethodName { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeMethodName { fn methodChanged(); } @@ -78,11 +78,11 @@ trait TraitAddReturnType { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddReturnType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method() -> u32; } @@ -95,11 +95,11 @@ trait TraitChangeReturnType { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeReturnType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method() -> u64; } @@ -112,11 +112,11 @@ trait TraitAddParameterToMethod { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddParameterToMethod { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(a: u32); } @@ -130,18 +130,18 @@ fn with_default(x: i32) {} } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeMethodParameterName { // FIXME(#38501) This should preferably always be clean. - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(b: u32); - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn with_default(y: i32) {} } @@ -154,11 +154,11 @@ trait TraitChangeMethodParameterType { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeMethodParameterType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(a: i64); } @@ -171,11 +171,11 @@ trait TraitChangeMethodParameterTypeRef { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeMethodParameterTypeRef { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(a: &mut i32); } @@ -188,11 +188,11 @@ trait TraitChangeMethodParametersOrder { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeMethodParametersOrder { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(b: i64, a: i32); } @@ -205,11 +205,11 @@ trait TraitAddMethodAutoImplementation { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddMethodAutoImplementation { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method() { } } @@ -223,8 +223,8 @@ trait TraitChangeOrderOfMethods { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeOrderOfMethods { fn method1(); fn method0(); @@ -239,11 +239,11 @@ trait TraitChangeModeSelfRefToMut { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeModeSelfRefToMut { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(&mut self); } @@ -255,13 +255,13 @@ fn method(self) {} } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeModeSelfOwnToMut: Sized { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn method(mut self) {} } @@ -273,11 +273,11 @@ trait TraitChangeModeSelfOwnToRef { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeModeSelfOwnToRef { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(&self); } @@ -290,11 +290,11 @@ trait TraitAddUnsafeModifier { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddUnsafeModifier { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] unsafe fn method(); } @@ -307,11 +307,11 @@ trait TraitAddExternModifier { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddExternModifier { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] extern fn method(); } @@ -324,11 +324,11 @@ trait TraitChangeExternCToRustIntrinsic { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeExternCToRustIntrinsic { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] extern "stdcall" fn method(); } @@ -341,11 +341,11 @@ trait TraitAddTypeParameterToMethod { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTypeParameterToMethod { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } @@ -358,11 +358,11 @@ trait TraitAddLifetimeParameterToMethod { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeParameterToMethod { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method<'a>(); } @@ -379,11 +379,11 @@ trait TraitAddTraitBoundToMethodTypeParameter { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTraitBoundToMethodTypeParameter { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } @@ -396,11 +396,11 @@ trait TraitAddBuiltinBoundToMethodTypeParameter { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddBuiltinBoundToMethodTypeParameter { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } @@ -413,11 +413,11 @@ trait TraitAddLifetimeBoundToMethodLifetimeParameter { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeBoundToMethodLifetimeParameter { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method<'a, 'b: 'a>(a: &'a u32, b: &'b u32); } @@ -430,11 +430,11 @@ trait TraitAddSecondTraitBoundToMethodTypeParameter { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondTraitBoundToMethodTypeParameter { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } @@ -447,11 +447,11 @@ trait TraitAddSecondBuiltinBoundToMethodTypeParameter { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondBuiltinBoundToMethodTypeParameter { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } @@ -464,11 +464,11 @@ trait TraitAddSecondLifetimeBoundToMethodLifetimeParameter { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondLifetimeBoundToMethodLifetimeParameter { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method<'a, 'b, 'c: 'a + 'b>(a: &'a u32, b: &'b u32, c: &'c u32); } @@ -478,14 +478,14 @@ trait TraitAddSecondLifetimeBoundToMethodLifetimeParameter { #[cfg(cfail1)] trait TraitAddAssociatedType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddAssociatedType { type Associated; @@ -506,11 +506,11 @@ trait TraitAddTraitBoundToAssociatedType { // Apparently the type bound contributes to the predicates of the trait, but // does not change the associated item itself. #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTraitBoundToAssociatedType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] type Associated: ReferencedTrait0; fn method(); @@ -527,11 +527,11 @@ trait TraitAddLifetimeBoundToAssociatedType<'a> { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeBoundToAssociatedType<'a> { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] type Associated: 'a; fn method(); @@ -548,11 +548,11 @@ trait TraitAddDefaultToAssociatedType { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddDefaultToAssociatedType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] type Associated = ReferenceType0; fn method(); @@ -567,8 +567,8 @@ trait TraitAddAssociatedConstant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddAssociatedConstant { const Value: u32; @@ -586,15 +586,15 @@ trait TraitAddInitializerToAssociatedConstant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddInitializerToAssociatedConstant { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] const Value: u32 = 1; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } @@ -609,15 +609,15 @@ trait TraitChangeTypeOfAssociatedConstant { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeTypeOfAssociatedConstant { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] const Value: f64; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(); } @@ -628,8 +628,8 @@ trait TraitChangeTypeOfAssociatedConstant { trait TraitAddSuperTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSuperTrait : ReferencedTrait0 { } @@ -639,8 +639,8 @@ trait TraitAddSuperTrait : ReferencedTrait0 { } trait TraitAddBuiltiBound { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddBuiltiBound : Send { } @@ -650,8 +650,8 @@ trait TraitAddBuiltiBound : Send { } trait TraitAddStaticLifetimeBound { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddStaticLifetimeBound : 'static { } @@ -661,16 +661,16 @@ trait TraitAddStaticLifetimeBound : 'static { } trait TraitAddTraitAsSecondBound : ReferencedTrait0 { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTraitAsSecondBound : ReferencedTrait0 + ReferencedTrait1 { } #[cfg(cfail1)] trait TraitAddTraitAsSecondBoundFromBuiltin : Send { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTraitAsSecondBoundFromBuiltin : Send + ReferencedTrait0 { } @@ -680,16 +680,16 @@ trait TraitAddTraitAsSecondBoundFromBuiltin : Send + ReferencedTrait0 { } trait TraitAddBuiltinBoundAsSecondBound : ReferencedTrait0 { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddBuiltinBoundAsSecondBound : ReferencedTrait0 + Send { } #[cfg(cfail1)] trait TraitAddBuiltinBoundAsSecondBoundFromBuiltin : Send { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddBuiltinBoundAsSecondBoundFromBuiltin: Send + Copy { } @@ -699,16 +699,16 @@ trait TraitAddBuiltinBoundAsSecondBoundFromBuiltin: Send + Copy { } trait TraitAddStaticBoundAsSecondBound : ReferencedTrait0 { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddStaticBoundAsSecondBound : ReferencedTrait0 + 'static { } #[cfg(cfail1)] trait TraitAddStaticBoundAsSecondBoundFromBuiltin : Send { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddStaticBoundAsSecondBoundFromBuiltin : Send + 'static { } @@ -718,8 +718,8 @@ trait TraitAddStaticBoundAsSecondBoundFromBuiltin : Send + 'static { } trait TraitAddTypeParameterToTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTypeParameterToTrait { } @@ -729,8 +729,8 @@ trait TraitAddTypeParameterToTrait { } trait TraitAddLifetimeParameterToTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeParameterToTrait<'a> { } @@ -740,8 +740,8 @@ trait TraitAddLifetimeParameterToTrait<'a> { } trait TraitAddTraitBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTraitBoundToTypeParameterOfTrait { } @@ -751,8 +751,8 @@ trait TraitAddTraitBoundToTypeParameterOfTrait { } trait TraitAddLifetimeBoundToTypeParameterOfTrait<'a, T> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeBoundToTypeParameterOfTrait<'a, T: 'a> { } @@ -762,8 +762,8 @@ trait TraitAddLifetimeBoundToTypeParameterOfTrait<'a, T: 'a> { } trait TraitAddLifetimeBoundToLifetimeParameterOfTrait<'a, 'b> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeBoundToLifetimeParameterOfTrait<'a: 'b, 'b> { } @@ -773,8 +773,8 @@ trait TraitAddLifetimeBoundToLifetimeParameterOfTrait<'a: 'b, 'b> { } trait TraitAddBuiltinBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddBuiltinBoundToTypeParameterOfTrait { } @@ -784,8 +784,8 @@ trait TraitAddBuiltinBoundToTypeParameterOfTrait { } trait TraitAddSecondTypeParameterToTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondTypeParameterToTrait { } @@ -795,8 +795,8 @@ trait TraitAddSecondTypeParameterToTrait { } trait TraitAddSecondLifetimeParameterToTrait<'a> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondLifetimeParameterToTrait<'a, 'b> { } @@ -806,8 +806,8 @@ trait TraitAddSecondLifetimeParameterToTrait<'a, 'b> { } trait TraitAddSecondTraitBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondTraitBoundToTypeParameterOfTrait { } @@ -817,8 +817,8 @@ trait TraitAddSecondTraitBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondLifetimeBoundToTypeParameterOfTrait<'a, 'b, T: 'a + 'b> { } @@ -828,8 +828,8 @@ trait TraitAddSecondLifetimeBoundToTypeParameterOfTrait<'a, 'b, T: 'a + 'b> { } trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTrait<'a: 'b, 'b, 'c> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTrait<'a: 'b + 'c, 'b, 'c> { } @@ -839,8 +839,8 @@ trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTrait<'a: 'b + 'c, 'b, 'c> trait TraitAddSecondBuiltinBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondBuiltinBoundToTypeParameterOfTrait { } @@ -855,8 +855,8 @@ struct ReferenceType1 {} trait TraitAddTraitBoundToTypeParameterOfTraitWhere { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddTraitBoundToTypeParameterOfTraitWhere where T: ReferencedTrait0 { } @@ -866,8 +866,8 @@ trait TraitAddTraitBoundToTypeParameterOfTraitWhere where T: ReferencedTrait0 trait TraitAddLifetimeBoundToTypeParameterOfTraitWhere<'a, T> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeBoundToTypeParameterOfTraitWhere<'a, T> where T: 'a { } @@ -877,8 +877,8 @@ trait TraitAddLifetimeBoundToTypeParameterOfTraitWhere<'a, T> where T: 'a { } trait TraitAddLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b> where 'a: 'b { } @@ -888,8 +888,8 @@ trait TraitAddLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b> where 'a: 'b trait TraitAddBuiltinBoundToTypeParameterOfTraitWhere { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddBuiltinBoundToTypeParameterOfTraitWhere where T: Send { } @@ -899,8 +899,8 @@ trait TraitAddBuiltinBoundToTypeParameterOfTraitWhere where T: Send { } trait TraitAddSecondTraitBoundToTypeParameterOfTraitWhere where T: ReferencedTrait0 { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondTraitBoundToTypeParameterOfTraitWhere where T: ReferencedTrait0 + ReferencedTrait1 { } @@ -911,8 +911,8 @@ trait TraitAddSecondTraitBoundToTypeParameterOfTraitWhere trait TraitAddSecondLifetimeBoundToTypeParameterOfTraitWhere<'a, 'b, T> where T: 'a { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondLifetimeBoundToTypeParameterOfTraitWhere<'a, 'b, T> where T: 'a + 'b { } @@ -922,8 +922,8 @@ trait TraitAddSecondLifetimeBoundToTypeParameterOfTraitWhere<'a, 'b, T> where T: trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b, 'c> where 'a: 'b { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b, 'c> where 'a: 'b + 'c { } @@ -933,8 +933,8 @@ trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b, 'c> whe trait TraitAddSecondBuiltinBoundToTypeParameterOfTraitWhere where T: Send { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitAddSecondBuiltinBoundToTypeParameterOfTraitWhere where T: Send + Sync { } @@ -945,11 +945,11 @@ mod change_return_type_of_method_indirectly_use { #[cfg(not(cfail1))] use super::ReferenceType1 as ReturnType; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeReturnType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method() -> ReturnType; } } @@ -963,11 +963,11 @@ mod change_method_parameter_type_indirectly_by_use { #[cfg(not(cfail1))] use super::ReferenceType1 as ArgType; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeArgType { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(a: ArgType); } } @@ -981,11 +981,11 @@ mod change_method_parameter_type_bound_indirectly_by_use { #[cfg(not(cfail1))] use super::ReferencedTrait1 as Bound; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeBoundOfMethodTypeParameter { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(a: T); } } @@ -1000,11 +1000,11 @@ mod change_method_parameter_type_bound_indirectly_by_use_where { #[cfg(not(cfail1))] use super::ReferencedTrait1 as Bound; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeBoundOfMethodTypeParameterWhere { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method(a: T) where T: Bound; } } @@ -1018,8 +1018,8 @@ mod change_method_type_parameter_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait1 as Bound; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeTraitBound { fn method(a: T); } @@ -1035,8 +1035,8 @@ mod change_method_type_parameter_bound_indirectly_where { #[cfg(not(cfail1))] use super::ReferencedTrait1 as Bound; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] trait TraitChangeTraitBoundWhere where T: Bound { fn method(a: T); } diff --git a/src/test/incremental/hashes/trait_impls.rs b/src/test/incremental/hashes/trait_impls.rs index fa28b2ebedd..70e066870b6 100644 --- a/src/test/incremental/hashes/trait_impls.rs +++ b/src/test/incremental/hashes/trait_impls.rs @@ -30,18 +30,18 @@ fn method_name() { } } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] pub trait ChangeMethodNameTrait { - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name2(); } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeMethodNameTrait for Foo { - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name2() { } } @@ -59,13 +59,13 @@ fn method_name() { } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeMethodBodyTrait for Foo { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] fn method_name() { () } @@ -86,13 +86,13 @@ fn method_name() { } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeMethodBodyTraitInlined for Foo { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_clean(label="hir_owner_items", cfg="cfail3")] #[inline] fn method_name() { panic!() @@ -117,11 +117,11 @@ pub trait ChangeMethodSelfnessTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeMethodSelfnessTrait for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name(&self) { () } @@ -145,11 +145,11 @@ pub trait RemoveMethodSelfnessTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl RemoveMethodSelfnessTrait for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name() {} } @@ -171,11 +171,11 @@ pub trait ChangeMethodSelfmutnessTrait { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeMethodSelfmutnessTrait for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name(&mut self) {} } @@ -197,8 +197,8 @@ pub trait ChangeItemKindTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeItemKindTrait for Foo { type name = (); } @@ -223,8 +223,8 @@ pub trait RemoveItemTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl RemoveItemTrait for Foo { type TypeName = (); } @@ -248,8 +248,8 @@ pub trait AddItemTrait { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl AddItemTrait for Foo { type TypeName = (); fn method_name() { } @@ -268,17 +268,17 @@ fn method_name() { } } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] pub trait ChangeHasValueTrait { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name() { } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeHasValueTrait for Foo { fn method_name() { } } @@ -295,11 +295,11 @@ fn method_name() { } } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl AddDefaultTrait for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] default fn method_name() { } } @@ -321,11 +321,11 @@ pub trait AddArgumentTrait { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl AddArgumentTrait for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name(&self, _x: u32) { } } @@ -347,11 +347,11 @@ pub trait ChangeArgumentTypeTrait { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeArgumentTypeTrait for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn method_name(&self, _x: char) { } } @@ -370,11 +370,11 @@ fn id(t: u32) -> u32 { t } } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl AddTypeParameterToImpl for Bar { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn id(t: T) -> T { t } } @@ -391,11 +391,11 @@ fn id(self) -> Self { self } } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl ChangeSelfTypeOfImpl for u64 { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn id(self) -> Self { self } } @@ -412,11 +412,11 @@ fn id(self) -> Self { self } } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl AddLifetimeBoundToImplParameter for T { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn id(self) -> Self { self } } @@ -433,11 +433,11 @@ fn id(self) -> Self { self } } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl AddTraitBoundToImplParameter for T { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] fn id(self) -> Self { self } } @@ -454,11 +454,11 @@ fn add_no_mangle_to_method(&self) { } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl AddNoMangleToMethod for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] #[no_mangle] fn add_no_mangle_to_method(&self) { } } @@ -475,11 +475,11 @@ fn make_method_inline(&self) -> u8 { 0 } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="hir_owner", cfg="cfail2")] +#[rustc_clean(label="hir_owner", cfg="cfail3")] impl MakeMethodInline for Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail3")] #[inline] fn make_method_inline(&self) -> u8 { 0 } } diff --git a/src/test/incremental/hashes/type_defs.rs b/src/test/incremental/hashes/type_defs.rs index 264e8f926ff..bbe1514ba9f 100644 --- a/src/test/incremental/hashes/type_defs.rs +++ b/src/test/incremental/hashes/type_defs.rs @@ -24,7 +24,7 @@ type ChangePrimitiveType = i32; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangePrimitiveType = i64; @@ -35,7 +35,7 @@ type ChangeMutability = &'static i32; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeMutability = &'static mut i32; @@ -46,7 +46,7 @@ type ChangeLifetime<'a> = (&'static i32, &'a i32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeLifetime<'a> = (&'a i32, &'a i32); @@ -60,7 +60,7 @@ type ChangeTypeStruct = Struct1; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeTypeStruct = Struct2; @@ -71,7 +71,7 @@ type ChangeTypeTuple = (u32, u64); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeTypeTuple = (u32, i64); @@ -91,7 +91,7 @@ enum Enum2 { type ChangeTypeEnum = Enum1; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeTypeEnum = Enum2; @@ -102,7 +102,7 @@ enum Enum2 { type AddTupleField = (i32, i64); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type AddTupleField = (i32, i64, i16); @@ -113,7 +113,7 @@ enum Enum2 { type ChangeNestedTupleField = (i32, (i64, i16)); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeNestedTupleField = (i32, (i64, i8)); @@ -124,7 +124,7 @@ enum Enum2 { type AddTypeParam = (T1, T1); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type AddTypeParam = (T1, T2); @@ -135,7 +135,7 @@ enum Enum2 { type AddTypeParamBound = (T1, u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type AddTypeParamBound = (T1, u32); @@ -146,7 +146,7 @@ enum Enum2 { type AddTypeParamBoundWhereClause where T1: Clone = (T1, u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type AddTypeParamBoundWhereClause where T1: Clone+Copy = (T1, u32); @@ -157,7 +157,7 @@ enum Enum2 { type AddLifetimeParam<'a> = (&'a u32, &'a u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type AddLifetimeParam<'a, 'b> = (&'a u32, &'b u32); @@ -168,7 +168,7 @@ enum Enum2 { type AddLifetimeParamBound<'a, 'b> = (&'a u32, &'b u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type AddLifetimeParamBound<'a, 'b: 'a> = (&'a u32, &'b u32); @@ -181,7 +181,7 @@ enum Enum2 { = (&'a u32, &'b u32, &'c u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type AddLifetimeParamBoundWhereClause<'a, 'b, 'c> where 'b: 'a, @@ -200,7 +200,7 @@ mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeTraitBoundIndirectly = (T, u32); } @@ -214,7 +214,7 @@ mod change_trait_bound_indirectly_in_where_clause { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] #[rustc_clean(cfg="cfail3")] type ChangeTraitBoundIndirectly where T : Trait = (T, u32); } diff --git a/src/test/incremental/hashes/unary_and_binary_exprs.rs b/src/test/incremental/hashes/unary_and_binary_exprs.rs index 776a0273ca7..89aa0b1a58b 100644 --- a/src/test/incremental/hashes/unary_and_binary_exprs.rs +++ b/src/test/incremental/hashes/unary_and_binary_exprs.rs @@ -21,7 +21,7 @@ pub fn const_negation() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn const_negation() -> i32 { -1 @@ -36,7 +36,7 @@ pub fn const_bitwise_not() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn const_bitwise_not() -> i32 { !99 @@ -51,7 +51,7 @@ pub fn var_negation(x: i32, y: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn var_negation(x: i32, y: i32) -> i32 { -y @@ -66,7 +66,7 @@ pub fn var_bitwise_not(x: i32, y: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn var_bitwise_not(x: i32, y: i32) -> i32 { !y @@ -81,7 +81,7 @@ pub fn var_deref(x: &i32, y: &i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built,typeck_tables_of", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn var_deref(x: &i32, y: &i32) -> i32 { *y @@ -96,7 +96,7 @@ pub fn first_const_add() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn first_const_add() -> i32 { 2 + 3 @@ -111,7 +111,7 @@ pub fn second_const_add() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn second_const_add() -> i32 { 1 + 3 @@ -126,7 +126,7 @@ pub fn first_var_add(a: i32, b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn first_var_add(a: i32, b: i32) -> i32 { b + 2 @@ -141,7 +141,7 @@ pub fn second_var_add(a: i32, b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn second_var_add(a: i32, b: i32) -> i32 { 1 + b @@ -156,7 +156,7 @@ pub fn plus_to_minus(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_minus(a: i32) -> i32 { 1 - a @@ -171,7 +171,7 @@ pub fn plus_to_mult(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_mult(a: i32) -> i32 { 1 * a @@ -186,7 +186,7 @@ pub fn plus_to_div(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_div(a: i32) -> i32 { 1 / a @@ -201,7 +201,7 @@ pub fn plus_to_mod(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_mod(a: i32) -> i32 { 1 % a @@ -216,7 +216,7 @@ pub fn and_to_or(a: bool, b: bool) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn and_to_or(a: bool, b: bool) -> bool { a || b @@ -231,7 +231,7 @@ pub fn bitwise_and_to_bitwise_or(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_bitwise_or(a: i32) -> i32 { 1 | a @@ -246,7 +246,7 @@ pub fn bitwise_and_to_bitwise_xor(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_bitwise_xor(a: i32) -> i32 { 1 ^ a @@ -261,7 +261,7 @@ pub fn bitwise_and_to_lshift(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_lshift(a: i32) -> i32 { a << 1 @@ -276,7 +276,7 @@ pub fn bitwise_and_to_rshift(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_rshift(a: i32) -> i32 { a >> 1 @@ -291,7 +291,7 @@ pub fn eq_to_uneq(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_uneq(a: i32) -> bool { a != 1 @@ -306,7 +306,7 @@ pub fn eq_to_lt(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_lt(a: i32) -> bool { a < 1 @@ -321,7 +321,7 @@ pub fn eq_to_gt(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_gt(a: i32) -> bool { a > 1 @@ -336,7 +336,7 @@ pub fn eq_to_le(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_le(a: i32) -> bool { a <= 1 @@ -351,7 +351,7 @@ pub fn eq_to_ge(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_ge(a: i32) -> bool { a >= 1 @@ -368,7 +368,7 @@ pub fn type_cast(a: u8) -> u64 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built,typeck_tables_of", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn type_cast(a: u8) -> u64 { let b = a as u32; @@ -385,7 +385,7 @@ pub fn value_cast(a: u32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn value_cast(a: u32) -> i32 { 2 as i32 @@ -403,7 +403,7 @@ pub fn place() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn place() -> i32 { let mut x = 10; @@ -423,7 +423,7 @@ pub fn rvalue() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn rvalue() -> i32 { let mut x = 10; @@ -440,7 +440,7 @@ pub fn index_to_slice(s: &[u8], i: usize, j: usize) -> u8 { } #[cfg(not(cfail1))] -#[rustc_clean(except="HirBody,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn index_to_slice(s: &[u8], i: usize, j: usize) -> u8 { s[j] diff --git a/src/test/incremental/hashes/while_let_loops.rs b/src/test/incremental/hashes/while_let_loops.rs index 615f1fe1fd0..908f60440fa 100644 --- a/src/test/incremental/hashes/while_let_loops.rs +++ b/src/test/incremental/hashes/while_let_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -48,7 +48,7 @@ pub fn change_loop_condition() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_condition() { let mut _x = 0; @@ -70,7 +70,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -93,7 +93,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -116,7 +116,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -141,7 +141,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -166,7 +166,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -191,7 +191,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -216,7 +216,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs index a427ffb96e3..365ec5fa567 100644 --- a/src/test/incremental/hashes/while_loops.rs +++ b/src/test/incremental/hashes/while_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -48,7 +48,7 @@ pub fn change_loop_condition() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_condition() { let mut _x = 0; @@ -70,7 +70,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -93,7 +93,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -116,7 +116,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -141,7 +141,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -166,7 +166,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -191,7 +191,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -216,7 +216,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/ich_method_call_trait_scope.rs b/src/test/incremental/ich_method_call_trait_scope.rs index 9dfd2ae2511..a9ec66346ac 100644 --- a/src/test/incremental/ich_method_call_trait_scope.rs +++ b/src/test/incremental/ich_method_call_trait_scope.rs @@ -26,15 +26,15 @@ mod mod3 { #[cfg(rpass2)] use Trait2; - #[rustc_clean(label="Hir", cfg="rpass2")] - #[rustc_clean(label="HirBody", cfg="rpass2")] + #[rustc_clean(label="hir_owner", cfg="rpass2")] + #[rustc_clean(label="hir_owner_items", cfg="rpass2")] #[rustc_dirty(label="typeck_tables_of", cfg="rpass2")] fn bar() { ().method(); } - #[rustc_clean(label="Hir", cfg="rpass2")] - #[rustc_clean(label="HirBody", cfg="rpass2")] + #[rustc_clean(label="hir_owner", cfg="rpass2")] + #[rustc_clean(label="hir_owner_items", cfg="rpass2")] #[rustc_clean(label="typeck_tables_of", cfg="rpass2")] fn baz() { 22; // no method call, traits in scope don't matter diff --git a/src/test/incremental/ich_nested_items.rs b/src/test/incremental/ich_nested_items.rs index b2b7e663151..a9232190eef 100644 --- a/src/test/incremental/ich_nested_items.rs +++ b/src/test/incremental/ich_nested_items.rs @@ -7,18 +7,21 @@ #![crate_type = "rlib"] #![feature(rustc_attrs)] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label = "hir_owner", cfg = "cfail2")] +#[rustc_dirty(label = "hir_owner_items", cfg = "cfail2")] pub fn foo() { #[cfg(cfail1)] - pub fn baz() { } // order is different... + pub fn baz() {} // order is different... - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail2")] - pub fn bar() { } // but that doesn't matter. + // FIXME: Make "hir_owner" use `rustc_clean` here. Currently "hir_owner" includes a reference to + // the parent node, which is the statement holding this item. Changing the position of + // `bar` in `foo` will update that reference and make `hir_owner(bar)` dirty. + #[rustc_dirty(label = "hir_owner", cfg = "cfail2")] + #[rustc_clean(label = "hir_owner_items", cfg = "cfail2")] + pub fn bar() {} // but that doesn't matter. #[cfg(cfail2)] - pub fn baz() { } // order is different... + pub fn baz() {} // order is different... - pub fn bap() { } // neither does adding a new item + pub fn bap() {} // neither does adding a new item } diff --git a/src/test/incremental/ich_resolve_results.rs b/src/test/incremental/ich_resolve_results.rs index 0e4ab6b78e2..c4674faabf5 100644 --- a/src/test/incremental/ich_resolve_results.rs +++ b/src/test/incremental/ich_resolve_results.rs @@ -28,18 +28,18 @@ mod mod3 { #[cfg(rpass3)] use mod2::Foo; - #[rustc_clean(label="Hir", cfg="rpass2")] - #[rustc_clean(label="HirBody", cfg="rpass2")] - #[rustc_clean(label="Hir", cfg="rpass3")] - #[rustc_dirty(label="HirBody", cfg="rpass3")] + #[rustc_clean(label="hir_owner", cfg="rpass2")] + #[rustc_clean(label="hir_owner_items", cfg="rpass2")] + #[rustc_clean(label="hir_owner", cfg="rpass3")] + #[rustc_dirty(label="hir_owner_items", cfg="rpass3")] fn in_expr() { Foo(0); } - #[rustc_clean(label="Hir", cfg="rpass2")] - #[rustc_clean(label="HirBody", cfg="rpass2")] - #[rustc_clean(label="Hir", cfg="rpass3")] - #[rustc_dirty(label="HirBody", cfg="rpass3")] + #[rustc_clean(label="hir_owner", cfg="rpass2")] + #[rustc_clean(label="hir_owner_items", cfg="rpass2")] + #[rustc_clean(label="hir_owner", cfg="rpass3")] + #[rustc_dirty(label="hir_owner_items", cfg="rpass3")] fn in_type() { test::(); } diff --git a/src/test/incremental/inlined_hir_34991/main.rs b/src/test/incremental/inlined_hir_34991/main.rs index 819b8434b08..bb76a0bb8fe 100644 --- a/src/test/incremental/inlined_hir_34991/main.rs +++ b/src/test/incremental/inlined_hir_34991/main.rs @@ -1,6 +1,6 @@ // Regression test for #34991: an ICE occurred here because we inline // some of the vector routines and give them a local def-id `X`. This -// got hashed after codegen (`Hir(X)`). When we load back up, we get an +// got hashed after codegen (`hir_owner(X)`). When we load back up, we get an // error because the `X` is remapped to the original def-id (in // libstd), and we can't hash a HIR node from std. diff --git a/src/test/incremental/issue-69596.rs b/src/test/incremental/issue-69596.rs new file mode 100644 index 00000000000..dc587fdc44b --- /dev/null +++ b/src/test/incremental/issue-69596.rs @@ -0,0 +1,21 @@ +// revisions: rpass1 rpass2 + +#![allow(unused_imports)] + +#[macro_export] +macro_rules! a_macro { + () => {}; +} + +#[cfg(rpass1)] +use a_macro as same_name; + +mod same_name {} + +mod needed_mod { + fn _crash() { + use super::same_name; + } +} + +fn main() {} diff --git a/src/test/incremental/source_loc_macros.rs b/src/test/incremental/source_loc_macros.rs index 51ea7d6d447..a360a66a64b 100644 --- a/src/test/incremental/source_loc_macros.rs +++ b/src/test/incremental/source_loc_macros.rs @@ -7,26 +7,26 @@ #![feature(rustc_attrs)] -#[rustc_clean(label="Hir", cfg="rpass2")] -#[rustc_clean(label="HirBody", cfg="rpass2")] +#[rustc_clean(label="hir_owner", cfg="rpass2")] +#[rustc_clean(label="hir_owner_items", cfg="rpass2")] fn line_same() { let _ = line!(); } -#[rustc_clean(label="Hir", cfg="rpass2")] -#[rustc_clean(label="HirBody", cfg="rpass2")] +#[rustc_clean(label="hir_owner", cfg="rpass2")] +#[rustc_clean(label="hir_owner_items", cfg="rpass2")] fn col_same() { let _ = column!(); } -#[rustc_clean(label="Hir", cfg="rpass2")] -#[rustc_clean(label="HirBody", cfg="rpass2")] +#[rustc_clean(label="hir_owner", cfg="rpass2")] +#[rustc_clean(label="hir_owner_items", cfg="rpass2")] fn file_same() { let _ = file!(); } -#[rustc_clean(label="Hir", cfg="rpass2")] -#[rustc_dirty(label="HirBody", cfg="rpass2")] +#[rustc_clean(label="hir_owner", cfg="rpass2")] +#[rustc_dirty(label="hir_owner_items", cfg="rpass2")] fn line_different() { #[cfg(rpass1)] { @@ -38,8 +38,8 @@ fn line_different() { } } -#[rustc_clean(label="Hir", cfg="rpass2")] -#[rustc_dirty(label="HirBody", cfg="rpass2")] +#[rustc_clean(label="hir_owner", cfg="rpass2")] +#[rustc_dirty(label="hir_owner_items", cfg="rpass2")] fn col_different() { #[cfg(rpass1)] { diff --git a/src/test/incremental/span_hash_stable/auxiliary/sub1.rs b/src/test/incremental/span_hash_stable/auxiliary/sub1.rs index 54fbe4465e3..2927ddec4e5 100644 --- a/src/test/incremental/span_hash_stable/auxiliary/sub1.rs +++ b/src/test/incremental/span_hash_stable/auxiliary/sub1.rs @@ -1,4 +1,4 @@ -#[rustc_clean(label="Hir", cfg="rpass2")] +#[rustc_clean(label="hir_owner", cfg="rpass2")] pub struct SomeType { pub x: u32, pub y: i64, diff --git a/src/test/incremental/span_hash_stable/auxiliary/sub2.rs b/src/test/incremental/span_hash_stable/auxiliary/sub2.rs index 34957616856..aa635077db8 100644 --- a/src/test/incremental/span_hash_stable/auxiliary/sub2.rs +++ b/src/test/incremental/span_hash_stable/auxiliary/sub2.rs @@ -1,4 +1,4 @@ -#[rustc_clean(label="Hir", cfg="rpass2")] +#[rustc_clean(label="hir_owner", cfg="rpass2")] pub struct SomeOtherType { pub a: i32, pub b: u64, diff --git a/src/test/incremental/spans_significant_w_debuginfo.rs b/src/test/incremental/spans_significant_w_debuginfo.rs index e6fdc7cb3a0..b87d829132b 100644 --- a/src/test/incremental/spans_significant_w_debuginfo.rs +++ b/src/test/incremental/spans_significant_w_debuginfo.rs @@ -12,6 +12,6 @@ pub fn main() {} #[cfg(rpass2)] -#[rustc_dirty(label="Hir", cfg="rpass2")] -#[rustc_dirty(label="HirBody", cfg="rpass2")] +#[rustc_dirty(label="hir_owner", cfg="rpass2")] +#[rustc_dirty(label="hir_owner_items", cfg="rpass2")] pub fn main() {} diff --git a/src/test/incremental/string_constant.rs b/src/test/incremental/string_constant.rs index b88acd2af75..11a42262c16 100644 --- a/src/test/incremental/string_constant.rs +++ b/src/test/incremental/string_constant.rs @@ -18,7 +18,7 @@ pub fn x() { } #[cfg(cfail2)] - #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] #[rustc_dirty(label="optimized_mir", cfg="cfail2")] pub fn x() { println!("{}", "2"); diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs b/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs index 42168dd273e..9c17c8745f8 100644 --- a/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs +++ b/src/test/incremental/thinlto/cgu_invalidated_when_import_added.rs @@ -4,7 +4,7 @@ // rust-lang/rust#59535: // -// This is analgous to cgu_invalidated_when_import_removed.rs, but it covers +// This is analogous to cgu_invalidated_when_import_removed.rs, but it covers // the other direction: // // We start with a call-graph like `[A] -> [B -> D] [C]` (where the letters are diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs index 19ce7b3e148..1214e37f982 100644 --- a/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs +++ b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs @@ -41,7 +41,7 @@ mod foo { // In cfail2, ThinLTO decides that foo() does not get inlined into main, and // instead bar() gets inlined into foo(). But faulty logic in our incr. // ThinLTO implementation thought that `main()` is unchanged and thus reused - // the object file still containing a call to the now non-existant bar(). + // the object file still containing a call to the now non-existent bar(). pub fn foo(){ bar() } diff --git a/src/test/incremental/unchecked_dirty_clean.rs b/src/test/incremental/unchecked_dirty_clean.rs index 66bdb670167..3bc8818aa6f 100644 --- a/src/test/incremental/unchecked_dirty_clean.rs +++ b/src/test/incremental/unchecked_dirty_clean.rs @@ -10,13 +10,13 @@ fn main() { - #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] //[cfail2]~^ ERROR found unchecked `#[rustc_dirty]` / `#[rustc_clean]` attribute { // empty block } - #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] //[cfail2]~^ ERROR found unchecked `#[rustc_dirty]` / `#[rustc_clean]` attribute { // empty block @@ -24,11 +24,11 @@ fn main() { } struct _Struct { - #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_dirty(label="hir_owner", cfg="cfail2")] //[cfail2]~^ ERROR found unchecked `#[rustc_dirty]` / `#[rustc_clean]` attribute _field1: i32, - #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="hir_owner", cfg="cfail2")] //[cfail2]~^ ERROR found unchecked `#[rustc_dirty]` / `#[rustc_clean]` attribute _field2: i32, } diff --git a/src/test/mir-opt/const-promotion-extern-static.rs b/src/test/mir-opt/const-promotion-extern-static.rs index f6f7d091091..c858a4c5ee7 100644 --- a/src/test/mir-opt/const-promotion-extern-static.rs +++ b/src/test/mir-opt/const-promotion-extern-static.rs @@ -4,9 +4,9 @@ static Y: i32 = 42; -static mut BAR: *const &'static i32 = [&Y].as_ptr(); +static mut BAR: *const &i32 = [&Y].as_ptr(); -static mut FOO: *const &'static i32 = [unsafe { &X }].as_ptr(); +static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); fn main() {} @@ -14,12 +14,12 @@ fn main() {} // START rustc.FOO.PromoteTemps.before.mir // bb0: { // ... -// _5 = const Scalar(alloc1+0) : &i32; +// _5 = const {alloc1+0: &i32}; // _4 = &(*_5); // _3 = [move _4]; // _2 = &_3; -// _1 = move _2 as &[&'static i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; +// _1 = move _2 as &[&i32] (Pointer(Unsize)); +// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // } // ... // bb2: { @@ -31,12 +31,12 @@ fn main() {} // START rustc.BAR.PromoteTemps.before.mir // bb0: { // ... -// _5 = const Scalar(alloc0+0) : &i32; +// _5 = const {alloc0+0: &i32}; // _4 = &(*_5); // _3 = [move _4]; // _2 = &_3; -// _1 = move _2 as &[&'static i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; +// _1 = move _2 as &[&i32] (Pointer(Unsize)); +// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // } // ... // bb2: { @@ -50,8 +50,8 @@ fn main() {} // ... // _6 = const BAR::promoted[0]; // _2 = &(*_6); -// _1 = move _2 as &[&'static i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; +// _1 = move _2 as &[&i32] (Pointer(Unsize)); +// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // } // ... // bb2: { @@ -63,8 +63,8 @@ fn main() {} // ... // _6 = const FOO::promoted[0]; // _2 = &(*_6); -// _1 = move _2 as &[&'static i32] (Pointer(Unsize)); -// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; +// _1 = move _2 as &[&i32] (Pointer(Unsize)); +// _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // } // ... // bb2: { diff --git a/src/test/mir-opt/const_prop/discriminant.rs b/src/test/mir-opt/const_prop/discriminant.rs index 667d21fc14e..636aa1af653 100644 --- a/src/test/mir-opt/const_prop/discriminant.rs +++ b/src/test/mir-opt/const_prop/discriminant.rs @@ -31,7 +31,7 @@ fn main() { // START rustc.main.ConstProp.after.mir // bb0: { // ... -// _3 = const Scalar(0x01) : std::option::Option; +// _3 = const {transmute(0x01): std::option::Option}; // _4 = const 1isize; // switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // } diff --git a/src/test/mir-opt/const_prop/issue-66971.rs b/src/test/mir-opt/const_prop/issue-66971.rs index 30c75303b3e..f332bb89509 100644 --- a/src/test/mir-opt/const_prop/issue-66971.rs +++ b/src/test/mir-opt/const_prop/issue-66971.rs @@ -29,7 +29,7 @@ fn main() { // START rustc.main.ConstProp.after.mir // bb0: { // ... -// _3 = const Scalar() : (); +// _3 = const (); // _2 = (move _3, const 0u8, const 0u8); // ... // _1 = const encode(move _2) -> bb1; diff --git a/src/test/mir-opt/const_prop/read_immutable_static.rs b/src/test/mir-opt/const_prop/read_immutable_static.rs index 693ef783985..d307cebd715 100644 --- a/src/test/mir-opt/const_prop/read_immutable_static.rs +++ b/src/test/mir-opt/const_prop/read_immutable_static.rs @@ -10,10 +10,10 @@ fn main() { // START rustc.main.ConstProp.before.mir // bb0: { // ... -// _3 = const Scalar(alloc0+0) : &u8; +// _3 = const {alloc0+0: &u8}; // _2 = (*_3); // ... -// _5 = const Scalar(alloc0+0) : &u8; +// _5 = const {alloc0+0: &u8}; // _4 = (*_5); // _1 = Add(move _2, move _4); // ... diff --git a/src/test/mir-opt/generator-tiny.rs b/src/test/mir-opt/generator-tiny.rs new file mode 100644 index 00000000000..09e943bd962 --- /dev/null +++ b/src/test/mir-opt/generator-tiny.rs @@ -0,0 +1,34 @@ +//! Tests that generators that cannot return or unwind don't have unnecessary +//! panic branches. + +// compile-flags: -Zno-landing-pads + +#![feature(generators, generator_trait)] + +struct HasDrop; + +impl Drop for HasDrop { + fn drop(&mut self) {} +} + +fn callee() {} + +fn main() { + let _gen = |_x: u8| { + let _d = HasDrop; + loop { + yield; + callee(); + } + }; +} + +// END RUST SOURCE + +// START rustc.main-{{closure}}.generator_resume.0.mir +// bb0: { +// ... +// switchInt(move _11) -> [0u32: bb1, 3u32: bb5, otherwise: bb6]; +// } +// ... +// END rustc.main-{{closure}}.generator_resume.0.mir diff --git a/src/test/mir-opt/no-drop-for-inactive-variant.rs b/src/test/mir-opt/no-drop-for-inactive-variant.rs new file mode 100644 index 00000000000..74a606af28f --- /dev/null +++ b/src/test/mir-opt/no-drop-for-inactive-variant.rs @@ -0,0 +1,43 @@ +// ignore-wasm32-bare compiled with panic=abort by default + +// Ensure that there are no drop terminators in `unwrap` (except the one along the cleanup +// path). + +fn unwrap(opt: Option) -> T { + match opt { + Some(x) => x, + None => panic!(), + } +} + +fn main() { + let _ = unwrap(Some(1i32)); +} + +// END RUST SOURCE +// START rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir +// fn unwrap(_1: std::option::Option) -> T { +// ... +// bb0: { +// ... +// switchInt(move _2) -> [0isize: bb2, 1isize: bb4, otherwise: bb3]; +// } +// bb1 (cleanup): { +// resume; +// } +// bb2: { +// ... +// const std::rt::begin_panic::<&str>(const "explicit panic") -> bb5; +// } +// bb3: { +// unreachable; +// } +// bb4: { +// ... +// return; +// } +// bb5 (cleanup): { +// drop(_1) -> bb1; +// } +// } +// END rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index 294fe247c38..ea106eaf595 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -26,16 +26,16 @@ fn main() { // goto -> bb7; // } // bb2: { -// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb3, otherwise: bb1]; +// switchInt((*(*((_1 as Some).0: &&i32)))) -> [0i32: bb3, otherwise: bb1]; // } // bb3: { // goto -> bb4; // } // bb4: { // _4 = &shallow _1; -// _5 = &shallow ((_1 as Some).0: &' &' i32); -// _6 = &shallow (*((_1 as Some).0: &' &' i32)); -// _7 = &shallow (*(*((_1 as Some).0: &' &' i32))); +// _5 = &shallow ((_1 as Some).0: &&i32); +// _6 = &shallow (*((_1 as Some).0: &&i32)); +// _7 = &shallow (*(*((_1 as Some).0: &&i32))); // StorageLive(_8); // _8 = _2; // switchInt(move _8) -> [false: bb6, otherwise: bb5]; @@ -72,7 +72,7 @@ fn main() { // goto -> bb7; // } // bb2: { -// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb3, otherwise: bb1]; +// switchInt((*(*((_1 as Some).0: &&i32)))) -> [0i32: bb3, otherwise: bb1]; // } // bb3: { // goto -> bb4; diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs index 6f03438ff72..e427fd55ad6 100644 --- a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs +++ b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs @@ -1,18 +1,18 @@ // compile-flags: -C overflow-checks=no -fn use_zst(_: ((), ())) { } +fn use_zst(_: ((), ())) {} struct Temp { - x: u8 + x: u8, } -fn use_u8(_: u8) { } +fn use_u8(_: u8) {} fn main() { let ((), ()) = ((), ()); use_zst(((), ())); - use_u8((Temp { x : 40 }).x + 2); + use_u8((Temp { x: 40 }).x + 2); } // END RUST SOURCE @@ -35,28 +35,28 @@ fn main() { // bb0: { // StorageLive(_1); // StorageLive(_2); -// _2 = const Scalar() : (); +// _2 = const (); // StorageLive(_3); -// _3 = const Scalar() : (); -// _1 = const Scalar() : ((), ()); +// _3 = const (); +// _1 = const {transmute(()): ((), ())}; // StorageDead(_3); // StorageDead(_2); // StorageDead(_1); // StorageLive(_4); // StorageLive(_6); -// _6 = const Scalar() : (); +// _6 = const (); // StorageLive(_7); -// _7 = const Scalar() : (); +// _7 = const (); // StorageDead(_7); // StorageDead(_6); -// _4 = const use_zst(const Scalar() : ((), ())) -> bb1; +// _4 = const use_zst(const {transmute(()): ((), ())}) -> bb1; // } // bb1: { // StorageDead(_4); // StorageLive(_8); // StorageLive(_10); // StorageLive(_11); -// _11 = const Scalar(0x28) : Temp; +// _11 = const {transmute(0x28) : Temp}; // _10 = const 40u8; // StorageDead(_10); // _8 = const use_u8(const 42u8) -> bb2; @@ -75,7 +75,7 @@ fn main() { // } // bb0: { // StorageLive(_1); -// _1 = const use_zst(const Scalar() : ((), ())) -> bb1; +// _1 = const use_zst(const {transmute(()): ((), ())}) -> bb1; // } // bb1: { // StorageDead(_1); diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs index 5dc15286bab..1c98766b968 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.rs +++ b/src/test/mir-opt/storage_live_dead_in_statics.rs @@ -35,12 +35,12 @@ fn main() { // END RUST SOURCE // START rustc.XXX.mir_map.0.mir -// let mut _0: &'static Foo; -// let _1: &'static Foo; +// let mut _0: &Foo; +// let _1: &Foo; // let _2: Foo; -// let mut _3: &'static [(u32, u32)]; -// let mut _4: &'static [(u32, u32); 42]; -// let _5: &'static [(u32, u32); 42]; +// let mut _3: &[(u32, u32)]; +// let mut _4: &[(u32, u32); 42]; +// let _5: &[(u32, u32); 42]; // let _6: [(u32, u32); 42]; // let mut _7: (u32, u32); // let mut _8: (u32, u32); @@ -178,7 +178,7 @@ fn main() { // _6 = [move _7, move _8, move _9, move _10, move _11, move _12, move _13, move _14, move _15, move _16, move _17, move _18, move _19, move _20, move _21, move _22, move _23, move _24, move _25, move _26, move _27, move _28, move _29, move _30, move _31, move _32, move _33, move _34, move _35, move _36, move _37, move _38, move _39, move _40, move _41, move _42, move _43, move _44, move _45, move _46, move _47, move _48]; // _5 = &_6; // _4 = &(*_5); -// _3 = move _4 as &'static [(u32, u32)] (Pointer(Unsize)); +// _3 = move _4 as &[(u32, u32)] (Pointer(Unsize)); // _2 = Foo { tup: const "hi", data: move _3 }; // _1 = &_2; // _0 = &(*_1); diff --git a/src/test/pretty/attr-literals.rs b/src/test/pretty/attr-literals.rs index 9db7e27b161..44d2c5db3e6 100644 --- a/src/test/pretty/attr-literals.rs +++ b/src/test/pretty/attr-literals.rs @@ -5,7 +5,7 @@ #![feature(rustc_attrs)] fn main() { - #![rustc_dummy("hi", 1, 2, 1.012, pi = 3.14, bye, name ("John"))] + #![rustc_dummy("hi", 1, 2, 1.012, pi = 3.14, bye, name("John"))] #[rustc_dummy = 8] fn f() { } diff --git a/src/test/pretty/delimited-token-groups.rs b/src/test/pretty/delimited-token-groups.rs index 7bbb7dc911f..66de0fc6cf7 100644 --- a/src/test/pretty/delimited-token-groups.rs +++ b/src/test/pretty/delimited-token-groups.rs @@ -7,7 +7,7 @@ macro_rules! mac { ($ ($ tt : tt) *) => () } mac! { struct S { field1 : u8, field2 : u16, } impl Clone for S { - fn clone () -> S + fn clone() -> S { panic ! () ; @@ -16,9 +16,8 @@ fn clone () -> S } mac! { - a - (aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa - aaaaaaaa aaaaaaaa) a + a(aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa + aaaaaaaa aaaaaaaa) a [aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa] a { diff --git a/src/test/pretty/if-attr.rs b/src/test/pretty/if-attr.rs new file mode 100644 index 00000000000..652604fc7f3 --- /dev/null +++ b/src/test/pretty/if-attr.rs @@ -0,0 +1,37 @@ +// pp-exact + +#[cfg(FALSE)] +fn simple_attr() { + + #[attr] + if true { } + + #[allow_warnings] + if true { } +} + +#[cfg(FALSE)] +fn if_else_chain() { + + #[first_attr] + if true { } else if false { } else { } +} + +#[cfg(FALSE)] +fn if_let() { + + #[attr] + if let Some(_) = Some(true) { } +} + +#[cfg(FALSE)] +fn let_attr_if() { + let _ = #[attr] if let _ = 0 { }; + let _ = #[attr] if true { }; + + let _ = #[attr] if let _ = 0 { } else { }; + let _ = #[attr] if true { } else { }; +} + + +fn main() { } diff --git a/src/test/pretty/issue-12590-a.rs b/src/test/pretty/issue-12590-a.rs index 1a9e85c42d8..ca1fef83cff 100644 --- a/src/test/pretty/issue-12590-a.rs +++ b/src/test/pretty/issue-12590-a.rs @@ -1,4 +1,5 @@ // pp-exact +// pretty-compare-only // The next line should not be expanded diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index 8aa4cdeb539..ee7586bae82 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -34,29 +34,29 @@ pub fn bar() ({ ((::alloc::fmt::format as for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((::core::fmt::Arguments::new_v1 as - fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments::<'_>::new_v1})((&([("test" - as - &'static str)] - as - [&str; 1]) - as - &[&str; 1]), - (&(match (() - as - ()) - { - () - => - ([] - as - [std::fmt::ArgumentV1<'_>; 0]), - } - as - [std::fmt::ArgumentV1<'_>; 0]) - as - &[std::fmt::ArgumentV1<'_>; 0])) + fn(&[&str], &[std::fmt::ArgumentV1]) -> std::fmt::Arguments {std::fmt::Arguments::new_v1})((&([("test" + as + &str)] + as + [&str; 1]) + as + &[&str; 1]), + (&(match (() + as + ()) + { + () + => + ([] + as + [std::fmt::ArgumentV1; 0]), + } + as + [std::fmt::ArgumentV1; 0]) + as + &[std::fmt::ArgumentV1; 0])) as - std::fmt::Arguments<'_>)) + std::fmt::Arguments)) as std::string::String); (res as std::string::String) } as std::string::String); diff --git a/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs b/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs index 643ca761aac..031a4825959 100644 --- a/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs +++ b/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs @@ -12,5 +12,5 @@ struct C { #[cfg(debug_assertions)] field: 0, - #[cfg(not (debug_assertions))] + #[cfg(not(debug_assertions))] field: 1,}; diff --git a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index 07ef2424cc8..d975af52f5b 100644 --- a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -7,14 +7,12 @@ extern crate rustc_hir; extern crate rustc_target; extern crate rustc_driver; +extern crate rustc_session; extern crate rustc_span; use std::any::Any; use std::sync::Arc; use std::path::Path; -use rustc_span::symbol::Symbol; -use rustc::session::Session; -use rustc::session::config::OutputFilenames; use rustc::ty::TyCtxt; use rustc::ty::query::Providers; use rustc::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn}; @@ -23,6 +21,9 @@ use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_data_structures::sync::MetadataRef; use rustc_data_structures::owning_ref::OwningRef; +use rustc_session::Session; +use rustc_session::config::OutputFilenames; +use rustc_span::symbol::Symbol; use rustc_target::spec::Target; pub struct NoLlvmMetadataLoader; @@ -89,7 +90,7 @@ fn link( outputs: &OutputFilenames, ) -> Result<(), ErrorReported> { use std::io::Write; - use rustc::session::config::CrateType; + use rustc_session::config::CrateType; use rustc_codegen_utils::link::out_filename; let crate_name = codegen_results.downcast::() .expect("in link: codegen_results is not a Symbol"); diff --git a/src/test/run-make-fulldeps/issue-19371/foo.rs b/src/test/run-make-fulldeps/issue-19371/foo.rs index 12da64fc88f..af84faa7511 100644 --- a/src/test/run-make-fulldeps/issue-19371/foo.rs +++ b/src/test/run-make-fulldeps/issue-19371/foo.rs @@ -1,13 +1,12 @@ #![feature(rustc_private)] -extern crate rustc; extern crate rustc_interface; extern crate rustc_driver; +extern crate rustc_session; extern crate rustc_span; -use rustc::session::DiagnosticOutput; -use rustc::session::config::{Input, Options, - OutputType, OutputTypes}; +use rustc_session::DiagnosticOutput; +use rustc_session::config::{Input, Options, OutputType, OutputTypes}; use rustc_interface::interface; use rustc_span::source_map::FileName; diff --git a/src/test/run-make-fulldeps/issue-69368/Makefile b/src/test/run-make-fulldeps/issue-69368/Makefile new file mode 100644 index 00000000000..dbb044d8f5d --- /dev/null +++ b/src/test/run-make-fulldeps/issue-69368/Makefile @@ -0,0 +1,18 @@ +-include ../tools.mk + +# Test that previously triggered a linker failure with root cause +# similar to one found in the issue #69368. +# +# The crate that provides oom lang item is missing some other lang +# items. Necessary to prevent the use of start-group / end-group. +# +# The weak lang items are defined in a separate compilation units, +# so that linker could omit them if not used. +# +# The crates that need those weak lang items are dependencies of +# crates that provide them. + +all: + $(RUSTC) a.rs + $(RUSTC) b.rs + $(RUSTC) c.rs diff --git a/src/test/run-make-fulldeps/issue-69368/a.rs b/src/test/run-make-fulldeps/issue-69368/a.rs new file mode 100644 index 00000000000..726db874637 --- /dev/null +++ b/src/test/run-make-fulldeps/issue-69368/a.rs @@ -0,0 +1,16 @@ +#![crate_type = "rlib"] +#![feature(lang_items)] +#![feature(panic_unwind)] +#![no_std] + +extern crate panic_unwind; + +#[panic_handler] +pub fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +extern "C" fn __rust_drop_panic() -> ! { + loop {} +} diff --git a/src/test/run-make-fulldeps/issue-69368/b.rs b/src/test/run-make-fulldeps/issue-69368/b.rs new file mode 100644 index 00000000000..4d6af026656 --- /dev/null +++ b/src/test/run-make-fulldeps/issue-69368/b.rs @@ -0,0 +1,8 @@ +#![crate_type = "rlib"] +#![feature(alloc_error_handler)] +#![no_std] + +#[alloc_error_handler] +pub fn error_handler(_: core::alloc::Layout) -> ! { + panic!(); +} diff --git a/src/test/run-make-fulldeps/issue-69368/c.rs b/src/test/run-make-fulldeps/issue-69368/c.rs new file mode 100644 index 00000000000..729c4249a05 --- /dev/null +++ b/src/test/run-make-fulldeps/issue-69368/c.rs @@ -0,0 +1,34 @@ +#![crate_type = "bin"] +#![feature(start)] +#![no_std] + +extern crate alloc; +extern crate a; +extern crate b; + +use alloc::vec::Vec; +use core::alloc::*; + +struct Allocator; + +unsafe impl GlobalAlloc for Allocator { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { + loop {} + } + + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + loop {} + } +} + +#[global_allocator] +static ALLOCATOR: Allocator = Allocator; + +#[start] +fn main(argc: isize, _argv: *const *const u8) -> isize { + let mut v = Vec::new(); + for i in 0..argc { + v.push(i); + } + v.iter().sum() +} diff --git a/src/test/rustdoc-js-std/return-specific-literal.js b/src/test/rustdoc-js-std/return-specific-literal.js new file mode 100644 index 00000000000..c7c347240b7 --- /dev/null +++ b/src/test/rustdoc-js-std/return-specific-literal.js @@ -0,0 +1,10 @@ +const QUERY = 'struct:"string"'; + +const EXPECTED = { + 'in_args': [ + { 'path': 'std::string::String', 'name': 'ne' }, + ], + 'returned': [ + { 'path': 'std::string::String', 'name': 'add' }, + ], +}; diff --git a/src/test/rustdoc-js-std/return-specific.js b/src/test/rustdoc-js-std/return-specific.js new file mode 100644 index 00000000000..d9a910553b8 --- /dev/null +++ b/src/test/rustdoc-js-std/return-specific.js @@ -0,0 +1,10 @@ +const QUERY = 'struct:string'; + +const EXPECTED = { + 'in_args': [ + { 'path': 'std::string::String', 'name': 'ne' }, + ], + 'returned': [ + { 'path': 'std::string::String', 'name': 'add' }, + ], +}; diff --git a/src/test/rustdoc-ui/coverage/html.rs b/src/test/rustdoc-ui/coverage/html.rs new file mode 100644 index 00000000000..181cb4c5061 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/html.rs @@ -0,0 +1,4 @@ +// compile-flags:-Z unstable-options --output-format html --show-coverage + +/// Foo +pub struct Xo; diff --git a/src/test/rustdoc-ui/coverage/html.stderr b/src/test/rustdoc-ui/coverage/html.stderr new file mode 100644 index 00000000000..adca375d4bc --- /dev/null +++ b/src/test/rustdoc-ui/coverage/html.stderr @@ -0,0 +1,2 @@ +error: html output format isn't supported for the --show-coverage option + diff --git a/src/test/rustdoc-ui/coverage/json.rs b/src/test/rustdoc-ui/coverage/json.rs new file mode 100644 index 00000000000..b1220b32e91 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/json.rs @@ -0,0 +1,27 @@ +// build-pass +// compile-flags:-Z unstable-options --output-format json --show-coverage + +pub mod foo { + /// Hello! + pub struct Foo; + /// Bar + pub enum Bar { A } +} + +/// X +pub struct X; + +/// Bar +pub mod bar { + /// bar + pub struct Bar; + /// X + pub enum X { Y } +} + +/// yolo +pub enum Yolo { X } + +pub struct Xo { + x: T, +} diff --git a/src/test/rustdoc-ui/coverage/json.stdout b/src/test/rustdoc-ui/coverage/json.stdout new file mode 100644 index 00000000000..63b22a7d94b --- /dev/null +++ b/src/test/rustdoc-ui/coverage/json.stdout @@ -0,0 +1 @@ +{"$DIR/json.rs":{"total":13,"with_docs":7}} diff --git a/src/test/rustdoc-ui/issue-61732.rs b/src/test/rustdoc-ui/issue-61732.rs index d4835c09224..4bd8efeaa3b 100644 --- a/src/test/rustdoc-ui/issue-61732.rs +++ b/src/test/rustdoc-ui/issue-61732.rs @@ -1,4 +1,4 @@ // This previously triggered an ICE. pub(in crate::r#mod) fn main() {} -//~^ ERROR expected module, found unresolved item +//~^ ERROR failed to resolve: maybe a missing crate `r#mod` diff --git a/src/test/rustdoc-ui/issue-61732.stderr b/src/test/rustdoc-ui/issue-61732.stderr index 6c8ba48864d..82134224911 100644 --- a/src/test/rustdoc-ui/issue-61732.stderr +++ b/src/test/rustdoc-ui/issue-61732.stderr @@ -1,11 +1,11 @@ -error[E0577]: expected module, found unresolved item `crate::r#mod` - --> $DIR/issue-61732.rs:3:8 +error[E0433]: failed to resolve: maybe a missing crate `r#mod`? + --> $DIR/issue-61732.rs:3:15 | LL | pub(in crate::r#mod) fn main() {} - | ^^^^^^^^^^^^ not a module + | ^^^^^ maybe a missing crate `r#mod`? error: Compilation failed, aborting rustdoc error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0577`. +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc/attributes.rs b/src/test/rustdoc/attributes.rs index 6ecdad3ec00..e9cd3514a07 100644 --- a/src/test/rustdoc/attributes.rs +++ b/src/test/rustdoc/attributes.rs @@ -15,3 +15,7 @@ pub extern "C" fn g() {} pub enum Foo { Bar, } + +// @has foo/struct.Repr.html '//*[@class="docblock attributes top-attr"]' '#[repr(C, align(8))]' +#[repr(C, align(8))] +pub struct Repr; diff --git a/src/test/rustdoc/crate-version-escape.rs b/src/test/rustdoc/crate-version-escape.rs new file mode 100644 index 00000000000..2f91eea339b --- /dev/null +++ b/src/test/rustdoc/crate-version-escape.rs @@ -0,0 +1,6 @@ +// compile-flags: --crate-version= -Z unstable-options + +#![crate_name = "foo"] + +// @has 'foo/index.html' '//div[@class="block version"]/p' 'Version ' +// @has 'foo/all.html' '//div[@class="block version"]/p' 'Version ' diff --git a/src/test/rustdoc/doc-spotlight.rs b/src/test/rustdoc/doc-spotlight.rs deleted file mode 100644 index ddd46c3c215..00000000000 --- a/src/test/rustdoc/doc-spotlight.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![feature(doc_spotlight)] - -pub struct Wrapper { - inner: T, -} - -impl SomeTrait for Wrapper {} - -#[doc(spotlight)] -pub trait SomeTrait { - // @has doc_spotlight/trait.SomeTrait.html - // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' - fn wrap_me(self) -> Wrapper where Self: Sized { - Wrapper { - inner: self, - } - } -} - -pub struct SomeStruct; -impl SomeTrait for SomeStruct {} - -impl SomeStruct { - // @has doc_spotlight/struct.SomeStruct.html - // @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' - // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' - pub fn new() -> SomeStruct { - SomeStruct - } -} - -// @has doc_spotlight/fn.bare_fn.html -// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' -pub fn bare_fn() -> SomeStruct { - SomeStruct -} diff --git a/src/test/rustdoc/issue-26606.rs b/src/test/rustdoc/issue-26606.rs index 6de419ec571..d2aa4bbbd12 100644 --- a/src/test/rustdoc/issue-26606.rs +++ b/src/test/rustdoc/issue-26606.rs @@ -7,5 +7,5 @@ extern crate issue_26606_macro; // @has issue_26606/constant.FOO.html -// @!has - '//a/@href' '../src/' +// @has - '//a/@href' '../src/issue_26606/auxiliary/issue-26606-macro.rs.html#3' make_item!(FOO); diff --git a/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs b/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs index 8917693d45e..837ed1f002f 100644 --- a/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs +++ b/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs @@ -2,16 +2,14 @@ #![feature(rustc_private)] -// We're testing linkage visibility; the compiler warns us, but we want to -// do the runtime check that these functions aren't exported. -#![allow(private_no_mangle_fns)] - extern crate rustc_metadata; use rustc_metadata::dynamic_lib::DynamicLibrary; #[no_mangle] -pub fn foo() { bar(); } +pub fn foo() { + bar(); +} pub fn foo2() { fn bar2() { @@ -21,11 +19,11 @@ fn bar2() { } #[no_mangle] -fn bar() { } +fn bar() {} #[allow(dead_code)] #[no_mangle] -fn baz() { } +fn baz() {} pub fn test() { let lib = DynamicLibrary::open(None).unwrap(); diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs index 4936bdb1ece..52620b2464b 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs @@ -29,10 +29,10 @@ fn name(&self) -> &'static str { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for $struct { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { $( - if !attr::contains_name(&krate.attrs, $attr) { + if !attr::contains_name(&krate.item.attrs, $attr) { cx.lint(CRATE_NOT_OKAY, |lint| { let msg = format!("crate is not marked with #![{}]", $attr); - lint.build(&msg).set_span(krate.span).emit() + lint.build(&msg).set_span(krate.item.span).emit() }); } )* diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs index 32b7ed9dfe2..6978d02c09d 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs @@ -27,10 +27,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { - if !attr::contains_name(&krate.attrs, Symbol::intern("crate_okay")) { + if !attr::contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) { cx.lint(CRATE_NOT_OKAY, |lint| { lint.build("crate is not marked with #![crate_okay]") - .set_span(krate.span) + .set_span(krate.item.span) .emit() }); } diff --git a/src/test/ui/allocator/custom.rs b/src/test/ui/allocator/custom.rs index 0b1f6d5a96e..c275db14b42 100644 --- a/src/test/ui/allocator/custom.rs +++ b/src/test/ui/allocator/custom.rs @@ -37,7 +37,7 @@ fn main() { unsafe { let layout = Layout::from_size_align(4, 2).unwrap(); - let ptr = Global.alloc(layout.clone()).unwrap(); + let (ptr, _) = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(HITS.load(Ordering::SeqCst), n + 1); Global.dealloc(ptr, layout.clone()); @@ -49,7 +49,7 @@ fn main() { drop(s); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); - let ptr = System.alloc(layout.clone()).unwrap(); + let (ptr, _) = System.alloc(layout.clone()).unwrap(); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); helper::work_with(&ptr); System.dealloc(ptr, layout); diff --git a/src/test/ui/allocator/xcrate-use.rs b/src/test/ui/allocator/xcrate-use.rs index 37b28c195df..e4746d1a7ec 100644 --- a/src/test/ui/allocator/xcrate-use.rs +++ b/src/test/ui/allocator/xcrate-use.rs @@ -20,13 +20,13 @@ fn main() { let n = GLOBAL.0.load(Ordering::SeqCst); let layout = Layout::from_size_align(4, 2).unwrap(); - let ptr = Global.alloc(layout.clone()).unwrap(); + let (ptr, _) = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1); Global.dealloc(ptr, layout.clone()); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); - let ptr = System.alloc(layout.clone()).unwrap(); + let (ptr, _) = System.alloc(layout.clone()).unwrap(); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); helper::work_with(&ptr); System.dealloc(ptr, layout); diff --git a/src/test/ui/anon-params-denied-2018.rs b/src/test/ui/anon-params-denied-2018.rs deleted file mode 100644 index 5721f5d2357..00000000000 --- a/src/test/ui/anon-params-denied-2018.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Tests that anonymous parameters are a hard error in edition 2018. - -// edition:2018 - -trait T { - fn foo(i32); //~ expected one of `:`, `@`, or `|`, found `)` - - fn bar_with_default_impl(String, String) {} - //~^ ERROR expected one of `:` - //~| ERROR expected one of `:` - - // do not complain about missing `b` - fn baz(a:usize, b, c: usize) -> usize { //~ ERROR expected one of `:` - a + b + c - } -} - -fn main() {} diff --git a/src/test/ui/anon-params-denied-2018.stderr b/src/test/ui/anon-params-denied-2018.stderr deleted file mode 100644 index e7a806a8468..00000000000 --- a/src/test/ui/anon-params-denied-2018.stderr +++ /dev/null @@ -1,74 +0,0 @@ -error: expected one of `:`, `@`, or `|`, found `)` - --> $DIR/anon-params-denied-2018.rs:6:15 - | -LL | fn foo(i32); - | ^ expected one of `:`, `@`, or `|` - | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn foo(self: i32); - | ^^^^^^^^^ -help: if this was a parameter name, give it a type - | -LL | fn foo(i32: TypeName); - | ^^^^^^^^^^^^^ -help: if this is a type, explicitly ignore the parameter name - | -LL | fn foo(_: i32); - | ^^^^^^ - -error: expected one of `:`, `@`, or `|`, found `,` - --> $DIR/anon-params-denied-2018.rs:8:36 - | -LL | fn bar_with_default_impl(String, String) {} - | ^ expected one of `:`, `@`, or `|` - | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn bar_with_default_impl(self: String, String) {} - | ^^^^^^^^^^^^ -help: if this was a parameter name, give it a type - | -LL | fn bar_with_default_impl(String: TypeName, String) {} - | ^^^^^^^^^^^^^^^^ -help: if this is a type, explicitly ignore the parameter name - | -LL | fn bar_with_default_impl(_: String, String) {} - | ^^^^^^^^^ - -error: expected one of `:`, `@`, or `|`, found `)` - --> $DIR/anon-params-denied-2018.rs:8:44 - | -LL | fn bar_with_default_impl(String, String) {} - | ^ expected one of `:`, `@`, or `|` - | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this was a parameter name, give it a type - | -LL | fn bar_with_default_impl(String, String: TypeName) {} - | ^^^^^^^^^^^^^^^^ -help: if this is a type, explicitly ignore the parameter name - | -LL | fn bar_with_default_impl(String, _: String) {} - | ^^^^^^^^^ - -error: expected one of `:`, `@`, or `|`, found `,` - --> $DIR/anon-params-denied-2018.rs:13:22 - | -LL | fn baz(a:usize, b, c: usize) -> usize { - | ^ expected one of `:`, `@`, or `|` - | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this was a parameter name, give it a type - | -LL | fn baz(a:usize, b: TypeName, c: usize) -> usize { - | ^^^^^^^^^^^ -help: if this is a type, explicitly ignore the parameter name - | -LL | fn baz(a:usize, _: b, c: usize) -> usize { - | ^^^^ - -error: aborting due to 4 previous errors - diff --git a/src/test/ui/anon-params-deprecated.fixed b/src/test/ui/anon-params-deprecated.fixed deleted file mode 100644 index fe42113eb2e..00000000000 --- a/src/test/ui/anon-params-deprecated.fixed +++ /dev/null @@ -1,19 +0,0 @@ -#![warn(anonymous_parameters)] -// Test for the anonymous_parameters deprecation lint (RFC 1685) - -// build-pass (FIXME(62277): could be check-pass?) -// edition:2015 -// run-rustfix - -trait T { - fn foo(_: i32); //~ WARNING anonymous parameters are deprecated - //~| WARNING hard error - - fn bar_with_default_impl(_: String, _: String) {} - //~^ WARNING anonymous parameters are deprecated - //~| WARNING hard error - //~| WARNING anonymous parameters are deprecated - //~| WARNING hard error -} - -fn main() {} diff --git a/src/test/ui/anon-params-deprecated.rs b/src/test/ui/anon-params-deprecated.rs deleted file mode 100644 index dc0357721ec..00000000000 --- a/src/test/ui/anon-params-deprecated.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![warn(anonymous_parameters)] -// Test for the anonymous_parameters deprecation lint (RFC 1685) - -// build-pass (FIXME(62277): could be check-pass?) -// edition:2015 -// run-rustfix - -trait T { - fn foo(i32); //~ WARNING anonymous parameters are deprecated - //~| WARNING hard error - - fn bar_with_default_impl(String, String) {} - //~^ WARNING anonymous parameters are deprecated - //~| WARNING hard error - //~| WARNING anonymous parameters are deprecated - //~| WARNING hard error -} - -fn main() {} diff --git a/src/test/ui/anon-params-deprecated.stderr b/src/test/ui/anon-params-deprecated.stderr deleted file mode 100644 index 4520559845f..00000000000 --- a/src/test/ui/anon-params-deprecated.stderr +++ /dev/null @@ -1,32 +0,0 @@ -warning: anonymous parameters are deprecated and will be removed in the next edition. - --> $DIR/anon-params-deprecated.rs:9:12 - | -LL | fn foo(i32); - | ^^^ help: try naming the parameter or explicitly ignoring it: `_: i32` - | -note: the lint level is defined here - --> $DIR/anon-params-deprecated.rs:1:9 - | -LL | #![warn(anonymous_parameters)] - | ^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! - = note: for more information, see issue #41686 - -warning: anonymous parameters are deprecated and will be removed in the next edition. - --> $DIR/anon-params-deprecated.rs:12:30 - | -LL | fn bar_with_default_impl(String, String) {} - | ^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: String` - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! - = note: for more information, see issue #41686 - -warning: anonymous parameters are deprecated and will be removed in the next edition. - --> $DIR/anon-params-deprecated.rs:12:38 - | -LL | fn bar_with_default_impl(String, String) {} - | ^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: String` - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! - = note: for more information, see issue #41686 - diff --git a/src/test/ui/anon-params/anon-params-denied-2018.rs b/src/test/ui/anon-params/anon-params-denied-2018.rs new file mode 100644 index 00000000000..5721f5d2357 --- /dev/null +++ b/src/test/ui/anon-params/anon-params-denied-2018.rs @@ -0,0 +1,18 @@ +// Tests that anonymous parameters are a hard error in edition 2018. + +// edition:2018 + +trait T { + fn foo(i32); //~ expected one of `:`, `@`, or `|`, found `)` + + fn bar_with_default_impl(String, String) {} + //~^ ERROR expected one of `:` + //~| ERROR expected one of `:` + + // do not complain about missing `b` + fn baz(a:usize, b, c: usize) -> usize { //~ ERROR expected one of `:` + a + b + c + } +} + +fn main() {} diff --git a/src/test/ui/anon-params/anon-params-denied-2018.stderr b/src/test/ui/anon-params/anon-params-denied-2018.stderr new file mode 100644 index 00000000000..e7a806a8468 --- /dev/null +++ b/src/test/ui/anon-params/anon-params-denied-2018.stderr @@ -0,0 +1,74 @@ +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/anon-params-denied-2018.rs:6:15 + | +LL | fn foo(i32); + | ^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name + | +LL | fn foo(self: i32); + | ^^^^^^^^^ +help: if this was a parameter name, give it a type + | +LL | fn foo(i32: TypeName); + | ^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn foo(_: i32); + | ^^^^^^ + +error: expected one of `:`, `@`, or `|`, found `,` + --> $DIR/anon-params-denied-2018.rs:8:36 + | +LL | fn bar_with_default_impl(String, String) {} + | ^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name + | +LL | fn bar_with_default_impl(self: String, String) {} + | ^^^^^^^^^^^^ +help: if this was a parameter name, give it a type + | +LL | fn bar_with_default_impl(String: TypeName, String) {} + | ^^^^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn bar_with_default_impl(_: String, String) {} + | ^^^^^^^^^ + +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/anon-params-denied-2018.rs:8:44 + | +LL | fn bar_with_default_impl(String, String) {} + | ^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn bar_with_default_impl(String, String: TypeName) {} + | ^^^^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn bar_with_default_impl(String, _: String) {} + | ^^^^^^^^^ + +error: expected one of `:`, `@`, or `|`, found `,` + --> $DIR/anon-params-denied-2018.rs:13:22 + | +LL | fn baz(a:usize, b, c: usize) -> usize { + | ^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn baz(a:usize, b: TypeName, c: usize) -> usize { + | ^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn baz(a:usize, _: b, c: usize) -> usize { + | ^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/anon-params/anon-params-deprecated.fixed b/src/test/ui/anon-params/anon-params-deprecated.fixed new file mode 100644 index 00000000000..fe42113eb2e --- /dev/null +++ b/src/test/ui/anon-params/anon-params-deprecated.fixed @@ -0,0 +1,19 @@ +#![warn(anonymous_parameters)] +// Test for the anonymous_parameters deprecation lint (RFC 1685) + +// build-pass (FIXME(62277): could be check-pass?) +// edition:2015 +// run-rustfix + +trait T { + fn foo(_: i32); //~ WARNING anonymous parameters are deprecated + //~| WARNING hard error + + fn bar_with_default_impl(_: String, _: String) {} + //~^ WARNING anonymous parameters are deprecated + //~| WARNING hard error + //~| WARNING anonymous parameters are deprecated + //~| WARNING hard error +} + +fn main() {} diff --git a/src/test/ui/anon-params/anon-params-deprecated.rs b/src/test/ui/anon-params/anon-params-deprecated.rs new file mode 100644 index 00000000000..dc0357721ec --- /dev/null +++ b/src/test/ui/anon-params/anon-params-deprecated.rs @@ -0,0 +1,19 @@ +#![warn(anonymous_parameters)] +// Test for the anonymous_parameters deprecation lint (RFC 1685) + +// build-pass (FIXME(62277): could be check-pass?) +// edition:2015 +// run-rustfix + +trait T { + fn foo(i32); //~ WARNING anonymous parameters are deprecated + //~| WARNING hard error + + fn bar_with_default_impl(String, String) {} + //~^ WARNING anonymous parameters are deprecated + //~| WARNING hard error + //~| WARNING anonymous parameters are deprecated + //~| WARNING hard error +} + +fn main() {} diff --git a/src/test/ui/anon-params/anon-params-deprecated.stderr b/src/test/ui/anon-params/anon-params-deprecated.stderr new file mode 100644 index 00000000000..4520559845f --- /dev/null +++ b/src/test/ui/anon-params/anon-params-deprecated.stderr @@ -0,0 +1,32 @@ +warning: anonymous parameters are deprecated and will be removed in the next edition. + --> $DIR/anon-params-deprecated.rs:9:12 + | +LL | fn foo(i32); + | ^^^ help: try naming the parameter or explicitly ignoring it: `_: i32` + | +note: the lint level is defined here + --> $DIR/anon-params-deprecated.rs:1:9 + | +LL | #![warn(anonymous_parameters)] + | ^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #41686 + +warning: anonymous parameters are deprecated and will be removed in the next edition. + --> $DIR/anon-params-deprecated.rs:12:30 + | +LL | fn bar_with_default_impl(String, String) {} + | ^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: String` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #41686 + +warning: anonymous parameters are deprecated and will be removed in the next edition. + --> $DIR/anon-params-deprecated.rs:12:38 + | +LL | fn bar_with_default_impl(String, String) {} + | ^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: String` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #41686 + diff --git a/src/test/ui/anon-params/anon-params-edition-hygiene.rs b/src/test/ui/anon-params/anon-params-edition-hygiene.rs new file mode 100644 index 00000000000..14e11c5696f --- /dev/null +++ b/src/test/ui/anon-params/anon-params-edition-hygiene.rs @@ -0,0 +1,10 @@ +// check-pass +// edition:2018 +// aux-build:anon-params-edition-hygiene.rs + +#[macro_use] +extern crate anon_params_edition_hygiene; + +generate_trait_2015!(u8); + +fn main() {} diff --git a/src/test/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs b/src/test/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs new file mode 100644 index 00000000000..aa4221becc2 --- /dev/null +++ b/src/test/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs @@ -0,0 +1,12 @@ +// edition:2015 + +#[macro_export] +macro_rules! generate_trait_2015 { + ($Type: ident) => { + trait Trait { + fn method($Type) {} + } + }; +} + +fn main() {} diff --git a/src/test/ui/asm/issue-62046.rs b/src/test/ui/asm/issue-62046.rs new file mode 100644 index 00000000000..105dadd5fd3 --- /dev/null +++ b/src/test/ui/asm/issue-62046.rs @@ -0,0 +1,11 @@ +// build-fail +// ignore-emscripten no asm! support + +#![feature(asm)] + +fn main() { + unsafe { + asm!("nop" : "+r"("r15")); + //~^ malformed inline assembly + } +} diff --git a/src/test/ui/asm/issue-62046.stderr b/src/test/ui/asm/issue-62046.stderr new file mode 100644 index 00000000000..a38a300548d --- /dev/null +++ b/src/test/ui/asm/issue-62046.stderr @@ -0,0 +1,11 @@ +error[E0668]: malformed inline assembly + --> $DIR/issue-62046.rs:8:9 + | +LL | asm!("nop" : "+r"("r15")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0668`. diff --git a/src/test/ui/asm/issue-69092.rs b/src/test/ui/asm/issue-69092.rs new file mode 100644 index 00000000000..caa5c2e0b9f --- /dev/null +++ b/src/test/ui/asm/issue-69092.rs @@ -0,0 +1,10 @@ +// build-fail +// ignore-emscripten no asm! support +// Regression test for #69092 + +#![feature(asm)] + +fn main() { + unsafe { asm!(".ascii \"Xen\0\""); } + //~^ ERROR: :1:9: error: expected string in '.ascii' directive +} diff --git a/src/test/ui/asm/issue-69092.stderr b/src/test/ui/asm/issue-69092.stderr new file mode 100644 index 00000000000..5661097cb8b --- /dev/null +++ b/src/test/ui/asm/issue-69092.stderr @@ -0,0 +1,11 @@ +error: :1:9: error: expected string in '.ascii' directive + .ascii "Xen + ^ + + --> $DIR/issue-69092.rs:8:14 + | +LL | unsafe { asm!(".ascii \"Xen\0\""); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/assign-to-method.rs b/src/test/ui/assign-to-method.rs deleted file mode 100644 index 95f066c382c..00000000000 --- a/src/test/ui/assign-to-method.rs +++ /dev/null @@ -1,22 +0,0 @@ -struct Cat { - meows : usize, - - how_hungry : isize, -} - -impl Cat { - pub fn speak(&self) { self.meows += 1; } -} - -fn cat(in_x : usize, in_y : isize) -> Cat { - Cat { - meows: in_x, - how_hungry: in_y - } -} - -fn main() { - let nyan : Cat = cat(52, 99); - nyan.speak = || println!("meow"); //~ ERROR attempted to take value of method - nyan.speak += || println!("meow"); //~ ERROR attempted to take value of method -} diff --git a/src/test/ui/assign-to-method.stderr b/src/test/ui/assign-to-method.stderr deleted file mode 100644 index feceadb6722..00000000000 --- a/src/test/ui/assign-to-method.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0615]: attempted to take value of method `speak` on type `Cat` - --> $DIR/assign-to-method.rs:20:8 - | -LL | nyan.speak = || println!("meow"); - | ^^^^^ - | - = help: methods are immutable and cannot be assigned to - -error[E0615]: attempted to take value of method `speak` on type `Cat` - --> $DIR/assign-to-method.rs:21:8 - | -LL | nyan.speak += || println!("meow"); - | ^^^^^ - | - = help: methods are immutable and cannot be assigned to - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0615`. diff --git a/src/test/ui/associated-item/associated-item-enum.stderr b/src/test/ui/associated-item/associated-item-enum.stderr index 6f89530eac9..cadf55454a7 100644 --- a/src/test/ui/associated-item/associated-item-enum.stderr +++ b/src/test/ui/associated-item/associated-item-enum.stderr @@ -8,7 +8,7 @@ LL | Enum::mispellable(); | ^^^^^^^^^^^ | | | variant or associated item not found in `Enum` - | help: there is a method with a similar name: `misspellable` + | help: there is an associated function with a similar name: `misspellable` error[E0599]: no variant or associated item named `mispellable_trait` found for enum `Enum` in the current scope --> $DIR/associated-item-enum.rs:18:11 diff --git a/src/test/ui/associated-type-bounds/duplicate.stderr b/src/test/ui/associated-type-bounds/duplicate.stderr index df1151d876c..82b2d32d09d 100644 --- a/src/test/ui/associated-type-bounds/duplicate.stderr +++ b/src/test/ui/associated-type-bounds/duplicate.stderr @@ -728,3 +728,4 @@ LL | type TADyn3 = dyn Iterator; error: aborting due to 96 previous errors +For more information about this error, try `rustc --explain E0719`. diff --git a/src/test/ui/async-await/async-fn-size-moved-locals.rs b/src/test/ui/async-await/async-fn-size-moved-locals.rs index 4a413381aa3..636fafc2bc4 100644 --- a/src/test/ui/async-await/async-fn-size-moved-locals.rs +++ b/src/test/ui/async-await/async-fn-size-moved-locals.rs @@ -110,9 +110,9 @@ async fn mixed_sizes() { } fn main() { - assert_eq!(1028, std::mem::size_of_val(&single())); - assert_eq!(1032, std::mem::size_of_val(&single_with_noop())); - assert_eq!(3084, std::mem::size_of_val(&joined())); - assert_eq!(3084, std::mem::size_of_val(&joined_with_noop())); - assert_eq!(7188, std::mem::size_of_val(&mixed_sizes())); + assert_eq!(1025, std::mem::size_of_val(&single())); + assert_eq!(1026, std::mem::size_of_val(&single_with_noop())); + assert_eq!(3078, std::mem::size_of_val(&joined())); + assert_eq!(3079, std::mem::size_of_val(&joined_with_noop())); + assert_eq!(7181, std::mem::size_of_val(&mixed_sizes())); } diff --git a/src/test/ui/async-await/async-fn-size-uninit-locals.rs b/src/test/ui/async-await/async-fn-size-uninit-locals.rs index 0558084f4f8..d5d7b3fc3f0 100644 --- a/src/test/ui/async-await/async-fn-size-uninit-locals.rs +++ b/src/test/ui/async-await/async-fn-size-uninit-locals.rs @@ -95,9 +95,9 @@ async fn join_retval() -> Joiner { } fn main() { - assert_eq!(8, std::mem::size_of_val(&single())); - assert_eq!(12, std::mem::size_of_val(&single_with_noop())); - assert_eq!(3084, std::mem::size_of_val(&joined())); - assert_eq!(3084, std::mem::size_of_val(&joined_with_noop())); - assert_eq!(3080, std::mem::size_of_val(&join_retval())); + assert_eq!(2, std::mem::size_of_val(&single())); + assert_eq!(3, std::mem::size_of_val(&single_with_noop())); + assert_eq!(3078, std::mem::size_of_val(&joined())); + assert_eq!(3078, std::mem::size_of_val(&joined_with_noop())); + assert_eq!(3074, std::mem::size_of_val(&join_retval())); } diff --git a/src/test/ui/async-await/async-fn-size.rs b/src/test/ui/async-await/async-fn-size.rs index b313992db4e..0c1f3636446 100644 --- a/src/test/ui/async-await/async-fn-size.rs +++ b/src/test/ui/async-await/async-fn-size.rs @@ -86,13 +86,13 @@ async fn await3_level5() -> u8 { fn main() { assert_eq!(2, std::mem::size_of_val(&base())); - assert_eq!(8, std::mem::size_of_val(&await1_level1())); - assert_eq!(12, std::mem::size_of_val(&await2_level1())); - assert_eq!(12, std::mem::size_of_val(&await3_level1())); - assert_eq!(24, std::mem::size_of_val(&await3_level2())); - assert_eq!(36, std::mem::size_of_val(&await3_level3())); - assert_eq!(48, std::mem::size_of_val(&await3_level4())); - assert_eq!(60, std::mem::size_of_val(&await3_level5())); + assert_eq!(3, std::mem::size_of_val(&await1_level1())); + assert_eq!(4, std::mem::size_of_val(&await2_level1())); + assert_eq!(5, std::mem::size_of_val(&await3_level1())); + assert_eq!(8, std::mem::size_of_val(&await3_level2())); + assert_eq!(11, std::mem::size_of_val(&await3_level3())); + assert_eq!(14, std::mem::size_of_val(&await3_level4())); + assert_eq!(17, std::mem::size_of_val(&await3_level5())); assert_eq!(1, wait(base())); assert_eq!(1, wait(await1_level1())); diff --git a/src/test/ui/async-await/dont-print-desugared-async.stderr b/src/test/ui/async-await/dont-print-desugared-async.stderr index 2bf1e77f09b..d80467c7fa8 100644 --- a/src/test/ui/async-await/dont-print-desugared-async.stderr +++ b/src/test/ui/async-await/dont-print-desugared-async.stderr @@ -2,10 +2,7 @@ error[E0596]: cannot borrow data in a `&` reference as mutable --> $DIR/dont-print-desugared-async.rs:5:20 | LL | async fn async_fn(&ref mut s: &[i32]) {} - | -^^^^^^^^^ - | || - | |cannot borrow as mutable through `&` reference - | help: consider changing this to be a mutable reference: `&mut ref mut s` + | ^^^^^^^^^ cannot borrow as mutable through `&` reference error: aborting due to previous error diff --git a/src/test/ui/async-await/expansion-in-attrs.rs b/src/test/ui/async-await/expansion-in-attrs.rs new file mode 100644 index 00000000000..af77c3463b5 --- /dev/null +++ b/src/test/ui/async-await/expansion-in-attrs.rs @@ -0,0 +1,13 @@ +// check-pass +// edition:2018 + +macro_rules! with_doc { + ($doc: expr) => { + #[doc = $doc] + async fn f() {} + }; +} + +with_doc!(concat!("")); + +fn main() {} diff --git a/src/test/ui/async-await/issue-54239-private-type-triggers-lint.rs b/src/test/ui/async-await/issue-54239-private-type-triggers-lint.rs new file mode 100644 index 00000000000..16cf7ad52e4 --- /dev/null +++ b/src/test/ui/async-await/issue-54239-private-type-triggers-lint.rs @@ -0,0 +1,17 @@ +// Regression test for #54239, shouldn't trigger lint. +// check-pass +// edition:2018 + +#![deny(missing_debug_implementations)] + +struct DontLookAtMe(i32); + +async fn secret() -> DontLookAtMe { + DontLookAtMe(41) +} + +pub async fn looking() -> i32 { // Shouldn't trigger lint here. + secret().await.0 +} + +fn main() {} diff --git a/src/test/ui/async-await/issues/issue-62009-1.stderr b/src/test/ui/async-await/issues/issue-62009-1.stderr index cd6670923c2..0624c049048 100644 --- a/src/test/ui/async-await/issues/issue-62009-1.stderr +++ b/src/test/ui/async-await/issues/issue-62009-1.stderr @@ -33,10 +33,10 @@ error[E0277]: the trait bound `[closure@$DIR/issue-62009-1.rs:16:5: 16:15]: std: LL | (|_| 2333).await; | ^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `[closure@$DIR/issue-62009-1.rs:16:5: 16:15]` | - ::: $SRC_DIR/libstd/future.rs:LL:COL + ::: $SRC_DIR/libcore/future/mod.rs:LL:COL | LL | F: Future, - | ------ required by this bound in `std::future::poll_with_tls_context` + | ------ required by this bound in `std::future::poll_with_context` error: aborting due to 4 previous errors diff --git a/src/test/ui/async-await/issues/issue-62097.nll.stderr b/src/test/ui/async-await/issues/issue-62097.nll.stderr index f72c645bf8d..2a399540e52 100644 --- a/src/test/ui/async-await/issues/issue-62097.nll.stderr +++ b/src/test/ui/async-await/issues/issue-62097.nll.stderr @@ -16,13 +16,13 @@ help: to force the closure to take ownership of `self` (and any other referenced LL | foo(move || self.bar()).await; | ^^^^^^^ -error[E0521]: borrowed data escapes outside of method +error[E0521]: borrowed data escapes outside of associated function --> $DIR/issue-62097.rs:13:9 | LL | pub async fn run_dummy_fn(&self) { - | ----- `self` is a reference that is only valid in the method body + | ----- `self` is a reference that is only valid in the associated function body LL | foo(|| self.bar()).await; - | ^^^^^^^^^^^^^^^^^^ `self` escapes the method body here + | ^^^^^^^^^^^^^^^^^^ `self` escapes the associated function body here error: aborting due to 2 previous errors diff --git a/src/test/ui/async-await/issues/issue-63388-1.nll.stderr b/src/test/ui/async-await/issues/issue-63388-1.nll.stderr index 696f79ec40f..464459d2d61 100644 --- a/src/test/ui/async-await/issues/issue-63388-1.nll.stderr +++ b/src/test/ui/async-await/issues/issue-63388-1.nll.stderr @@ -9,7 +9,7 @@ LL | ) -> &dyn Foo LL | / { LL | | foo LL | | } - | |_____^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + | |_____^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` error: aborting due to previous error diff --git a/src/test/ui/async-await/no-const-async.stderr b/src/test/ui/async-await/no-const-async.stderr index 07559cd240b..4e59bb50767 100644 --- a/src/test/ui/async-await/no-const-async.stderr +++ b/src/test/ui/async-await/no-const-async.stderr @@ -1,8 +1,8 @@ error: functions cannot be both `const` and `async` - --> $DIR/no-const-async.rs:4:1 + --> $DIR/no-const-async.rs:4:5 | LL | pub const async fn x() {} - | ^^^^-----^-----^^^^^^^^^^ + | ----^^^^^-^^^^^---------- | | | | | `async` because of this | `const` because of this diff --git a/src/test/ui/async-await/no-std.rs b/src/test/ui/async-await/no-std.rs new file mode 100644 index 00000000000..63e93cdff7e --- /dev/null +++ b/src/test/ui/async-await/no-std.rs @@ -0,0 +1,13 @@ +// edition:2018 +// check-pass + +#![no_std] +#![crate_type = "rlib"] + +use core::future::Future; + +async fn a(f: impl Future) { + f.await; +} + +fn main() {} diff --git a/src/test/ui/auto-ref-slice-plus-ref.stderr b/src/test/ui/auto-ref-slice-plus-ref.stderr index 50ca5cad4bc..dc7deb8a7c7 100644 --- a/src/test/ui/auto-ref-slice-plus-ref.stderr +++ b/src/test/ui/auto-ref-slice-plus-ref.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `test_mut` found for struct `std::vec::Vec<{intege --> $DIR/auto-ref-slice-plus-ref.rs:7:7 | LL | a.test_mut(); - | ^^^^^^^^ help: there is a method with a similar name: `get_mut` + | ^^^^^^^^ help: there is an associated function with a similar name: `get_mut` | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test_mut`, perhaps you need to implement it diff --git a/src/test/ui/auto-trait-validation.stderr b/src/test/ui/auto-trait-validation.stderr index 51422fab81f..4040e66c6af 100644 --- a/src/test/ui/auto-trait-validation.stderr +++ b/src/test/ui/auto-trait-validation.stderr @@ -1,20 +1,26 @@ error[E0567]: auto traits cannot have generic parameters - --> $DIR/auto-trait-validation.rs:3:1 + --> $DIR/auto-trait-validation.rs:3:19 | LL | auto trait Generic {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | -------^^^ help: remove the parameters + | | + | auto trait cannot have generic parameters error[E0568]: auto traits cannot have super traits - --> $DIR/auto-trait-validation.rs:5:1 + --> $DIR/auto-trait-validation.rs:5:20 | LL | auto trait Bound : Copy {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ----- ^^^^ help: remove the super traits + | | + | auto trait cannot have super traits error[E0380]: auto traits cannot have methods or associated items - --> $DIR/auto-trait-validation.rs:7:1 + --> $DIR/auto-trait-validation.rs:7:25 | LL | auto trait MyTrait { fn foo() {} } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ------- ^^^ + | | + | auto trait cannot have items error: aborting due to 3 previous errors diff --git a/src/test/ui/binding/ambiguity-item.rs b/src/test/ui/binding/ambiguity-item.rs new file mode 100644 index 00000000000..10613cc6164 --- /dev/null +++ b/src/test/ui/binding/ambiguity-item.rs @@ -0,0 +1,18 @@ +// Identifier pattern referring to an ambiguity item is an error (issue #46079). + +mod m { + pub fn f() {} +} +use m::*; + +mod n { + pub fn f() {} +} +use n::*; // OK, no conflict with `use m::*;` + +fn main() { + let v = f; //~ ERROR `f` is ambiguous + match v { + f => {} //~ ERROR `f` is ambiguous + } +} diff --git a/src/test/ui/binding/ambiguity-item.stderr b/src/test/ui/binding/ambiguity-item.stderr new file mode 100644 index 00000000000..615193c0d02 --- /dev/null +++ b/src/test/ui/binding/ambiguity-item.stderr @@ -0,0 +1,41 @@ +error[E0659]: `f` is ambiguous (glob import vs glob import in the same module) + --> $DIR/ambiguity-item.rs:14:13 + | +LL | let v = f; + | ^ ambiguous name + | +note: `f` could refer to the function imported here + --> $DIR/ambiguity-item.rs:6:5 + | +LL | use m::*; + | ^^^^ + = help: consider adding an explicit import of `f` to disambiguate +note: `f` could also refer to the function imported here + --> $DIR/ambiguity-item.rs:11:5 + | +LL | use n::*; // OK, no conflict with `use m::*;` + | ^^^^ + = help: consider adding an explicit import of `f` to disambiguate + +error[E0659]: `f` is ambiguous (glob import vs glob import in the same module) + --> $DIR/ambiguity-item.rs:16:9 + | +LL | f => {} + | ^ ambiguous name + | +note: `f` could refer to the function imported here + --> $DIR/ambiguity-item.rs:6:5 + | +LL | use m::*; + | ^^^^ + = help: consider adding an explicit import of `f` to disambiguate +note: `f` could also refer to the function imported here + --> $DIR/ambiguity-item.rs:11:5 + | +LL | use n::*; // OK, no conflict with `use m::*;` + | ^^^^ + = help: consider adding an explicit import of `f` to disambiguate + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0659`. diff --git a/src/test/ui/binding/const-param.rs b/src/test/ui/binding/const-param.rs new file mode 100644 index 00000000000..3c7f4d071f6 --- /dev/null +++ b/src/test/ui/binding/const-param.rs @@ -0,0 +1,12 @@ +// Identifier pattern referring to a const generic parameter is an error (issue #68853). + +#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete + +fn check() { + match 1 { + N => {} //~ ERROR const parameters cannot be referenced in patterns + _ => {} + } +} + +fn main() {} diff --git a/src/test/ui/binding/const-param.stderr b/src/test/ui/binding/const-param.stderr new file mode 100644 index 00000000000..25b1c75c9a0 --- /dev/null +++ b/src/test/ui/binding/const-param.stderr @@ -0,0 +1,17 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/const-param.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error[E0158]: const parameters cannot be referenced in patterns + --> $DIR/const-param.rs:7:9 + | +LL | N => {} + | ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0158`. diff --git a/src/test/ui/blind/blind-item-block-middle.stderr b/src/test/ui/blind/blind-item-block-middle.stderr index 264e7fc8e73..d8d15615d7c 100644 --- a/src/test/ui/blind/blind-item-block-middle.stderr +++ b/src/test/ui/blind/blind-item-block-middle.stderr @@ -1,8 +1,15 @@ error[E0308]: mismatched types --> $DIR/blind-item-block-middle.rs:6:9 | +LL | mod foo { pub struct bar; } + | --------------- unit struct defined here +... LL | let bar = 5; - | ^^^ expected integer, found struct `foo::bar` + | ^^^ + | | + | expected integer, found struct `foo::bar` + | `bar` is interpreted as a unit struct, not a new binding + | help: introduce a new binding instead: `other_bar` error: aborting due to previous error diff --git a/src/test/ui/block-expr-precedence.stderr b/src/test/ui/block-expr-precedence.stderr index 1307b5d6ddd..decee1f2f16 100644 --- a/src/test/ui/block-expr-precedence.stderr +++ b/src/test/ui/block-expr-precedence.stderr @@ -4,5 +4,5 @@ warning: unnecessary trailing semicolons LL | if (true) { 12; };;; -num; | ^^ help: remove these semicolons | - = note: `#[warn(redundant_semicolon)]` on by default + = note: `#[warn(redundant_semicolons)]` on by default diff --git a/src/test/ui/block-result/issue-3563.stderr b/src/test/ui/block-result/issue-3563.stderr index be551f6e889..5255e48bee1 100644 --- a/src/test/ui/block-result/issue-3563.stderr +++ b/src/test/ui/block-result/issue-3563.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `b` found for reference `&Self` in the current sco --> $DIR/issue-3563.rs:3:17 | LL | || self.b() - | ^ help: there is a method with a similar name: `a` + | ^ help: there is an associated function with a similar name: `a` error: aborting due to previous error diff --git a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs new file mode 100644 index 00000000000..b67d494866b --- /dev/null +++ b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs @@ -0,0 +1,224 @@ +// Tests using a combination of pattern features has the expected borrow checking behavior +#![feature(bindings_after_at)] +#![feature(or_patterns)] +#![feature(box_patterns)] + +#![feature(move_ref_pattern)] + +enum Test { + Foo, + Bar, + _Baz, +} + +// bindings_after_at + slice_patterns + +fn bindings_after_at_slice_patterns_move_binding(x: [String; 4]) { + match x { + a @ [.., _] => (), + _ => (), + }; + + &x; + //~^ ERROR borrow of moved value +} + +fn bindings_after_at_slice_patterns_borrows_binding_mut(mut x: [String; 4]) { + let r = match x { + ref mut foo @ [.., _] => Some(foo), + _ => None, + }; + + &x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn bindings_after_at_slice_patterns_borrows_slice_mut1(mut x: [String; 4]) { + let r = match x { + ref foo @ [.., ref mut bar] => (), + //~^ ERROR cannot borrow + _ => (), + }; + + drop(r); +} + +fn bindings_after_at_slice_patterns_borrows_slice_mut2(mut x: [String; 4]) { + let r = match x { + [ref foo @ .., ref bar] => Some(foo), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn bindings_after_at_slice_patterns_borrows_both(mut x: [String; 4]) { + let r = match x { + ref foo @ [.., ref bar] => Some(foo), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +// bindings_after_at + or_patterns + +fn bindings_after_at_or_patterns_move(x: Option) { + match x { + foo @ Some(Test::Foo | Test::Bar) => (), + _ => (), + } + + &x; + //~^ ERROR borrow of moved value +} + +fn bindings_after_at_or_patterns_borrows(mut x: Option) { + let r = match x { + ref foo @ Some(Test::Foo | Test::Bar) => Some(foo), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn bindings_after_at_or_patterns_borrows_mut(mut x: Option) { + let r = match x { + ref mut foo @ Some(Test::Foo | Test::Bar) => Some(foo), + _ => None, + }; + + &x; + //~^ ERROR cannot borrow + + drop(r); +} + +// bindings_after_at + box_patterns + +fn bindings_after_at_box_patterns_borrows_both(mut x: Option>) { + let r = match x { + ref foo @ Some(box ref s) => Some(foo), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn bindings_after_at_box_patterns_borrows_mut(mut x: Option>) { + match x { + ref foo @ Some(box ref mut s) => (), + //~^ ERROR cannot borrow + _ => (), + }; +} + +// bindings_after_at + slice_patterns + or_patterns + +fn bindings_after_at_slice_patterns_or_patterns_moves(x: [Option; 4]) { + match x { + a @ [.., Some(Test::Foo | Test::Bar)] => (), + _ => (), + }; + + &x; + //~^ ERROR borrow of moved value +} + +fn bindings_after_at_slice_patterns_or_patterns_borrows_binding(mut x: [Option; 4]) { + let r = match x { + ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(a), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn bindings_after_at_slice_patterns_or_patterns_borrows_slice(mut x: [Option; 4]) { + let r = match x { + ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(b), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +// bindings_after_at + slice_patterns + box_patterns + +fn bindings_after_at_slice_patterns_box_patterns_borrows(mut x: [Option>; 4]) { + let r = match x { + [_, ref a @ Some(box ref b), ..] => Some(a), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +// bindings_after_at + slice_patterns + or_patterns + box_patterns + +fn bindings_after_at_slice_patterns_or_patterns_box_patterns_borrows( + mut x: [Option>; 4] +) { + let r = match x { + [_, ref a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn bindings_after_at_slice_patterns_or_patterns_box_patterns_borrows_mut( + mut x: [Option>; 4] +) { + let r = match x { + [_, ref mut a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), + _ => None, + }; + + &x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn bindings_after_at_slice_patterns_or_patterns_box_patterns_borrows_binding( + mut x: [Option>; 4] +) { + let r = match x { + ref a @ [_, ref b @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), + _ => None, + }; + + &mut x; + //~^ ERROR cannot borrow + + drop(r); +} + +fn main() {} diff --git a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr new file mode 100644 index 00000000000..35ed2763c2b --- /dev/null +++ b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr @@ -0,0 +1,208 @@ +error: cannot borrow value as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:40:9 + | +LL | ref foo @ [.., ref mut bar] => (), + | -------^^^^^^^^-----------^ + | | | + | | mutable borrow, by `bar`, occurs here + | immutable borrow, by `foo`, occurs here + +error: cannot borrow value as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:124:9 + | +LL | ref foo @ Some(box ref mut s) => (), + | -------^^^^^^^^^^^^---------^ + | | | + | | mutable borrow, by `s`, occurs here + | immutable borrow, by `foo`, occurs here + +error[E0382]: borrow of moved value: `x` + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:22:5 + | +LL | fn bindings_after_at_slice_patterns_move_binding(x: [String; 4]) { + | - move occurs because `x` has type `[std::string::String; 4]`, which does not implement the `Copy` trait +LL | match x { +LL | a @ [.., _] => (), + | ----------- value moved here +... +LL | &x; + | ^^ value borrowed here after move + +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:32:5 + | +LL | ref mut foo @ [.., _] => Some(foo), + | --------------------- mutable borrow occurs here +... +LL | &x; + | ^^ immutable borrow occurs here +... +LL | drop(r); + | - mutable borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:54:5 + | +LL | [ref foo @ .., ref bar] => Some(foo), + | ------------ immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:66:5 + | +LL | ref foo @ [.., ref bar] => Some(foo), + | ----------------------- immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0382]: borrow of moved value: `x` + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:80:5 + | +LL | fn bindings_after_at_or_patterns_move(x: Option) { + | - move occurs because `x` has type `std::option::Option`, which does not implement the `Copy` trait +LL | match x { +LL | foo @ Some(Test::Foo | Test::Bar) => (), + | --------------------------------- + | | + | value moved here + | value moved here +... +LL | &x; + | ^^ value borrowed here after move + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:90:5 + | +LL | ref foo @ Some(Test::Foo | Test::Bar) => Some(foo), + | ------------------------------------- immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:102:5 + | +LL | ref mut foo @ Some(Test::Foo | Test::Bar) => Some(foo), + | ----------------------------------------- mutable borrow occurs here +... +LL | &x; + | ^^ immutable borrow occurs here +... +LL | drop(r); + | - mutable borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:116:5 + | +LL | ref foo @ Some(box ref s) => Some(foo), + | ------------------------- immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0382]: borrow of moved value: `x` + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:138:5 + | +LL | fn bindings_after_at_slice_patterns_or_patterns_moves(x: [Option; 4]) { + | - move occurs because `x` has type `[std::option::Option; 4]`, which does not implement the `Copy` trait +LL | match x { +LL | a @ [.., Some(Test::Foo | Test::Bar)] => (), + | ------------------------------------- + | | + | value moved here + | value moved here +... +LL | &x; + | ^^ value borrowed here after move + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:148:5 + | +LL | ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(a), + | ------------------------------------------------- immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:160:5 + | +LL | ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(b), + | ---------- immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:174:5 + | +LL | [_, ref a @ Some(box ref b), ..] => Some(a), + | ----------------------- immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:190:5 + | +LL | [_, ref a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), + | ------------------------------------------- immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:204:5 + | +LL | [_, ref mut a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), + | ----------------------------------------------- mutable borrow occurs here +... +LL | &x; + | ^^ immutable borrow occurs here +... +LL | drop(r); + | - mutable borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:218:5 + | +LL | ref a @ [_, ref b @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), + | ------------------------------------------------------------ immutable borrow occurs here +... +LL | &mut x; + | ^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error: aborting due to 17 previous errors + +Some errors have detailed explanations: E0382, E0502. +For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/chalkify/builtin-copy-clone.rs b/src/test/ui/chalkify/builtin-copy-clone.rs deleted file mode 100644 index d403514b553..00000000000 --- a/src/test/ui/chalkify/builtin-copy-clone.rs +++ /dev/null @@ -1,44 +0,0 @@ -// run-pass -// compile-flags: -Z chalk - -// Test that `Clone` is correctly implemented for builtin types. - -#[derive(Copy, Clone)] -struct S(i32); - -fn test_clone(arg: T) { - let _ = arg.clone(); -} - -fn test_copy(arg: T) { - let _ = arg; - let _ = arg; -} - -fn test_copy_clone(arg: T) { - test_copy(arg); - test_clone(arg); -} - -fn foo() { } - -fn main() { - test_copy_clone(foo); - let f: fn() = foo; - test_copy_clone(f); - // FIXME: add closures when they're considered WF - test_copy_clone([1; 56]); - test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)); - test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, true, 'a', 1.1)); - test_copy_clone(()); - test_copy_clone(((1, 1), (1, 1, 1), (1.1, 1, 1, 'a'), ())); - - let a = ( - (S(1), S(0)), - ( - (S(0), S(0), S(1)), - S(0) - ) - ); - test_copy_clone(a); -} diff --git a/src/test/ui/chalkify/inherent_impl.rs b/src/test/ui/chalkify/inherent_impl.rs deleted file mode 100644 index 44e120c1eeb..00000000000 --- a/src/test/ui/chalkify/inherent_impl.rs +++ /dev/null @@ -1,42 +0,0 @@ -// run-pass -// compile-flags: -Z chalk - -trait Foo { } - -impl Foo for i32 { } - -struct S { - x: T, -} - -fn only_foo(_x: &T) { } - -impl S { - // Test that we have the correct environment inside an inherent method. - fn dummy_foo(&self) { - only_foo(&self.x) - } -} - -trait Bar { } -impl Bar for u32 { } - -fn only_bar() { } - -impl S { - // Test that the environment of `dummy_bar` adds up with the environment - // of the inherent impl. - fn dummy_bar(&self) { - only_foo(&self.x); - only_bar::(); - } -} - -fn main() { - let s = S { - x: 5, - }; - - s.dummy_foo(); - s.dummy_bar::(); -} diff --git a/src/test/ui/chalkify/lower_env1.rs b/src/test/ui/chalkify/lower_env1.rs deleted file mode 100644 index afb6bddbf26..00000000000 --- a/src/test/ui/chalkify/lower_env1.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![feature(rustc_attrs)] -#![allow(dead_code)] - -trait Foo { } - -#[rustc_dump_program_clauses] //~ ERROR program clause dump -trait Bar where Self: Foo { } - -#[rustc_dump_env_program_clauses] //~ ERROR program clause dump -fn bar() { -} - -fn main() { -} diff --git a/src/test/ui/chalkify/lower_env1.stderr b/src/test/ui/chalkify/lower_env1.stderr deleted file mode 100644 index bc426e0707b..00000000000 --- a/src/test/ui/chalkify/lower_env1.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: program clause dump - --> $DIR/lower_env1.rs:6:1 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { FromEnv(Self: Foo) :- FromEnv(Self: Bar). } - = note: forall { Implemented(Self: Bar) :- FromEnv(Self: Bar). } - = note: forall { WellFormed(Self: Bar) :- Implemented(Self: Bar), WellFormed(Self: Foo). } - -error: program clause dump - --> $DIR/lower_env1.rs:9:1 - | -LL | #[rustc_dump_env_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { FromEnv(Self: Foo) :- FromEnv(Self: Bar). } - = note: forall { Implemented(Self: Bar) :- FromEnv(Self: Bar). } - = note: forall { Implemented(Self: Foo) :- FromEnv(Self: Foo). } - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/chalkify/lower_env2.rs b/src/test/ui/chalkify/lower_env2.rs deleted file mode 100644 index a067575a9cf..00000000000 --- a/src/test/ui/chalkify/lower_env2.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![feature(rustc_attrs)] -#![allow(dead_code)] - -trait Foo { } - -#[rustc_dump_program_clauses] //~ ERROR program clause dump -struct S<'a, T: ?Sized> where T: Foo { - data: &'a T, -} - -#[rustc_dump_env_program_clauses] //~ ERROR program clause dump -fn bar(_x: S<'_, T>) { // note that we have an implicit `T: Sized` bound -} - -fn main() { -} diff --git a/src/test/ui/chalkify/lower_env2.stderr b/src/test/ui/chalkify/lower_env2.stderr deleted file mode 100644 index 613a568a854..00000000000 --- a/src/test/ui/chalkify/lower_env2.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: program clause dump - --> $DIR/lower_env2.rs:6:1 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall<'a, T> { FromEnv(T: Foo) :- FromEnv(S<'a, T>). } - = note: forall<'a, T> { TypeOutlives(T: 'a) :- FromEnv(S<'a, T>). } - = note: forall<'a, T> { WellFormed(S<'a, T>) :- WellFormed(T: Foo), TypeOutlives(T: 'a). } - -error: program clause dump - --> $DIR/lower_env2.rs:11:1 - | -LL | #[rustc_dump_env_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall<'a, T> { FromEnv(T: Foo) :- FromEnv(S<'a, T>). } - = note: forall<'a, T> { TypeOutlives(T: 'a) :- FromEnv(S<'a, T>). } - = note: forall { Implemented(Self: Foo) :- FromEnv(Self: Foo). } - = note: forall { Implemented(Self: std::marker::Sized) :- FromEnv(Self: std::marker::Sized). } - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/chalkify/lower_env3.rs b/src/test/ui/chalkify/lower_env3.rs deleted file mode 100644 index 61ed3cbb277..00000000000 --- a/src/test/ui/chalkify/lower_env3.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![feature(rustc_attrs)] -#![allow(dead_code)] - -trait Foo { - #[rustc_dump_env_program_clauses] //~ ERROR program clause dump - fn foo(&self); -} - -impl Foo for T where T: Clone { - #[rustc_dump_env_program_clauses] //~ ERROR program clause dump - fn foo(&self) { - } -} - -fn main() { -} diff --git a/src/test/ui/chalkify/lower_env3.stderr b/src/test/ui/chalkify/lower_env3.stderr deleted file mode 100644 index a1fc83bfea8..00000000000 --- a/src/test/ui/chalkify/lower_env3.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: program clause dump - --> $DIR/lower_env3.rs:5:5 - | -LL | #[rustc_dump_env_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { Implemented(Self: Foo) :- FromEnv(Self: Foo). } - -error: program clause dump - --> $DIR/lower_env3.rs:10:5 - | -LL | #[rustc_dump_env_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { FromEnv(Self: std::marker::Sized) :- FromEnv(Self: std::clone::Clone). } - = note: forall { Implemented(Self: std::clone::Clone) :- FromEnv(Self: std::clone::Clone). } - = note: forall { Implemented(Self: std::marker::Sized) :- FromEnv(Self: std::marker::Sized). } - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/chalkify/lower_impl.rs b/src/test/ui/chalkify/lower_impl.rs deleted file mode 100644 index 1bd44a9f498..00000000000 --- a/src/test/ui/chalkify/lower_impl.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![feature(rustc_attrs)] - -trait Foo { } - -#[rustc_dump_program_clauses] //~ ERROR program clause dump -impl Foo for T where T: Iterator { } - -trait Bar { - type Assoc; -} - -impl Bar for T where T: Iterator { - #[rustc_dump_program_clauses] //~ ERROR program clause dump - type Assoc = Vec; -} - -fn main() { - println!("hello"); -} diff --git a/src/test/ui/chalkify/lower_impl.stderr b/src/test/ui/chalkify/lower_impl.stderr deleted file mode 100644 index d6827fbff3d..00000000000 --- a/src/test/ui/chalkify/lower_impl.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: program clause dump - --> $DIR/lower_impl.rs:5:1 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { Implemented(T: Foo) :- ProjectionEq(::Item == i32), TypeOutlives(T: 'static), Implemented(T: std::iter::Iterator), Implemented(T: std::marker::Sized). } - -error: program clause dump - --> $DIR/lower_impl.rs:13:5 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { Normalize(::Assoc -> std::vec::Vec) :- Implemented(T: Bar). } - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/chalkify/lower_struct.rs b/src/test/ui/chalkify/lower_struct.rs deleted file mode 100644 index aecccea5c14..00000000000 --- a/src/test/ui/chalkify/lower_struct.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(rustc_attrs)] - -#[rustc_dump_program_clauses] //~ ERROR program clause dump -struct Foo<'a, T> where Box: Clone { - _x: std::marker::PhantomData<&'a T>, -} - -fn main() { } diff --git a/src/test/ui/chalkify/lower_struct.stderr b/src/test/ui/chalkify/lower_struct.stderr deleted file mode 100644 index 0331c2fca16..00000000000 --- a/src/test/ui/chalkify/lower_struct.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: program clause dump - --> $DIR/lower_struct.rs:3:1 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall<'a, T> { FromEnv(T: std::marker::Sized) :- FromEnv(Foo<'a, T>). } - = note: forall<'a, T> { FromEnv(std::boxed::Box: std::clone::Clone) :- FromEnv(Foo<'a, T>). } - = note: forall<'a, T> { TypeOutlives(T: 'a) :- FromEnv(Foo<'a, T>). } - = note: forall<'a, T> { WellFormed(Foo<'a, T>) :- WellFormed(T: std::marker::Sized), WellFormed(std::boxed::Box: std::clone::Clone), TypeOutlives(T: 'a). } - -error: aborting due to previous error - diff --git a/src/test/ui/chalkify/lower_trait.rs b/src/test/ui/chalkify/lower_trait.rs deleted file mode 100644 index 0e1956022f9..00000000000 --- a/src/test/ui/chalkify/lower_trait.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(rustc_attrs)] - -trait Bar { } - -#[rustc_dump_program_clauses] //~ ERROR program clause dump -trait Foo { - #[rustc_dump_program_clauses] //~ ERROR program clause dump - type Assoc: Bar + ?Sized; -} - -fn main() { - println!("hello"); -} diff --git a/src/test/ui/chalkify/lower_trait.stderr b/src/test/ui/chalkify/lower_trait.stderr deleted file mode 100644 index ed3bded398a..00000000000 --- a/src/test/ui/chalkify/lower_trait.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: program clause dump - --> $DIR/lower_trait.rs:5:1 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { FromEnv(>::Assoc: Bar) :- FromEnv(Self: Foo). } - = note: forall { FromEnv(S: std::marker::Sized) :- FromEnv(Self: Foo). } - = note: forall { Implemented(Self: Foo) :- FromEnv(Self: Foo). } - = note: forall { WellFormed(Self: Foo) :- Implemented(Self: Foo), WellFormed(S: std::marker::Sized), WellFormed(>::Assoc: Bar). } - -error: program clause dump - --> $DIR/lower_trait.rs:7:5 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall { ProjectionEq(>::Assoc == ^3) :- Normalize(>::Assoc -> ^3). } - = note: forall { FromEnv(Self: Foo) :- FromEnv(Unnormalized(>::Assoc)). } - = note: forall { ProjectionEq(>::Assoc == Unnormalized(>::Assoc)). } - = note: forall { WellFormed(Unnormalized(>::Assoc)) :- WellFormed(Self: Foo). } - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/chalkify/lower_trait_higher_rank.rs b/src/test/ui/chalkify/lower_trait_higher_rank.rs deleted file mode 100644 index 715f09632bd..00000000000 --- a/src/test/ui/chalkify/lower_trait_higher_rank.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(rustc_attrs)] - -#[rustc_dump_program_clauses] //~ ERROR program clause dump -trait Foo where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8 -{ -} - -fn main() { - println!("hello"); -} diff --git a/src/test/ui/chalkify/lower_trait_higher_rank.stderr b/src/test/ui/chalkify/lower_trait_higher_rank.stderr deleted file mode 100644 index 79bbc9fa6b3..00000000000 --- a/src/test/ui/chalkify/lower_trait_higher_rank.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: program clause dump - --> $DIR/lower_trait_higher_rank.rs:3:1 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall<'a, Self, F> { FromEnv(F: std::ops::Fn<(&'a (u8, u16),)>) :- FromEnv(Self: Foo). } - = note: forall<'a, Self, F> { ProjectionEq(>::Output == &'a u8) :- FromEnv(Self: Foo). } - = note: forall { Implemented(Self: Foo) :- FromEnv(Self: Foo). } - = note: forall { WellFormed(Self: Foo) :- Implemented(Self: Foo), forall<'a> { WellFormed(F: std::ops::Fn<(&'a (u8, u16),)>) }, forall<'a> { ProjectionEq(>::Output == &'a u8) }. } - -error: aborting due to previous error - diff --git a/src/test/ui/chalkify/lower_trait_where_clause.rs b/src/test/ui/chalkify/lower_trait_where_clause.rs deleted file mode 100644 index 78fa39f1dc1..00000000000 --- a/src/test/ui/chalkify/lower_trait_where_clause.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(rustc_attrs)] - -use std::borrow::Borrow; - -#[rustc_dump_program_clauses] //~ ERROR program clause dump -trait Foo<'a, 'b, T, U> -where - T: Borrow + ?Sized, - U: ?Sized + 'b, - 'a: 'b, - Box:, // NOTE(#53696) this checks an empty list of bounds. -{ -} - -fn main() { - println!("hello"); -} diff --git a/src/test/ui/chalkify/lower_trait_where_clause.stderr b/src/test/ui/chalkify/lower_trait_where_clause.stderr deleted file mode 100644 index 408f3712a70..00000000000 --- a/src/test/ui/chalkify/lower_trait_where_clause.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: program clause dump - --> $DIR/lower_trait_where_clause.rs:5:1 - | -LL | #[rustc_dump_program_clauses] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: forall<'a, 'b, Self, T, U> { FromEnv(T: std::borrow::Borrow) :- FromEnv(Self: Foo<'a, 'b, T, U>). } - = note: forall<'a, 'b, Self, T, U> { Implemented(Self: Foo<'a, 'b, T, U>) :- FromEnv(Self: Foo<'a, 'b, T, U>). } - = note: forall<'a, 'b, Self, T, U> { RegionOutlives('a: 'b) :- FromEnv(Self: Foo<'a, 'b, T, U>). } - = note: forall<'a, 'b, Self, T, U> { TypeOutlives(U: 'b) :- FromEnv(Self: Foo<'a, 'b, T, U>). } - = note: forall<'a, 'b, Self, T, U> { TypeOutlives(std::boxed::Box: ') :- FromEnv(Self: Foo<'a, 'b, T, U>). } - = note: forall<'a, 'b, Self, T, U> { WellFormed(Self: Foo<'a, 'b, T, U>) :- Implemented(Self: Foo<'a, 'b, T, U>), WellFormed(T: std::borrow::Borrow), TypeOutlives(U: 'b), RegionOutlives('a: 'b), TypeOutlives(std::boxed::Box: '). } - -error: aborting due to previous error - diff --git a/src/test/ui/chalkify/projection.rs b/src/test/ui/chalkify/projection.rs deleted file mode 100644 index d6a8dd7a4a2..00000000000 --- a/src/test/ui/chalkify/projection.rs +++ /dev/null @@ -1,25 +0,0 @@ -// run-pass -// compile-flags: -Z chalk - -trait Foo { } - -trait Bar { - type Item: Foo; -} - -impl Foo for i32 { } -impl Bar for i32 { - type Item = i32; -} - -fn only_foo() { } - -fn only_bar() { - // `T` implements `Bar` hence `::Item` must also implement `Bar` - only_foo::() -} - -fn main() { - only_bar::(); - only_foo::<::Item>(); -} diff --git a/src/test/ui/chalkify/super_trait.rs b/src/test/ui/chalkify/super_trait.rs deleted file mode 100644 index eeff9fd9b80..00000000000 --- a/src/test/ui/chalkify/super_trait.rs +++ /dev/null @@ -1,19 +0,0 @@ -// run-pass -// compile-flags: -Z chalk - -trait Foo { } -trait Bar: Foo { } - -impl Foo for i32 { } -impl Bar for i32 { } - -fn only_foo() { } - -fn only_bar() { - // `T` implements `Bar` hence `T` must also implement `Foo` - only_foo::() -} - -fn main() { - only_bar::() -} diff --git a/src/test/ui/chalkify/trait_implied_bound.rs b/src/test/ui/chalkify/trait_implied_bound.rs deleted file mode 100644 index 8a2e1cf5990..00000000000 --- a/src/test/ui/chalkify/trait_implied_bound.rs +++ /dev/null @@ -1,18 +0,0 @@ -// run-pass -// compile-flags: -Z chalk - -trait Foo { } -trait Bar where U: Foo { } - -impl Foo for i32 { } -impl Bar for i32 { } - -fn only_foo() { } - -fn only_bar>() { - only_foo::() -} - -fn main() { - only_bar::() -} diff --git a/src/test/ui/chalkify/type_implied_bound.rs b/src/test/ui/chalkify/type_implied_bound.rs deleted file mode 100644 index 8673f5319bd..00000000000 --- a/src/test/ui/chalkify/type_implied_bound.rs +++ /dev/null @@ -1,29 +0,0 @@ -// run-pass -// compile-flags: -Z chalk - -trait Eq { } -trait Hash: Eq { } - -impl Eq for i32 { } -impl Hash for i32 { } - -struct Set { - _x: T, -} - -fn only_eq() { } - -fn take_a_set(_: &Set) { - // `Set` is an input type of `take_a_set`, hence we know that - // `T` must implement `Hash`, and we know in turn that `T` must - // implement `Eq`. - only_eq::() -} - -fn main() { - let set = Set { - _x: 5, - }; - - take_a_set(&set); -} diff --git a/src/test/ui/chalkify/type_inference.rs b/src/test/ui/chalkify/type_inference.rs deleted file mode 100644 index 62a53ec0317..00000000000 --- a/src/test/ui/chalkify/type_inference.rs +++ /dev/null @@ -1,26 +0,0 @@ -// compile-flags: -Z chalk - -trait Foo { } -impl Foo for i32 { } - -trait Bar { } -impl Bar for i32 { } -impl Bar for u32 { } - -fn only_foo(_x: T) { } - -fn only_bar(_x: T) { } - -fn main() { - let x = 5.0; - - // The only type which implements `Foo` is `i32`, so the chalk trait solver - // is expecting a variable of type `i32`. This behavior differs from the - // old-style trait solver. I guess this will change, that's why I'm - // adding that test. - only_foo(x); //~ ERROR mismatched types - - // Here we have two solutions so we get back the behavior of the old-style - // trait solver. - only_bar(x); //~ ERROR the trait bound `{float}: Bar` is not satisfied -} diff --git a/src/test/ui/chalkify/type_inference.stderr b/src/test/ui/chalkify/type_inference.stderr deleted file mode 100644 index b8152caf3d2..00000000000 --- a/src/test/ui/chalkify/type_inference.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/type_inference.rs:21:14 - | -LL | only_foo(x); - | ^ expected `i32`, found floating-point number - -error[E0277]: the trait bound `{float}: Bar` is not satisfied - --> $DIR/type_inference.rs:25:5 - | -LL | fn only_bar(_x: T) { } - | -------- --- required by this bound in `only_bar` -... -LL | only_bar(x); - | ^^^^^^^^ the trait `Bar` is not implemented for `{float}` - | - = help: the following implementations were found: - - - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/coherence/coherence-negative-impls-safe.stderr b/src/test/ui/coherence/coherence-negative-impls-safe.stderr index 4db66af6783..1bd37f39590 100644 --- a/src/test/ui/coherence/coherence-negative-impls-safe.stderr +++ b/src/test/ui/coherence/coherence-negative-impls-safe.stderr @@ -1,9 +1,10 @@ error[E0198]: negative impls cannot be unsafe - --> $DIR/coherence-negative-impls-safe.rs:7:1 + --> $DIR/coherence-negative-impls-safe.rs:7:13 | LL | unsafe impl !Send for TestType {} - | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | + | ------ -^^^^ + | | | + | | negative because of this | unsafe because of this error: aborting due to previous error diff --git a/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs new file mode 100644 index 00000000000..c51c908a426 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs @@ -0,0 +1,24 @@ +#![feature(cfg_accessible)] + +#[cfg_accessible] //~ ERROR malformed `cfg_accessible` attribute input +struct S1; + +#[cfg_accessible = "value"] //~ ERROR malformed `cfg_accessible` attribute input +struct S2; + +#[cfg_accessible()] //~ ERROR `cfg_accessible` path is not specified +struct S3; + +#[cfg_accessible(std, core)] //~ ERROR multiple `cfg_accessible` paths are specified +struct S4; + +#[cfg_accessible("std")] //~ ERROR `cfg_accessible` path cannot be a literal +struct S5; + +#[cfg_accessible(std = "value")] //~ ERROR `cfg_accessible` path cannot accept arguments +struct S6; + +#[cfg_accessible(std(value))] //~ ERROR `cfg_accessible` path cannot accept arguments +struct S7; + +fn main() {} diff --git a/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr new file mode 100644 index 00000000000..86706c76635 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr @@ -0,0 +1,44 @@ +error: malformed `cfg_accessible` attribute input + --> $DIR/cfg_accessible-input-validation.rs:3:1 + | +LL | #[cfg_accessible] + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]` + +error: malformed `cfg_accessible` attribute input + --> $DIR/cfg_accessible-input-validation.rs:6:1 + | +LL | #[cfg_accessible = "value"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]` + +error: `cfg_accessible` path is not specified + --> $DIR/cfg_accessible-input-validation.rs:9:1 + | +LL | #[cfg_accessible()] + | ^^^^^^^^^^^^^^^^^^^ + +error: multiple `cfg_accessible` paths are specified + --> $DIR/cfg_accessible-input-validation.rs:12:23 + | +LL | #[cfg_accessible(std, core)] + | ^^^^ + +error: `cfg_accessible` path cannot be a literal + --> $DIR/cfg_accessible-input-validation.rs:15:18 + | +LL | #[cfg_accessible("std")] + | ^^^^^ + +error: `cfg_accessible` path cannot accept arguments + --> $DIR/cfg_accessible-input-validation.rs:18:18 + | +LL | #[cfg_accessible(std = "value")] + | ^^^^^^^^^^^^^ + +error: `cfg_accessible` path cannot accept arguments + --> $DIR/cfg_accessible-input-validation.rs:21:18 + | +LL | #[cfg_accessible(std(value))] + | ^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs b/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs new file mode 100644 index 00000000000..8bc93fa3243 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs @@ -0,0 +1,9 @@ +#![feature(cfg_accessible)] + +#[cfg_accessible(Z)] //~ ERROR cannot determine whether the path is accessible or not +struct S; + +#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not +struct Z; + +fn main() {} diff --git a/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr b/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr new file mode 100644 index 00000000000..9641441a819 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr @@ -0,0 +1,14 @@ +error: cannot determine whether the path is accessible or not + --> $DIR/cfg_accessible-stuck.rs:6:1 + | +LL | #[cfg_accessible(S)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: cannot determine whether the path is accessible or not + --> $DIR/cfg_accessible-stuck.rs:3:1 + | +LL | #[cfg_accessible(Z)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs b/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs new file mode 100644 index 00000000000..e9247e67a2a --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs @@ -0,0 +1,2 @@ +#[cfg_accessible(std)] //~ ERROR use of unstable library feature 'cfg_accessible' +fn main() {} diff --git a/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr b/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr new file mode 100644 index 00000000000..2f55b9559c7 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature 'cfg_accessible': `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible-unstable.rs:1:3 + | +LL | #[cfg_accessible(std)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/conditional-compilation/cfg_accessible.rs b/src/test/ui/conditional-compilation/cfg_accessible.rs new file mode 100644 index 00000000000..07b0be5b1ae --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible.rs @@ -0,0 +1,43 @@ +#![feature(cfg_accessible)] + +mod m { + pub struct ExistingPublic; + struct ExistingPrivate; +} + +#[cfg_accessible(m::ExistingPublic)] +struct ExistingPublic; + +// FIXME: Not implemented yet. +#[cfg_accessible(m::ExistingPrivate)] //~ ERROR not sure whether the path is accessible or not +struct ExistingPrivate; + +// FIXME: Not implemented yet. +#[cfg_accessible(m::NonExistent)] //~ ERROR not sure whether the path is accessible or not +struct ExistingPrivate; + +#[cfg_accessible(n::AccessibleExpanded)] // OK, `cfg_accessible` can wait and retry. +struct AccessibleExpanded; + +macro_rules! generate_accessible_expanded { + () => { + mod n { + pub struct AccessibleExpanded; + } + }; +} + +generate_accessible_expanded!(); + +struct S { + field: u8, +} + +// FIXME: Not implemented yet. +#[cfg_accessible(S::field)] //~ ERROR not sure whether the path is accessible or not +struct Field; + +fn main() { + ExistingPublic; + AccessibleExpanded; +} diff --git a/src/test/ui/conditional-compilation/cfg_accessible.stderr b/src/test/ui/conditional-compilation/cfg_accessible.stderr new file mode 100644 index 00000000000..167765cd66e --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible.stderr @@ -0,0 +1,38 @@ +error: not sure whether the path is accessible or not + --> $DIR/cfg_accessible.rs:12:18 + | +LL | #[cfg_accessible(m::ExistingPrivate)] + | ^^^^^^^^^^^^^^^^^^ + | +note: `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible.rs:12:18 + | +LL | #[cfg_accessible(m::ExistingPrivate)] + | ^^^^^^^^^^^^^^^^^^ + +error: not sure whether the path is accessible or not + --> $DIR/cfg_accessible.rs:16:18 + | +LL | #[cfg_accessible(m::NonExistent)] + | ^^^^^^^^^^^^^^ + | +note: `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible.rs:16:18 + | +LL | #[cfg_accessible(m::NonExistent)] + | ^^^^^^^^^^^^^^ + +error: not sure whether the path is accessible or not + --> $DIR/cfg_accessible.rs:37:18 + | +LL | #[cfg_accessible(S::field)] + | ^^^^^^^^ + | +note: `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible.rs:37:18 + | +LL | #[cfg_accessible(S::field)] + | ^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/conflicting-repr-hints.stderr b/src/test/ui/conflicting-repr-hints.stderr index 43b76bf6497..0b78532c737 100644 --- a/src/test/ui/conflicting-repr-hints.stderr +++ b/src/test/ui/conflicting-repr-hints.stderr @@ -76,5 +76,5 @@ LL | | } error: aborting due to 10 previous errors -Some errors have detailed explanations: E0566, E0587. +Some errors have detailed explanations: E0566, E0587, E0634. For more information about an error, try `rustc --explain E0566`. diff --git a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr index 4869f483634..b39a160b529 100644 --- a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr @@ -1,8 +1,8 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:12:5 + --> $DIR/into-iter-no-impls-length-33.rs:12:19 | LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` + | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` | = note: required by `std::array::IntoIter::::new` @@ -19,10 +19,10 @@ LL | IntoIter::new([0i32; 33]) = note: the return type of a function must have a statically known size error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:18:5 + --> $DIR/into-iter-no-impls-length-33.rs:18:19 | LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` + | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` | = note: required by `std::array::IntoIter::::new` @@ -39,10 +39,10 @@ LL | IntoIter::new([0i32; 33]) = note: the return type of a function must have a statically known size error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:24:5 + --> $DIR/into-iter-no-impls-length-33.rs:24:19 | LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` + | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` | = note: required by `std::array::IntoIter::::new` @@ -59,10 +59,10 @@ LL | IntoIter::new([0i32; 33]) = note: the return type of a function must have a statically known size error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:30:5 + --> $DIR/into-iter-no-impls-length-33.rs:30:19 | LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` + | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` | = note: required by `std::array::IntoIter::::new` @@ -79,10 +79,10 @@ LL | IntoIter::new([0i32; 33]) = note: the return type of a function must have a statically known size error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:36:5 + --> $DIR/into-iter-no-impls-length-33.rs:36:19 | LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` + | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` | = note: required by `std::array::IntoIter::::new` @@ -99,10 +99,10 @@ LL | IntoIter::new([0i32; 33]) = note: the return type of a function must have a statically known size error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:42:5 + --> $DIR/into-iter-no-impls-length-33.rs:42:19 | LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` + | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` | = note: required by `std::array::IntoIter::::new` @@ -119,10 +119,10 @@ LL | IntoIter::new([0i32; 33]) = note: the return type of a function must have a statically known size error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:48:5 + --> $DIR/into-iter-no-impls-length-33.rs:48:19 | LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` + | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` | = note: required by `std::array::IntoIter::::new` diff --git a/src/test/ui/const-generics/cannot-infer-const-args.stderr b/src/test/ui/const-generics/cannot-infer-const-args.stderr index 8379cbd4908..c1d7022d56b 100644 --- a/src/test/ui/const-generics/cannot-infer-const-args.stderr +++ b/src/test/ui/const-generics/cannot-infer-const-args.stderr @@ -10,7 +10,7 @@ error[E0282]: type annotations needed --> $DIR/cannot-infer-const-args.rs:9:5 | LL | foo(); - | ^^^ cannot infer type for fn item `fn() -> usize {foo::<_: usize>}` + | ^^^ cannot infer type for fn item `fn() -> usize {foo::<{_: usize}>}` error: aborting due to previous error diff --git a/src/test/ui/const-generics/const-generic-type_name.rs b/src/test/ui/const-generics/const-generic-type_name.rs index 28586426b44..469843d6aae 100644 --- a/src/test/ui/const-generics/const-generic-type_name.rs +++ b/src/test/ui/const-generics/const-generic-type_name.rs @@ -7,5 +7,5 @@ struct S; fn main() { - assert_eq!(std::any::type_name::>(), "const_generic_type_name::S<3usize>"); + assert_eq!(std::any::type_name::>(), "const_generic_type_name::S<3>"); } diff --git a/src/test/ui/const-generics/fn-const-param-infer.stderr b/src/test/ui/const-generics/fn-const-param-infer.stderr index 44eab8baa40..05d2dff8e98 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.stderr +++ b/src/test/ui/const-generics/fn-const-param-infer.stderr @@ -10,12 +10,12 @@ error[E0308]: mismatched types --> $DIR/fn-const-param-infer.rs:16:31 | LL | let _: Checked = Checked::; - | ---------------- ^^^^^^^^^^^^^^^^^^ expected `not_one`, found `not_two` + | ---------------- ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}` | | | expected due to this | - = note: expected struct `Checked` - found struct `Checked` + = note: expected struct `Checked<{not_one as fn(usize) -> bool}>` + found struct `Checked<{not_two as fn(usize) -> bool}>` error[E0308]: mismatched types --> $DIR/fn-const-param-infer.rs:20:24 @@ -36,12 +36,12 @@ error[E0308]: mismatched types --> $DIR/fn-const-param-infer.rs:25:40 | LL | let _: Checked<{generic::}> = Checked::<{generic::}>; - | ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `generic::`, found `generic::` + | ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic:: as fn(usize) -> bool}`, found `{generic:: as fn(usize) -> bool}` | | | expected due to this | - = note: expected struct `Checked>` - found struct `Checked>` + = note: expected struct `Checked<{generic:: as fn(usize) -> bool}>` + found struct `Checked<{generic:: as fn(usize) -> bool}>` error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/issues/issue-62456.rs b/src/test/ui/const-generics/issues/issue-62456.rs new file mode 100644 index 00000000000..c5e6fe9104b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62456.rs @@ -0,0 +1,9 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn foo() { + let _ = [0u64; N + 1]; + //~^ ERROR array lengths can't depend on generic parameters +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-62456.stderr b/src/test/ui/const-generics/issues/issue-62456.stderr new file mode 100644 index 00000000000..9cdccf8407c --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62456.stderr @@ -0,0 +1,16 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/issue-62456.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error: array lengths can't depend on generic parameters + --> $DIR/issue-62456.rs:5:20 + | +LL | let _ = [0u64; N + 1]; + | ^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62504.rs b/src/test/ui/const-generics/issues/issue-62504.rs new file mode 100644 index 00000000000..74ed3d354fc --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62504.rs @@ -0,0 +1,25 @@ +// Regression test for #62504 + +#![feature(const_generics)] +#![allow(incomplete_features)] + +trait HasSize { + const SIZE: usize; +} + +impl HasSize for ArrayHolder<{ X }> { + const SIZE: usize = X; +} + +struct ArrayHolder([u32; X]); + +impl ArrayHolder<{ X }> { + pub const fn new() -> Self { + ArrayHolder([0; Self::SIZE]) + //~^ ERROR: array lengths can't depend on generic parameters + } +} + +fn main() { + let mut array = ArrayHolder::new(); +} diff --git a/src/test/ui/const-generics/issues/issue-62504.stderr b/src/test/ui/const-generics/issues/issue-62504.stderr new file mode 100644 index 00000000000..c2a752ec171 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62504.stderr @@ -0,0 +1,8 @@ +error: array lengths can't depend on generic parameters + --> $DIR/issue-62504.rs:18:25 + | +LL | ArrayHolder([0; Self::SIZE]) + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-65675.rs b/src/test/ui/const-generics/issues/issue-65675.rs deleted file mode 100644 index 3ca527313f9..00000000000 --- a/src/test/ui/const-generics/issues/issue-65675.rs +++ /dev/null @@ -1,10 +0,0 @@ -// run-pass -// compile-flags: -Z chalk - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash - -pub struct Foo([T; N]); -impl Foo {} - -fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-65675.stderr b/src/test/ui/const-generics/issues/issue-65675.stderr deleted file mode 100644 index 60b388e6278..00000000000 --- a/src/test/ui/const-generics/issues/issue-65675.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash - --> $DIR/issue-65675.rs:4:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - diff --git a/src/test/ui/const-generics/issues/issue-67739.rs b/src/test/ui/const-generics/issues/issue-67739.rs new file mode 100644 index 00000000000..79c5ac9dd18 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67739.rs @@ -0,0 +1,18 @@ +// Regression test for #67739 + +#![allow(incomplete_features)] +#![feature(const_generics)] + +use std::mem; + +pub trait Trait { + type Associated: Sized; + + fn associated_size(&self) -> usize { + [0u8; mem::size_of::()]; + //~^ ERROR: array lengths can't depend on generic parameters + 0 + } +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-67739.stderr b/src/test/ui/const-generics/issues/issue-67739.stderr new file mode 100644 index 00000000000..a31b556c086 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67739.stderr @@ -0,0 +1,8 @@ +error: array lengths can't depend on generic parameters + --> $DIR/issue-67739.rs:12:15 + | +LL | [0u8; mem::size_of::()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/raw-ptr-const-param.rs b/src/test/ui/const-generics/raw-ptr-const-param.rs index f69c37fbb8f..f0349f46962 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param.rs @@ -4,6 +4,6 @@ struct Const; fn main() { - let _: Const<{15 as *const _}> = Const::<{10 as *const _}>; //~ mismatched types - let _: Const<{10 as *const _}> = Const::<{10 as *const _}>; + let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>; //~ mismatched types + let _: Const<{ 10 as *const _ }> = Const::<{ 10 as *const _ }>; } diff --git a/src/test/ui/const-generics/raw-ptr-const-param.stderr b/src/test/ui/const-generics/raw-ptr-const-param.stderr index 9cd39b61dc9..d9794f60a19 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param.stderr @@ -7,15 +7,15 @@ LL | #![feature(const_generics, const_compare_raw_pointers)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/raw-ptr-const-param.rs:7:38 + --> $DIR/raw-ptr-const-param.rs:7:40 | -LL | let _: Const<{15 as *const _}> = Const::<{10 as *const _}>; - | ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{pointer}`, found `{pointer}` +LL | let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>; + | ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}` | | | expected due to this | - = note: expected struct `Const<{pointer}>` - found struct `Const<{pointer}>` + = note: expected struct `Const<{0xf as *const u32}>` + found struct `Const<{0xa as *const u32}>` error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr index e0df787f80a..305f259eac2 100644 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr @@ -12,7 +12,7 @@ error: any use of this value will cause an error LL | const I32_REF_U8_UNION: u8 = unsafe { Nonsense { int_32_ref: &3 }.uint_8 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes | = note: `#[deny(const_err)]` on by default @@ -22,7 +22,7 @@ error: any use of this value will cause an error LL | const I32_REF_U16_UNION: u16 = unsafe { Nonsense { int_32_ref: &3 }.uint_16 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:34:45 @@ -30,7 +30,7 @@ error: any use of this value will cause an error LL | const I32_REF_U32_UNION: u32 = unsafe { Nonsense { int_32_ref: &3 }.uint_32 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:37:5 @@ -54,7 +54,7 @@ error: any use of this value will cause an error LL | const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:46:45 @@ -62,7 +62,7 @@ error: any use of this value will cause an error LL | const I32_REF_I16_UNION: i16 = unsafe { Nonsense { int_32_ref: &3 }.int_16 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:49:45 @@ -70,7 +70,7 @@ error: any use of this value will cause an error LL | const I32_REF_I32_UNION: i32 = unsafe { Nonsense { int_32_ref: &3 }.int_32 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:52:5 @@ -94,7 +94,7 @@ error: any use of this value will cause an error LL | const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:61:5 @@ -110,7 +110,7 @@ error: any use of this value will cause an error LL | const I32_REF_BOOL_UNION: bool = unsafe { Nonsense { int_32_ref: &3 }.truthy_falsey }; | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:67:47 @@ -118,7 +118,7 @@ error: any use of this value will cause an error LL | const I32_REF_CHAR_UNION: char = unsafe { Nonsense { int_32_ref: &3 }.character }; | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:70:39 @@ -126,7 +126,7 @@ error: any use of this value will cause an error LL | const STR_U8_UNION: u8 = unsafe { Nonsense { stringy: "3" }.uint_8 }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:73:41 @@ -134,7 +134,7 @@ error: any use of this value will cause an error LL | const STR_U16_UNION: u16 = unsafe { Nonsense { stringy: "3" }.uint_16 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:76:41 @@ -142,7 +142,7 @@ error: any use of this value will cause an error LL | const STR_U32_UNION: u32 = unsafe { Nonsense { stringy: "3" }.uint_32 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:79:5 @@ -158,7 +158,7 @@ error: any use of this value will cause an error LL | const STR_U128_UNION: u128 = unsafe { Nonsense { stringy: "3" }.uint_128 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:85:39 @@ -166,7 +166,7 @@ error: any use of this value will cause an error LL | const STR_I8_UNION: i8 = unsafe { Nonsense { stringy: "3" }.int_8 }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:88:41 @@ -174,7 +174,7 @@ error: any use of this value will cause an error LL | const STR_I16_UNION: i16 = unsafe { Nonsense { stringy: "3" }.int_16 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:91:41 @@ -182,7 +182,7 @@ error: any use of this value will cause an error LL | const STR_I32_UNION: i32 = unsafe { Nonsense { stringy: "3" }.int_32 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:94:5 @@ -198,7 +198,7 @@ error: any use of this value will cause an error LL | const STR_I128_UNION: i128 = unsafe { Nonsense { stringy: "3" }.int_128 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:100:41 @@ -206,7 +206,7 @@ error: any use of this value will cause an error LL | const STR_F32_UNION: f32 = unsafe { Nonsense { stringy: "3" }.float_32 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:103:5 @@ -222,7 +222,7 @@ error: any use of this value will cause an error LL | const STR_BOOL_UNION: bool = unsafe { Nonsense { stringy: "3" }.truthy_falsey }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:109:43 @@ -230,7 +230,7 @@ error: any use of this value will cause an error LL | const STR_CHAR_UNION: char = unsafe { Nonsense { stringy: "3" }.character }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: aborting due to 29 previous errors diff --git a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs index 9afcdf77610..6b86feb5921 100644 --- a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs +++ b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs @@ -17,8 +17,6 @@ #[lang = "eh_personality"] fn eh() {} -#[lang = "eh_unwind_resume"] -fn eh_unwind_resume() {} #[panic_handler] fn panic(_info: &PanicInfo) -> ! { diff --git a/src/test/ui/consts/const-eval/const_prop_errors.rs b/src/test/ui/consts/const-eval/const_prop_errors.rs index 48cfea82bd6..f9a36d37943 100644 --- a/src/test/ui/consts/const-eval/const_prop_errors.rs +++ b/src/test/ui/consts/const-eval/const_prop_errors.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass pub trait Foo { fn foo(self) -> u32; diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr b/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr index 2cba833a748..cc40728e6b5 100644 --- a/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr @@ -30,7 +30,7 @@ error: any use of this value will cause an error LL | const Z2: i32 = unsafe { *(42 as *const i32) }; | -------------------------^^^^^^^^^^^^^^^^^^^--- | | - | a memory access tried to interpret some bytes as a pointer + | unable to turn bytes into a pointer error: any use of this value will cause an error --> $DIR/const_raw_ptr_ops.rs:17:26 @@ -38,7 +38,7 @@ error: any use of this value will cause an error LL | const Z3: i32 = unsafe { *(44 as *const i32) }; | -------------------------^^^^^^^^^^^^^^^^^^^--- | | - | a memory access tried to interpret some bytes as a pointer + | unable to turn bytes into a pointer error: aborting due to 5 previous errors diff --git a/src/test/ui/consts/const-eval/const_signed_pat.rs b/src/test/ui/consts/const-eval/const_signed_pat.rs index d209e604486..c61239bb677 100644 --- a/src/test/ui/consts/const-eval/const_signed_pat.rs +++ b/src/test/ui/consts/const-eval/const_signed_pat.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass fn main() { const MIN: i8 = -5; diff --git a/src/test/ui/consts/const-eval/dangling.rs b/src/test/ui/consts/const-eval/dangling.rs index b5d72d46f28..c6b8e8eb611 100644 --- a/src/test/ui/consts/const-eval/dangling.rs +++ b/src/test/ui/consts/const-eval/dangling.rs @@ -6,7 +6,7 @@ const TEST: () = { unsafe { //~ NOTE let slice: *const [u8] = mem::transmute((1usize, usize::MAX)); let _val = &*slice; //~ ERROR: any use of this value will cause an error - //~^ NOTE: total size is bigger than largest supported object + //~^ NOTE: slice is bigger than largest supported object //~^^ on by default } }; diff --git a/src/test/ui/consts/const-eval/dangling.stderr b/src/test/ui/consts/const-eval/dangling.stderr index 286de080097..b9ddc93b03b 100644 --- a/src/test/ui/consts/const-eval/dangling.stderr +++ b/src/test/ui/consts/const-eval/dangling.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | / const TEST: () = { unsafe { LL | | let slice: *const [u8] = mem::transmute((1usize, usize::MAX)); LL | | let _val = &*slice; - | | ^^^^^^^ invalid slice: total size is bigger than largest supported object + | | ^^^^^^^ invalid metadata in wide pointer: slice is bigger than largest supported object LL | | LL | | LL | | } }; diff --git a/src/test/ui/consts/const-eval/double_check.rs b/src/test/ui/consts/const-eval/double_check.rs index ff2fff7fb79..f156d259abb 100644 --- a/src/test/ui/consts/const-eval/double_check.rs +++ b/src/test/ui/consts/const-eval/double_check.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass enum Foo { A = 5, diff --git a/src/test/ui/consts/const-eval/double_promotion.rs b/src/test/ui/consts/const-eval/double_promotion.rs index a9a3f071bf8..48f4426d9cf 100644 --- a/src/test/ui/consts/const-eval/double_promotion.rs +++ b/src/test/ui/consts/const-eval/double_promotion.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(const_fn, rustc_attrs)] diff --git a/src/test/ui/consts/const-eval/duration_conversion.rs b/src/test/ui/consts/const-eval/duration_conversion.rs index 029d4e5e373..87b12937dd4 100644 --- a/src/test/ui/consts/const-eval/duration_conversion.rs +++ b/src/test/ui/consts/const-eval/duration_conversion.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass use std::time::Duration; diff --git a/src/test/ui/consts/const-eval/extern_fat_pointer.rs b/src/test/ui/consts/const-eval/extern_fat_pointer.rs index e2b3bc83c3f..f210d1a0a90 100644 --- a/src/test/ui/consts/const-eval/extern_fat_pointer.rs +++ b/src/test/ui/consts/const-eval/extern_fat_pointer.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(extern_types)] diff --git a/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs b/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs index ce0e11f29f4..4444cdfcda9 100644 --- a/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs +++ b/src/test/ui/consts/const-eval/ice-generic-assoc-const.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass pub trait Nullable { const NULL: Self; diff --git a/src/test/ui/consts/const-eval/ice-packed.rs b/src/test/ui/consts/const-eval/ice-packed.rs index 250bf954e9a..4758a5a9d56 100644 --- a/src/test/ui/consts/const-eval/ice-packed.rs +++ b/src/test/ui/consts/const-eval/ice-packed.rs @@ -1,4 +1,7 @@ -// build-pass (FIXME(62277): could be check-pass?) +// Regression test for #50356: Compiler panic when using repr(packed) +// associated constant in a match arm + +// check-pass #[derive(Copy, Clone, PartialEq, Eq)] #[repr(packed)] pub struct Num(u64); diff --git a/src/test/ui/consts/const-eval/issue-47971.rs b/src/test/ui/consts/const-eval/issue-47971.rs index 9de150bd052..b98e76031d4 100644 --- a/src/test/ui/consts/const-eval/issue-47971.rs +++ b/src/test/ui/consts/const-eval/issue-47971.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass struct S(pub &'static u32, pub u32); diff --git a/src/test/ui/consts/const-eval/issue-49296.stderr b/src/test/ui/consts/const-eval/issue-49296.stderr index 48809e0ae64..798f130a4ba 100644 --- a/src/test/ui/consts/const-eval/issue-49296.stderr +++ b/src/test/ui/consts/const-eval/issue-49296.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | const X: u64 = *wat(42); | ---------------^^^^^^^^- | | - | dangling pointer was dereferenced + | pointer to alloc2 was dereferenced after this allocation got freed | = note: `#[deny(const_err)]` on by default diff --git a/src/test/ui/consts/const-eval/issue-50706.rs b/src/test/ui/consts/const-eval/issue-50706.rs index bf69bc28da4..a13c27f2e78 100644 --- a/src/test/ui/consts/const-eval/issue-50706.rs +++ b/src/test/ui/consts/const-eval/issue-50706.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass pub struct Stats; diff --git a/src/test/ui/consts/const-eval/issue-51300.rs b/src/test/ui/consts/const-eval/issue-51300.rs index 4753bf0f7b1..8e68e8c9117 100644 --- a/src/test/ui/consts/const-eval/issue-51300.rs +++ b/src/test/ui/consts/const-eval/issue-51300.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // https://github.com/rust-lang/rust/issues/51300 #[derive(PartialEq, Eq, Clone, Copy)] diff --git a/src/test/ui/consts/const-eval/issue-53157.rs b/src/test/ui/consts/const-eval/issue-53157.rs index ac0940b33e4..850338625bc 100644 --- a/src/test/ui/consts/const-eval/issue-53157.rs +++ b/src/test/ui/consts/const-eval/issue-53157.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass macro_rules! m { () => {{ diff --git a/src/test/ui/consts/const-eval/issue-53401.rs b/src/test/ui/consts/const-eval/issue-53401.rs index d300e0b5125..31c946c3cb7 100644 --- a/src/test/ui/consts/const-eval/issue-53401.rs +++ b/src/test/ui/consts/const-eval/issue-53401.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass pub const STATIC_TRAIT: &dyn Test = &(); diff --git a/src/test/ui/consts/const-eval/issue-55541.rs b/src/test/ui/consts/const-eval/issue-55541.rs index d04570c67ff..4c9e10d9cbe 100644 --- a/src/test/ui/consts/const-eval/issue-55541.rs +++ b/src/test/ui/consts/const-eval/issue-55541.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // Test that we can handle newtypes wrapping extern types diff --git a/src/test/ui/consts/const-eval/no_lint_for_statically_known_error.rs b/src/test/ui/consts/const-eval/no_lint_for_statically_known_error.rs index cea367528c9..910ca3c4bcb 100644 --- a/src/test/ui/consts/const-eval/no_lint_for_statically_known_error.rs +++ b/src/test/ui/consts/const-eval/no_lint_for_statically_known_error.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // if `X` were used instead of `x`, `X - 10` would result in a lint. // This file should never produce a lint, no matter how the const diff --git a/src/test/ui/consts/const-eval/promote_mutable_zst_mir_borrowck.rs b/src/test/ui/consts/const-eval/promote_mutable_zst_mir_borrowck.rs index ca75d65a39a..edda10e6e82 100644 --- a/src/test/ui/consts/const-eval/promote_mutable_zst_mir_borrowck.rs +++ b/src/test/ui/consts/const-eval/promote_mutable_zst_mir_borrowck.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass pub fn main() { let y: &'static mut [u8; 0] = &mut []; diff --git a/src/test/ui/consts/const-eval/pub_const_err.rs b/src/test/ui/consts/const-eval/pub_const_err.rs index 4ff140fee7a..ad165d40a76 100644 --- a/src/test/ui/consts/const-eval/pub_const_err.rs +++ b/src/test/ui/consts/const-eval/pub_const_err.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(const_err)] #![crate_type = "lib"] diff --git a/src/test/ui/consts/const-eval/pub_const_err_bin.rs b/src/test/ui/consts/const-eval/pub_const_err_bin.rs index 7f1586336e7..078e4c896df 100644 --- a/src/test/ui/consts/const-eval/pub_const_err_bin.rs +++ b/src/test/ui/consts/const-eval/pub_const_err_bin.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![warn(const_err)] pub const Z: u32 = 0 - 1; diff --git a/src/test/ui/consts/const-eval/simple_with_undef.rs b/src/test/ui/consts/const-eval/simple_with_undef.rs index 8a9f3fe974d..1a416dd460d 100644 --- a/src/test/ui/consts/const-eval/simple_with_undef.rs +++ b/src/test/ui/consts/const-eval/simple_with_undef.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass const PARSE_BOOL: Option<&'static str> = None; static FOO: (Option<&str>, u32) = (PARSE_BOOL, 42); diff --git a/src/test/ui/consts/const-eval/transmute-const.stderr b/src/test/ui/consts/const-eval/transmute-const.stderr index 47f89fccf7a..e93a6887ba8 100644 --- a/src/test/ui/consts/const-eval/transmute-const.stderr +++ b/src/test/ui/consts/const-eval/transmute-const.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/transmute-const.rs:5:1 | LL | static FOO: bool = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3, but expected something less or equal to 1 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const-eval/ub-enum.rs b/src/test/ui/consts/const-eval/ub-enum.rs index 483285aa4e1..1922d59891f 100644 --- a/src/test/ui/consts/const-eval/ub-enum.rs +++ b/src/test/ui/consts/const-eval/ub-enum.rs @@ -1,34 +1,36 @@ +#![feature(const_transmute, never_type)] #![allow(const_err)] // make sure we cannot allow away the errors tested here +use std::mem; #[repr(transparent)] #[derive(Copy, Clone)] struct Wrap(T); +#[derive(Copy, Clone)] +enum Never {} + +// # simple enum with discriminant 0 + #[repr(usize)] #[derive(Copy, Clone)] enum Enum { A = 0, } -#[repr(C)] -union TransmuteEnum { - in1: &'static u8, - in2: usize, - out1: Enum, - out2: Wrap, -} -const GOOD_ENUM: Enum = unsafe { TransmuteEnum { in2: 0 }.out1 }; +const GOOD_ENUM: Enum = unsafe { mem::transmute(0usize) }; -const BAD_ENUM: Enum = unsafe { TransmuteEnum { in2: 1 }.out1 }; +const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; //~^ ERROR is undefined behavior -const BAD_ENUM_PTR: Enum = unsafe { TransmuteEnum { in1: &1 }.out1 }; +const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; //~^ ERROR is undefined behavior -const BAD_ENUM_WRAPPED: Wrap = unsafe { TransmuteEnum { in1: &1 }.out2 }; +const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; //~^ ERROR is undefined behavior +// # simple enum with discriminant 2 + // (Potentially) invalid enum discriminant #[repr(usize)] #[derive(Copy, Clone)] @@ -36,39 +38,58 @@ enum Enum2 { A = 2, } -#[repr(C)] -union TransmuteEnum2 { - in1: usize, - in2: &'static u8, - in3: (), - out1: Enum2, - out2: Wrap, // something wrapping the enum so that we test layout first, not enum - out3: Option, -} -const BAD_ENUM2: Enum2 = unsafe { TransmuteEnum2 { in1: 0 }.out1 }; +const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; //~^ ERROR is undefined behavior -const BAD_ENUM2_PTR: Enum2 = unsafe { TransmuteEnum2 { in2: &0 }.out1 }; +const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; //~^ ERROR is undefined behavior -const BAD_ENUM2_WRAPPED: Wrap = unsafe { TransmuteEnum2 { in2: &0 }.out2 }; +// something wrapping the enum so that we test layout first, not enum +const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; //~^ ERROR is undefined behavior // Undef enum discriminant. -const BAD_ENUM2_UNDEF : Enum2 = unsafe { TransmuteEnum2 { in3: () }.out1 }; +#[repr(C)] +union MaybeUninit { + uninit: (), + init: T, +} +const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR is undefined behavior // Pointer value in an enum with a niche that is not just 0. -const BAD_ENUM2_OPTION_PTR: Option = unsafe { TransmuteEnum2 { in2: &0 }.out3 }; +const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; //~^ ERROR is undefined behavior +// # valid discriminant for uninhabited variant + +// An enum with 3 variants of which some are uninhabited -- so the uninhabited variants *do* +// have a discriminant. +enum UninhDiscriminant { + A, + B(!), + C, + D(Never), +} + +const GOOD_INHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(0u8) }; // variant A +const GOOD_INHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(2u8) }; // variant C + +const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; +//~^ ERROR is undefined behavior +const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; +//~^ ERROR is undefined behavior + +// # other + // Invalid enum field content (mostly to test printing of paths for enum tuple // variants and tuples). -#[repr(C)] -union TransmuteChar { - a: u32, - b: char, -} // Need to create something which does not clash with enum layout optimizations. -const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { TransmuteChar { a: !0 }.b })); +const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); +//~^ ERROR is undefined behavior + +// All variants are uninhabited but also have data. +const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(1u64) }; +//~^ ERROR is undefined behavior +const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(1u64) }; //~^ ERROR is undefined behavior fn main() { diff --git a/src/test/ui/consts/const-eval/ub-enum.stderr b/src/test/ui/consts/const-eval/ub-enum.stderr index 8ebc9dbec8a..10a3d2fa1ab 100644 --- a/src/test/ui/consts/const-eval/ub-enum.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.stderr @@ -1,75 +1,107 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:23:1 | -LL | const BAD_ENUM: Enum = unsafe { TransmuteEnum { in2: 1 }.out1 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 1, but expected a valid enum discriminant +LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 1, but expected a valid enum discriminant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:26:1 | -LL | const BAD_ENUM_PTR: Enum = unsafe { TransmuteEnum { in1: &1 }.out1 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected a valid enum discriminant +LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:29:1 | -LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { TransmuteEnum { in1: &1 }.out2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected something that cannot possibly fail to be equal to 0 +LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .0., but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:48:1 + --> $DIR/ub-enum.rs:41:1 | -LL | const BAD_ENUM2: Enum2 = unsafe { TransmuteEnum2 { in1: 0 }.out1 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected a valid enum discriminant +LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected a valid enum discriminant | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:50:1 + --> $DIR/ub-enum.rs:43:1 | -LL | const BAD_ENUM2_PTR: Enum2 = unsafe { TransmuteEnum2 { in2: &0 }.out1 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected a valid enum discriminant +LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:52:1 + --> $DIR/ub-enum.rs:46:1 | -LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { TransmuteEnum2 { in2: &0 }.out2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected something that cannot possibly fail to be equal to 2 +LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .0., but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:56:1 + --> $DIR/ub-enum.rs:55:1 | -LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { TransmuteEnum2 { in3: () }.out1 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid enum discriminant +LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at ., but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:60:1 + --> $DIR/ub-enum.rs:59:1 | -LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { TransmuteEnum2 { in2: &0 }.out3 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected a valid enum discriminant +LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:71:1 + --> $DIR/ub-enum.rs:76:1 | -LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { TransmuteChar { a: !0 }.b })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 4294967295 at ..0.1, but expected something less or equal to 1114111 +LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at ..0 | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. -error: aborting due to 9 previous errors +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:78:1 + | +LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at ..0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:86:1 + | +LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 4294967295 at ..0.1, but expected a valid unicode codepoint + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:90:1 + | +LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(1u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at ..0.1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:92:1 + | +LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(1u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at ..0.1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-nonnull.rs b/src/test/ui/consts/const-eval/ub-nonnull.rs index 8ce64ced7df..1f46b6c98ad 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.rs +++ b/src/test/ui/consts/const-eval/ub-nonnull.rs @@ -25,11 +25,11 @@ //~^ ERROR it is undefined behavior to use this value #[repr(C)] -union Transmute { +union MaybeUninit { uninit: (), - out: NonZeroU8, + init: T, } -const UNINIT: NonZeroU8 = unsafe { Transmute { uninit: () }.out }; +const UNINIT: NonZeroU8 = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR it is undefined behavior to use this value // Also test other uses of rustc_layout_scalar_valid_range_start diff --git a/src/test/ui/consts/const-eval/ub-nonnull.stderr b/src/test/ui/consts/const-eval/ub-nonnull.stderr index 4d9d258f808..adad1b4f7fa 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.stderr @@ -13,7 +13,7 @@ LL | / const OUT_OF_BOUNDS_PTR: NonNull = { unsafe { LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL! LL | | let out_of_bounds_ptr = &ptr[255]; - | | ^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of allocation 8 which has size 1 + | | ^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc8 which has size 1 LL | | mem::transmute(out_of_bounds_ptr) LL | | } }; | |____- @@ -43,8 +43,8 @@ LL | const NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(0usize) }; error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:32:1 | -LL | const UNINIT: NonZeroU8 = unsafe { Transmute { uninit: () }.out }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected something greater or equal to 1 +LL | const UNINIT: NonZeroU8 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .0, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const-eval/ub-ref.rs b/src/test/ui/consts/const-eval/ub-ref.rs index 03ac12c8b1a..562ec99111b 100644 --- a/src/test/ui/consts/const-eval/ub-ref.rs +++ b/src/test/ui/consts/const-eval/ub-ref.rs @@ -6,11 +6,18 @@ const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; //~^ ERROR it is undefined behavior to use this value -//~^^ type validation failed: encountered unaligned reference (required 2 byte alignment but found 1) +//~^^ type validation failed: encountered an unaligned reference (required 2 byte alignment but found 1) + +const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; +//~^ ERROR it is undefined behavior to use this value +//~^^ type validation failed: encountered an unaligned box (required 2 byte alignment but found 1) const NULL: &u16 = unsafe { mem::transmute(0usize) }; //~^ ERROR it is undefined behavior to use this value +const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; +//~^ ERROR it is undefined behavior to use this value + // It is very important that we reject this: We do promote `&(4 * REF_AS_USIZE)`, // but that would fail to compile; so we ended up breaking user code that would // have worked fine had we not promoted. @@ -20,7 +27,13 @@ const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; //~^ ERROR it is undefined behavior to use this value +const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; +//~^ ERROR it is undefined behavior to use this value + const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; //~^ ERROR it is undefined behavior to use this value +const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; +//~^ ERROR it is undefined behavior to use this value + fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-ref.stderr b/src/test/ui/consts/const-eval/ub-ref.stderr index 01bde413c0d..fb3df8ace4e 100644 --- a/src/test/ui/consts/const-eval/ub-ref.stderr +++ b/src/test/ui/consts/const-eval/ub-ref.stderr @@ -2,20 +2,36 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref.rs:7:1 | LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned reference (required 2 byte alignment but found 1) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned reference (required 2 byte alignment but found 1) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref.rs:11:1 | +LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned box (required 2 byte alignment but found 1) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref.rs:15:1 + | LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref.rs:18:1 + | +LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL box | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref.rs:17:1 + --> $DIR/ub-ref.rs:24:1 | LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes @@ -23,7 +39,7 @@ LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref.rs:20:1 + --> $DIR/ub-ref.rs:27:1 | LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected plain (non-pointer) bytes @@ -31,13 +47,29 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref.rs:23:1 + --> $DIR/ub-ref.rs:30:1 + | +LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at ., but expected plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref.rs:33:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling reference (created from integer) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (created from integer) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref.rs:36:1 + | +LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (created from integer) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.rs b/src/test/ui/consts/const-eval/ub-uninhabit.rs index d2745d71bdb..e7350ae2716 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.rs +++ b/src/test/ui/consts/const-eval/ub-uninhabit.rs @@ -7,18 +7,18 @@ enum Bar {} #[repr(C)] -union TransmuteUnion { - a: A, - b: B, +union MaybeUninit { + uninit: (), + init: T, } -const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b }; +const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR it is undefined behavior to use this value const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; //~^ ERROR it is undefined behavior to use this value -const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b }; +const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR it is undefined behavior to use this value fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.stderr index 3877f3cab6d..8ce4279a8b7 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.stderr +++ b/src/test/ui/consts/const-eval/ub-uninhabit.stderr @@ -1,8 +1,8 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:15:1 | -LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type +LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. @@ -10,15 +10,15 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:18:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at . + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at . | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:21:1 | -LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type +LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at [0] | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const-eval/ub-upvars.stderr b/src/test/ui/consts/const-eval/ub-upvars.stderr index ea6ab3ae5b5..972c9eb38c8 100644 --- a/src/test/ui/consts/const-eval/ub-upvars.stderr +++ b/src/test/ui/consts/const-eval/ub-upvars.stderr @@ -6,7 +6,7 @@ LL | | let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) }; LL | | let another_var = 13; LL | | move || { let _ = bad_ref; let _ = another_var; } LL | | }; - | |__^ type validation failed: encountered 0 at ..., but expected something greater or equal to 1 + | |__^ type validation failed: encountered a NULL reference at ... | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.rs b/src/test/ui/consts/const-eval/ub-wide-ptr.rs index a5c2a57c6c8..0200bfe9f08 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs @@ -1,72 +1,18 @@ // ignore-tidy-linelength +#![feature(const_transmute)] #![allow(unused)] #![allow(const_err)] // make sure we cannot allow away the errors tested here +use std::mem; + // normalize-stderr-test "offset \d+" -> "offset N" -// normalize-stderr-test "allocation \d+" -> "allocation N" +// normalize-stderr-test "alloc\d+" -> "allocN" // normalize-stderr-test "size \d+" -> "size N" #[repr(C)] -union BoolTransmute { - val: u8, - bl: bool, -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SliceRepr { - ptr: *const u8, - len: usize, -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct BadSliceRepr { - ptr: *const u8, - len: &'static u8, -} - -#[repr(C)] -union SliceTransmute { - repr: SliceRepr, - bad: BadSliceRepr, - addr: usize, - slice: &'static [u8], - raw_slice: *const [u8], - str: &'static str, - my_str: &'static MyStr, - my_slice: &'static MySliceBool, -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct DynRepr { - ptr: *const u8, - vtable: *const u8, -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct DynRepr2 { - ptr: *const u8, - vtable: *const u64, -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct BadDynRepr { - ptr: *const u8, - vtable: usize, -} - -#[repr(C)] -union DynTransmute { - repr: DynRepr, - repr2: DynRepr2, - bad: BadDynRepr, - addr: usize, - rust: &'static dyn Trait, - raw_rust: *const dyn Trait, +union MaybeUninit { + uninit: (), + init: T, } trait Trait {} @@ -81,90 +27,103 @@ impl Trait for bool {} // # str // OK -const STR_VALID: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.str}; +const STR_VALID: &str = unsafe { mem::transmute((&42u8, 1usize)) }; // bad str -const STR_TOO_LONG: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str}; +const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; +//~^ ERROR it is undefined behavior to use this value +const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); //~^ ERROR it is undefined behavior to use this value // bad str -const STR_LENGTH_PTR: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str}; +const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; //~^ ERROR it is undefined behavior to use this value // bad str in user-defined unsized type -const MY_STR_LENGTH_PTR: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str}; +const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; +//~^ ERROR it is undefined behavior to use this value +const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; //~^ ERROR it is undefined behavior to use this value // invalid UTF-8 -const STR_NO_UTF8: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str }; +const STR_NO_UTF8: &str = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; //~^ ERROR it is undefined behavior to use this value // invalid UTF-8 in user-defined str-like -const MYSTR_NO_UTF8: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str }; +const MYSTR_NO_UTF8: &MyStr = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; //~^ ERROR it is undefined behavior to use this value // # slice // OK -const SLICE_VALID: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.slice}; +const SLICE_VALID: &[u8] = unsafe { mem::transmute((&42u8, 1usize)) }; // bad slice: length uninit -const SLICE_LENGTH_UNINIT: &[u8] = unsafe { SliceTransmute { addr: 42 }.slice}; +const SLICE_LENGTH_UNINIT: &[u8] = unsafe { //~^ ERROR it is undefined behavior to use this value + let uninit_len = MaybeUninit:: { uninit: () }; + mem::transmute((42, uninit_len)) +}; // bad slice: length too big -const SLICE_TOO_LONG: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice}; +const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; //~^ ERROR it is undefined behavior to use this value // bad slice: length not an int -const SLICE_LENGTH_PTR: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice}; +const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; +//~^ ERROR it is undefined behavior to use this value +// bad slice box: length too big +const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; +//~^ ERROR it is undefined behavior to use this value +// bad slice box: length not an int +const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; //~^ ERROR it is undefined behavior to use this value // bad data *inside* the slice -const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }]; +const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; //~^ ERROR it is undefined behavior to use this value // good MySliceBool const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); // bad: sized field is not okay -const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]); +const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); //~^ ERROR it is undefined behavior to use this value // bad: unsized part is not okay -const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]); +const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); //~^ ERROR it is undefined behavior to use this value // # raw slice -const RAW_SLICE_VALID: *const [u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.raw_slice}; // ok -const RAW_SLICE_TOO_LONG: *const [u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.raw_slice}; // ok because raw -const RAW_SLICE_MUCH_TOO_LONG: *const [u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: usize::max_value() } }.raw_slice}; // ok because raw -const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { SliceTransmute { addr: 42 }.raw_slice}; +const RAW_SLICE_VALID: *const [u8] = unsafe { mem::transmute((&42u8, 1usize)) }; // ok +const RAW_SLICE_TOO_LONG: *const [u8] = unsafe { mem::transmute((&42u8, 999usize)) }; // ok because raw +const RAW_SLICE_MUCH_TOO_LONG: *const [u8] = unsafe { mem::transmute((&42u8, usize::MAX)) }; // ok because raw +const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { //~^ ERROR it is undefined behavior to use this value + let uninit_len = MaybeUninit:: { uninit: () }; + mem::transmute((42, uninit_len)) +}; // # trait object // bad trait object -const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust}; +const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) }; //~^ ERROR it is undefined behavior to use this value // bad trait object -const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust}; +const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; //~^ ERROR it is undefined behavior to use this value // bad trait object -const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust}; +const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) }; //~^ ERROR it is undefined behavior to use this value // bad data *inside* the trait object -const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = &unsafe { BoolTransmute { val: 3 }.bl }; +const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; //~^ ERROR it is undefined behavior to use this value // # raw trait object -const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 0 } }.raw_rust}; +const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; //~^ ERROR it is undefined behavior to use this value -const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.raw_rust}; +const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; //~^ ERROR it is undefined behavior to use this value -const RAW_TRAIT_OBJ_CONTENT_INVALID: *const dyn Trait = &unsafe { BoolTransmute { val: 3 }.bl } as *const _; // ok because raw +const RAW_TRAIT_OBJ_CONTENT_INVALID: *const dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) } as *const dyn Trait; // ok because raw // Const eval fails for these, so they need to be statics to error. static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe { - DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 0 } }.rust + mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) //~^ ERROR could not evaluate static initializer }; static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe { - DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust + mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) //~^ ERROR could not evaluate static initializer }; -fn main() { - let _ = RAW_TRAIT_OBJ_VTABLE_NULL; - let _ = RAW_TRAIT_OBJ_VTABLE_INVALID; -} +fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr index ce57d680dc9..80e60dbb58a 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr @@ -1,159 +1,199 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:86:1 + --> $DIR/ub-wide-ptr.rs:32:1 | -LL | const STR_TOO_LONG: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling reference (not entirely in bounds) +LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:89:1 + --> $DIR/ub-wide-ptr.rs:34:1 | -LL | const STR_LENGTH_PTR: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer +LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object at .0 | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:92:1 + --> $DIR/ub-wide-ptr.rs:37:1 | -LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer +LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:96:1 + --> $DIR/ub-wide-ptr.rs:40:1 | -LL | const STR_NO_UTF8: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at . +LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:99:1 + --> $DIR/ub-wide-ptr.rs:42:1 | -LL | const MYSTR_NO_UTF8: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at ..0 +LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:106:1 + --> $DIR/ub-wide-ptr.rs:46:1 | -LL | const SLICE_LENGTH_UNINIT: &[u8] = unsafe { SliceTransmute { addr: 42 }.slice}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered undefined pointer +LL | const STR_NO_UTF8: &str = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at . | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:109:1 + --> $DIR/ub-wide-ptr.rs:49:1 + | +LL | const MYSTR_NO_UTF8: &MyStr = unsafe { mem::transmute::<&[u8], _>(&[0xFF]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at ..0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:56:1 + | +LL | / const SLICE_LENGTH_UNINIT: &[u8] = unsafe { +LL | | +LL | | let uninit_len = MaybeUninit:: { uninit: () }; +LL | | mem::transmute((42, uninit_len)) +LL | | }; + | |__^ type validation failed: encountered undefined pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:62:1 | -LL | const SLICE_TOO_LONG: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling reference (not entirely in bounds) +LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:112:1 + --> $DIR/ub-wide-ptr.rs:65:1 | -LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer +LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:116:1 + --> $DIR/ub-wide-ptr.rs:68:1 | -LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .[0], but expected something less or equal to 1 +LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:122:1 + --> $DIR/ub-wide-ptr.rs:71:1 | -LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at ..0, but expected something less or equal to 1 +LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:125:1 + --> $DIR/ub-wide-ptr.rs:75:1 | -LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at ..1[0], but expected something less or equal to 1 +LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .[0], but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:132:1 + --> $DIR/ub-wide-ptr.rs:81:1 | -LL | const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { SliceTransmute { addr: 42 }.raw_slice}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered undefined pointer +LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at ..0, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:137:1 + --> $DIR/ub-wide-ptr.rs:84:1 | -LL | const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable +LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at ..1[0], but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:140:1 + --> $DIR/ub-wide-ptr.rs:91:1 | -LL | const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable +LL | / const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { +LL | | +LL | | let uninit_len = MaybeUninit:: { uninit: () }; +LL | | mem::transmute((42, uninit_len)) +LL | | }; + | |__^ type validation failed: encountered undefined pointer | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:143:1 + --> $DIR/ub-wide-ptr.rs:99:1 + | +LL | const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:102:1 | -LL | const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable +LL | const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:147:1 + --> $DIR/ub-wide-ptr.rs:105:1 + | +LL | const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:109:1 | -LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = &unsafe { BoolTransmute { val: 3 }.bl }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .., but expected something less or equal to 1 +LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .., but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:151:1 + --> $DIR/ub-wide-ptr.rs:113:1 | -LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 0 } }.raw_rust}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable +LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:153:1 + --> $DIR/ub-wide-ptr.rs:115:1 | -LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.raw_rust}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable +LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:159:5 + --> $DIR/ub-wide-ptr.rs:121:5 | -LL | DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 0 } }.rust - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid use of NULL pointer +LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid use of NULL pointer error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:163:5 + --> $DIR/ub-wide-ptr.rs:125:5 | -LL | DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocation N which has size N +LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N -error: aborting due to 20 previous errors +error: aborting due to 24 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/union-ub.stderr b/src/test/ui/consts/const-eval/union-ub.stderr index fa67bc0d8e7..9d90d6e8548 100644 --- a/src/test/ui/consts/const-eval/union-ub.stderr +++ b/src/test/ui/consts/const-eval/union-ub.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/union-ub.rs:31:1 | LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected something less or equal to 1 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr index c98e206e88c..cede356a6b8 100644 --- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr +++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr @@ -4,7 +4,7 @@ warning: any use of this value will cause an error LL | unsafe { std::mem::transmute(()) } | ^^^^^^^^^^^^^^^^^^^^^^^ | | - | entering unreachable code + | transmuting to uninhabited type | inside call to `foo` at $DIR/validate_uninhabited_zsts.rs:14:26 ... LL | const FOO: [Empty; 3] = [foo(); 3]; @@ -20,7 +20,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/validate_uninhabited_zsts.rs:17:1 | LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Empty at [0] | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const-eval/zst_operand_eval.rs b/src/test/ui/consts/const-eval/zst_operand_eval.rs index 7edb6bd03da..5f7ddf7f758 100644 --- a/src/test/ui/consts/const-eval/zst_operand_eval.rs +++ b/src/test/ui/consts/const-eval/zst_operand_eval.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass static ASSERT: () = [()][!(std::mem::size_of::() == 4) as usize]; diff --git a/src/test/ui/consts/const-int-arithmetic-overflow.rs b/src/test/ui/consts/const-int-arithmetic-overflow.rs index 75dac812f1e..99bbeaafda5 100644 --- a/src/test/ui/consts/const-int-arithmetic-overflow.rs +++ b/src/test/ui/consts/const-int-arithmetic-overflow.rs @@ -8,7 +8,7 @@ const fn add(x: i8, y: i8) -> i8 { x+y } const fn sub(x: i8, y: i8) -> i8 { x-y } const fn mul(x: i8, y: i8) -> i8 { x*y } -// div and rem are always checked, so we cannot test their result in case of oveflow. +// div and rem are always checked, so we cannot test their result in case of overflow. const fn neg(x: i8) -> i8 { -x } fn main() { diff --git a/src/test/ui/consts/const-int-conversion-rpass.rs b/src/test/ui/consts/const-int-conversion-rpass.rs index d52dbbae1e7..6484169dd9a 100644 --- a/src/test/ui/consts/const-int-conversion-rpass.rs +++ b/src/test/ui/consts/const-int-conversion-rpass.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(const_int_conversion)] - const REVERSE: u32 = 0x12345678_u32.reverse_bits(); const FROM_BE_BYTES: i32 = i32::from_be_bytes([0x12, 0x34, 0x56, 0x78]); const FROM_LE_BYTES: i32 = i32::from_le_bytes([0x12, 0x34, 0x56, 0x78]); diff --git a/src/test/ui/consts/const-int-unchecked.stderr b/src/test/ui/consts/const-int-unchecked.stderr index bf31e0b0732..cf70454b6bf 100644 --- a/src/test/ui/consts/const-int-unchecked.stderr +++ b/src/test/ui/consts/const-int-unchecked.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | const SHL_U8: u8 = unsafe { intrinsics::unchecked_shl(5_u8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shl` + | overflowing shift by 8 in `unchecked_shl` | = note: `#[deny(const_err)]` on by default @@ -14,7 +14,7 @@ error: any use of this value will cause an error LL | const SHL_U16: u16 = unsafe { intrinsics::unchecked_shl(5_u16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shl` + | overflowing shift by 16 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:19:31 @@ -22,7 +22,7 @@ error: any use of this value will cause an error LL | const SHL_U32: u32 = unsafe { intrinsics::unchecked_shl(5_u32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shl` + | overflowing shift by 32 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:21:31 @@ -30,7 +30,7 @@ error: any use of this value will cause an error LL | const SHL_U64: u64 = unsafe { intrinsics::unchecked_shl(5_u64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shl` + | overflowing shift by 64 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:23:33 @@ -38,7 +38,7 @@ error: any use of this value will cause an error LL | const SHL_U128: u128 = unsafe { intrinsics::unchecked_shl(5_u128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shl` + | overflowing shift by 128 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:28:29 @@ -46,7 +46,7 @@ error: any use of this value will cause an error LL | const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shl` + | overflowing shift by 8 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:30:31 @@ -54,7 +54,7 @@ error: any use of this value will cause an error LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shl` + | overflowing shift by 16 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:32:31 @@ -62,7 +62,7 @@ error: any use of this value will cause an error LL | const SHL_I32: i32 = unsafe { intrinsics::unchecked_shl(5_i32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shl` + | overflowing shift by 32 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:34:31 @@ -70,7 +70,7 @@ error: any use of this value will cause an error LL | const SHL_I64: i64 = unsafe { intrinsics::unchecked_shl(5_i64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shl` + | overflowing shift by 64 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:36:33 @@ -78,7 +78,7 @@ error: any use of this value will cause an error LL | const SHL_I128: i128 = unsafe { intrinsics::unchecked_shl(5_i128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shl` + | overflowing shift by 128 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:41:33 @@ -86,7 +86,7 @@ error: any use of this value will cause an error LL | const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 255 in `unchecked_shl` + | overflowing shift by 255 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:43:35 @@ -94,7 +94,7 @@ error: any use of this value will cause an error LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65535 in `unchecked_shl` + | overflowing shift by 65535 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:45:35 @@ -102,7 +102,7 @@ error: any use of this value will cause an error LL | const SHL_I32_NEG: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967295 in `unchecked_shl` + | overflowing shift by 4294967295 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:47:35 @@ -110,7 +110,7 @@ error: any use of this value will cause an error LL | const SHL_I64_NEG: i64 = unsafe { intrinsics::unchecked_shl(5_i64, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551615 in `unchecked_shl` + | overflowing shift by 18446744073709551615 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:49:37 @@ -118,7 +118,7 @@ error: any use of this value will cause an error LL | const SHL_I128_NEG: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -1) }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shl` + | overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:55:40 @@ -126,7 +126,7 @@ error: any use of this value will cause an error LL | const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) }; | ---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 250 in `unchecked_shl` + | overflowing shift by 250 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:57:42 @@ -134,7 +134,7 @@ error: any use of this value will cause an error LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65523 in `unchecked_shl` + | overflowing shift by 65523 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:59:42 @@ -142,7 +142,7 @@ error: any use of this value will cause an error LL | const SHL_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -25) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967271 in `unchecked_shl` + | overflowing shift by 4294967271 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:61:42 @@ -150,7 +150,7 @@ error: any use of this value will cause an error LL | const SHL_I64_NEG_RANDOM: i64 = unsafe { intrinsics::unchecked_shl(5_i64, -30) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551586 in `unchecked_shl` + | overflowing shift by 18446744073709551586 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:63:44 @@ -158,7 +158,7 @@ error: any use of this value will cause an error LL | const SHL_I128_NEG_RANDOM: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -93) }; | -------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shl` + | overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:70:29 @@ -166,7 +166,7 @@ error: any use of this value will cause an error LL | const SHR_U8: u8 = unsafe { intrinsics::unchecked_shr(5_u8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shr` + | overflowing shift by 8 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:72:31 @@ -174,7 +174,7 @@ error: any use of this value will cause an error LL | const SHR_U16: u16 = unsafe { intrinsics::unchecked_shr(5_u16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shr` + | overflowing shift by 16 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:74:31 @@ -182,7 +182,7 @@ error: any use of this value will cause an error LL | const SHR_U32: u32 = unsafe { intrinsics::unchecked_shr(5_u32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shr` + | overflowing shift by 32 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:76:31 @@ -190,7 +190,7 @@ error: any use of this value will cause an error LL | const SHR_U64: u64 = unsafe { intrinsics::unchecked_shr(5_u64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shr` + | overflowing shift by 64 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:78:33 @@ -198,7 +198,7 @@ error: any use of this value will cause an error LL | const SHR_U128: u128 = unsafe { intrinsics::unchecked_shr(5_u128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shr` + | overflowing shift by 128 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:83:29 @@ -206,7 +206,7 @@ error: any use of this value will cause an error LL | const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shr` + | overflowing shift by 8 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:85:31 @@ -214,7 +214,7 @@ error: any use of this value will cause an error LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shr` + | overflowing shift by 16 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:87:31 @@ -222,7 +222,7 @@ error: any use of this value will cause an error LL | const SHR_I32: i32 = unsafe { intrinsics::unchecked_shr(5_i32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shr` + | overflowing shift by 32 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:89:31 @@ -230,7 +230,7 @@ error: any use of this value will cause an error LL | const SHR_I64: i64 = unsafe { intrinsics::unchecked_shr(5_i64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shr` + | overflowing shift by 64 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:91:33 @@ -238,7 +238,7 @@ error: any use of this value will cause an error LL | const SHR_I128: i128 = unsafe { intrinsics::unchecked_shr(5_i128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shr` + | overflowing shift by 128 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:96:33 @@ -246,7 +246,7 @@ error: any use of this value will cause an error LL | const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 255 in `unchecked_shr` + | overflowing shift by 255 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:98:35 @@ -254,7 +254,7 @@ error: any use of this value will cause an error LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65535 in `unchecked_shr` + | overflowing shift by 65535 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:100:35 @@ -262,7 +262,7 @@ error: any use of this value will cause an error LL | const SHR_I32_NEG: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967295 in `unchecked_shr` + | overflowing shift by 4294967295 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:102:35 @@ -270,7 +270,7 @@ error: any use of this value will cause an error LL | const SHR_I64_NEG: i64 = unsafe { intrinsics::unchecked_shr(5_i64, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551615 in `unchecked_shr` + | overflowing shift by 18446744073709551615 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:104:37 @@ -278,7 +278,7 @@ error: any use of this value will cause an error LL | const SHR_I128_NEG: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -1) }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shr` + | overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:110:40 @@ -286,7 +286,7 @@ error: any use of this value will cause an error LL | const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) }; | ---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 250 in `unchecked_shr` + | overflowing shift by 250 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:112:42 @@ -294,7 +294,7 @@ error: any use of this value will cause an error LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65523 in `unchecked_shr` + | overflowing shift by 65523 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:114:42 @@ -302,7 +302,7 @@ error: any use of this value will cause an error LL | const SHR_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -25) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967271 in `unchecked_shr` + | overflowing shift by 4294967271 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:116:42 @@ -310,7 +310,7 @@ error: any use of this value will cause an error LL | const SHR_I64_NEG_RANDOM: i64 = unsafe { intrinsics::unchecked_shr(5_i64, -30) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551586 in `unchecked_shr` + | overflowing shift by 18446744073709551586 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:118:44 @@ -318,7 +318,7 @@ error: any use of this value will cause an error LL | const SHR_I128_NEG_RANDOM: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -93) }; | -------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shr` + | overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:123:25 @@ -326,7 +326,7 @@ error: any use of this value will cause an error LL | const _: u16 = unsafe { std::intrinsics::unchecked_add(40000u16, 30000) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_add` + | overflow executing `unchecked_add` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:126:25 @@ -334,7 +334,7 @@ error: any use of this value will cause an error LL | const _: u32 = unsafe { std::intrinsics::unchecked_sub(14u32, 22) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_sub` + | overflow executing `unchecked_sub` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:129:25 @@ -342,7 +342,7 @@ error: any use of this value will cause an error LL | const _: u16 = unsafe { std::intrinsics::unchecked_mul(300u16, 250u16) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_mul` + | overflow executing `unchecked_mul` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:132:25 @@ -358,7 +358,7 @@ error: any use of this value will cause an error LL | const _: i32 = unsafe { std::intrinsics::unchecked_div(i32::min_value(), -1) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_div` + | overflow executing `unchecked_div` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:137:25 @@ -374,7 +374,7 @@ error: any use of this value will cause an error LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::min_value(), -1) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_rem` + | overflow executing `unchecked_rem` error: aborting due to 47 previous errors diff --git a/src/test/ui/consts/const-points-to-static.stderr b/src/test/ui/consts/const-points-to-static.stderr index 8949358e293..f2ca7ff7825 100644 --- a/src/test/ui/consts/const-points-to-static.stderr +++ b/src/test/ui/consts/const-points-to-static.stderr @@ -8,7 +8,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-points-to-static.rs:5:1 | LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to a static variable | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs new file mode 100644 index 00000000000..1ad5134e71c --- /dev/null +++ b/src/test/ui/consts/const_discriminant.rs @@ -0,0 +1,40 @@ +// run-pass +#![feature(const_discriminant)] +#![allow(dead_code)] + +use std::mem::{discriminant, Discriminant}; + +// `discriminant(const_expr)` may get const-propagated. +// As we want to check that const-eval is equal to ordinary exection, +// we wrap `const_expr` with a function which is not const to prevent this. +#[inline(never)] +fn identity(x: T) -> T { x } + +enum Test { + A(u8), + B, + C { a: u8, b: u8 }, +} + +const TEST_A: Discriminant = discriminant(&Test::A(5)); +const TEST_A_OTHER: Discriminant = discriminant(&Test::A(17)); +const TEST_B: Discriminant = discriminant(&Test::B); + +enum Void {} + +enum SingleVariant { + V, + Never(Void), +} + +const TEST_V: Discriminant = discriminant(&SingleVariant::V); + +fn main() { + assert_eq!(TEST_A, TEST_A_OTHER); + assert_eq!(TEST_A, discriminant(identity(&Test::A(17)))); + assert_eq!(TEST_B, discriminant(identity(&Test::B))); + assert_ne!(TEST_A, TEST_B); + assert_ne!(TEST_B, discriminant(identity(&Test::C { a: 42, b: 7 }))); + + assert_eq!(TEST_V, discriminant(identity(&SingleVariant::V))); +} diff --git a/src/test/ui/consts/const_let_eq.rs b/src/test/ui/consts/const_let_eq.rs index a2364c392f2..818819f9ff6 100644 --- a/src/test/ui/consts/const_let_eq.rs +++ b/src/test/ui/consts/const_let_eq.rs @@ -5,6 +5,7 @@ struct Bar { x: T } struct W(u32); struct A { a: u32 } +#[allow(redundant_semicolons)] const fn basics((a,): (u32,)) -> u32 { // Deferred assignment: let b: u32; diff --git a/src/test/ui/consts/const_let_eq_float.rs b/src/test/ui/consts/const_let_eq_float.rs index 0c927a0484d..bc0ef26eb2f 100644 --- a/src/test/ui/consts/const_let_eq_float.rs +++ b/src/test/ui/consts/const_let_eq_float.rs @@ -7,6 +7,7 @@ struct Bar { x: T } struct W(f32); struct A { a: f32 } +#[allow(redundant_semicolons)] const fn basics((a,): (f32,)) -> f32 { // Deferred assignment: let b: f32; diff --git a/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs b/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs new file mode 100644 index 00000000000..4ed908312fb --- /dev/null +++ b/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs @@ -0,0 +1,15 @@ +// check-pass +#![feature(const_eval_limit)] +#![const_eval_limit="1000"] + +const CONSTANT: usize = limit(); + +fn main() { + assert_eq!(CONSTANT, 1764); +} + +const fn limit() -> usize { + let x = 42; + + x * 42 +} diff --git a/src/test/ui/consts/const_limit/const_eval_limit_overflow.rs b/src/test/ui/consts/const_limit/const_eval_limit_overflow.rs new file mode 100644 index 00000000000..1c49593cd53 --- /dev/null +++ b/src/test/ui/consts/const_limit/const_eval_limit_overflow.rs @@ -0,0 +1,15 @@ +#![feature(const_eval_limit)] +#![const_eval_limit="18_446_744_073_709_551_615"] +//~^ ERROR `limit` must be a non-negative integer + +const CONSTANT: usize = limit(); + +fn main() { + assert_eq!(CONSTANT, 1764); +} + +const fn limit() -> usize { + let x = 42; + + x * 42 +} diff --git a/src/test/ui/consts/const_limit/const_eval_limit_overflow.stderr b/src/test/ui/consts/const_limit/const_eval_limit_overflow.stderr new file mode 100644 index 00000000000..7f5d5e6cd4c --- /dev/null +++ b/src/test/ui/consts/const_limit/const_eval_limit_overflow.stderr @@ -0,0 +1,10 @@ +error: `limit` must be a non-negative integer + --> $DIR/const_eval_limit_overflow.rs:2:1 + | +LL | #![const_eval_limit="18_446_744_073_709_551_615"] + | ^^^^^^^^^^^^^^^^^^^^----------------------------^ + | | + | not a valid integer + +error: aborting due to previous error + diff --git a/src/test/ui/consts/const_limit/const_eval_limit_reached.rs b/src/test/ui/consts/const_limit/const_eval_limit_reached.rs new file mode 100644 index 00000000000..d962398d413 --- /dev/null +++ b/src/test/ui/consts/const_limit/const_eval_limit_reached.rs @@ -0,0 +1,21 @@ +// ignore-tidy-linelength +// only-x86_64 +// check-pass +// NOTE: We always compile this test with -Copt-level=0 because higher opt-levels +// optimize away the const function +// compile-flags:-Copt-level=0 +#![feature(const_eval_limit)] +#![const_eval_limit="2"] + +const CONSTANT: usize = limit(); +//~^ WARNING Constant evaluating a complex constant, this might take some time + +fn main() { + assert_eq!(CONSTANT, 1764); +} + +const fn limit() -> usize { //~ WARNING Constant evaluating a complex constant, this might take some time + let x = 42; + + x * 42 +} diff --git a/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr b/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr new file mode 100644 index 00000000000..e0871ff7185 --- /dev/null +++ b/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr @@ -0,0 +1,16 @@ +warning: Constant evaluating a complex constant, this might take some time + --> $DIR/const_eval_limit_reached.rs:17:1 + | +LL | / const fn limit() -> usize { +LL | | let x = 42; +LL | | +LL | | x * 42 +LL | | } + | |_^ + +warning: Constant evaluating a complex constant, this might take some time + --> $DIR/const_eval_limit_reached.rs:10:1 + | +LL | const CONSTANT: usize = limit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.rs b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.rs new file mode 100644 index 00000000000..61119d7511d --- /dev/null +++ b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.rs @@ -0,0 +1,14 @@ +#![const_eval_limit="42"] +//~^ ERROR the `#[const_eval_limit]` attribute is an experimental feature [E0658] + +const CONSTANT: usize = limit(); + +fn main() { + assert_eq!(CONSTANT, 1764); +} + +const fn limit() -> usize { + let x = 42; + + x * 42 +} diff --git a/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.stderr b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.stderr new file mode 100644 index 00000000000..5bd29c7dfd2 --- /dev/null +++ b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[const_eval_limit]` attribute is an experimental feature + --> $DIR/feature-gate-const_eval_limit.rs:1:1 + | +LL | #![const_eval_limit="42"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #67217 for more information + = help: add `#![feature(const_eval_limit)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/dangling-alloc-id-ice.stderr b/src/test/ui/consts/dangling-alloc-id-ice.stderr index bac9f555d27..0e213555052 100644 --- a/src/test/ui/consts/dangling-alloc-id-ice.stderr +++ b/src/test/ui/consts/dangling-alloc-id-ice.stderr @@ -5,7 +5,7 @@ LL | / const FOO: &() = { LL | | let y = (); LL | | unsafe { Foo { y: &y }.long_live_the_unit } LL | | }; - | |__^ type validation failed: encountered dangling pointer in final constant + | |__^ encountered dangling pointer in final constant | = note: `#[deny(const_err)]` on by default diff --git a/src/test/ui/consts/dangling_raw_ptr.stderr b/src/test/ui/consts/dangling_raw_ptr.stderr index 4748be37dff..4d4c2876c45 100644 --- a/src/test/ui/consts/dangling_raw_ptr.stderr +++ b/src/test/ui/consts/dangling_raw_ptr.stderr @@ -5,7 +5,7 @@ LL | / const FOO: *const u32 = { LL | | let x = 42; LL | | &x LL | | }; - | |__^ type validation failed: encountered dangling pointer in final constant + | |__^ encountered dangling pointer in final constant | = note: `#[deny(const_err)]` on by default diff --git a/src/test/ui/consts/issue-63952.stderr b/src/test/ui/consts/issue-63952.stderr index d5ed970fc35..5e85be45b16 100644 --- a/src/test/ui/consts/issue-63952.stderr +++ b/src/test/ui/consts/issue-63952.stderr @@ -8,7 +8,7 @@ LL | | ptr: &42, ... | LL | | .slice LL | | }; - | |__^ invalid slice: total size is bigger than largest supported object + | |__^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/issue-69191-ice-on-uninhabited-enum-field.rs b/src/test/ui/consts/issue-69191-ice-on-uninhabited-enum-field.rs new file mode 100644 index 00000000000..5b7c7be42cf --- /dev/null +++ b/src/test/ui/consts/issue-69191-ice-on-uninhabited-enum-field.rs @@ -0,0 +1,91 @@ +// build-pass +// +// (this is deliberately *not* check-pass; I have confirmed that the bug in +// question does not replicate when one uses `cargo check` alone.) + +pub enum Void {} + +enum UninhabitedUnivariant { + _Variant(Void), +} + +enum UninhabitedMultivariant2 { + _Variant(Void), + _Warriont(Void), +} + +enum UninhabitedMultivariant3 { + _Variant(Void), + _Warriont(Void), + _Worrynot(Void), +} + +#[repr(C)] +enum UninhabitedUnivariantC { + _Variant(Void), +} + +#[repr(i32)] +enum UninhabitedUnivariant32 { + _Variant(Void), +} + +fn main() { + let _seed: UninhabitedUnivariant = None.unwrap(); + match _seed { + UninhabitedUnivariant::_Variant(_x) => {} + } + + let _seed: UninhabitedMultivariant2 = None.unwrap(); + match _seed { + UninhabitedMultivariant2::_Variant(_x) => {} + UninhabitedMultivariant2::_Warriont(_x) => {} + } + + let _seed: UninhabitedMultivariant2 = None.unwrap(); + match _seed { + UninhabitedMultivariant2::_Variant(_x) => {} + _ => {} + } + + let _seed: UninhabitedMultivariant2 = None.unwrap(); + match _seed { + UninhabitedMultivariant2::_Warriont(_x) => {} + _ => {} + } + + let _seed: UninhabitedMultivariant3 = None.unwrap(); + match _seed { + UninhabitedMultivariant3::_Variant(_x) => {} + UninhabitedMultivariant3::_Warriont(_x) => {} + UninhabitedMultivariant3::_Worrynot(_x) => {} + } + + let _seed: UninhabitedMultivariant3 = None.unwrap(); + match _seed { + UninhabitedMultivariant3::_Variant(_x) => {} + _ => {} + } + + let _seed: UninhabitedMultivariant3 = None.unwrap(); + match _seed { + UninhabitedMultivariant3::_Warriont(_x) => {} + _ => {} + } + + let _seed: UninhabitedMultivariant3 = None.unwrap(); + match _seed { + UninhabitedMultivariant3::_Worrynot(_x) => {} + _ => {} + } + + let _seed: UninhabitedUnivariantC = None.unwrap(); + match _seed { + UninhabitedUnivariantC::_Variant(_x) => {} + } + + let _seed: UninhabitedUnivariant32 = None.unwrap(); + match _seed { + UninhabitedUnivariant32::_Variant(_x) => {} + } +} diff --git a/src/test/ui/consts/min_const_fn/allow_const_fn_ptr_feature_gate.stderr b/src/test/ui/consts/min_const_fn/allow_const_fn_ptr_feature_gate.stderr index c8d060f5cdc..7794cc7583d 100644 --- a/src/test/ui/consts/min_const_fn/allow_const_fn_ptr_feature_gate.stderr +++ b/src/test/ui/consts/min_const_fn/allow_const_fn_ptr_feature_gate.stderr @@ -4,7 +4,6 @@ error[E0658]: internal implementation detail LL | #[rustc_allow_const_fn_ptr] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr b/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr index da00c49963e..c7e902132e9 100644 --- a/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr +++ b/src/test/ui/consts/miri_unleashed/abi-mismatch.stderr @@ -16,7 +16,7 @@ error: any use of this value will cause an error LL | my_fn(); | ^^^^^^^ | | - | tried to call a function with ABI C using caller ABI Rust + | calling a function with ABI C using caller ABI Rust | inside call to `call_rust_fn` at $DIR/abi-mismatch.rs:13:17 ... LL | const VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr index 15e13942481..ad777cfe8ea 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static.stderr @@ -48,7 +48,7 @@ LL | | static FOO: AtomicUsize = AtomicUsize::new(0); LL | | unsafe { &*(&FOO as *const _ as *const usize) } LL | | LL | | }; - | |__^ constant accesses static + | |__^ type validation failed: encountered a reference pointing to a static variable | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. @@ -97,7 +97,7 @@ LL | | static FOO: usize = 0; LL | | &FOO LL | | LL | | }; - | |__^ constant accesses static + | |__^ type validation failed: encountered a reference pointing to a static variable | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/miri_unleashed/mutable_const.stderr b/src/test/ui/consts/miri_unleashed/mutable_const.stderr index 86f27784701..8456e8ec687 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_const.stderr +++ b/src/test/ui/consts/miri_unleashed/mutable_const.stderr @@ -11,7 +11,7 @@ LL | / const MUTATING_BEHIND_RAW: () = { LL | | // Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time. LL | | unsafe { LL | | *MUTABLE_BEHIND_RAW = 99 - | | ^^^^^^^^^^^^^^^^^^^^^^^^ tried to modify constant memory + | | ^^^^^^^^^^^^^^^^^^^^^^^^ writing to alloc1 which is read-only LL | | } LL | | }; | |__- diff --git a/src/test/ui/consts/miri_unleashed/mutable_const2.stderr b/src/test/ui/consts/miri_unleashed/mutable_const2.stderr index 3eb8e0ec182..dda9ddf1f48 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_const2.stderr +++ b/src/test/ui/consts/miri_unleashed/mutable_const2.stderr @@ -10,7 +10,7 @@ error: internal compiler error: mutable allocation in constant LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:355:17 +thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:360:17 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr index 24da983cf08..21753074007 100644 --- a/src/test/ui/consts/offset_from_ub.stderr +++ b/src/test/ui/consts/offset_from_ub.stderr @@ -26,7 +26,7 @@ error: any use of this value will cause an error LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | a memory access tried to interpret some bytes as a pointer + | unable to turn bytes into a pointer | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:28:14 | ::: $DIR/offset_from_ub.rs:26:1 @@ -43,7 +43,7 @@ error: any use of this value will cause an error LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | exact_div: 1 cannot be divided by 2 without remainder + | exact_div: 1isize cannot be divided by 2isize without remainder | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:36:14 | ::: $DIR/offset_from_ub.rs:31:1 @@ -81,7 +81,7 @@ error: any use of this value will cause an error LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | a memory access tried to interpret some bytes as a pointer + | unable to turn bytes into a pointer | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:49:14 | ::: $DIR/offset_from_ub.rs:45:1 diff --git a/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr b/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr index 296a55ef160..5a477714596 100644 --- a/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr +++ b/src/test/ui/consts/transmute-size-mismatch-before-typeck.stderr @@ -21,7 +21,7 @@ LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^ | = note: source type: `usize` (word size) - = note: target type: `&'static [u8]` (2 * word size) + = note: target type: `&[u8]` (2 * word size) error: could not evaluate constant pattern --> $DIR/transmute-size-mismatch-before-typeck.rs:10:9 diff --git a/src/test/ui/consts/validate_never_arrays.stderr b/src/test/ui/consts/validate_never_arrays.stderr index cb995b8216f..77f0a2ebd40 100644 --- a/src/test/ui/consts/validate_never_arrays.stderr +++ b/src/test/ui/consts/validate_never_arrays.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/validate_never_arrays.rs:3:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at . + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .[0] | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. @@ -10,7 +10,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/validate_never_arrays.rs:6:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .[0] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .[0] | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. @@ -18,7 +18,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/validate_never_arrays.rs:7:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .[0] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .[0] | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/definition-reachable/nested-fn.rs b/src/test/ui/definition-reachable/nested-fn.rs index b596ba8936a..b665b049f32 100644 --- a/src/test/ui/definition-reachable/nested-fn.rs +++ b/src/test/ui/definition-reachable/nested-fn.rs @@ -1,4 +1,4 @@ -// Check that functions visible to macros through paths with >2 segements are +// Check that functions visible to macros through paths with >2 segments are // considered reachable // aux-build:field-method-macro.rs diff --git a/src/test/ui/did_you_mean/issue-40006.stderr b/src/test/ui/did_you_mean/issue-40006.stderr index 613d7eee594..76e87a3749c 100644 --- a/src/test/ui/did_you_mean/issue-40006.stderr +++ b/src/test/ui/did_you_mean/issue-40006.stderr @@ -32,11 +32,11 @@ LL | X() {} LL | } | - the item list ends here -error: expected `[`, found `#` +error: expected one of `!` or `[`, found `#` --> $DIR/issue-40006.rs:19:17 | LL | fn xxx() { ### } - | ^ expected `[` + | ^ expected one of `!` or `[` error: expected one of `!` or `::`, found `=` --> $DIR/issue-40006.rs:22:7 diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.rs b/src/test/ui/directory_ownership/macro-expanded-mod.rs index 376c1a9cd66..9cb159603a8 100644 --- a/src/test/ui/directory_ownership/macro-expanded-mod.rs +++ b/src/test/ui/directory_ownership/macro-expanded-mod.rs @@ -1,7 +1,9 @@ // Test that macro-expanded non-inline modules behave correctly macro_rules! mod_decl { - ($i:ident) => { mod $i; } //~ ERROR Cannot declare a non-inline module inside a block + ($i:ident) => { + mod $i; //~ ERROR Cannot declare a non-inline module inside a block + }; } mod macro_expanded_mod_helper { diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.stderr b/src/test/ui/directory_ownership/macro-expanded-mod.stderr index c7780c869d6..f90419247c9 100644 --- a/src/test/ui/directory_ownership/macro-expanded-mod.stderr +++ b/src/test/ui/directory_ownership/macro-expanded-mod.stderr @@ -1,8 +1,8 @@ error: Cannot declare a non-inline module inside a block unless it has a path attribute - --> $DIR/macro-expanded-mod.rs:4:25 + --> $DIR/macro-expanded-mod.rs:5:9 | -LL | ($i:ident) => { mod $i; } - | ^^ +LL | mod $i; + | ^^^^^^^ ... LL | mod_decl!(foo); | --------------- in this macro invocation diff --git a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr index 46acc7e66d8..d034942ca5d 100644 --- a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr +++ b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr @@ -1,8 +1,8 @@ error: Cannot declare a non-inline module inside a block unless it has a path attribute - --> $DIR/non-inline-mod-restriction.rs:4:9 + --> $DIR/non-inline-mod-restriction.rs:4:5 | LL | mod foo; - | ^^^ + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/drop/dynamic-drop.rs b/src/test/ui/drop/dynamic-drop.rs index 451686d9ae2..d31736f142c 100644 --- a/src/test/ui/drop/dynamic-drop.rs +++ b/src/test/ui/drop/dynamic-drop.rs @@ -3,6 +3,7 @@ #![feature(generators, generator_trait, untagged_unions)] #![feature(move_ref_pattern)] +#![feature(bindings_after_at)] #![allow(unused_assignments)] #![allow(unused_variables)] @@ -291,6 +292,44 @@ fn subslice_mixed_min_lengths(a: &Allocator, c: i32) { } } +fn bindings_after_at_dynamic_init_move(a: &Allocator, c: bool) { + let foo = if c { Some(a.alloc()) } else { None }; + let _x; + + if let bar @ Some(_) = foo { + _x = bar; + } +} + +fn bindings_after_at_dynamic_init_ref(a: &Allocator, c: bool) { + let foo = if c { Some(a.alloc()) } else { None }; + let _x; + + if let bar @ Some(_baz) = &foo { + _x = bar; + } +} + +fn bindings_after_at_dynamic_drop_move(a: &Allocator, c: bool) { + let foo = if c { Some(a.alloc()) } else { None }; + + if let bar @ Some(_) = foo { + bar + } else { + None + }; +} + +fn bindings_after_at_dynamic_drop_ref(a: &Allocator, c: bool) { + let foo = if c { Some(a.alloc()) } else { None }; + + if let bar @ Some(_baz) = &foo { + bar + } else { + &None + }; +} + fn move_ref_pattern(a: &Allocator) { let mut tup = (a.alloc(), a.alloc(), a.alloc(), a.alloc()); let (ref _a, ref mut _b, _c, mut _d) = tup; @@ -471,5 +510,14 @@ fn main() { run_test(|a| panic_after_init_temp(a)); run_test(|a| panic_after_init_by_loop(a)); + run_test(|a| bindings_after_at_dynamic_init_move(a, true)); + run_test(|a| bindings_after_at_dynamic_init_move(a, false)); + run_test(|a| bindings_after_at_dynamic_init_ref(a, true)); + run_test(|a| bindings_after_at_dynamic_init_ref(a, false)); + run_test(|a| bindings_after_at_dynamic_drop_move(a, true)); + run_test(|a| bindings_after_at_dynamic_drop_move(a, false)); + run_test(|a| bindings_after_at_dynamic_drop_ref(a, true)); + run_test(|a| bindings_after_at_dynamic_drop_ref(a, false)); + run_test_nopanic(|a| union1(a)); } diff --git a/src/test/ui/editions/edition-keywords-2015-2018-expansion.stderr b/src/test/ui/editions/edition-keywords-2015-2018-expansion.stderr index f44f81fce71..88a1f5dc673 100644 --- a/src/test/ui/editions/edition-keywords-2015-2018-expansion.stderr +++ b/src/test/ui/editions/edition-keywords-2015-2018-expansion.stderr @@ -7,8 +7,8 @@ LL | produces_async! {} = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: you can escape reserved keywords to use them as identifiers | -LL | () => (pub fn r#async () { }) - | ^^^^^^^ +LL | () => (pub fn r#async() {}) + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr index 22a7495ca23..e12d1a48463 100644 --- a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr +++ b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr @@ -33,10 +33,10 @@ LL | r#async = consumes_async_raw!(async); | ^^^^^ no rules expected this token in macro call error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` - --> <::edition_kw_macro_2015::passes_ident macros>:1:22 + --> $DIR/auxiliary/edition-kw-macro-2015.rs:27:23 | -LL | ($ i : ident) => ($ i) - | ^ expected one of `move`, `|`, or `||` +LL | ($i: ident) => ($i) + | ^ expected one of `move`, `|`, or `||` | ::: $DIR/edition-keywords-2018-2015-parsing.rs:16:8 | diff --git a/src/test/ui/editions/edition-keywords-2018-2018-expansion.stderr b/src/test/ui/editions/edition-keywords-2018-2018-expansion.stderr index a8fc58fc0cb..5eaa1d03a4a 100644 --- a/src/test/ui/editions/edition-keywords-2018-2018-expansion.stderr +++ b/src/test/ui/editions/edition-keywords-2018-2018-expansion.stderr @@ -7,8 +7,8 @@ LL | produces_async! {} = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: you can escape reserved keywords to use them as identifiers | -LL | () => (pub fn r#async () { }) - | ^^^^^^^ +LL | () => (pub fn r#async() {}) + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr index 7488fcc2e58..110165fc077 100644 --- a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr +++ b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr @@ -33,10 +33,10 @@ LL | r#async = consumes_async_raw!(async); | ^^^^^ no rules expected this token in macro call error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` - --> <::edition_kw_macro_2018::passes_ident macros>:1:22 + --> $DIR/auxiliary/edition-kw-macro-2018.rs:27:23 | -LL | ($ i : ident) => ($ i) - | ^ expected one of `move`, `|`, or `||` +LL | ($i: ident) => ($i) + | ^ expected one of `move`, `|`, or `||` | ::: $DIR/edition-keywords-2018-2018-parsing.rs:16:8 | diff --git a/src/test/ui/enum/enum-discrim-too-small2.stderr b/src/test/ui/enum/enum-discrim-too-small2.stderr index 3aa88df29f1..fadf6ab86b4 100644 --- a/src/test/ui/enum/enum-discrim-too-small2.stderr +++ b/src/test/ui/enum/enum-discrim-too-small2.stderr @@ -9,24 +9,31 @@ note: the lint level is defined here | LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `223` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i16` --> $DIR/enum-discrim-too-small2.rs:15:12 | LL | Ci16 = 55555, | ^^^^^ + | + = note: the literal `55555` does not fit into the type `i16` whose range is `-32768..=32767` error: literal out of range for `i32` --> $DIR/enum-discrim-too-small2.rs:22:12 | LL | Ci32 = 3_000_000_000, | ^^^^^^^^^^^^^ + | + = note: the literal `3_000_000_000` does not fit into the type `i32` whose range is `-2147483648..=2147483647` error: literal out of range for `i64` --> $DIR/enum-discrim-too-small2.rs:29:12 | LL | Ci64 = 9223372036854775809, | ^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `9223372036854775809` does not fit into the type `i64` whose range is `-9223372036854775808..=9223372036854775807` error: aborting due to 4 previous errors diff --git a/src/test/ui/error-codes/E0121.stderr b/src/test/ui/error-codes/E0121.stderr index 5a5c6b40c5a..ad854837ae5 100644 --- a/src/test/ui/error-codes/E0121.stderr +++ b/src/test/ui/error-codes/E0121.stderr @@ -14,7 +14,7 @@ LL | static BAR: _ = "test"; | ^ | | | not allowed in type signatures - | help: replace `_` with the correct type: `&'static str` + | help: replace `_` with the correct type: `&str` error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0197.stderr b/src/test/ui/error-codes/E0197.stderr index 51ed9c83bc9..35e1042649e 100644 --- a/src/test/ui/error-codes/E0197.stderr +++ b/src/test/ui/error-codes/E0197.stderr @@ -1,8 +1,8 @@ error[E0197]: inherent impls cannot be unsafe - --> $DIR/E0197.rs:3:1 + --> $DIR/E0197.rs:3:13 | LL | unsafe impl Foo { } - | ------^^^^^^^^^^^^^ + | ------ ^^^ inherent impl for this type | | | unsafe because of this diff --git a/src/test/ui/error-codes/E0198.stderr b/src/test/ui/error-codes/E0198.stderr index 90e8b4abd12..bb2efefb427 100644 --- a/src/test/ui/error-codes/E0198.stderr +++ b/src/test/ui/error-codes/E0198.stderr @@ -1,9 +1,10 @@ error[E0198]: negative impls cannot be unsafe - --> $DIR/E0198.rs:5:1 + --> $DIR/E0198.rs:5:13 | LL | unsafe impl !Send for Foo { } - | ------^^^^^^^^^^^^^^^^^^^^^^^ - | | + | ------ -^^^^ + | | | + | | negative because of this | unsafe because of this error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0396-fixed.stderr b/src/test/ui/error-codes/E0396-fixed.stderr index 7222f87da24..68505552562 100644 --- a/src/test/ui/error-codes/E0396-fixed.stderr +++ b/src/test/ui/error-codes/E0396-fixed.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | const VALUE: u8 = unsafe { *REG_ADDR }; | ---------------------------^^^^^^^^^--- | | - | a memory access tried to interpret some bytes as a pointer + | unable to turn bytes into a pointer | = note: `#[deny(const_err)]` on by default diff --git a/src/test/ui/error-codes/E0583.stderr b/src/test/ui/error-codes/E0583.stderr index ef7a48bc8a4..dbe70035595 100644 --- a/src/test/ui/error-codes/E0583.stderr +++ b/src/test/ui/error-codes/E0583.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `module_that_doesnt_exist` - --> $DIR/E0583.rs:1:5 + --> $DIR/E0583.rs:1:1 | LL | mod module_that_doesnt_exist; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: name the file either module_that_doesnt_exist.rs or module_that_doesnt_exist/mod.rs inside the directory "$DIR" + = help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs" error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0624.rs b/src/test/ui/error-codes/E0624.rs index 45f72a565ca..4c68b70fb16 100644 --- a/src/test/ui/error-codes/E0624.rs +++ b/src/test/ui/error-codes/E0624.rs @@ -8,5 +8,5 @@ fn method(&self) {} fn main() { let foo = inner::Foo; - foo.method(); //~ ERROR method `method` is private [E0624] + foo.method(); //~ ERROR associated function `method` is private [E0624] } diff --git a/src/test/ui/error-codes/E0624.stderr b/src/test/ui/error-codes/E0624.stderr index 01ac24cfbbe..65256c8dd2d 100644 --- a/src/test/ui/error-codes/E0624.stderr +++ b/src/test/ui/error-codes/E0624.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `method` is private +error[E0624]: associated function `method` is private --> $DIR/E0624.rs:11:9 | LL | foo.method(); diff --git a/src/test/ui/error-codes/E0719.stderr b/src/test/ui/error-codes/E0719.stderr index a046fbfc3d0..0e4bbf083ba 100644 --- a/src/test/ui/error-codes/E0719.stderr +++ b/src/test/ui/error-codes/E0719.stderr @@ -16,3 +16,4 @@ LL | fn test() -> Box> { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0719`. diff --git a/src/test/ui/explain.stdout b/src/test/ui/explain.stdout index 9ea56271f59..c50c46ee564 100644 --- a/src/test/ui/explain.stdout +++ b/src/test/ui/explain.stdout @@ -46,7 +46,7 @@ This pattern should be rewritten. There are a few possible ways to do this: - change the original fn declaration to match the expected signature, and do the cast in the fn body (the preferred option) -- cast the fn item fo a fn pointer before calling transmute, as shown here: +- cast the fn item of a fn pointer before calling transmute, as shown here: ``` let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_)); diff --git a/src/test/ui/explore-issue-38412.stderr b/src/test/ui/explore-issue-38412.stderr index 4c80989951a..94a2cfe013d 100644 --- a/src/test/ui/explore-issue-38412.stderr +++ b/src/test/ui/explore-issue-38412.stderr @@ -79,19 +79,19 @@ LL | r.unstable_undeclared(); = note: see issue #38412 for more information = help: add `#![feature(unstable_undeclared)]` to the crate attributes to enable -error[E0624]: method `pub_crate` is private +error[E0624]: associated function `pub_crate` is private --> $DIR/explore-issue-38412.rs:50:7 | LL | r.pub_crate(); | ^^^^^^^^^ -error[E0624]: method `pub_mod` is private +error[E0624]: associated function `pub_mod` is private --> $DIR/explore-issue-38412.rs:51:7 | LL | r.pub_mod(); | ^^^^^^^ -error[E0624]: method `private` is private +error[E0624]: associated function `private` is private --> $DIR/explore-issue-38412.rs:52:7 | LL | r.private(); @@ -115,19 +115,19 @@ LL | t.unstable_undeclared(); = note: see issue #38412 for more information = help: add `#![feature(unstable_undeclared)]` to the crate attributes to enable -error[E0624]: method `pub_crate` is private +error[E0624]: associated function `pub_crate` is private --> $DIR/explore-issue-38412.rs:63:7 | LL | t.pub_crate(); | ^^^^^^^^^ -error[E0624]: method `pub_mod` is private +error[E0624]: associated function `pub_mod` is private --> $DIR/explore-issue-38412.rs:64:7 | LL | t.pub_mod(); | ^^^^^^^ -error[E0624]: method `private` is private +error[E0624]: associated function `private` is private --> $DIR/explore-issue-38412.rs:65:7 | LL | t.private(); diff --git a/src/test/ui/feature-gates/feature-gate-doc_spotlight.rs b/src/test/ui/feature-gates/feature-gate-doc_spotlight.rs deleted file mode 100644 index 452b45b3445..00000000000 --- a/src/test/ui/feature-gates/feature-gate-doc_spotlight.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[doc(spotlight)] //~ ERROR: `#[doc(spotlight)]` is experimental -trait SomeTrait {} - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-doc_spotlight.stderr b/src/test/ui/feature-gates/feature-gate-doc_spotlight.stderr deleted file mode 100644 index 010d74054a4..00000000000 --- a/src/test/ui/feature-gates/feature-gate-doc_spotlight.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: `#[doc(spotlight)]` is experimental - --> $DIR/feature-gate-doc_spotlight.rs:1:1 - | -LL | #[doc(spotlight)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #45040 for more information - = help: add `#![feature(doc_spotlight)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-no-debug-2.rs b/src/test/ui/feature-gates/feature-gate-no-debug-2.rs deleted file mode 100644 index b399bd2cc0f..00000000000 --- a/src/test/ui/feature-gates/feature-gate-no-debug-2.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![deny(deprecated)] -#![feature(no_debug)] - -#[no_debug] //~ ERROR use of deprecated attribute `no_debug` -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-no-debug-2.stderr b/src/test/ui/feature-gates/feature-gate-no-debug-2.stderr deleted file mode 100644 index 9a6f898f2a5..00000000000 --- a/src/test/ui/feature-gates/feature-gate-no-debug-2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: use of deprecated attribute `no_debug`: the `#[no_debug]` attribute was an experimental feature that has been deprecated due to lack of demand. See https://github.com/rust-lang/rust/issues/29721 - --> $DIR/feature-gate-no-debug-2.rs:4:1 - | -LL | #[no_debug] - | ^^^^^^^^^^^ help: remove this attribute - | -note: the lint level is defined here - --> $DIR/feature-gate-no-debug-2.rs:1:9 - | -LL | #![deny(deprecated)] - | ^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/feature-gates/feature-gate-no-debug.rs b/src/test/ui/feature-gates/feature-gate-no-debug.rs deleted file mode 100644 index a472c4c7663..00000000000 --- a/src/test/ui/feature-gates/feature-gate-no-debug.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(deprecated)] - -#[no_debug] //~ ERROR the `#[no_debug]` attribute was -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-no-debug.stderr b/src/test/ui/feature-gates/feature-gate-no-debug.stderr deleted file mode 100644 index e146d643bcb..00000000000 --- a/src/test/ui/feature-gates/feature-gate-no-debug.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: the `#[no_debug]` attribute was an experimental feature that has been deprecated due to lack of demand - --> $DIR/feature-gate-no-debug.rs:3:1 - | -LL | #[no_debug] - | ^^^^^^^^^^^ - | - = note: see issue #29721 for more information - = help: add `#![feature(no_debug)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr b/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr index d29c373a33c..490d29ad8a3 100644 --- a/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr +++ b/src/test/ui/feature-gates/feature-gate-optin-builtin-traits.stderr @@ -8,10 +8,10 @@ LL | auto trait AutoDummyTrait {} = help: add `#![feature(optin_builtin_traits)]` to the crate attributes to enable error[E0658]: negative trait bounds are not yet fully implemented; use marker types for now - --> $DIR/feature-gate-optin-builtin-traits.rs:9:1 + --> $DIR/feature-gate-optin-builtin-traits.rs:9:6 | LL | impl !AutoDummyTrait for DummyStruct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = note: see issue #13231 for more information = help: add `#![feature(optin_builtin_traits)]` to the crate attributes to enable diff --git a/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr b/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr index 082d897c01d..82dec1fd4cf 100644 --- a/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr +++ b/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr @@ -4,7 +4,6 @@ error[E0658]: the `#[rustc_variance]` attribute is just used for rustc unit test LL | #[rustc_variance] | ^^^^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error[E0658]: the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable @@ -13,7 +12,6 @@ error[E0658]: the `#[rustc_error]` attribute is just used for rustc unit tests a LL | #[rustc_error] | ^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error[E0658]: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable niche optimizations in libcore and will never be stable @@ -22,7 +20,6 @@ error[E0658]: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just u LL | #[rustc_nonnull_optimization_guaranteed] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: aborting due to 3 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr b/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr index 58f8b4e7035..1e039f17a0d 100644 --- a/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr +++ b/src/test/ui/feature-gates/feature-gate-rustc-attrs.stderr @@ -4,7 +4,6 @@ error[E0658]: attributes starting with `rustc` are reserved for use by the `rust LL | #[rustc::unknown] | ^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: expected attribute, found macro `rustc::unknown` @@ -19,7 +18,6 @@ error[E0658]: attributes starting with `rustc` are reserved for use by the `rust LL | #[unknown::rustc] | ^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: expected attribute, found macro `unknown::rustc` @@ -34,7 +32,6 @@ error[E0658]: attributes starting with `rustc` are reserved for use by the `rust LL | #[rustc_unknown] | ^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: cannot find attribute `rustc_unknown` in this scope @@ -49,7 +46,6 @@ error[E0658]: the `#[rustc_dummy]` attribute is just used for rustc unit tests a LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: aborting due to 7 previous errors diff --git a/src/test/ui/fn-in-pat.rs b/src/test/ui/fn-in-pat.rs index b83252012b8..2d7c86b8666 100644 --- a/src/test/ui/fn-in-pat.rs +++ b/src/test/ui/fn-in-pat.rs @@ -8,7 +8,7 @@ fn hof(_: F) where F: FnMut(()) {} fn ice() { hof(|c| match c { - A::new() => (), //~ ERROR expected tuple struct or tuple variant, found method + A::new() => (), //~ ERROR expected tuple struct or tuple variant, found associated function _ => () }) } diff --git a/src/test/ui/fn-in-pat.stderr b/src/test/ui/fn-in-pat.stderr index 5d6632f2fc2..2482d632695 100644 --- a/src/test/ui/fn-in-pat.stderr +++ b/src/test/ui/fn-in-pat.stderr @@ -1,4 +1,4 @@ -error[E0164]: expected tuple struct or tuple variant, found method `A::new` +error[E0164]: expected tuple struct or tuple variant, found associated function `A::new` --> $DIR/fn-in-pat.rs:11:9 | LL | A::new() => (), diff --git a/src/test/ui/generator-yielding-or-returning-itself.rs b/src/test/ui/generator-yielding-or-returning-itself.rs deleted file mode 100644 index 30788e3c186..00000000000 --- a/src/test/ui/generator-yielding-or-returning-itself.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![feature(generator_trait)] -#![feature(generators)] - -// Test that we cannot create a generator that returns a value of its -// own type. - -use std::ops::Generator; - -pub fn want_cyclic_generator_return(_: T) - where T: Generator -{ -} - -fn supply_cyclic_generator_return() { - want_cyclic_generator_return(|| { - //~^ ERROR type mismatch - if false { yield None.unwrap(); } - None.unwrap() - }) -} - -pub fn want_cyclic_generator_yield(_: T) - where T: Generator -{ -} - -fn supply_cyclic_generator_yield() { - want_cyclic_generator_yield(|| { - //~^ ERROR type mismatch - if false { yield None.unwrap(); } - None.unwrap() - }) -} - -fn main() { } diff --git a/src/test/ui/generator-yielding-or-returning-itself.stderr b/src/test/ui/generator-yielding-or-returning-itself.stderr deleted file mode 100644 index fc8064d8225..00000000000 --- a/src/test/ui/generator-yielding-or-returning-itself.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _] as std::ops::Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _]` - --> $DIR/generator-yielding-or-returning-itself.rs:15:5 - | -LL | pub fn want_cyclic_generator_return(_: T) - | ---------------------------- -LL | where T: Generator - | ---------- required by this bound in `want_cyclic_generator_return` -... -LL | want_cyclic_generator_return(|| { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size - | - = note: closures cannot capture themselves or take themselves as argument; - this error may be the result of a recent compiler bug-fix, - see issue #46062 - for more information - -error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _] as std::ops::Generator>::Yield == [generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _]` - --> $DIR/generator-yielding-or-returning-itself.rs:28:5 - | -LL | pub fn want_cyclic_generator_yield(_: T) - | --------------------------- -LL | where T: Generator - | --------- required by this bound in `want_cyclic_generator_yield` -... -LL | want_cyclic_generator_yield(|| { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size - | - = note: closures cannot capture themselves or take themselves as argument; - this error may be the result of a recent compiler bug-fix, - see issue #46062 - for more information - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/generator/discriminant.rs b/src/test/ui/generator/discriminant.rs new file mode 100644 index 00000000000..8a0f8a380ab --- /dev/null +++ b/src/test/ui/generator/discriminant.rs @@ -0,0 +1,134 @@ +//! Tests that generator discriminant sizes and ranges are chosen optimally and that they are +//! reflected in the output of `mem::discriminant`. + +// run-pass + +#![feature(generators, generator_trait, core_intrinsics)] + +use std::intrinsics::discriminant_value; +use std::marker::Unpin; +use std::mem::size_of_val; +use std::{cmp, ops::*}; + +macro_rules! yield25 { + ($e:expr) => { + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + }; +} + +/// Yields 250 times. +macro_rules! yield250 { + () => { + yield250!(()) + }; + + ($e:expr) => { + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + }; +} + +fn cycle(gen: impl Generator<()> + Unpin, expected_max_discr: u64) { + let mut gen = Box::pin(gen); + let mut max_discr = 0; + loop { + max_discr = cmp::max(max_discr, discriminant_value(gen.as_mut().get_mut())); + match gen.as_mut().resume(()) { + GeneratorState::Yielded(_) => {} + GeneratorState::Complete(_) => { + assert_eq!(max_discr, expected_max_discr); + return; + } + } + } +} + +fn main() { + // Has only one invalid discr. value. + let gen_u8_tiny_niche = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + } + }; + + // Uses all values in the u8 discriminant. + let gen_u8_full = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + yield; // 256 + } + }; + + // Barely needs a u16 discriminant. + let gen_u16 = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + yield; // 256 + yield; // 257 + } + }; + + assert_eq!(size_of_val(&gen_u8_tiny_niche()), 1); + assert_eq!(size_of_val(&Some(gen_u8_tiny_niche())), 1); // uses niche + assert_eq!(size_of_val(&Some(Some(gen_u8_tiny_niche()))), 2); // cannot use niche anymore + assert_eq!(size_of_val(&gen_u8_full()), 1); + assert_eq!(size_of_val(&Some(gen_u8_full())), 2); // cannot use niche + assert_eq!(size_of_val(&gen_u16()), 2); + assert_eq!(size_of_val(&Some(gen_u16())), 2); // uses niche + + cycle(gen_u8_tiny_niche(), 254); + cycle(gen_u8_full(), 255); + cycle(gen_u16(), 256); +} diff --git a/src/test/ui/generator/generator-yielding-or-returning-itself.rs b/src/test/ui/generator/generator-yielding-or-returning-itself.rs new file mode 100644 index 00000000000..30788e3c186 --- /dev/null +++ b/src/test/ui/generator/generator-yielding-or-returning-itself.rs @@ -0,0 +1,35 @@ +#![feature(generator_trait)] +#![feature(generators)] + +// Test that we cannot create a generator that returns a value of its +// own type. + +use std::ops::Generator; + +pub fn want_cyclic_generator_return(_: T) + where T: Generator +{ +} + +fn supply_cyclic_generator_return() { + want_cyclic_generator_return(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +pub fn want_cyclic_generator_yield(_: T) + where T: Generator +{ +} + +fn supply_cyclic_generator_yield() { + want_cyclic_generator_yield(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +fn main() { } diff --git a/src/test/ui/generator/generator-yielding-or-returning-itself.stderr b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr new file mode 100644 index 00000000000..fc8064d8225 --- /dev/null +++ b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr @@ -0,0 +1,35 @@ +error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _] as std::ops::Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _]` + --> $DIR/generator-yielding-or-returning-itself.rs:15:5 + | +LL | pub fn want_cyclic_generator_return(_: T) + | ---------------------------- +LL | where T: Generator + | ---------- required by this bound in `want_cyclic_generator_return` +... +LL | want_cyclic_generator_return(|| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size + | + = note: closures cannot capture themselves or take themselves as argument; + this error may be the result of a recent compiler bug-fix, + see issue #46062 + for more information + +error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _] as std::ops::Generator>::Yield == [generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _]` + --> $DIR/generator-yielding-or-returning-itself.rs:28:5 + | +LL | pub fn want_cyclic_generator_yield(_: T) + | --------------------------- +LL | where T: Generator + | --------- required by this bound in `want_cyclic_generator_yield` +... +LL | want_cyclic_generator_yield(|| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size + | + = note: closures cannot capture themselves or take themselves as argument; + this error may be the result of a recent compiler bug-fix, + see issue #46062 + for more information + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/generator/issue-64620-yield-array-element.rs b/src/test/ui/generator/issue-64620-yield-array-element.rs new file mode 100644 index 00000000000..2cbe8f51614 --- /dev/null +++ b/src/test/ui/generator/issue-64620-yield-array-element.rs @@ -0,0 +1,9 @@ +// Regression test for #64620 + +#![feature(generators)] + +pub fn crash(arr: [usize; 1]) { + yield arr[0]; //~ ERROR: yield expression outside of generator literal +} + +fn main() {} diff --git a/src/test/ui/generator/issue-64620-yield-array-element.stderr b/src/test/ui/generator/issue-64620-yield-array-element.stderr new file mode 100644 index 00000000000..48383c2ed08 --- /dev/null +++ b/src/test/ui/generator/issue-64620-yield-array-element.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of generator literal + --> $DIR/issue-64620-yield-array-element.rs:6:5 + | +LL | yield arr[0]; + | ^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0627`. diff --git a/src/test/ui/generator/issue-69039.rs b/src/test/ui/generator/issue-69039.rs index 60004f3b0ae..ccc141860aa 100644 --- a/src/test/ui/generator/issue-69039.rs +++ b/src/test/ui/generator/issue-69039.rs @@ -4,11 +4,15 @@ use std::ops::{Generator, GeneratorState}; +fn mkstr(my_name: String, my_mood: String) -> String { + format!("{} is {}", my_name.trim(), my_mood.trim()) +} + fn my_scenario() -> impl Generator { |_arg: String| { let my_name = yield "What is your name?"; let my_mood = yield "How are you feeling?"; - format!("{} is {}", my_name.trim(), my_mood.trim()) + mkstr(my_name, my_mood) } } diff --git a/src/test/ui/generator/resume-arg-size.rs b/src/test/ui/generator/resume-arg-size.rs new file mode 100644 index 00000000000..4f08ac0702b --- /dev/null +++ b/src/test/ui/generator/resume-arg-size.rs @@ -0,0 +1,28 @@ +#![feature(generators)] + +// run-pass + +use std::mem::size_of_val; + +fn main() { + // Generator taking a `Copy`able resume arg. + let gen_copy = |mut x: usize| { + loop { + drop(x); + x = yield; + } + }; + + // Generator taking a non-`Copy` resume arg. + let gen_move = |mut x: Box| { + loop { + drop(x); + x = yield; + } + }; + + // Neither of these generators have the resume arg live across the `yield`, so they should be + // 4 Bytes in size (only storing the discriminant) + assert_eq!(size_of_val(&gen_copy), 1); + assert_eq!(size_of_val(&gen_move), 1); +} diff --git a/src/test/ui/generator/size-moved-locals.rs b/src/test/ui/generator/size-moved-locals.rs index 2864fbb2f3c..74c60d98154 100644 --- a/src/test/ui/generator/size-moved-locals.rs +++ b/src/test/ui/generator/size-moved-locals.rs @@ -58,7 +58,7 @@ fn overlap_move_points() -> impl Generator { } } -fn overlap_x_and_y() -> impl Generator{ +fn overlap_x_and_y() -> impl Generator { static || { let x = Foo([0; FOO_SIZE]); yield; @@ -70,8 +70,8 @@ fn overlap_x_and_y() -> impl Generator{ } fn main() { - assert_eq!(1028, std::mem::size_of_val(&move_before_yield())); - assert_eq!(1032, std::mem::size_of_val(&move_before_yield_with_noop())); - assert_eq!(2056, std::mem::size_of_val(&overlap_move_points())); - assert_eq!(1032, std::mem::size_of_val(&overlap_x_and_y())); + assert_eq!(1025, std::mem::size_of_val(&move_before_yield())); + assert_eq!(1026, std::mem::size_of_val(&move_before_yield_with_noop())); + assert_eq!(2051, std::mem::size_of_val(&overlap_move_points())); + assert_eq!(1026, std::mem::size_of_val(&overlap_x_and_y())); } diff --git a/src/test/ui/generator/too-many-parameters.stderr b/src/test/ui/generator/too-many-parameters.stderr index a297ee43de9..22d40db3f26 100644 --- a/src/test/ui/generator/too-many-parameters.stderr +++ b/src/test/ui/generator/too-many-parameters.stderr @@ -6,3 +6,4 @@ LL | |(), ()| { error: aborting due to previous error +For more information about this error, try `rustc --explain E0628`. diff --git a/src/test/ui/generic-associated-types/empty_generics.rs b/src/test/ui/generic-associated-types/empty_generics.rs index 522e23ca43d..6eb25a92f34 100644 --- a/src/test/ui/generic-associated-types/empty_generics.rs +++ b/src/test/ui/generic-associated-types/empty_generics.rs @@ -3,7 +3,7 @@ trait Foo { type Bar<,>; - //~^ ERROR expected one of `>`, `const`, identifier, or lifetime, found `,` + //~^ ERROR expected one of `#`, `>`, `const`, identifier, or lifetime, found `,` } fn main() {} diff --git a/src/test/ui/generic-associated-types/empty_generics.stderr b/src/test/ui/generic-associated-types/empty_generics.stderr index bd5708d8140..1599d683ad6 100644 --- a/src/test/ui/generic-associated-types/empty_generics.stderr +++ b/src/test/ui/generic-associated-types/empty_generics.stderr @@ -1,10 +1,10 @@ -error: expected one of `>`, `const`, identifier, or lifetime, found `,` +error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,` --> $DIR/empty_generics.rs:5:14 | LL | trait Foo { | - while parsing this item list starting here LL | type Bar<,>; - | ^ expected one of `>`, `const`, identifier, or lifetime + | ^ expected one of `#`, `>`, `const`, identifier, or lifetime LL | LL | } | - the item list ends here diff --git a/src/test/ui/hygiene/macro-metavars-legacy.rs b/src/test/ui/hygiene/macro-metavars-legacy.rs new file mode 100644 index 00000000000..09070f0f561 --- /dev/null +++ b/src/test/ui/hygiene/macro-metavars-legacy.rs @@ -0,0 +1,29 @@ +// Ensure macro metavariables are compared with legacy hygiene + +#![feature(rustc_attrs)] + +// run-pass + +macro_rules! make_mac { + ( $($dollar:tt $arg:ident),+ ) => { + macro_rules! mac { + ( $($dollar $arg : ident),+ ) => { + $( $dollar $arg )-+ + } + } + } +} + +macro_rules! show_hygiene { + ( $dollar:tt $arg:ident ) => { + make_mac!($dollar $arg, $dollar arg); + } +} + +show_hygiene!( $arg ); + +fn main() { + let x = 5; + let y = 3; + assert_eq!(2, mac!(x, y)); +} diff --git a/src/test/ui/hygiene/macro-metavars-transparent.rs b/src/test/ui/hygiene/macro-metavars-transparent.rs new file mode 100644 index 00000000000..e475b5728a0 --- /dev/null +++ b/src/test/ui/hygiene/macro-metavars-transparent.rs @@ -0,0 +1,24 @@ +// Ensure macro metavariables are not compared without removing transparent +// marks. + +#![feature(rustc_attrs)] + +// run-pass + +#[rustc_macro_transparency = "transparent"] +macro_rules! k { + ($($s:tt)*) => { + macro_rules! m { + ($y:tt) => { + $($s)* + } + } + } +} + +k!(1 + $y); + +fn main() { + let x = 2; + assert_eq!(3, m!(x)); +} diff --git a/src/test/ui/if-attrs/bad-cfg.rs b/src/test/ui/if-attrs/bad-cfg.rs new file mode 100644 index 00000000000..3f84929a00e --- /dev/null +++ b/src/test/ui/if-attrs/bad-cfg.rs @@ -0,0 +1,5 @@ +#![feature(stmt_expr_attributes)] + +fn main() { + let _ = #[cfg(FALSE)] if true {}; //~ ERROR removing an expression +} diff --git a/src/test/ui/if-attrs/bad-cfg.stderr b/src/test/ui/if-attrs/bad-cfg.stderr new file mode 100644 index 00000000000..8a2890886a1 --- /dev/null +++ b/src/test/ui/if-attrs/bad-cfg.stderr @@ -0,0 +1,8 @@ +error: removing an expression is not supported in this position + --> $DIR/bad-cfg.rs:4:13 + | +LL | let _ = #[cfg(FALSE)] if true {}; + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/if-attrs/builtin-if-attr.rs b/src/test/ui/if-attrs/builtin-if-attr.rs new file mode 100644 index 00000000000..7e290661501 --- /dev/null +++ b/src/test/ui/if-attrs/builtin-if-attr.rs @@ -0,0 +1,12 @@ +// check-pass + +fn main() { + #[allow(unused_variables)] + if true { + let a = 1; + } else if false { + let b = 1; + } else { + let c = 1; + } +} diff --git a/src/test/ui/if-attrs/cfg-false-if-attr.rs b/src/test/ui/if-attrs/cfg-false-if-attr.rs new file mode 100644 index 00000000000..1f77a1bb342 --- /dev/null +++ b/src/test/ui/if-attrs/cfg-false-if-attr.rs @@ -0,0 +1,43 @@ +// check-pass + +#[cfg(FALSE)] +fn simple_attr() { + #[attr] if true {} + #[allow_warnings] if true {} +} + +#[cfg(FALSE)] +fn if_else_chain() { + #[first_attr] if true { + } else if false { + } else { + } +} + +#[cfg(FALSE)] +fn if_let() { + #[attr] if let Some(_) = Some(true) {} +} + +fn bar() { + #[cfg(FALSE)] + if true { + let x: () = true; // Should not error due to the #[cfg(FALSE)] + } + + #[cfg_attr(not(unset_attr), cfg(FALSE))] + if true { + let a: () = true; // Should not error due to the applied #[cfg(FALSE)] + } +} + +macro_rules! custom_macro { + ($expr:expr) => {} +} + +custom_macro! { + #[attr] if true {} +} + + +fn main() {} diff --git a/src/test/ui/if-attrs/else-attrs.rs b/src/test/ui/if-attrs/else-attrs.rs new file mode 100644 index 00000000000..85da7cf6bb8 --- /dev/null +++ b/src/test/ui/if-attrs/else-attrs.rs @@ -0,0 +1,25 @@ +#[cfg(FALSE)] +fn if_else_parse_error() { + if true { + } #[attr] else if false { //~ ERROR expected + } +} + +#[cfg(FALSE)] +fn else_attr_ifparse_error() { + if true { + } else #[attr] if false { //~ ERROR outer attributes are not allowed + } else { + } +} + +#[cfg(FALSE)] +fn else_parse_error() { + if true { + } else if false { + } #[attr] else { //~ ERROR expected + } +} + +fn main() { +} diff --git a/src/test/ui/if-attrs/else-attrs.stderr b/src/test/ui/if-attrs/else-attrs.stderr new file mode 100644 index 00000000000..2733377054d --- /dev/null +++ b/src/test/ui/if-attrs/else-attrs.stderr @@ -0,0 +1,26 @@ +error: expected expression, found keyword `else` + --> $DIR/else-attrs.rs:4:15 + | +LL | } #[attr] else if false { + | ^^^^ expected expression + +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/else-attrs.rs:11:12 + | +LL | } else #[attr] if false { + | _______----_^^^^^^^_- + | | | | + | | | help: remove the attributes + | | the branch belongs to this `else` +LL | | } else { +LL | | } + | |_____- the attributes are attached to this branch + +error: expected expression, found keyword `else` + --> $DIR/else-attrs.rs:20:15 + | +LL | } #[attr] else { + | ^^^^ expected expression + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/if-attrs/gate-whole-expr.rs b/src/test/ui/if-attrs/gate-whole-expr.rs new file mode 100644 index 00000000000..63772d54b53 --- /dev/null +++ b/src/test/ui/if-attrs/gate-whole-expr.rs @@ -0,0 +1,15 @@ +// run-pass + +fn main() { + let x = 1; + + #[cfg(FALSE)] + if false { + x = 2; + } else if true { + x = 3; + } else { + x = 4; + } + assert_eq!(x, 1); +} diff --git a/src/test/ui/if-attrs/let-chains-attr.rs b/src/test/ui/if-attrs/let-chains-attr.rs new file mode 100644 index 00000000000..5237a9ff396 --- /dev/null +++ b/src/test/ui/if-attrs/let-chains-attr.rs @@ -0,0 +1,13 @@ +// check-pass + +#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete + +#[cfg(FALSE)] +fn foo() { + #[attr] + if let Some(_) = Some(true) && let Ok(_) = Ok(1) { + } else if let Some(false) = Some(true) { + } +} + +fn main() {} diff --git a/src/test/ui/if-attrs/let-chains-attr.stderr b/src/test/ui/if-attrs/let-chains-attr.stderr new file mode 100644 index 00000000000..a6c91bb9203 --- /dev/null +++ b/src/test/ui/if-attrs/let-chains-attr.stderr @@ -0,0 +1,8 @@ +warning: the feature `let_chains` is incomplete and may cause the compiler to crash + --> $DIR/let-chains-attr.rs:3:12 + | +LL | #![feature(let_chains)] + | ^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + diff --git a/src/test/ui/if-attrs/stmt-expr-gated.rs b/src/test/ui/if-attrs/stmt-expr-gated.rs new file mode 100644 index 00000000000..38599c8e67c --- /dev/null +++ b/src/test/ui/if-attrs/stmt-expr-gated.rs @@ -0,0 +1,6 @@ +fn main() { + let _ = #[deny(warnings)] if true { //~ ERROR attributes on expressions + } else if false { + } else { + }; +} diff --git a/src/test/ui/if-attrs/stmt-expr-gated.stderr b/src/test/ui/if-attrs/stmt-expr-gated.stderr new file mode 100644 index 00000000000..47dac39a9ae --- /dev/null +++ b/src/test/ui/if-attrs/stmt-expr-gated.stderr @@ -0,0 +1,12 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/stmt-expr-gated.rs:2:13 + | +LL | let _ = #[deny(warnings)] if true { + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/impl-trait/issue-57200.rs b/src/test/ui/impl-trait/issue-57200.rs new file mode 100644 index 00000000000..e0c71d1ac9a --- /dev/null +++ b/src/test/ui/impl-trait/issue-57200.rs @@ -0,0 +1,15 @@ +// Regression test for #57200 +// FIXME: The error is temporary hack, we'll revisit here at some point. + +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] + +fn bug<'a, 'b, T>() +where + 'a: 'b, +{ + let f: impl Fn(&'a T) -> &'b T = |x| x; + //~^ ERROR: lifetimes in impl Trait types in bindings are not currently supported +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-57200.stderr b/src/test/ui/impl-trait/issue-57200.stderr new file mode 100644 index 00000000000..b44f332d58c --- /dev/null +++ b/src/test/ui/impl-trait/issue-57200.stderr @@ -0,0 +1,8 @@ +error: lifetimes in impl Trait types in bindings are not currently supported + --> $DIR/issue-57200.rs:11:12 + | +LL | let f: impl Fn(&'a T) -> &'b T = |x| x; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/issue-57201.rs b/src/test/ui/impl-trait/issue-57201.rs new file mode 100644 index 00000000000..c1a98d8897b --- /dev/null +++ b/src/test/ui/impl-trait/issue-57201.rs @@ -0,0 +1,15 @@ +// Regression test for #57201 +// FIXME: The error is temporary hack, we'll revisit here at some point. + +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] + +fn bug<'a, 'b, T>() +where + 'a: 'b, +{ + let f: &impl Fn(&'a T) -> &'b T = &|x| x; + //~^ ERROR: lifetimes in impl Trait types in bindings are not currently supported +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-57201.stderr b/src/test/ui/impl-trait/issue-57201.stderr new file mode 100644 index 00000000000..462b17bf45e --- /dev/null +++ b/src/test/ui/impl-trait/issue-57201.stderr @@ -0,0 +1,8 @@ +error: lifetimes in impl Trait types in bindings are not currently supported + --> $DIR/issue-57201.rs:11:13 + | +LL | let f: &impl Fn(&'a T) -> &'b T = &|x| x; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/issue-60473.rs b/src/test/ui/impl-trait/issue-60473.rs new file mode 100644 index 00000000000..50cf0c8c6d6 --- /dev/null +++ b/src/test/ui/impl-trait/issue-60473.rs @@ -0,0 +1,17 @@ +// Regression test for #60473 + +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] + +struct A<'a>(&'a ()); + +trait Trait { +} + +impl Trait for () { +} + +fn main() { + let x: impl Trait = (); // FIXME: The error doesn't seem correct. + //~^ ERROR: opaque type expands to a recursive type +} diff --git a/src/test/ui/impl-trait/issue-60473.stderr b/src/test/ui/impl-trait/issue-60473.stderr new file mode 100644 index 00000000000..2d95be4e52c --- /dev/null +++ b/src/test/ui/impl-trait/issue-60473.stderr @@ -0,0 +1,11 @@ +error[E0720]: opaque type expands to a recursive type + --> $DIR/issue-60473.rs:15:12 + | +LL | let x: impl Trait = (); // FIXME: The error doesn't seem correct. + | ^^^^^^^^^^^^^ expands to a recursive type + | + = note: type resolves to itself + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/src/test/ui/impl-trait/issue-67166.rs b/src/test/ui/impl-trait/issue-67166.rs new file mode 100644 index 00000000000..de7433a9bfc --- /dev/null +++ b/src/test/ui/impl-trait/issue-67166.rs @@ -0,0 +1,11 @@ +// Regression test for #67166 + +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] + +pub fn run() { + let _foo: Box = Box::new(()); // FIXME: The error doesn't much make sense. + //~^ ERROR: opaque type expands to a recursive type +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-67166.stderr b/src/test/ui/impl-trait/issue-67166.stderr new file mode 100644 index 00000000000..56cba3cff0b --- /dev/null +++ b/src/test/ui/impl-trait/issue-67166.stderr @@ -0,0 +1,11 @@ +error[E0720]: opaque type expands to a recursive type + --> $DIR/issue-67166.rs:7:19 + | +LL | let _foo: Box = Box::new(()); // FIXME: The error doesn't much make sense. + | ^^^^^^^^^^^^^^ expands to a recursive type + | + = note: type resolves to itself + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs index 1c3b5ac7613..00f3490991b 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs @@ -12,7 +12,7 @@ impl<'a> LifetimeTrait<'a> for &'a i32 {} fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } //~^ ERROR cannot infer an appropriate lifetime -// Tests that a closure type contianing 'b cannot be returned from a type where +// Tests that a closure type containing 'b cannot be returned from a type where // only 'a was expected. fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { //~^ ERROR lifetime mismatch diff --git a/src/test/ui/imports/issue-55884-2.stderr b/src/test/ui/imports/issue-55884-2.stderr index f16d2adb365..490c08446b5 100644 --- a/src/test/ui/imports/issue-55884-2.stderr +++ b/src/test/ui/imports/issue-55884-2.stderr @@ -4,11 +4,26 @@ error[E0603]: struct import `ParseOptions` is private LL | pub use parser::ParseOptions; | ^^^^^^^^^^^^ this struct import is private | -note: the struct import `ParseOptions` is defined here +note: the struct import `ParseOptions` is defined here... --> $DIR/issue-55884-2.rs:9:9 | LL | use ParseOptions; | ^^^^^^^^^^^^ +note: ...and refers to the struct import `ParseOptions` which is defined here... + --> $DIR/issue-55884-2.rs:12:9 + | +LL | pub use parser::ParseOptions; + | ^^^^^^^^^^^^^^^^^^^^ consider importing it directly +note: ...and refers to the struct import `ParseOptions` which is defined here... + --> $DIR/issue-55884-2.rs:6:13 + | +LL | pub use options::*; + | ^^^^^^^^^^ consider importing it directly +note: ...and refers to the struct `ParseOptions` which is defined here + --> $DIR/issue-55884-2.rs:2:5 + | +LL | pub struct ParseOptions {} + | ^^^^^^^^^^^^^^^^^^^^^^^ consider importing it directly error: aborting due to previous error diff --git a/src/test/ui/imports/reexports.stderr b/src/test/ui/imports/reexports.stderr index 7b0d63574ec..d63fbc7ec67 100644 --- a/src/test/ui/imports/reexports.stderr +++ b/src/test/ui/imports/reexports.stderr @@ -16,11 +16,16 @@ error[E0603]: module import `foo` is private LL | use b::a::foo::S; | ^^^ this module import is private | -note: the module import `foo` is defined here +note: the module import `foo` is defined here... --> $DIR/reexports.rs:21:17 | LL | pub use super::foo; // This is OK since the value `foo` is visible enough. | ^^^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/reexports.rs:16:5 + | +LL | mod foo { + | ^^^^^^^ error[E0603]: module import `foo` is private --> $DIR/reexports.rs:34:15 @@ -28,11 +33,16 @@ error[E0603]: module import `foo` is private LL | use b::b::foo::S as T; | ^^^ this module import is private | -note: the module import `foo` is defined here +note: the module import `foo` is defined here... --> $DIR/reexports.rs:26:17 | LL | pub use super::*; // This is also OK since the value `foo` is visible enough. | ^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/reexports.rs:16:5 + | +LL | mod foo { + | ^^^^^^^ warning: glob import doesn't reexport anything because no candidate is public enough --> $DIR/reexports.rs:9:17 diff --git a/src/test/ui/init-large-type.rs b/src/test/ui/init-large-type.rs index a304fc9356b..ce905572f2a 100644 --- a/src/test/ui/init-large-type.rs +++ b/src/test/ui/init-large-type.rs @@ -1,3 +1,4 @@ +// compile-flags: -O // run-pass #![allow(unused_must_use)] @@ -10,17 +11,13 @@ #![feature(intrinsics)] -use std::thread; - -extern "rust-intrinsic" { - pub fn init() -> T; -} +use std::{mem, thread}; const SIZE: usize = 1024 * 1024; fn main() { // do the test in a new thread to avoid (spurious?) stack overflows thread::spawn(|| { - let _memory: [u8; SIZE] = unsafe { init() }; + let _memory: [u8; SIZE] = unsafe { mem::zeroed() }; }).join(); } diff --git a/src/test/ui/init-unsafe.rs b/src/test/ui/init-unsafe.rs deleted file mode 100644 index 3d65cfc2340..00000000000 --- a/src/test/ui/init-unsafe.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![allow(deprecated)] -#![feature(core_intrinsics)] - -use std::intrinsics::{init}; - -// Test that the `init` intrinsic is really unsafe -pub fn main() { - let stuff = init::(); //~ ERROR call to unsafe function is unsafe -} diff --git a/src/test/ui/init-unsafe.stderr b/src/test/ui/init-unsafe.stderr deleted file mode 100644 index e1126316af3..00000000000 --- a/src/test/ui/init-unsafe.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/init-unsafe.rs:8:17 - | -LL | let stuff = init::(); - | ^^^^^^^^^^^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/intrinsics/intrinsic-move-val.rs b/src/test/ui/intrinsics/intrinsic-move-val.rs index 75b4ec365fe..b672f1ed26e 100644 --- a/src/test/ui/intrinsics/intrinsic-move-val.rs +++ b/src/test/ui/intrinsics/intrinsic-move-val.rs @@ -5,7 +5,6 @@ mod rusti { extern "rust-intrinsic" { - pub fn init() -> T; pub fn move_val_init(dst: *mut T, src: T); } } @@ -15,17 +14,17 @@ pub fn main() { // sanity check check_drops_state(0, None); - let mut x: Box = box D(1); - assert_eq!(x.0, 1); + let mut x: Option> = Some(box D(1)); + assert_eq!(x.as_ref().unwrap().0, 1); // A normal overwrite, to demonstrate `check_drops_state`. - x = box D(2); + x = Some(box D(2)); // At this point, one destructor has run, because the // overwrite of `x` drops its initial value. check_drops_state(1, Some(1)); - let mut y: Box = rusti::init(); + let mut y: Option> = std::mem::zeroed(); // An initial binding does not overwrite anything. check_drops_state(1, Some(1)); @@ -51,9 +50,9 @@ pub fn main() { // during such a destructor call. We do so after the end of // this scope. - assert_eq!(y.0, 2); - y.0 = 3; - assert_eq!(y.0, 3); + assert_eq!(y.as_ref().unwrap().0, 2); + y.as_mut().unwrap().0 = 3; + assert_eq!(y.as_ref().unwrap().0, 3); check_drops_state(1, Some(1)); } diff --git a/src/test/ui/intrinsics/intrinsic-uninit.rs b/src/test/ui/intrinsics/intrinsic-uninit.rs deleted file mode 100644 index 9555efb639b..00000000000 --- a/src/test/ui/intrinsics/intrinsic-uninit.rs +++ /dev/null @@ -1,13 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 - -#![feature(intrinsics)] - -mod rusti { - extern "rust-intrinsic" { - pub fn uninit() -> T; - } -} -pub fn main() { - let _a : isize = unsafe {rusti::uninit()}; -} diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs new file mode 100644 index 00000000000..02f8ecaa4ee --- /dev/null +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -0,0 +1,170 @@ +// run-pass +// ignore-wasm32-bare compiled with panic=abort by default + +// This test checks panic emitted from `mem::{uninitialized,zeroed}`. + +#![feature(never_type)] +#![allow(deprecated, invalid_value)] + +use std::{ + mem::{self, MaybeUninit, ManuallyDrop}, + panic, + ptr::NonNull, + num, +}; + +#[allow(dead_code)] +struct Foo { + x: u8, + y: !, +} + +enum Bar {} + +#[allow(dead_code)] +enum OneVariant { Variant(i32) } + +// An enum with ScalarPair layout +#[allow(dead_code)] +enum LR { + Left(i64), + Right(i64), +} +#[allow(dead_code, non_camel_case_types)] +enum LR_NonZero { + Left(num::NonZeroI64), + Right(num::NonZeroI64), +} + +fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { + let err = panic::catch_unwind(op).err(); + assert_eq!( + err.as_ref().and_then(|a| a.downcast_ref::()).map(|s| &**s), + Some(msg) + ); +} + +fn main() { + unsafe { + // Uninhabited types + test_panic_msg( + || mem::uninitialized::(), + "attempted to instantiate uninhabited type `!`" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to instantiate uninhabited type `!`" + ); + test_panic_msg( + || MaybeUninit::::uninit().assume_init(), + "attempted to instantiate uninhabited type `!`" + ); + + test_panic_msg( + || mem::uninitialized::(), + "attempted to instantiate uninhabited type `Foo`" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to instantiate uninhabited type `Foo`" + ); + test_panic_msg( + || MaybeUninit::::uninit().assume_init(), + "attempted to instantiate uninhabited type `Foo`" + ); + + test_panic_msg( + || mem::uninitialized::(), + "attempted to instantiate uninhabited type `Bar`" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to instantiate uninhabited type `Bar`" + ); + test_panic_msg( + || MaybeUninit::::uninit().assume_init(), + "attempted to instantiate uninhabited type `Bar`" + ); + + // Types that do not like zero-initialziation + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `fn()` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to zero-initialize type `fn()`, which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::<*const dyn Send>(), + "attempted to leave type `*const dyn std::marker::Send` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::zeroed::<*const dyn Send>(), + "attempted to zero-initialize type `*const dyn std::marker::Send`, which is invalid" + ); + + /* FIXME(#66151) we conservatively do not error here yet. + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `LR_NonZero` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to zero-initialize type `LR_NonZero`, which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::>(), + "attempted to leave type `std::mem::ManuallyDrop` uninitialized, \ + which is invalid" + ); + test_panic_msg( + || mem::zeroed::>(), + "attempted to zero-initialize type `std::mem::ManuallyDrop`, \ + which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::<(NonNull, u32, u32)>(), + "attempted to leave type `(std::ptr::NonNull, u32, u32)` uninitialized, \ + which is invalid" + ); + test_panic_msg( + || mem::zeroed::<(NonNull, u32, u32)>(), + "attempted to zero-initialize type `(std::ptr::NonNull, u32, u32)`, \ + which is invalid" + ); + */ + + // Types that can be zero, but not uninit. + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `bool` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `LR` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::uninitialized::>(), + "attempted to leave type `std::mem::ManuallyDrop` uninitialized, which is invalid" + ); + + // Some things that should work. + let _val = mem::zeroed::(); + let _val = mem::zeroed::(); + let _val = mem::zeroed::>(); + let _val = mem::zeroed::(); + let _val = mem::zeroed::>(); + let _val = mem::zeroed::>>(); + let _val = mem::uninitialized::>(); + + // These are UB because they have not been officially blessed, but we await the resolution + // of before doing + // anything about that. + let _val = mem::uninitialized::(); + let _val = mem::uninitialized::<*const ()>(); + } +} diff --git a/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr b/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr index c95df5b4534..52296042eb4 100644 --- a/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr +++ b/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `baz` - --> $DIR/auxiliary/foo/bar.rs:1:9 + --> $DIR/auxiliary/foo/bar.rs:1:1 | LL | pub mod baz; - | ^^^ + | ^^^^^^^^^^^^ | - = help: name the file either bar/baz.rs or bar/baz/mod.rs inside the directory "$DIR/auxiliary/foo" + = help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs" error: aborting due to previous error diff --git a/src/test/ui/issues/auxiliary/issue-69725.rs b/src/test/ui/issues/auxiliary/issue-69725.rs new file mode 100644 index 00000000000..13606e498ef --- /dev/null +++ b/src/test/ui/issues/auxiliary/issue-69725.rs @@ -0,0 +1,8 @@ +#[derive(Clone)] +pub struct Struct(A); + +impl Struct { + pub fn new() -> Self { + todo!() + } +} diff --git a/src/test/ui/issues/issue-16683.nll.stderr b/src/test/ui/issues/issue-16683.nll.stderr index f76e7a4e44f..b82b0b552e2 100644 --- a/src/test/ui/issues/issue-16683.nll.stderr +++ b/src/test/ui/issues/issue-16683.nll.stderr @@ -1,10 +1,10 @@ -error[E0521]: borrowed data escapes outside of method +error[E0521]: borrowed data escapes outside of associated function --> $DIR/issue-16683.rs:4:9 | LL | fn b(&self) { - | ----- `self` is a reference that is only valid in the method body + | ----- `self` is a reference that is only valid in the associated function body LL | self.a(); - | ^^^^^^^^ `self` escapes the method body here + | ^^^^^^^^ `self` escapes the associated function body here error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17758.nll.stderr b/src/test/ui/issues/issue-17758.nll.stderr index 92e21f4dc17..23557b4d956 100644 --- a/src/test/ui/issues/issue-17758.nll.stderr +++ b/src/test/ui/issues/issue-17758.nll.stderr @@ -1,10 +1,10 @@ -error[E0521]: borrowed data escapes outside of method +error[E0521]: borrowed data escapes outside of associated function --> $DIR/issue-17758.rs:7:9 | LL | fn bar(&self) { - | ----- `self` is a reference that is only valid in the method body + | ----- `self` is a reference that is only valid in the associated function body LL | self.foo(); - | ^^^^^^^^^^ `self` escapes the method body here + | ^^^^^^^^^^ `self` escapes the associated function body here error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20616-8.rs b/src/test/ui/issues/issue-20616-8.rs index c9e8b61e50b..3ceb58d1252 100644 --- a/src/test/ui/issues/issue-20616-8.rs +++ b/src/test/ui/issues/issue-20616-8.rs @@ -29,7 +29,7 @@ type Type_8<'a,,> = &'a (); -//~^ error: expected one of `>`, `const`, identifier, or lifetime, found `,` +//~^ error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,` //type Type_9 = Box; // error: expected identifier, found `,` diff --git a/src/test/ui/issues/issue-20616-8.stderr b/src/test/ui/issues/issue-20616-8.stderr index 479469634c5..e9f37e50fff 100644 --- a/src/test/ui/issues/issue-20616-8.stderr +++ b/src/test/ui/issues/issue-20616-8.stderr @@ -1,8 +1,8 @@ -error: expected one of `>`, `const`, identifier, or lifetime, found `,` +error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,` --> $DIR/issue-20616-8.rs:31:16 | LL | type Type_8<'a,,> = &'a (); - | ^ expected one of `>`, `const`, identifier, or lifetime + | ^ expected one of `#`, `>`, `const`, identifier, or lifetime error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20616-9.rs b/src/test/ui/issues/issue-20616-9.rs index 1c509f26fd6..7f84284481e 100644 --- a/src/test/ui/issues/issue-20616-9.rs +++ b/src/test/ui/issues/issue-20616-9.rs @@ -32,4 +32,4 @@ type Type_9 = Box; -//~^ error: expected one of `>`, `const`, identifier, or lifetime, found `,` +//~^ error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,` diff --git a/src/test/ui/issues/issue-20616-9.stderr b/src/test/ui/issues/issue-20616-9.stderr index b7e3322b7aa..dc309d1bce1 100644 --- a/src/test/ui/issues/issue-20616-9.stderr +++ b/src/test/ui/issues/issue-20616-9.stderr @@ -1,8 +1,8 @@ -error: expected one of `>`, `const`, identifier, or lifetime, found `,` +error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,` --> $DIR/issue-20616-9.rs:34:15 | LL | type Type_9 = Box; - | ^ expected one of `>`, `const`, identifier, or lifetime + | ^ expected one of `#`, `>`, `const`, identifier, or lifetime error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21174.stderr b/src/test/ui/issues/issue-21174.stderr index 5ac5a8665bc..09402c3d814 100644 --- a/src/test/ui/issues/issue-21174.stderr +++ b/src/test/ui/issues/issue-21174.stderr @@ -4,8 +4,8 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | let new: T::B = unsafe { std::mem::transmute(value) }; | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `>::A` (size can vary because of ::A) - = note: target type: `>::B` (size can vary because of ::B) + = note: source type: `::A` (this type does not have a fixed size) + = note: target type: `::B` (this type does not have a fixed size) error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21202.rs b/src/test/ui/issues/issue-21202.rs index 2c5f1394449..f62de7ce7db 100644 --- a/src/test/ui/issues/issue-21202.rs +++ b/src/test/ui/issues/issue-21202.rs @@ -8,7 +8,7 @@ mod B { use crate1::A::Foo; fn bar(f: Foo) { Foo::foo(&f); - //~^ ERROR: method `foo` is private + //~^ ERROR: associated function `foo` is private } } diff --git a/src/test/ui/issues/issue-21202.stderr b/src/test/ui/issues/issue-21202.stderr index d19e42d8d9c..18669add205 100644 --- a/src/test/ui/issues/issue-21202.stderr +++ b/src/test/ui/issues/issue-21202.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `foo` is private +error[E0624]: associated function `foo` is private --> $DIR/issue-21202.rs:10:9 | LL | Foo::foo(&f); diff --git a/src/test/ui/issues/issue-23080-2.rs b/src/test/ui/issues/issue-23080-2.rs index 319aa2a5cce..d20bb4bd907 100644 --- a/src/test/ui/issues/issue-23080-2.rs +++ b/src/test/ui/issues/issue-23080-2.rs @@ -3,8 +3,7 @@ #![feature(optin_builtin_traits)] unsafe auto trait Trait { -//~^ ERROR E0380 - type Output; + type Output; //~ ERROR E0380 } fn call_method(x: T) {} diff --git a/src/test/ui/issues/issue-23080-2.stderr b/src/test/ui/issues/issue-23080-2.stderr index 1103de0d910..fcd1ecfa982 100644 --- a/src/test/ui/issues/issue-23080-2.stderr +++ b/src/test/ui/issues/issue-23080-2.stderr @@ -1,11 +1,10 @@ error[E0380]: auto traits cannot have methods or associated items - --> $DIR/issue-23080-2.rs:5:1 + --> $DIR/issue-23080-2.rs:6:10 | -LL | / unsafe auto trait Trait { -LL | | -LL | | type Output; -LL | | } - | |_^ +LL | unsafe auto trait Trait { + | ----- auto trait cannot have items +LL | type Output; + | ^^^^^^ error[E0275]: overflow evaluating the requirement `<() as Trait>::Output` | diff --git a/src/test/ui/issues/issue-23080.rs b/src/test/ui/issues/issue-23080.rs index fdfee698144..fa5c35316bc 100644 --- a/src/test/ui/issues/issue-23080.rs +++ b/src/test/ui/issues/issue-23080.rs @@ -1,8 +1,7 @@ #![feature(optin_builtin_traits)] unsafe auto trait Trait { -//~^ ERROR E0380 - fn method(&self) { + fn method(&self) { //~ ERROR E0380 println!("Hello"); } } diff --git a/src/test/ui/issues/issue-23080.stderr b/src/test/ui/issues/issue-23080.stderr index 91c27217324..dbb9861b578 100644 --- a/src/test/ui/issues/issue-23080.stderr +++ b/src/test/ui/issues/issue-23080.stderr @@ -1,13 +1,10 @@ error[E0380]: auto traits cannot have methods or associated items - --> $DIR/issue-23080.rs:3:1 + --> $DIR/issue-23080.rs:4:8 | -LL | / unsafe auto trait Trait { -LL | | -LL | | fn method(&self) { -LL | | println!("Hello"); -LL | | } -LL | | } - | |_^ +LL | unsafe auto trait Trait { + | ----- auto trait cannot have items +LL | fn method(&self) { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-28344.stderr b/src/test/ui/issues/issue-28344.stderr index 77bc8292094..e34ac45e69d 100644 --- a/src/test/ui/issues/issue-28344.stderr +++ b/src/test/ui/issues/issue-28344.stderr @@ -11,7 +11,7 @@ LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^ | | | function or associated item not found in `dyn std::ops::BitXor<_>` - | help: there is a method with a similar name: `bitxor` + | help: there is an associated function with a similar name: `bitxor` error[E0191]: the value of the associated type `Output` (from trait `std::ops::BitXor`) must be specified --> $DIR/issue-28344.rs:8:13 @@ -26,7 +26,7 @@ LL | let g = BitXor::bitor; | ^^^^^ | | | function or associated item not found in `dyn std::ops::BitXor<_>` - | help: there is a method with a similar name: `bitxor` + | help: there is an associated function with a similar name: `bitxor` error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-33504.stderr b/src/test/ui/issues/issue-33504.stderr index 522df6a07c2..1e61178f42e 100644 --- a/src/test/ui/issues/issue-33504.stderr +++ b/src/test/ui/issues/issue-33504.stderr @@ -1,8 +1,15 @@ error[E0308]: mismatched types --> $DIR/issue-33504.rs:7:13 | +LL | struct Test; + | ------------ unit struct defined here +... LL | let Test = 1; - | ^^^^ expected integer, found struct `Test` + | ^^^^ + | | + | expected integer, found struct `Test` + | `Test` is interpreted as a unit struct, not a new binding + | help: introduce a new binding instead: `other_test` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3763.rs b/src/test/ui/issues/issue-3763.rs index 5d17a30ab36..451321c5503 100644 --- a/src/test/ui/issues/issue-3763.rs +++ b/src/test/ui/issues/issue-3763.rs @@ -1,3 +1,6 @@ +// compile-flags: -Zsave-analysis +// Also regression test for #69416 + mod my_mod { pub struct MyStruct { priv_field: isize @@ -18,9 +21,9 @@ fn main() { let _woohoo = (Box::new(my_struct)).priv_field; //~^ ERROR field `priv_field` of struct `my_mod::MyStruct` is private - (&my_struct).happyfun(); //~ ERROR method `happyfun` is private + (&my_struct).happyfun(); //~ ERROR associated function `happyfun` is private - (Box::new(my_struct)).happyfun(); //~ ERROR method `happyfun` is private + (Box::new(my_struct)).happyfun(); //~ ERROR associated function `happyfun` is private let nope = my_struct.priv_field; //~^ ERROR field `priv_field` of struct `my_mod::MyStruct` is private } diff --git a/src/test/ui/issues/issue-3763.stderr b/src/test/ui/issues/issue-3763.stderr index 50169286b1c..d548477a88f 100644 --- a/src/test/ui/issues/issue-3763.stderr +++ b/src/test/ui/issues/issue-3763.stderr @@ -1,29 +1,29 @@ error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private - --> $DIR/issue-3763.rs:15:19 + --> $DIR/issue-3763.rs:18:19 | LL | let _woohoo = (&my_struct).priv_field; | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private - --> $DIR/issue-3763.rs:18:19 + --> $DIR/issue-3763.rs:21:19 | LL | let _woohoo = (Box::new(my_struct)).priv_field; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0624]: method `happyfun` is private - --> $DIR/issue-3763.rs:21:18 +error[E0624]: associated function `happyfun` is private + --> $DIR/issue-3763.rs:24:18 | LL | (&my_struct).happyfun(); | ^^^^^^^^ -error[E0624]: method `happyfun` is private - --> $DIR/issue-3763.rs:23:27 +error[E0624]: associated function `happyfun` is private + --> $DIR/issue-3763.rs:26:27 | LL | (Box::new(my_struct)).happyfun(); | ^^^^^^^^ error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private - --> $DIR/issue-3763.rs:24:16 + --> $DIR/issue-3763.rs:27:16 | LL | let nope = my_struct.priv_field; | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/issues/issue-40883.rs b/src/test/ui/issues/issue-40883.rs index 37e61b1b0e6..8a4aef46dd5 100644 --- a/src/test/ui/issues/issue-40883.rs +++ b/src/test/ui/issues/issue-40883.rs @@ -71,15 +71,16 @@ pub fn supersize_me(out: &mut Vec) { #[inline(never)] fn verify_stack_usage(before_ptr: *mut Vec) { - // to check stack usage, create locals before and after + // To check stack usage, create locals before and after // and check the difference in addresses between them. let mut stack_var: Vec = vec![]; test::black_box(&mut stack_var); let stack_usage = isize::abs( (&mut stack_var as *mut _ as isize) - (before_ptr as isize)) as usize; - // give space for 2 copies of `Big` + 128 "misc" bytes. - if stack_usage > mem::size_of::() * 2 + 128 { + // Give space for 2 copies of `Big` + 272 "misc" bytes + // (value observed on x86_64-pc-windows-gnu). + if stack_usage > mem::size_of::() * 2 + 272 { panic!("used {} bytes of stack, but `struct Big` is only {} bytes", stack_usage, mem::size_of::()); } diff --git a/src/test/ui/issues/issue-4968.stderr b/src/test/ui/issues/issue-4968.stderr index 35435d0e618..5451cf42355 100644 --- a/src/test/ui/issues/issue-4968.stderr +++ b/src/test/ui/issues/issue-4968.stderr @@ -1,8 +1,15 @@ error[E0308]: mismatched types --> $DIR/issue-4968.rs:5:16 | +LL | const A: (isize,isize) = (4,2); + | ------------------------------- constant defined here +LL | fn main() { LL | match 42 { A => () } - | ^ expected integer, found tuple + | ^ + | | + | expected integer, found tuple + | `A` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_a` | = note: expected type `{integer}` found tuple `(isize, isize)` diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr index 5db521536a8..b97131a1992 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `as_deref` found for enum `std::option::Option<{in --> $DIR/option-as_deref.rs:2:29 | LL | let _result = &Some(42).as_deref(); - | ^^^^^^^^ help: there is a method with a similar name: `as_ref` + | ^^^^^^^^ help: there is an associated function with a similar name: `as_ref` | = note: the method `as_deref` exists but the following trait bounds were not satisfied: `{integer}: std::ops::Deref` diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr index 2dfc6d53750..f33e9c7823e 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `as_deref` found for enum `std::result::Result<{in --> $DIR/result-as_deref.rs:4:27 | LL | let _result = &Ok(42).as_deref(); - | ^^^^^^^^ help: there is a method with a similar name: `as_ref` + | ^^^^^^^^ help: there is an associated function with a similar name: `as_ref` | = note: the method `as_deref` exists but the following trait bounds were not satisfied: `{integer}: std::ops::Deref` diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.stderr index 1d65c57e5e9..68ebfab95c4 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `as_deref_err` found for enum `std::result::Result --> $DIR/result-as_deref_err.rs:4:28 | LL | let _result = &Err(41).as_deref_err(); - | ^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut` + | ^^^^^^^^^^^^ help: there is an associated function with a similar name: `as_deref_mut` | = note: the method `as_deref_err` exists but the following trait bounds were not satisfied: `{integer}: std::ops::Deref` diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr index 2f4bf0c94b5..d2ba1049b76 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `as_deref_mut` found for enum `std::result::Result --> $DIR/result-as_deref_mut.rs:4:31 | LL | let _result = &mut Ok(42).as_deref_mut(); - | ^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_err` + | ^^^^^^^^^^^^ help: there is an associated function with a similar name: `as_deref_err` | = note: the method `as_deref_mut` exists but the following trait bounds were not satisfied: `{integer}: std::ops::DerefMut` diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.stderr index b76d36c804e..d724ae5c74b 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `as_deref_mut_err` found for enum `std::result::Re --> $DIR/result-as_deref_mut_err.rs:4:32 | LL | let _result = &mut Err(41).as_deref_mut_err(); - | ^^^^^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut` + | ^^^^^^^^^^^^^^^^ help: there is an associated function with a similar name: `as_deref_mut` | = note: the method `as_deref_mut_err` exists but the following trait bounds were not satisfied: `{integer}: std::ops::DerefMut` diff --git a/src/test/ui/issues/issue-5100.stderr b/src/test/ui/issues/issue-5100.stderr index c81d6dcaf02..a89980964ca 100644 --- a/src/test/ui/issues/issue-5100.stderr +++ b/src/test/ui/issues/issue-5100.stderr @@ -1,6 +1,9 @@ error[E0308]: mismatched types --> $DIR/issue-5100.rs:8:9 | +LL | enum A { B, C } + | - unit variant defined here +... LL | match (true, false) { | ------------- this expression has type `(bool, bool)` LL | A::B => (), diff --git a/src/test/ui/issues/issue-53498.rs b/src/test/ui/issues/issue-53498.rs index c87d4236492..9e0437c46f4 100644 --- a/src/test/ui/issues/issue-53498.rs +++ b/src/test/ui/issues/issue-53498.rs @@ -13,5 +13,5 @@ fn foo() {} } fn main() { - test::Foo::::foo(); //~ ERROR method `foo` is private + test::Foo::::foo(); //~ ERROR associated function `foo` is private } diff --git a/src/test/ui/issues/issue-53498.stderr b/src/test/ui/issues/issue-53498.stderr index 3fd48233dae..042848c27bb 100644 --- a/src/test/ui/issues/issue-53498.stderr +++ b/src/test/ui/issues/issue-53498.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `foo` is private +error[E0624]: associated function `foo` is private --> $DIR/issue-53498.rs:16:5 | LL | test::Foo::::foo(); diff --git a/src/test/ui/issues/issue-55587.stderr b/src/test/ui/issues/issue-55587.stderr index bb0d15a23d6..faf78cfe8d9 100644 --- a/src/test/ui/issues/issue-55587.stderr +++ b/src/test/ui/issues/issue-55587.stderr @@ -1,4 +1,4 @@ -error[E0164]: expected tuple struct or tuple variant, found method `Path::new` +error[E0164]: expected tuple struct or tuple variant, found associated function `Path::new` --> $DIR/issue-55587.rs:4:9 | LL | let Path::new(); diff --git a/src/test/ui/issues/issue-63364.stderr b/src/test/ui/issues/issue-63364.stderr index 60ff318f35a..0375359aeab 100644 --- a/src/test/ui/issues/issue-63364.stderr +++ b/src/test/ui/issues/issue-63364.stderr @@ -5,6 +5,7 @@ LL | for n in 100_000.. { | ^^^^^^^ | = note: `#[deny(overflowing_literals)]` on by default + = note: the literal `100_000` does not fit into the type `u16` whose range is `0..=65535` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-66473.rs b/src/test/ui/issues/issue-66473.rs index cc298a28b97..9db4521bb42 100644 Binary files a/src/test/ui/issues/issue-66473.rs and b/src/test/ui/issues/issue-66473.rs differ diff --git a/src/test/ui/issues/issue-66473.stderr b/src/test/ui/issues/issue-66473.stderr index dbeef44bad0..b370b125cfe 100644 Binary files a/src/test/ui/issues/issue-66473.stderr and b/src/test/ui/issues/issue-66473.stderr differ diff --git a/src/test/ui/issues/issue-69396-const-no-type-in-macro.rs b/src/test/ui/issues/issue-69396-const-no-type-in-macro.rs new file mode 100644 index 00000000000..69fc0c1cbb9 --- /dev/null +++ b/src/test/ui/issues/issue-69396-const-no-type-in-macro.rs @@ -0,0 +1,17 @@ +macro_rules! suite { + ( $( $fn:ident; )* ) => { + $( + const A = "A".$fn(); + //~^ ERROR the name `A` is defined multiple times + //~| ERROR missing type for `const` item + //~| ERROR the type placeholder `_` is not allowed within types + )* + } +} + +suite! { + len; + is_empty; +} + +fn main() {} diff --git a/src/test/ui/issues/issue-69396-const-no-type-in-macro.stderr b/src/test/ui/issues/issue-69396-const-no-type-in-macro.stderr new file mode 100644 index 00000000000..1af5368d2b6 --- /dev/null +++ b/src/test/ui/issues/issue-69396-const-no-type-in-macro.stderr @@ -0,0 +1,53 @@ +error[E0428]: the name `A` is defined multiple times + --> $DIR/issue-69396-const-no-type-in-macro.rs:4:13 + | +LL | const A = "A".$fn(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | `A` redefined here + | previous definition of the value `A` here +... +LL | / suite! { +LL | | len; +LL | | is_empty; +LL | | } + | |_- in this macro invocation + | + = note: `A` must be defined only once in the value namespace of this module + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: missing type for `const` item + --> $DIR/issue-69396-const-no-type-in-macro.rs:4:19 + | +LL | const A = "A".$fn(); + | ^ help: provide a type for the item: `A: usize` +... +LL | / suite! { +LL | | len; +LL | | is_empty; +LL | | } + | |_- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/issue-69396-const-no-type-in-macro.rs:4:19 + | +LL | const A = "A".$fn(); + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `bool` +... +LL | / suite! { +LL | | len; +LL | | is_empty; +LL | | } + | |_- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0121, E0428. +For more information about an error, try `rustc --explain E0121`. diff --git a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs new file mode 100644 index 00000000000..d060f26fb2a --- /dev/null +++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs @@ -0,0 +1,23 @@ +trait TraitA { + const VALUE: usize; +} + +struct A; +impl TraitA for A { + const VALUE: usize = 0; +} + +trait TraitB { + type MyA: TraitA; + const VALUE: usize = Self::MyA::VALUE; +} + +struct B; +impl TraitB for B { //~ ERROR not all trait items implemented, missing: `MyA` + type M = A; //~ ERROR type `M` is not a member of trait `TraitB` +} + +fn main() { + let _ = [0; B::VALUE]; + //~^ ERROR array lengths can't depend on generic parameters +} diff --git a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr new file mode 100644 index 00000000000..c6b2b4d27a2 --- /dev/null +++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr @@ -0,0 +1,25 @@ +error[E0437]: type `M` is not a member of trait `TraitB` + --> $DIR/issue-69602-type-err-during-codegen-ice.rs:17:5 + | +LL | type M = A; + | ^^^^^^^^^^^^^ not a member of trait `TraitB` + +error[E0046]: not all trait items implemented, missing: `MyA` + --> $DIR/issue-69602-type-err-during-codegen-ice.rs:16:1 + | +LL | type MyA: TraitA; + | ----------------- `MyA` from trait +... +LL | impl TraitB for B { + | ^^^^^^^^^^^^^^^^^ missing `MyA` in implementation + +error: array lengths can't depend on generic parameters + --> $DIR/issue-69602-type-err-during-codegen-ice.rs:21:17 + | +LL | let _ = [0; B::VALUE]; + | ^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0046, E0437. +For more information about an error, try `rustc --explain E0046`. diff --git a/src/test/ui/issues/issue-69725.rs b/src/test/ui/issues/issue-69725.rs new file mode 100644 index 00000000000..b8130b41f21 --- /dev/null +++ b/src/test/ui/issues/issue-69725.rs @@ -0,0 +1,11 @@ +// aux-build:issue-69725.rs + +extern crate issue_69725; +use issue_69725::Struct; + +fn crash() { + let _ = Struct::::new().clone(); + //~^ ERROR: no method named `clone` found +} + +fn main() {} diff --git a/src/test/ui/issues/issue-69725.stderr b/src/test/ui/issues/issue-69725.stderr new file mode 100644 index 00000000000..667383e072a --- /dev/null +++ b/src/test/ui/issues/issue-69725.stderr @@ -0,0 +1,18 @@ +error[E0599]: no method named `clone` found for struct `issue_69725::Struct` in the current scope + --> $DIR/issue-69725.rs:7:32 + | +LL | let _ = Struct::::new().clone(); + | ^^^^^ method not found in `issue_69725::Struct` + | + ::: $DIR/auxiliary/issue-69725.rs:2:1 + | +LL | pub struct Struct(A); + | ------------------------ doesn't satisfy `issue_69725::Struct: std::clone::Clone` + | + = note: the method `clone` exists but the following trait bounds were not satisfied: + `A: std::clone::Clone` + which is required by `issue_69725::Struct: std::clone::Clone` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/issues/issue-7867.stderr b/src/test/ui/issues/issue-7867.stderr index 4a29464aebd..0d3121d6045 100644 --- a/src/test/ui/issues/issue-7867.stderr +++ b/src/test/ui/issues/issue-7867.stderr @@ -1,6 +1,9 @@ error[E0308]: mismatched types --> $DIR/issue-7867.rs:7:9 | +LL | enum A { B, C } + | - unit variant defined here +... LL | match (true, false) { | ------------- this expression has type `(bool, bool)` LL | A::B => (), diff --git a/src/test/ui/json-bom-plus-crlf-multifile.stderr b/src/test/ui/json-bom-plus-crlf-multifile.stderr index ab0feb3c451..026943669f8 100644 --- a/src/test/ui/json-bom-plus-crlf-multifile.stderr +++ b/src/test/ui/json-bom-plus-crlf-multifile.stderr @@ -1,10 +1,6 @@ -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -15,15 +11,17 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":612,"byte_end":618,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types -"} -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a + +This error occurs when the compiler was unable to infer the concrete type of a variable. It can occur for several cases, the most common of which is a mismatch in the expected type that the compiler inferred for a variable's initializing expression, and the actual type explicitly assigned to the variable. +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":612,"byte_end":618,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types +"} +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -34,15 +32,17 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":672,"byte_end":678,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types -"} -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a + +This error occurs when the compiler was unable to infer the concrete type of a variable. It can occur for several cases, the most common of which is a mismatch in the expected type that the compiler inferred for a variable's initializing expression, and the actual type explicitly assigned to the variable. +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":672,"byte_end":678,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types +"} +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -53,15 +53,17 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":735,"byte_end":741,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types -"} -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a + +This error occurs when the compiler was unable to infer the concrete type of a variable. It can occur for several cases, the most common of which is a mismatch in the expected type that the compiler inferred for a variable's initializing expression, and the actual type explicitly assigned to the variable. +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":735,"byte_end":741,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types +"} +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -72,6 +74,12 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +This error occurs when the compiler was unable to infer the concrete type of a +variable. It can occur for several cases, the most common of which is a +mismatch in the expected type that the compiler inferred for a variable's +initializing expression, and the actual type explicitly assigned to the +variable. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":801,"byte_end":809,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `std::string::String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":792,"byte_end":798,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types "} {"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors diff --git a/src/test/ui/json-bom-plus-crlf.stderr b/src/test/ui/json-bom-plus-crlf.stderr index 1dd898db3ad..735a46b8c87 100644 --- a/src/test/ui/json-bom-plus-crlf.stderr +++ b/src/test/ui/json-bom-plus-crlf.stderr @@ -1,10 +1,6 @@ -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -15,15 +11,17 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":597,"byte_end":603,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types -"} -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a + +This error occurs when the compiler was unable to infer the concrete type of a variable. It can occur for several cases, the most common of which is a mismatch in the expected type that the compiler inferred for a variable's initializing expression, and the actual type explicitly assigned to the variable. +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":597,"byte_end":603,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types +"} +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -34,15 +32,17 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":657,"byte_end":663,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types -"} -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a + +This error occurs when the compiler was unable to infer the concrete type of a variable. It can occur for several cases, the most common of which is a mismatch in the expected type that the compiler inferred for a variable's initializing expression, and the actual type explicitly assigned to the variable. +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":657,"byte_end":663,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types +"} +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -53,15 +53,17 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":720,"byte_end":726,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types -"} -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a + +This error occurs when the compiler was unable to infer the concrete type of a variable. It can occur for several cases, the most common of which is a mismatch in the expected type that the compiler inferred for a variable's initializing expression, and the actual type explicitly assigned to the variable. +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":720,"byte_end":726,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types +"} +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -72,6 +74,12 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +This error occurs when the compiler was unable to infer the concrete type of a +variable. It can occur for several cases, the most common of which is a +mismatch in the expected type that the compiler inferred for a variable's +initializing expression, and the actual type explicitly assigned to the +variable. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":786,"byte_end":794,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `std::string::String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":777,"byte_end":783,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types "} {"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.rs b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.rs index b07de3e341c..a46ce67d40d 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.rs +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.rs @@ -1,3 +1,4 @@ use extern::foo; //~ ERROR expected identifier, found keyword `extern` + //~| ERROR unresolved import `r#extern` fn main() {} diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr index 05802f2d367..edbb36452b6 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr @@ -9,5 +9,12 @@ help: you can escape reserved keywords to use them as identifiers LL | use r#extern::foo; | ^^^^^^^^ -error: aborting due to previous error +error[E0432]: unresolved import `r#extern` + --> $DIR/keyword-extern-as-identifier-use.rs:1:5 + | +LL | use extern::foo; + | ^^^^^^ maybe a missing crate `r#extern`? + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0432`. diff --git a/src/test/ui/label/label_break_value_illegal_uses.stderr b/src/test/ui/label/label_break_value_illegal_uses.stderr index fd8850dd8da..a2c75882be0 100644 --- a/src/test/ui/label/label_break_value_illegal_uses.stderr +++ b/src/test/ui/label/label_break_value_illegal_uses.stderr @@ -2,7 +2,10 @@ error: expected `{`, found `'b` --> $DIR/label_break_value_illegal_uses.rs:6:12 | LL | unsafe 'b: {} - | ^^ expected `{` + | ^^---- + | | + | expected `{` + | help: try placing this code inside a block: `{ 'b: {} }` error: expected `{`, found `'b` --> $DIR/label_break_value_illegal_uses.rs:10:13 diff --git a/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.nll.stderr index 291edc505cd..4c788211576 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.nll.stderr @@ -7,7 +7,7 @@ LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { | lifetime `'a` defined here LL | LL | if x > y { x } else { y } - | ^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` error: aborting due to previous error diff --git a/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.nll.stderr index 15ee58574ec..11e7fa96d7e 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.nll.stderr @@ -7,7 +7,7 @@ LL | fn foo<'a>(&self, x: &'a i32) -> &i32 { | lifetime `'a` defined here LL | LL | x - | ^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` + | ^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` error: aborting due to previous error diff --git a/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.nll.stderr index a27a91e38f1..c41f08e691a 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.nll.stderr @@ -7,7 +7,7 @@ LL | fn foo<'a>(&self, x: &'a Foo) -> &'a Foo { | lifetime `'a` defined here LL | LL | if true { x } else { self } - | ^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + | ^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` error: aborting due to previous error diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr index 5f922d8560b..1a19e81f235 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr @@ -6,7 +6,7 @@ LL | fn foo<'a>(&self, x: &i32) -> &i32 { | | | let's call the lifetime of this reference `'2` LL | x - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to previous error diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr index 91d7597c87f..87b13dc1591 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr @@ -6,7 +6,7 @@ LL | fn foo<'a>(&self, x: &Foo) -> &Foo { | | | let's call the lifetime of this reference `'2` LL | if true { x } else { self } - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to previous error diff --git a/src/test/ui/lint/deny-overflowing-literals.stderr b/src/test/ui/lint/deny-overflowing-literals.stderr index 7f59495023e..127dd4127c2 100644 --- a/src/test/ui/lint/deny-overflowing-literals.stderr +++ b/src/test/ui/lint/deny-overflowing-literals.stderr @@ -5,6 +5,7 @@ LL | let x: u8 = 256; | ^^^ | = note: `#[deny(overflowing_literals)]` on by default + = note: the literal `256` does not fit into the type `u8` whose range is `0..=255` error: range endpoint is out of range for `u8` --> $DIR/deny-overflowing-literals.rs:5:14 diff --git a/src/test/ui/lint/issue-69485-var-size-diffs-too-large.rs b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.rs new file mode 100644 index 00000000000..49d489d9168 --- /dev/null +++ b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.rs @@ -0,0 +1,10 @@ +// build-fail +// only-x86_64 + +fn main() { + Bug::V([0; !0]); //~ ERROR is too big for the current +} + +enum Bug { + V([u8; !0]), +} diff --git a/src/test/ui/lint/issue-69485-var-size-diffs-too-large.stderr b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.stderr new file mode 100644 index 00000000000..d31ce9cfe0c --- /dev/null +++ b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.stderr @@ -0,0 +1,8 @@ +error: the type `[u8; 18446744073709551615]` is too big for the current architecture + --> $DIR/issue-69485-var-size-diffs-too-large.rs:5:12 + | +LL | Bug::V([0; !0]); + | ^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-pre-expansion-extern-module.rs b/src/test/ui/lint/lint-pre-expansion-extern-module.rs new file mode 100644 index 00000000000..30e2ed8b7a6 --- /dev/null +++ b/src/test/ui/lint/lint-pre-expansion-extern-module.rs @@ -0,0 +1,7 @@ +// check-pass +// compile-flags: -W rust-2018-compatibility +// error-pattern: `try` is a keyword in the 2018 edition + +fn main() {} + +mod lint_pre_expansion_extern_module_aux; diff --git a/src/test/ui/lint/lint-pre-expansion-extern-module.stderr b/src/test/ui/lint/lint-pre-expansion-extern-module.stderr new file mode 100644 index 00000000000..c683a3fa670 --- /dev/null +++ b/src/test/ui/lint/lint-pre-expansion-extern-module.stderr @@ -0,0 +1,10 @@ +warning: `try` is a keyword in the 2018 edition + --> $DIR/lint_pre_expansion_extern_module_aux.rs:3:8 + | +LL | pub fn try() {} + | ^^^ help: you can use a raw identifier to stay compatible: `r#try` + | + = note: `-W keyword-idents` implied by `-W rust-2018-compatibility` + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.stderr b/src/test/ui/lint/lint-range-endpoint-overflow.stderr index dff61e022eb..d2df7372741 100644 --- a/src/test/ui/lint/lint-range-endpoint-overflow.stderr +++ b/src/test/ui/lint/lint-range-endpoint-overflow.stderr @@ -15,18 +15,24 @@ error: literal out of range for `u8` | LL | let range_c = 0..=256; | ^^^ + | + = note: the literal `256` does not fit into the type `u8` whose range is `0..=255` error: literal out of range for `u8` --> $DIR/lint-range-endpoint-overflow.rs:7:19 | LL | let range_d = 256..5; | ^^^ + | + = note: the literal `256` does not fit into the type `u8` whose range is `0..=255` error: literal out of range for `u8` --> $DIR/lint-range-endpoint-overflow.rs:8:22 | LL | let range_e = 0..257; | ^^^ + | + = note: the literal `257` does not fit into the type `u8` whose range is `0..=255` error: range endpoint is out of range for `u8` --> $DIR/lint-range-endpoint-overflow.rs:9:20 diff --git a/src/test/ui/lint/lint-type-limits2.stderr b/src/test/ui/lint/lint-type-limits2.stderr index bf510823b56..1e3c88dfc46 100644 --- a/src/test/ui/lint/lint-type-limits2.stderr +++ b/src/test/ui/lint/lint-type-limits2.stderr @@ -17,6 +17,7 @@ note: the lint level is defined here | LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` error: aborting due to previous error diff --git a/src/test/ui/lint/lint-type-limits3.stderr b/src/test/ui/lint/lint-type-limits3.stderr index 00441f99e60..150e9a2aa47 100644 --- a/src/test/ui/lint/lint-type-limits3.stderr +++ b/src/test/ui/lint/lint-type-limits3.stderr @@ -17,6 +17,7 @@ note: the lint level is defined here | LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `200` does not fit into the type `i8` whose range is `-128..=127` error: aborting due to previous error diff --git a/src/test/ui/lint/lint-type-overflow.stderr b/src/test/ui/lint/lint-type-overflow.stderr index ec15313158d..7715c0d3a4d 100644 --- a/src/test/ui/lint/lint-type-overflow.stderr +++ b/src/test/ui/lint/lint-type-overflow.stderr @@ -9,108 +9,143 @@ note: the lint level is defined here | LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `256` does not fit into the type `u8` whose range is `0..=255` error: literal out of range for `u8` --> $DIR/lint-type-overflow.rs:13:14 | LL | let x1 = 256_u8; | ^^^^^^ + | + = note: the literal `256_u8` does not fit into the type `u8` whose range is `0..=255` error: literal out of range for `i8` --> $DIR/lint-type-overflow.rs:16:18 | LL | let x1: i8 = 128; | ^^^ + | + = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` --> $DIR/lint-type-overflow.rs:18:19 | LL | let x3: i8 = -129; | ^^^ + | + = note: the literal `129` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` --> $DIR/lint-type-overflow.rs:19:19 | LL | let x3: i8 = -(129); | ^^^^^ + | + = note: the literal `129` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` --> $DIR/lint-type-overflow.rs:20:20 | LL | let x3: i8 = -{129}; | ^^^ + | + = note: the literal `129` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` --> $DIR/lint-type-overflow.rs:22:10 | LL | test(1000); | ^^^^ + | + = note: the literal `1000` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` --> $DIR/lint-type-overflow.rs:24:13 | LL | let x = 128_i8; | ^^^^^^ + | + = note: the literal `128_i8` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` --> $DIR/lint-type-overflow.rs:28:14 | LL | let x = -129_i8; | ^^^^^^ + | + = note: the literal `129_i8` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i32` --> $DIR/lint-type-overflow.rs:32:18 | LL | let x: i32 = 2147483648; | ^^^^^^^^^^ + | + = note: the literal `2147483648` does not fit into the type `i32` whose range is `-2147483648..=2147483647` error: literal out of range for `i32` --> $DIR/lint-type-overflow.rs:33:13 | LL | let x = 2147483648_i32; | ^^^^^^^^^^^^^^ + | + = note: the literal `2147483648_i32` does not fit into the type `i32` whose range is `-2147483648..=2147483647` error: literal out of range for `i32` --> $DIR/lint-type-overflow.rs:36:19 | LL | let x: i32 = -2147483649; | ^^^^^^^^^^ + | + = note: the literal `2147483649` does not fit into the type `i32` whose range is `-2147483648..=2147483647` error: literal out of range for `i32` --> $DIR/lint-type-overflow.rs:37:14 | LL | let x = -2147483649_i32; | ^^^^^^^^^^^^^^ + | + = note: the literal `2147483649_i32` does not fit into the type `i32` whose range is `-2147483648..=2147483647` error: literal out of range for `i32` --> $DIR/lint-type-overflow.rs:38:13 | LL | let x = 2147483648; | ^^^^^^^^^^ + | + = note: the literal `2147483648` does not fit into the type `i32` whose range is `-2147483648..=2147483647` error: literal out of range for `i64` --> $DIR/lint-type-overflow.rs:40:13 | LL | let x = 9223372036854775808_i64; | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `9223372036854775808_i64` does not fit into the type `i64` whose range is `-9223372036854775808..=9223372036854775807` error: literal out of range for `i64` --> $DIR/lint-type-overflow.rs:42:13 | LL | let x = 18446744073709551615_i64; | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `18446744073709551615_i64` does not fit into the type `i64` whose range is `-9223372036854775808..=9223372036854775807` error: literal out of range for `i64` --> $DIR/lint-type-overflow.rs:43:19 | LL | let x: i64 = -9223372036854775809; | ^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `9223372036854775809` does not fit into the type `i64` whose range is `-9223372036854775808..=9223372036854775807` error: literal out of range for `i64` --> $DIR/lint-type-overflow.rs:44:14 | LL | let x = -9223372036854775809_i64; | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `9223372036854775809_i64` does not fit into the type `i64` whose range is `-9223372036854775808..=9223372036854775807` error: aborting due to 18 previous errors diff --git a/src/test/ui/lint/lint-type-overflow2.stderr b/src/test/ui/lint/lint-type-overflow2.stderr index dfc691ab910..61e33b7a260 100644 --- a/src/test/ui/lint/lint-type-overflow2.stderr +++ b/src/test/ui/lint/lint-type-overflow2.stderr @@ -9,30 +9,39 @@ note: the lint level is defined here | LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `f32` --> $DIR/lint-type-overflow2.rs:9:14 | LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ + | + = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `std::f32::INFINITY` error: literal out of range for `f32` --> $DIR/lint-type-overflow2.rs:10:14 | LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ + | + = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `std::f32::INFINITY` error: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:11:14 | LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `std::f64::INFINITY` error: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:12:14 | LL | let x = 1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `std::f64::INFINITY` error: aborting due to 5 previous errors diff --git a/src/test/ui/lint/lint_pre_expansion_extern_module_aux.rs b/src/test/ui/lint/lint_pre_expansion_extern_module_aux.rs new file mode 100644 index 00000000000..71dec40ea44 --- /dev/null +++ b/src/test/ui/lint/lint_pre_expansion_extern_module_aux.rs @@ -0,0 +1,3 @@ +// ignore-test: not a test + +pub fn try() {} diff --git a/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.rs b/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.rs index f207b235735..08a5c6c2b63 100644 --- a/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.rs +++ b/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.rs @@ -1,6 +1,6 @@ // aux-build:redundant-semi-proc-macro-def.rs -#![deny(redundant_semicolon)] +#![deny(redundant_semicolons)] extern crate redundant_semi_proc_macro; use redundant_semi_proc_macro::should_preserve_spans; diff --git a/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr b/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr index 8c5ee58dc12..a79fba9bf3f 100644 --- a/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr +++ b/src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr @@ -1,4 +1,4 @@ -TokenStream [Ident { ident: "fn", span: #0 bytes(197..199) }, Ident { ident: "span_preservation", span: #0 bytes(200..217) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(217..219) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "let", span: #0 bytes(227..230) }, Ident { ident: "tst", span: #0 bytes(231..234) }, Punct { ch: '=', spacing: Alone, span: #0 bytes(235..236) }, Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(237), hi: BytePos(240), ctxt: #0 } }, Punct { ch: ';', spacing: Joint, span: #0 bytes(240..241) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(241..242) }, Ident { ident: "match", span: #0 bytes(288..293) }, Ident { ident: "tst", span: #0 bytes(294..297) }, Group { delimiter: Brace, stream: TokenStream [Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(482), hi: BytePos(485), ctxt: #0 } }, Punct { ch: '=', spacing: Joint, span: #0 bytes(486..488) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(486..488) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(489..491) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(491..492) }, Ident { ident: "_", span: #0 bytes(501..502) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(503..505) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(503..505) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(506..508) }], span: #0 bytes(298..514) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(514..515) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(515..516) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(516..517) }], span: #0 bytes(221..561) }] +TokenStream [Ident { ident: "fn", span: #0 bytes(198..200) }, Ident { ident: "span_preservation", span: #0 bytes(201..218) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(218..220) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "let", span: #0 bytes(228..231) }, Ident { ident: "tst", span: #0 bytes(232..235) }, Punct { ch: '=', spacing: Alone, span: #0 bytes(236..237) }, Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(238), hi: BytePos(241), ctxt: #0 } }, Punct { ch: ';', spacing: Joint, span: #0 bytes(241..242) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(242..243) }, Ident { ident: "match", span: #0 bytes(289..294) }, Ident { ident: "tst", span: #0 bytes(295..298) }, Group { delimiter: Brace, stream: TokenStream [Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(483), hi: BytePos(486), ctxt: #0 } }, Punct { ch: '=', spacing: Joint, span: #0 bytes(487..489) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(487..489) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(490..492) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(492..493) }, Ident { ident: "_", span: #0 bytes(502..503) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(504..506) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(504..506) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(507..509) }], span: #0 bytes(299..515) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(515..516) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(516..517) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(517..518) }], span: #0 bytes(222..562) }] error: unnecessary trailing semicolon --> $DIR/redundant-semi-proc-macro.rs:9:19 | @@ -8,8 +8,8 @@ LL | let tst = 123;; note: the lint level is defined here --> $DIR/redundant-semi-proc-macro.rs:3:9 | -LL | #![deny(redundant_semicolon)] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![deny(redundant_semicolons)] + | ^^^^^^^^^^^^^^^^^^^^ error: unnecessary trailing semicolons --> $DIR/redundant-semi-proc-macro.rs:16:7 diff --git a/src/test/ui/lint/suggestions.rs b/src/test/ui/lint/suggestions.rs index 29297d08dca..518b5f211e5 100644 --- a/src/test/ui/lint/suggestions.rs +++ b/src/test/ui/lint/suggestions.rs @@ -1,7 +1,6 @@ // ignore-tidy-tab #![warn(unused_mut, unused_parens)] // UI tests pass `-A unused`—see Issue #43896 -#![feature(no_debug)] #[no_mangle] const DISCOVERY: usize = 1; //~^ ERROR const items should never be `#[no_mangle]` @@ -39,9 +38,6 @@ struct Equinox { warp_factor: f32, } -#[no_debug] // should suggest removal of deprecated attribute -//~^ WARN deprecated -//~| HELP remove this attribute fn main() { while true { //~^ WARN denote infinite loops diff --git a/src/test/ui/lint/suggestions.stderr b/src/test/ui/lint/suggestions.stderr index 4e218ed0f1a..0ef5d72609a 100644 --- a/src/test/ui/lint/suggestions.stderr +++ b/src/test/ui/lint/suggestions.stderr @@ -1,5 +1,5 @@ warning: denote infinite loops with `loop { ... }` - --> $DIR/suggestions.rs:46:5 + --> $DIR/suggestions.rs:42:5 | LL | while true { | ^^^^^^^^^^ help: use `loop` @@ -7,7 +7,7 @@ LL | while true { = note: `#[warn(while_true)]` on by default warning: unnecessary parentheses around assigned value - --> $DIR/suggestions.rs:49:31 + --> $DIR/suggestions.rs:45:31 | LL | let mut registry_no = (format!("NX-{}", 74205)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove these parentheses @@ -18,16 +18,8 @@ note: the lint level is defined here LL | #![warn(unused_mut, unused_parens)] // UI tests pass `-A unused`—see Issue #43896 | ^^^^^^^^^^^^^ -warning: use of deprecated attribute `no_debug`: the `#[no_debug]` attribute was an experimental feature that has been deprecated due to lack of demand. See https://github.com/rust-lang/rust/issues/29721 - --> $DIR/suggestions.rs:42:1 - | -LL | #[no_debug] // should suggest removal of deprecated attribute - | ^^^^^^^^^^^ help: remove this attribute - | - = note: `#[warn(deprecated)]` on by default - warning: variable does not need to be mutable - --> $DIR/suggestions.rs:49:13 + --> $DIR/suggestions.rs:45:13 | LL | let mut registry_no = (format!("NX-{}", 74205)); | ----^^^^^^^^^^^ @@ -41,7 +33,7 @@ LL | #![warn(unused_mut, unused_parens)] // UI tests pass `-A unused`—see Issu | ^^^^^^^^^^ warning: variable does not need to be mutable - --> $DIR/suggestions.rs:55:13 + --> $DIR/suggestions.rs:51:13 | LL | let mut | _____________^ @@ -53,7 +45,7 @@ LL | || b = 1; | help: remove this `mut` error: const items should never be `#[no_mangle]` - --> $DIR/suggestions.rs:6:14 + --> $DIR/suggestions.rs:5:14 | LL | #[no_mangle] const DISCOVERY: usize = 1; | -----^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +55,7 @@ LL | #[no_mangle] const DISCOVERY: usize = 1; = note: `#[deny(no_mangle_const_items)]` on by default warning: functions generic over types or consts must be mangled - --> $DIR/suggestions.rs:12:1 + --> $DIR/suggestions.rs:11:1 | LL | #[no_mangle] | ------------ help: remove this attribute @@ -74,7 +66,7 @@ LL | pub fn defiant(_t: T) {} = note: `#[warn(no_mangle_generic_items)]` on by default warning: the `warp_factor:` in this pattern is redundant - --> $DIR/suggestions.rs:61:23 + --> $DIR/suggestions.rs:57:23 | LL | Equinox { warp_factor: warp_factor } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use shorthand field pattern: `warp_factor` @@ -82,7 +74,7 @@ LL | Equinox { warp_factor: warp_factor } => {} = note: `#[warn(non_shorthand_field_patterns)]` on by default error: const items should never be `#[no_mangle]` - --> $DIR/suggestions.rs:22:18 + --> $DIR/suggestions.rs:21:18 | LL | #[no_mangle] pub const DAUNTLESS: bool = true; | ---------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +82,7 @@ LL | #[no_mangle] pub const DAUNTLESS: bool = true; | help: try a static value: `pub static` warning: functions generic over types or consts must be mangled - --> $DIR/suggestions.rs:25:18 + --> $DIR/suggestions.rs:24:18 | LL | #[no_mangle] pub fn val_jean() {} | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ @@ -98,7 +90,7 @@ LL | #[no_mangle] pub fn val_jean() {} | help: remove this attribute error: const items should never be `#[no_mangle]` - --> $DIR/suggestions.rs:30:18 + --> $DIR/suggestions.rs:29:18 | LL | #[no_mangle] pub(crate) const VETAR: bool = true; | ----------------^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +98,7 @@ LL | #[no_mangle] pub(crate) const VETAR: bool = true; | help: try a static value: `pub static` warning: functions generic over types or consts must be mangled - --> $DIR/suggestions.rs:33:18 + --> $DIR/suggestions.rs:32:18 | LL | #[no_mangle] pub(crate) fn crossfield() {} | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index 2432eb78b87..a7a788b877a 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -9,6 +9,7 @@ note: the lint level is defined here | LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ + = note: the literal `255i8` does not fit into the type `i8` whose range is `-128..=127` warning: literal out of range for i8 --> $DIR/type-overflow.rs:10:16 @@ -16,7 +17,7 @@ warning: literal out of range for i8 LL | let fail = 0b1000_0001i8; | ^^^^^^^^^^^^^ help: consider using `u8` instead: `0b1000_0001u8` | - = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into an `i8` and will become `-127i8` + = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into the type `i8` and will become `-127i8` warning: literal out of range for i64 --> $DIR/type-overflow.rs:12:16 @@ -24,7 +25,7 @@ warning: literal out of range for i64 LL | let fail = 0x8000_0000_0000_0000i64; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x8000_0000_0000_0000u64` | - = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into an `i64` and will become `-9223372036854775808i64` + = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into the type `i64` and will become `-9223372036854775808i64` warning: literal out of range for u32 --> $DIR/type-overflow.rs:14:16 @@ -32,7 +33,7 @@ warning: literal out of range for u32 LL | let fail = 0x1_FFFF_FFFFu32; | ^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x1_FFFF_FFFFu64` | - = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into an `u32` and will become `4294967295u32` + = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into the type `u32` and will become `4294967295u32` warning: literal out of range for i128 --> $DIR/type-overflow.rs:16:22 @@ -40,7 +41,7 @@ warning: literal out of range for i128 LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into an `i128` and will become `-170141183460469231731687303715884105728i128` + = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into the type `i128` and will become `-170141183460469231731687303715884105728i128` = help: consider using `u128` instead warning: literal out of range for i32 @@ -49,7 +50,7 @@ warning: literal out of range for i32 LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; | ^^^^^^^^^^^^^^^^^^^^^ | - = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32` + = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into the type `i32` and will become `-2i32` = help: consider using `i128` instead warning: literal out of range for i8 @@ -58,5 +59,5 @@ warning: literal out of range for i8 LL | let fail = -0b1111_1111i8; | ^^^^^^^^^^^^^ help: consider using `i16` instead: `0b1111_1111i16` | - = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8` + = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into the type `i8` and will become `-1i8` diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index 6d669184deb..bf0562713a4 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,4 +1,4 @@ -error: the type `&'static T` does not permit zero-initialization +error: the type `&T` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:29:32 | LL | let _val: &'static T = mem::zeroed(); @@ -14,7 +14,7 @@ LL | #![deny(invalid_value)] | ^^^^^^^^^^^^^ = note: references must be non-null -error: the type `&'static T` does not permit being left uninitialized +error: the type `&T` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:30:32 | LL | let _val: &'static T = mem::uninitialized(); @@ -25,7 +25,7 @@ LL | let _val: &'static T = mem::uninitialized(); | = note: references must be non-null -error: the type `Wrap<&'static T>` does not permit zero-initialization +error: the type `Wrap<&T>` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:32:38 | LL | let _val: Wrap<&'static T> = mem::zeroed(); @@ -40,7 +40,7 @@ note: references must be non-null (in this struct field) LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ -error: the type `Wrap<&'static T>` does not permit being left uninitialized +error: the type `Wrap<&T>` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:33:38 | LL | let _val: Wrap<&'static T> = mem::uninitialized(); @@ -121,7 +121,7 @@ LL | let _val: Void = mem::uninitialized(); | = note: enums with no variants have no valid value -error: the type `&'static i32` does not permit zero-initialization +error: the type `&i32` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:49:34 | LL | let _val: &'static i32 = mem::zeroed(); @@ -132,7 +132,7 @@ LL | let _val: &'static i32 = mem::zeroed(); | = note: references must be non-null -error: the type `&'static i32` does not permit being left uninitialized +error: the type `&i32` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:50:34 | LL | let _val: &'static i32 = mem::uninitialized(); @@ -366,7 +366,7 @@ LL | let _val: NonBig = mem::uninitialized(); | = note: `NonBig` must be initialized inside its custom valid range -error: the type `&'static i32` does not permit zero-initialization +error: the type `&i32` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:84:34 | LL | let _val: &'static i32 = mem::transmute(0usize); @@ -377,7 +377,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize); | = note: references must be non-null -error: the type `&'static [i32]` does not permit zero-initialization +error: the type `&[i32]` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:85:36 | LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); diff --git a/src/test/ui/lint/use_suggestion_json.stderr b/src/test/ui/lint/use_suggestion_json.stderr index 0dc0d247af5..7176f17bc3f 100644 --- a/src/test/ui/lint/use_suggestion_json.stderr +++ b/src/test/ui/lint/use_suggestion_json.stderr @@ -2,7 +2,7 @@ "message": "cannot find type `Iter` in this scope", "code": { "code": "E0412", - "explanation": "The type name used is not in scope. + "explanation": "A used type name is not in scope. Erroneous code examples: diff --git a/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr b/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr index 41ed545cf16..2f3d48bf039 100644 --- a/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr +++ b/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr @@ -13,66 +13,70 @@ LL | pong!(); error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `error` --> $DIR/main.rs:10:20 | -LL | / macro_rules! pong { -LL | | () => { syntax error }; - | | ^^^^^ expected one of 8 possible tokens -LL | | } - | |_- in this expansion of `pong!` +LL | / macro_rules! pong { +LL | | () => { syntax error }; + | | ^^^^^ expected one of 8 possible tokens +LL | | } + | |__- in this expansion of `pong!` ... -LL | ping!(); - | -------- in this macro invocation +LL | ping!(); + | -------- in this macro invocation | - ::: <::ping::ping macros>:1:1 + ::: $DIR/auxiliary/ping.rs:5:1 | -LL | () => { pong ! () ; } - | --------------------- - | | | - | | in this macro invocation - | in this expansion of `ping!` +LL | / macro_rules! ping { +LL | | () => { +LL | | pong!(); + | | -------- in this macro invocation +LL | | } +LL | | } + | |_- in this expansion of `ping!` error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `error` --> $DIR/main.rs:10:20 | -LL | / macro_rules! pong { -LL | | () => { syntax error }; - | | ^^^^^ expected one of 8 possible tokens -LL | | } - | |_- in this expansion of `pong!` (#5) +LL | / macro_rules! pong { +LL | | () => { syntax error }; + | | ^^^^^ expected one of 8 possible tokens +LL | | } + | |__- in this expansion of `pong!` (#5) ... -LL | deep!(); - | -------- in this macro invocation (#1) +LL | deep!(); + | -------- in this macro invocation (#1) | - ::: <::ping::deep macros>:1:1 + ::: $DIR/auxiliary/ping.rs:5:1 | -LL | () => { foo ! () ; } - | -------------------- - | | | - | | in this macro invocation (#2) - | in this expansion of `deep!` (#1) - | - ::: <::ping::foo macros>:1:1 - | -LL | () => { bar ! () ; } - | -------------------- - | | | - | | in this macro invocation (#3) - | in this expansion of `foo!` (#2) - | - ::: <::ping::bar macros>:1:1 - | -LL | () => { ping ! () ; } - | --------------------- - | | | - | | in this macro invocation (#4) - | in this expansion of `bar!` (#3) - | - ::: <::ping::ping macros>:1:1 - | -LL | () => { pong ! () ; } - | --------------------- - | | | - | | in this macro invocation (#5) - | in this expansion of `ping!` (#4) +LL | / macro_rules! ping { +LL | | () => { +LL | | pong!(); + | | -------- in this macro invocation (#5) +LL | | } +LL | | } + | |_- in this expansion of `ping!` (#4) +... +LL | / macro_rules! deep { +LL | | () => { +LL | | foo!(); + | | ------- in this macro invocation (#2) +LL | | } +LL | | } + | |__- in this expansion of `deep!` (#1) +... +LL | / macro_rules! foo { +LL | | () => { +LL | | bar!(); + | | ------- in this macro invocation (#3) +LL | | } +LL | | } + | |__- in this expansion of `foo!` (#2) +... +LL | / macro_rules! bar { +LL | | () => { +LL | | ping!(); + | | -------- in this macro invocation (#4) +LL | | } +LL | | } + | |__- in this expansion of `bar!` (#3) error: aborting due to 3 previous errors diff --git a/src/test/ui/macros/assert-trailing-junk.rs b/src/test/ui/macros/assert-trailing-junk.rs index 676ae05bf0f..cd7faf9bf8b 100644 --- a/src/test/ui/macros/assert-trailing-junk.rs +++ b/src/test/ui/macros/assert-trailing-junk.rs @@ -13,12 +13,12 @@ fn main() { //~^ ERROR no rules expected assert!(true "whatever" blah); - //~^ WARN unexpected string literal + //~^ ERROR unexpected string literal //~^^ ERROR no rules expected assert!(true;); - //~^ WARN macro requires an expression + //~^ ERROR macro requires an expression assert!(false || true "error message"); - //~^ WARN unexpected string literal + //~^ ERROR unexpected string literal } diff --git a/src/test/ui/macros/assert-trailing-junk.stderr b/src/test/ui/macros/assert-trailing-junk.stderr index 4d18a531a80..84a6768b3f4 100644 --- a/src/test/ui/macros/assert-trailing-junk.stderr +++ b/src/test/ui/macros/assert-trailing-junk.stderr @@ -18,15 +18,13 @@ LL | assert!(true, "whatever" blah); | | | help: missing comma here -warning: unexpected string literal +error: unexpected string literal --> $DIR/assert-trailing-junk.rs:15:18 | LL | assert!(true "whatever" blah); | -^^^^^^^^^^ | | | help: try adding a comma - | - = note: this is going to be an error in the future error: no rules expected the token `blah` --> $DIR/assert-trailing-junk.rs:15:29 @@ -36,25 +34,21 @@ LL | assert!(true "whatever" blah); | | | help: missing comma here -warning: macro requires an expression as an argument +error: macro requires an expression as an argument --> $DIR/assert-trailing-junk.rs:19:5 | LL | assert!(true;); | ^^^^^^^^^^^^-^^ | | | help: try removing semicolon - | - = note: this is going to be an error in the future -warning: unexpected string literal +error: unexpected string literal --> $DIR/assert-trailing-junk.rs:22:27 | LL | assert!(false || true "error message"); | -^^^^^^^^^^^^^^^ | | | help: try adding a comma - | - = note: this is going to be an error in the future -error: aborting due to 4 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/macros/issue-58490.rs b/src/test/ui/macros/issue-58490.rs new file mode 100644 index 00000000000..97e71c9a1ce --- /dev/null +++ b/src/test/ui/macros/issue-58490.rs @@ -0,0 +1,26 @@ +// Regression test for #58490 + +macro_rules! a { + ( @1 $i:item ) => { + a! { @2 $i } + }; + ( @2 $i:item ) => { + $i + }; +} +mod b { + a! { + @1 + #[macro_export] + macro_rules! b { () => () } + } + #[macro_export] + macro_rules! b { () => () } + //~^ ERROR: the name `b` is defined multiple times +} +mod c { + #[allow(unused_imports)] + use crate::b; +} + +fn main() {} diff --git a/src/test/ui/macros/issue-58490.stderr b/src/test/ui/macros/issue-58490.stderr new file mode 100644 index 00000000000..b1f0896f3b6 --- /dev/null +++ b/src/test/ui/macros/issue-58490.stderr @@ -0,0 +1,14 @@ +error[E0428]: the name `b` is defined multiple times + --> $DIR/issue-58490.rs:18:5 + | +LL | macro_rules! b { () => () } + | -------------- previous definition of the macro `b` here +... +LL | macro_rules! b { () => () } + | ^^^^^^^^^^^^^^ `b` redefined here + | + = note: `b` must be defined only once in the macro namespace of this module + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0428`. diff --git a/src/test/ui/macros/macro-comma-behavior.core.stderr b/src/test/ui/macros/macro-comma-behavior.core.stderr index dd0cac659fd..83a88ab3bd9 100644 --- a/src/test/ui/macros/macro-comma-behavior.core.stderr +++ b/src/test/ui/macros/macro-comma-behavior.core.stderr @@ -1,41 +1,41 @@ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:21:23 + --> $DIR/macro-comma-behavior.rs:20:23 | LL | assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:24:23 + --> $DIR/macro-comma-behavior.rs:23:23 | LL | assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:30:29 + --> $DIR/macro-comma-behavior.rs:29:29 | LL | debug_assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:33:29 + --> $DIR/macro-comma-behavior.rs:32:29 | LL | debug_assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:54:19 + --> $DIR/macro-comma-behavior.rs:53:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:72:21 + --> $DIR/macro-comma-behavior.rs:71:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:81:24 + --> $DIR/macro-comma-behavior.rs:80:24 | LL | write!(f, "{}",)?; | ^^ diff --git a/src/test/ui/macros/macro-comma-behavior.rs b/src/test/ui/macros/macro-comma-behavior.rs index 006319aa9f5..04714c65b5c 100644 --- a/src/test/ui/macros/macro-comma-behavior.rs +++ b/src/test/ui/macros/macro-comma-behavior.rs @@ -9,7 +9,6 @@ #[cfg(std)] use std::fmt; #[cfg(core)] use core::fmt; #[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {} -#[cfg(core)] #[lang = "eh_unwind_resume"] fn eh_unwind_resume() {} #[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} } // (see documentation of the similarly-named test in run-pass) diff --git a/src/test/ui/macros/macro-comma-behavior.std.stderr b/src/test/ui/macros/macro-comma-behavior.std.stderr index 4372d89fbf5..26445f2c5c5 100644 --- a/src/test/ui/macros/macro-comma-behavior.std.stderr +++ b/src/test/ui/macros/macro-comma-behavior.std.stderr @@ -1,59 +1,59 @@ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:21:23 + --> $DIR/macro-comma-behavior.rs:20:23 | LL | assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:24:23 + --> $DIR/macro-comma-behavior.rs:23:23 | LL | assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:30:29 + --> $DIR/macro-comma-behavior.rs:29:29 | LL | debug_assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:33:29 + --> $DIR/macro-comma-behavior.rs:32:29 | LL | debug_assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:38:18 + --> $DIR/macro-comma-behavior.rs:37:18 | LL | eprint!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:50:18 + --> $DIR/macro-comma-behavior.rs:49:18 | LL | format!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:54:19 + --> $DIR/macro-comma-behavior.rs:53:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:61:17 + --> $DIR/macro-comma-behavior.rs:60:17 | LL | print!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:72:21 + --> $DIR/macro-comma-behavior.rs:71:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:81:24 + --> $DIR/macro-comma-behavior.rs:80:24 | LL | write!(f, "{}",)?; | ^^ diff --git a/src/test/ui/macros/trace-macro.stderr b/src/test/ui/macros/trace-macro.stderr index 202a9235adb..6217decd8ef 100644 --- a/src/test/ui/macros/trace-macro.stderr +++ b/src/test/ui/macros/trace-macro.stderr @@ -5,5 +5,5 @@ LL | println!("Hello, World!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expanding `println! { "Hello, World!" }` - = note: to `{ $crate :: io :: _print ($crate :: format_args_nl ! ("Hello, World!")) ; }` + = note: to `{ $crate :: io :: _print($crate :: format_args_nl ! ("Hello, World!")) ; }` diff --git a/src/test/ui/macros/unknown-builtin.rs b/src/test/ui/macros/unknown-builtin.rs index a96b99ae4ff..716a0005ba3 100644 --- a/src/test/ui/macros/unknown-builtin.rs +++ b/src/test/ui/macros/unknown-builtin.rs @@ -1,3 +1,8 @@ +// FIXME: missing sysroot spans (#53081) +// ignore-i586-unknown-linux-gnu +// ignore-i586-unknown-linux-musl +// ignore-i686-unknown-linux-musl + // error-pattern: cannot find a built-in macro with name `line` #![feature(rustc_attrs)] diff --git a/src/test/ui/macros/unknown-builtin.stderr b/src/test/ui/macros/unknown-builtin.stderr index f1e828a46d8..ed163750a6e 100644 --- a/src/test/ui/macros/unknown-builtin.stderr +++ b/src/test/ui/macros/unknown-builtin.stderr @@ -1,14 +1,18 @@ error: cannot find a built-in macro with name `unknown` - --> $DIR/unknown-builtin.rs:6:1 + --> $DIR/unknown-builtin.rs:11:1 | LL | macro_rules! unknown { () => () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot find a built-in macro with name `line` - --> <::core::macros::builtin::line macros>:1:1 + --> $SRC_DIR/libcore/macros/mod.rs:LL:COL | -LL | () => { } ; - | ^^^^^^^^^^^ +LL | / macro_rules! line { +LL | | () => { +LL | | /* compiler built-in */ +LL | | }; +LL | | } + | |_____^ error: aborting due to 2 previous errors diff --git a/src/test/ui/match/match-fn-call.stderr b/src/test/ui/match/match-fn-call.stderr index 2d7a0f16141..297aa4cd95d 100644 --- a/src/test/ui/match/match-fn-call.stderr +++ b/src/test/ui/match/match-fn-call.stderr @@ -1,4 +1,4 @@ -error[E0164]: expected tuple struct or tuple variant, found method `Path::new` +error[E0164]: expected tuple struct or tuple variant, found associated function `Path::new` --> $DIR/match-fn-call.rs:6:9 | LL | Path::new("foo") => println!("foo"), @@ -6,7 +6,7 @@ LL | Path::new("foo") => println!("foo"), | = help: for more information, visit https://doc.rust-lang.org/book/ch18-00-patterns.html -error[E0164]: expected tuple struct or tuple variant, found method `Path::new` +error[E0164]: expected tuple struct or tuple variant, found associated function `Path::new` --> $DIR/match-fn-call.rs:8:9 | LL | Path::new("bar") => println!("bar"), diff --git a/src/test/ui/match/match-tag-nullary.stderr b/src/test/ui/match/match-tag-nullary.stderr index 3703a59edb8..723c7fa92b1 100644 --- a/src/test/ui/match/match-tag-nullary.stderr +++ b/src/test/ui/match/match-tag-nullary.stderr @@ -1,6 +1,9 @@ error[E0308]: mismatched types --> $DIR/match-tag-nullary.rs:4:40 | +LL | enum B { B } + | - unit variant defined here +LL | LL | fn main() { let x: A = A::A; match x { B::B => { } } } | - ^^^^ expected enum `A`, found enum `B` | | diff --git a/src/test/ui/methods/assign-to-method.rs b/src/test/ui/methods/assign-to-method.rs new file mode 100644 index 00000000000..85beaee8df0 --- /dev/null +++ b/src/test/ui/methods/assign-to-method.rs @@ -0,0 +1,24 @@ +// compile-flags: -Zsave-analysis +// Also regression test for #69409 + +struct Cat { + meows : usize, + how_hungry : isize, +} + +impl Cat { + pub fn speak(&self) { self.meows += 1; } +} + +fn cat(in_x : usize, in_y : isize) -> Cat { + Cat { + meows: in_x, + how_hungry: in_y + } +} + +fn main() { + let nyan : Cat = cat(52, 99); + nyan.speak = || println!("meow"); //~ ERROR attempted to take value of method + nyan.speak += || println!("meow"); //~ ERROR attempted to take value of method +} diff --git a/src/test/ui/methods/assign-to-method.stderr b/src/test/ui/methods/assign-to-method.stderr new file mode 100644 index 00000000000..c0dd529b681 --- /dev/null +++ b/src/test/ui/methods/assign-to-method.stderr @@ -0,0 +1,19 @@ +error[E0615]: attempted to take value of method `speak` on type `Cat` + --> $DIR/assign-to-method.rs:22:10 + | +LL | nyan.speak = || println!("meow"); + | ^^^^^ + | + = help: methods are immutable and cannot be assigned to + +error[E0615]: attempted to take value of method `speak` on type `Cat` + --> $DIR/assign-to-method.rs:23:10 + | +LL | nyan.speak += || println!("meow"); + | ^^^^^ + | + = help: methods are immutable and cannot be assigned to + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0615`. diff --git a/src/test/ui/methods/method-on-ambiguous-numeric-type.stderr b/src/test/ui/methods/method-on-ambiguous-numeric-type.stderr index 10950834ad3..c6dde67cfeb 100644 --- a/src/test/ui/methods/method-on-ambiguous-numeric-type.stderr +++ b/src/test/ui/methods/method-on-ambiguous-numeric-type.stderr @@ -42,10 +42,13 @@ LL | ($ident:ident) => { let $ident: i32 = 42; } error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` --> $DIR/method-on-ambiguous-numeric-type.rs:30:9 | -LL | mac!(bar); - | ---------- you must specify a type for this binding, like `i32` LL | bar.pow(2); | ^^^ + | +help: you must specify a type for this binding, like `i32` + | +LL | ($ident:ident) => { let $ident: i32 = 42; } + | ^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/src/test/ui/methods/method-path-in-pattern.rs b/src/test/ui/methods/method-path-in-pattern.rs index f94be1734b7..40645309552 100644 --- a/src/test/ui/methods/method-path-in-pattern.rs +++ b/src/test/ui/methods/method-path-in-pattern.rs @@ -13,20 +13,20 @@ impl MyTrait for Foo {} fn main() { match 0u32 { Foo::bar => {} - //~^ ERROR expected unit struct, unit variant or constant, found method `Foo::bar` + //~^ ERROR expected unit struct, unit variant or constant, found associated function } match 0u32 { ::bar => {} - //~^ ERROR expected unit struct, unit variant or constant, found method `Foo::bar` + //~^ ERROR expected unit struct, unit variant or constant, found associated function } match 0u32 { ::trait_bar => {} - //~^ ERROR expected unit struct, unit variant or constant, found method `Foo::trait_bar` + //~^ ERROR expected unit struct, unit variant or constant, found associated function } if let Foo::bar = 0u32 {} - //~^ ERROR expected unit struct, unit variant or constant, found method `Foo::bar` + //~^ ERROR expected unit struct, unit variant or constant, found associated function if let ::bar = 0u32 {} - //~^ ERROR expected unit struct, unit variant or constant, found method `Foo::bar` + //~^ ERROR expected unit struct, unit variant or constant, found associated function if let Foo::trait_bar = 0u32 {} - //~^ ERROR expected unit struct, unit variant or constant, found method `Foo::trait_bar` + //~^ ERROR expected unit struct, unit variant or constant, found associated function } diff --git a/src/test/ui/methods/method-path-in-pattern.stderr b/src/test/ui/methods/method-path-in-pattern.stderr index 6b0c5946ff8..1d1bdb6b052 100644 --- a/src/test/ui/methods/method-path-in-pattern.stderr +++ b/src/test/ui/methods/method-path-in-pattern.stderr @@ -1,34 +1,34 @@ -error[E0533]: expected unit struct, unit variant or constant, found method `Foo::bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar` --> $DIR/method-path-in-pattern.rs:15:9 | LL | Foo::bar => {} | ^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found method `Foo::bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar` --> $DIR/method-path-in-pattern.rs:19:9 | LL | ::bar => {} | ^^^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found method `Foo::trait_bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::trait_bar` --> $DIR/method-path-in-pattern.rs:23:9 | LL | ::trait_bar => {} | ^^^^^^^^^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found method `Foo::bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar` --> $DIR/method-path-in-pattern.rs:26:12 | LL | if let Foo::bar = 0u32 {} | ^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found method `Foo::bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar` --> $DIR/method-path-in-pattern.rs:28:12 | LL | if let ::bar = 0u32 {} | ^^^^^^^^^^ -error[E0533]: expected unit struct, unit variant or constant, found method `Foo::trait_bar` +error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::trait_bar` --> $DIR/method-path-in-pattern.rs:30:12 | LL | if let Foo::trait_bar = 0u32 {} diff --git a/src/test/ui/methods/method-resolvable-path-in-pattern.rs b/src/test/ui/methods/method-resolvable-path-in-pattern.rs index c05160792d3..2973800a4d4 100644 --- a/src/test/ui/methods/method-resolvable-path-in-pattern.rs +++ b/src/test/ui/methods/method-resolvable-path-in-pattern.rs @@ -9,6 +9,6 @@ impl MyTrait for Foo {} fn main() { match 0u32 { ::trait_bar => {} - //~^ ERROR expected unit struct, unit variant or constant, found method `MyTrait::trait_bar` + //~^ ERROR expected unit struct, unit variant or constant, found associated function } } diff --git a/src/test/ui/methods/method-resolvable-path-in-pattern.stderr b/src/test/ui/methods/method-resolvable-path-in-pattern.stderr index 4b25b694e13..7c454a9a777 100644 --- a/src/test/ui/methods/method-resolvable-path-in-pattern.stderr +++ b/src/test/ui/methods/method-resolvable-path-in-pattern.stderr @@ -1,4 +1,4 @@ -error[E0532]: expected unit struct, unit variant or constant, found method `MyTrait::trait_bar` +error[E0532]: expected unit struct, unit variant or constant, found associated function `MyTrait::trait_bar` --> $DIR/method-resolvable-path-in-pattern.rs:11:9 | LL | ::trait_bar => {} diff --git a/src/test/ui/mir/issue-60390.rs b/src/test/ui/mir/issue-60390.rs new file mode 100644 index 00000000000..fd9d6b46dd4 --- /dev/null +++ b/src/test/ui/mir/issue-60390.rs @@ -0,0 +1,8 @@ +// check-pass +// compile-flags: --emit=mir,link +// Regression test for #60390, this ICE requires `--emit=mir` flag. + +fn main() { + enum Inner { Member(u32) }; + Inner::Member(0); +} diff --git a/src/test/ui/mir/mir_assign_eval_order.rs b/src/test/ui/mir/mir_assign_eval_order.rs index 1594421b0b1..799bf7f3a12 100644 --- a/src/test/ui/mir/mir_assign_eval_order.rs +++ b/src/test/ui/mir/mir_assign_eval_order.rs @@ -12,7 +12,7 @@ fn evaluate_reborrow_before_assign() { let y = &mut &2; let z = &3; // There's an implicit reborrow of `x` on the right-hand side of the - // assignement. Note that writing an explicit reborrow would not show this + // assignment. Note that writing an explicit reborrow would not show this // bug, as now there would be two reborrows on the right-hand side and at // least one of them would happen before the left-hand side is evaluated. *{ x = z; &mut *y } = x; diff --git a/src/test/ui/mismatched_types/E0409.stderr b/src/test/ui/mismatched_types/E0409.stderr index f5c8b02ae27..ef03b67b1b0 100644 --- a/src/test/ui/mismatched_types/E0409.stderr +++ b/src/test/ui/mismatched_types/E0409.stderr @@ -1,4 +1,4 @@ -error[E0409]: variable `y` is bound in inconsistent ways within the same match arm +error[E0409]: variable `y` is bound inconsistently across alternatives separated by `|` --> $DIR/E0409.rs:5:23 | LL | (0, ref y) | (y, 0) => {} diff --git a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr index 98b74e5f5cb..91b3fe15c4b 100644 --- a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr +++ b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `missing` - --> $DIR/foo.rs:4:5 + --> $DIR/foo.rs:4:1 | LL | mod missing; - | ^^^^^^^ + | ^^^^^^^^^^^^ | - = help: name the file either foo/missing.rs or foo/missing/mod.rs inside the directory "$DIR" + = help: to create the module `missing`, create file "$DIR/foo/missing.rs" error: aborting due to previous error diff --git a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr index 457e8fcccbf..f519de46c76 100644 --- a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr +++ b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `missing` - --> $DIR/foo_inline.rs:4:9 + --> $DIR/foo_inline.rs:4:5 | LL | mod missing; - | ^^^^^^^ + | ^^^^^^^^^^^^ | - = help: name the file either missing.rs or missing/mod.rs inside the directory "$DIR/foo_inline/inline" + = help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs" error: aborting due to previous error diff --git a/src/test/ui/mod/mod_file_disambig.rs b/src/test/ui/mod/mod_file_disambig.rs index ef203ef082b..7b182421d34 100644 --- a/src/test/ui/mod/mod_file_disambig.rs +++ b/src/test/ui/mod/mod_file_disambig.rs @@ -2,4 +2,5 @@ fn main() { assert_eq!(mod_file_aux::bar(), 10); + //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` } diff --git a/src/test/ui/mod/mod_file_disambig.stderr b/src/test/ui/mod/mod_file_disambig.stderr index 2b77d866fb3..490633a3fb0 100644 --- a/src/test/ui/mod/mod_file_disambig.stderr +++ b/src/test/ui/mod/mod_file_disambig.stderr @@ -1,11 +1,18 @@ error[E0584]: file for module `mod_file_disambig_aux` found at both mod_file_disambig_aux.rs and mod_file_disambig_aux/mod.rs - --> $DIR/mod_file_disambig.rs:1:5 + --> $DIR/mod_file_disambig.rs:1:1 | LL | mod mod_file_disambig_aux; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: delete or rename one of them to remove the ambiguity -error: aborting due to previous error +error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` + --> $DIR/mod_file_disambig.rs:4:16 + | +LL | assert_eq!(mod_file_aux::bar(), 10); + | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0584`. +Some errors have detailed explanations: E0433, E0584. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/never_type/panic-uninitialized-zeroed.rs b/src/test/ui/never_type/panic-uninitialized-zeroed.rs deleted file mode 100644 index e0c30160b9e..00000000000 --- a/src/test/ui/never_type/panic-uninitialized-zeroed.rs +++ /dev/null @@ -1,102 +0,0 @@ -// run-pass -// ignore-wasm32-bare compiled with panic=abort by default -// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results -// in a runtime panic. - -#![feature(never_type)] -#![allow(deprecated, invalid_value)] - -use std::{mem, panic}; - -#[allow(dead_code)] -struct Foo { - x: u8, - y: !, -} - -enum Bar {} - -fn main() { - unsafe { - assert_eq!( - panic::catch_unwind(|| { - mem::uninitialized::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type !" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::zeroed::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type !" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::MaybeUninit::::uninit().assume_init() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type !" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::uninitialized::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Foo" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::zeroed::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Foo" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::MaybeUninit::::uninit().assume_init() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Foo" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::uninitialized::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Bar" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::zeroed::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Bar" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::MaybeUninit::::uninit().assume_init() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Bar" - })), - Some(true) - ); - } -} diff --git a/src/test/ui/nll/dont-print-desugared.stderr b/src/test/ui/nll/dont-print-desugared.stderr index 45d7cbcdfbe..88773def8b7 100644 --- a/src/test/ui/nll/dont-print-desugared.stderr +++ b/src/test/ui/nll/dont-print-desugared.stderr @@ -2,10 +2,7 @@ error[E0596]: cannot borrow data in a `&` reference as mutable --> $DIR/dont-print-desugared.rs:4:10 | LL | for &ref mut x in s {} - | -^^^^^^^^^ - | || - | |cannot borrow as mutable through `&` reference - | help: consider changing this to be a mutable reference: `&mut ref mut x` + | ^^^^^^^^^ cannot borrow as mutable through `&` reference error[E0597]: `y` does not live long enough --> $DIR/dont-print-desugared.rs:17:16 diff --git a/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs b/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs index 0d96767a05d..a111acca66f 100644 --- a/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs +++ b/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs @@ -1,4 +1,4 @@ -// Ths test case is exploring the space of how blocs with tail +// This test case is exploring the space of how blocks with tail // expressions and statements can be composed, trying to keep each // case on one line so that we can compare them via a vertical scan // with the human eye. diff --git a/src/test/ui/nll/outlives-suggestion-simple.rs b/src/test/ui/nll/outlives-suggestion-simple.rs index ee5a80ae648..41e4d83aa92 100644 --- a/src/test/ui/nll/outlives-suggestion-simple.rs +++ b/src/test/ui/nll/outlives-suggestion-simple.rs @@ -70,7 +70,7 @@ pub struct Foo2<'a> { impl<'a> Foo2<'a> { // should not produce outlives suggestions to name 'self fn get_bar(&self) -> Bar2 { - Bar2::new(&self) //~ERROR borrowed data escapes outside of method + Bar2::new(&self) //~ERROR borrowed data escapes outside of associated function } } diff --git a/src/test/ui/nll/outlives-suggestion-simple.stderr b/src/test/ui/nll/outlives-suggestion-simple.stderr index cf55603cd71..6300ea66511 100644 --- a/src/test/ui/nll/outlives-suggestion-simple.stderr +++ b/src/test/ui/nll/outlives-suggestion-simple.stderr @@ -93,16 +93,16 @@ LL | self.x | = help: consider adding the following bound: `'b: 'a` -error[E0521]: borrowed data escapes outside of method +error[E0521]: borrowed data escapes outside of associated function --> $DIR/outlives-suggestion-simple.rs:73:9 | LL | fn get_bar(&self) -> Bar2 { | ----- | | - | `self` declared here, outside of the method body - | `self` is a reference that is only valid in the method body + | `self` declared here, outside of the associated function body + | `self` is a reference that is only valid in the associated function body LL | Bar2::new(&self) - | ^^^^^^^^^^^^^^^^ `self` escapes the method body here + | ^^^^^^^^^^^^^^^^ `self` escapes the associated function body here error: aborting due to 9 previous errors diff --git a/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs b/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs new file mode 100644 index 00000000000..1f7c060386b --- /dev/null +++ b/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs @@ -0,0 +1,33 @@ +// Regression test for issue #69490 + +// check-pass + +pub trait Trait { + const S: &'static str; +} + +impl Trait<()> for T +where + T: for<'a> Trait<&'a ()>, +{ + // Use of `T::S` here caused an ICE + const S: &'static str = T::S; +} + +// Some similar cases that didn't ICE: + +impl<'a, T> Trait<()> for (T,) +where + T: Trait<&'a ()>, +{ + const S: &'static str = T::S; +} + +impl Trait<()> for [T; 1] +where + T: Trait fn(&'a ())>, +{ + const S: &'static str = T::S; +} + +fn main() {} diff --git a/src/test/ui/no-landing-pads.rs b/src/test/ui/no-landing-pads.rs deleted file mode 100644 index d9d53210612..00000000000 --- a/src/test/ui/no-landing-pads.rs +++ /dev/null @@ -1,23 +0,0 @@ -// run-pass -// compile-flags: -Z no-landing-pads -C codegen-units=1 -// ignore-emscripten no threads support - -use std::thread; - -static mut HIT: bool = false; - -struct A; - -impl Drop for A { - fn drop(&mut self) { - unsafe { HIT = true; } - } -} - -fn main() { - thread::spawn(move|| -> () { - let _a = A; - panic!(); - }).join().unwrap_err(); - assert!(unsafe { !HIT }); -} diff --git a/src/test/ui/no_owned_box_lang_item.rs b/src/test/ui/no_owned_box_lang_item.rs index b76699c19ac..58e45ff73a5 100644 --- a/src/test/ui/no_owned_box_lang_item.rs +++ b/src/test/ui/no_owned_box_lang_item.rs @@ -12,5 +12,4 @@ fn main() { } #[lang = "eh_personality"] extern fn eh_personality() {} -#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {} #[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} } diff --git a/src/test/ui/on-unimplemented/feature-gate-on-unimplemented.stderr b/src/test/ui/on-unimplemented/feature-gate-on-unimplemented.stderr index 71baf92b2d4..a4b33963fb0 100644 --- a/src/test/ui/on-unimplemented/feature-gate-on-unimplemented.stderr +++ b/src/test/ui/on-unimplemented/feature-gate-on-unimplemented.stderr @@ -4,7 +4,6 @@ error[E0658]: this is an internal attribute that will never be stable LL | #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/or-patterns/box-patterns.rs b/src/test/ui/or-patterns/box-patterns.rs new file mode 100644 index 00000000000..aafd4799383 --- /dev/null +++ b/src/test/ui/or-patterns/box-patterns.rs @@ -0,0 +1,37 @@ +// Test or-patterns with box-patterns + +// run-pass + +#![feature(or_patterns)] +#![feature(box_patterns)] + +#[derive(Debug, PartialEq)] +enum MatchArm { + Arm(usize), + Wild, +} + +#[derive(Debug)] +enum Test { + Foo, + Bar, + Baz, + Qux, +} + +fn test(x: Option>) -> MatchArm { + match x { + Some(box Test::Foo | box Test::Bar) => MatchArm::Arm(0), + Some(box Test::Baz) => MatchArm::Arm(1), + Some(_) => MatchArm::Arm(2), + _ => MatchArm::Wild, + } +} + +fn main() { + assert_eq!(test(Some(Box::new(Test::Foo))), MatchArm::Arm(0)); + assert_eq!(test(Some(Box::new(Test::Bar))), MatchArm::Arm(0)); + assert_eq!(test(Some(Box::new(Test::Baz))), MatchArm::Arm(1)); + assert_eq!(test(Some(Box::new(Test::Qux))), MatchArm::Arm(2)); + assert_eq!(test(None), MatchArm::Wild); +} diff --git a/src/test/ui/or-patterns/inconsistent-modes.rs b/src/test/ui/or-patterns/inconsistent-modes.rs index 28b5f0c02fe..fd5cb01ab42 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.rs +++ b/src/test/ui/or-patterns/inconsistent-modes.rs @@ -5,22 +5,22 @@ fn main() { // One level: let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0); - //~^ ERROR variable `a` is bound in inconsistent ways + //~^ ERROR variable `a` is bound inconsistently let Ok(ref mut a) | Err(a): Result = Ok(0); - //~^ ERROR variable `a` is bound in inconsistent ways + //~^ ERROR variable `a` is bound inconsistently let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); - //~^ ERROR variable `a` is bound in inconsistent ways + //~^ ERROR variable `a` is bound inconsistently //~| ERROR mismatched types let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); - //~^ ERROR variable `a` is bound in inconsistent ways - //~| ERROR variable `b` is bound in inconsistent ways + //~^ ERROR variable `a` is bound inconsistently + //~| ERROR variable `b` is bound inconsistently //~| ERROR mismatched types // Two levels: let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0); - //~^ ERROR variable `a` is bound in inconsistent ways + //~^ ERROR variable `a` is bound inconsistently // Three levels: - let Ok([ Ok((Ok(ref a) | Err(a),)) | Err(a) ]) | Err(a) = Err(&1); - //~^ ERROR variable `a` is bound in inconsistent ways + let Ok([Ok((Ok(ref a) | Err(a),)) | Err(a)]) | Err(a) = Err(&1); + //~^ ERROR variable `a` is bound inconsistently } diff --git a/src/test/ui/or-patterns/inconsistent-modes.stderr b/src/test/ui/or-patterns/inconsistent-modes.stderr index 8c01e00bae3..c5dcef36e05 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.stderr +++ b/src/test/ui/or-patterns/inconsistent-modes.stderr @@ -1,4 +1,4 @@ -error[E0409]: variable `a` is bound in inconsistent ways within the same match arm +error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` --> $DIR/inconsistent-modes.rs:7:25 | LL | let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0); @@ -6,7 +6,7 @@ LL | let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0); | | | first binding -error[E0409]: variable `a` is bound in inconsistent ways within the same match arm +error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` --> $DIR/inconsistent-modes.rs:9:29 | LL | let Ok(ref mut a) | Err(a): Result = Ok(0); @@ -14,25 +14,25 @@ LL | let Ok(ref mut a) | Err(a): Result = Ok(0); | | | first binding -error[E0409]: variable `a` is bound in inconsistent ways within the same match arm +error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` --> $DIR/inconsistent-modes.rs:11:33 | LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); | - first binding ^ bound in different ways -error[E0409]: variable `a` is bound in inconsistent ways within the same match arm +error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` --> $DIR/inconsistent-modes.rs:14:39 | LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); | - first binding ^ bound in different ways -error[E0409]: variable `b` is bound in inconsistent ways within the same match arm +error[E0409]: variable `b` is bound inconsistently across alternatives separated by `|` --> $DIR/inconsistent-modes.rs:14:46 | LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); | - first binding ^ bound in different ways -error[E0409]: variable `a` is bound in inconsistent ways within the same match arm +error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` --> $DIR/inconsistent-modes.rs:20:38 | LL | let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0); @@ -40,13 +40,13 @@ LL | let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0); | | | first binding -error[E0409]: variable `a` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:24:34 +error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` + --> $DIR/inconsistent-modes.rs:24:33 | -LL | let Ok([ Ok((Ok(ref a) | Err(a),)) | Err(a) ]) | Err(a) = Err(&1); - | - ^ bound in different ways - | | - | first binding +LL | let Ok([Ok((Ok(ref a) | Err(a),)) | Err(a)]) | Err(a) = Err(&1); + | - ^ bound in different ways + | | + | first binding error[E0308]: mismatched types --> $DIR/inconsistent-modes.rs:11:25 diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs new file mode 100644 index 00000000000..59533cefea6 --- /dev/null +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs @@ -0,0 +1,9 @@ +#![feature(or_patterns)] + +fn main() { + let 0 | (1 | 2) = 0; //~ ERROR refutable pattern in local binding + match 0 { + //~^ ERROR non-exhaustive patterns + 0 | (1 | 2) => {} + } +} diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr new file mode 100644 index 00000000000..58286e87869 --- /dev/null +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -0,0 +1,25 @@ +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:4:9 + | +LL | let 0 | (1 | 2) = 0; + | ^^^^^^^^^^^ patterns `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html +help: you might want to use `if let` to ignore the variant that isn't matched + | +LL | if let 0 | (1 | 2) = 0 { /* */ } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0004]: non-exhaustive patterns: `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:5:11 + | +LL | match 0 { + | ^ patterns `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0004, E0005. +For more information about an error, try `rustc --explain E0004`. diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs new file mode 100644 index 00000000000..1de563dedbf --- /dev/null +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs @@ -0,0 +1,9 @@ +// check-pass + +#![feature(or_patterns)] + +fn main() { + let 0 | (1 | _) = 0; + if let 0 | (1 | 2) = 0 {} + if let x @ 0 | x @ (1 | 2) = 0 {} +} diff --git a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr index d5e029d668d..1dabb7c9754 100644 --- a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr +++ b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr @@ -101,7 +101,7 @@ LL | if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) { | | expected `usize`, found `isize` | first introduced with type `usize` here | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:38:47 @@ -112,7 +112,7 @@ LL | if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) | | expected `usize`, found `isize` | first introduced with type `usize` here | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:42:22 @@ -123,7 +123,7 @@ LL | if let (x, y) | (y, x) = (0u8, 1u16) { | | expected `u16`, found `u8` | first introduced with type `u16` here | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:42:25 @@ -134,7 +134,7 @@ LL | if let (x, y) | (y, x) = (0u8, 1u16) { | | expected `u8`, found `u16` | first introduced with type `u8` here | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:47:44 @@ -147,7 +147,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) LL | = Some((0u8, Some((1u16, 2u32)))) | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:47:53 @@ -160,7 +160,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) LL | = Some((0u8, Some((1u16, 2u32)))) | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:47:62 @@ -173,7 +173,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) LL | = Some((0u8, Some((1u16, 2u32)))) | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:47:65 @@ -184,7 +184,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) LL | = Some((0u8, Some((1u16, 2u32)))) | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` | - = note: in the same arm, a binding must have the same type in all alternatives + = note: a binding must have the same type in all alternatives error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:55:39 diff --git a/src/test/ui/or-patterns/slice-patterns.rs b/src/test/ui/or-patterns/slice-patterns.rs new file mode 100644 index 00000000000..05c907e8246 --- /dev/null +++ b/src/test/ui/or-patterns/slice-patterns.rs @@ -0,0 +1,53 @@ +// Test or-patterns with slice-patterns + +// run-pass + +#![feature(or_patterns)] + +#[derive(Debug, PartialEq)] +enum MatchArm { + Arm(usize), + Wild, +} + +#[derive(Debug)] +enum Test { + Foo, + Bar, + Baz, + Qux, +} + +fn test(foo: &[Option]) -> MatchArm { + match foo { + [.., Some(Test::Qux | Test::Foo)] => MatchArm::Arm(0), + [Some(Test::Foo), .., Some(Test::Baz | Test::Bar)] => MatchArm::Arm(1), + [.., Some(Test::Bar | Test::Baz), _] => MatchArm::Arm(2), + _ => MatchArm::Wild, + } +} + +fn main() { + let foo = vec![ + Some(Test::Foo), + Some(Test::Bar), + Some(Test::Baz), + Some(Test::Qux), + ]; + + // path 1a + assert_eq!(test(&foo), MatchArm::Arm(0)); + // path 1b + assert_eq!(test(&[Some(Test::Bar), Some(Test::Foo)]), MatchArm::Arm(0)); + // path 2a + assert_eq!(test(&foo[..3]), MatchArm::Arm(1)); + // path 2b + assert_eq!(test(&[Some(Test::Foo), Some(Test::Foo), Some(Test::Bar)]), MatchArm::Arm(1)); + // path 3a + assert_eq!(test(&foo[1..3]), MatchArm::Arm(2)); + // path 3b + assert_eq!(test(&[Some(Test::Bar), Some(Test::Baz), Some(Test::Baz), Some(Test::Bar)]), + MatchArm::Arm(2)); + // path 4 + assert_eq!(test(&foo[4..]), MatchArm::Wild); +} diff --git a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs index abe34a39caf..3e5cdad7ab9 100644 --- a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs +++ b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs @@ -11,5 +11,3 @@ fn panic_impl(info: &PanicInfo) -> ! { loop {} } #[lang = "eh_personality"] fn eh_personality() {} -#[lang = "eh_unwind_resume"] -fn eh_unwind_resume() {} diff --git a/src/test/ui/panic-while-printing.rs b/src/test/ui/panic-while-printing.rs new file mode 100644 index 00000000000..7e9fa16b084 --- /dev/null +++ b/src/test/ui/panic-while-printing.rs @@ -0,0 +1,24 @@ +// run-pass +// ignore-emscripten no subprocess support + +#![feature(set_stdio)] + +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::io::set_panic; + +pub struct A; + +impl Display for A { + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + panic!(); + } +} + +fn main() { + set_panic(Some(Box::new(Vec::new()))); + assert!(std::panic::catch_unwind(|| { + eprintln!("{}", A); + }) + .is_err()); +} diff --git a/src/test/ui/parser/attr-dangling-in-fn.stderr b/src/test/ui/parser/attr-dangling-in-fn.stderr index 71488d2e5c3..b1bb3ab3b17 100644 --- a/src/test/ui/parser/attr-dangling-in-fn.stderr +++ b/src/test/ui/parser/attr-dangling-in-fn.stderr @@ -1,8 +1,8 @@ error: expected statement after outer attribute - --> $DIR/attr-dangling-in-fn.rs:5:1 + --> $DIR/attr-dangling-in-fn.rs:4:3 | -LL | } - | ^ +LL | #[foo = "bar"] + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs index 118bff8144c..09f494bdc2f 100644 --- a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs @@ -38,42 +38,36 @@ fn main() {} //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; } -//~^ ERROR attributes are not yet allowed on `if` expressions #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } //~^ ERROR expected one of #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } -//~^ ERROR attributes are not yet allowed on `if` expressions -//~| ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; } -//~^ ERROR attributes are not yet allowed on `if` expressions #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } //~^ ERROR expected one of #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } -//~^ ERROR attributes are not yet allowed on `if` expressions -//~| ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr index 4775b9b7bc0..6dfe7aad6ea 100644 --- a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr @@ -136,23 +136,17 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. -error: attributes are not yet allowed on `if` expressions - --> $DIR/attr-stmt-expr-attr-bad.rs:41:32 - | -LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; } - | ^^^^^^^ - -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:43:37 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:41:37 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } - | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:45:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:43:38 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } | ^^^^^^^^ @@ -160,75 +154,65 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:47:40 + --> $DIR/attr-stmt-expr-attr-bad.rs:45:40 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:49:45 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:47:45 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } - | ^ --- help: try placing this code inside a block: `{ {}; }` - | | - | expected `{` + | ---- ^^^^^^^ -- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:51:46 + --> $DIR/attr-stmt-expr-attr-bad.rs:49:46 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. -error: attributes are not yet allowed on `if` expressions - --> $DIR/attr-stmt-expr-attr-bad.rs:53:45 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:51:45 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } - | ^^^^^^^ + | ---- ^^^^^^^ ------- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:53:45 - | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } - | ^ -------- help: try placing this code inside a block: `{ if 0 {}; }` - | | - | expected `{` - -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:56:50 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:53:50 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } - | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:58:51 + --> $DIR/attr-stmt-expr-attr-bad.rs:55:51 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. -error: attributes are not yet allowed on `if` expressions - --> $DIR/attr-stmt-expr-attr-bad.rs:60:32 - | -LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; } - | ^^^^^^^ - -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:62:45 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:57:45 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } - | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:64:46 + --> $DIR/attr-stmt-expr-attr-bad.rs:59:46 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } | ^^^^^^^^ @@ -236,52 +220,48 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:66:48 + --> $DIR/attr-stmt-expr-attr-bad.rs:61:48 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:68:53 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:63:53 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } - | ^ --- help: try placing this code inside a block: `{ {}; }` - | | - | expected `{` + | ---- ^^^^^^^ -- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:70:54 + --> $DIR/attr-stmt-expr-attr-bad.rs:65:54 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. -error: attributes are not yet allowed on `if` expressions - --> $DIR/attr-stmt-expr-attr-bad.rs:72:53 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:67:53 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } - | ^^^^^^^ + | ---- ^^^^^^^ --------------- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:72:53 - | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } - | ^ ---------------- help: try placing this code inside a block: `{ if let _ = 0 {}; }` - | | - | expected `{` - -error: expected `{`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:75:66 +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/attr-stmt-expr-attr-bad.rs:69:66 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } - | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:77:67 + --> $DIR/attr-stmt-expr-attr-bad.rs:71:67 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } | ^^^^^^^^ @@ -289,57 +269,57 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]} = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:80:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:74:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } - | ------- ^^^^^^^^ not permitted following an outer attibute + | ------- ^^^^^^^^ not permitted following an outer attribute | | | previous outer attribute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:82:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:76:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } - | ------- ^^^^^^^^ not permitted following an outer attibute + | ------- ^^^^^^^^ not permitted following an outer attribute | | | previous outer attribute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:84:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:78:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } - | ------- ^^^^^^^^ not permitted following an outer attibute + | ------- ^^^^^^^^ not permitted following an outer attribute | | | previous outer attribute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:86:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:80:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } - | ------- ^^^^^^^^ not permitted following an outer attibute + | ------- ^^^^^^^^ not permitted following an outer attribute | | | previous outer attribute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:88:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:82:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } - | ------- ^^^^^^^^ not permitted following an outer attibute + | ------- ^^^^^^^^ not permitted following an outer attribute | | | previous outer attribute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. error[E0586]: inclusive range with no end - --> $DIR/attr-stmt-expr-attr-bad.rs:94:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:88:35 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } | ^^^ help: use `..` instead @@ -347,13 +327,13 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) error: expected one of `=>`, `if`, or `|`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:94:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:88:38 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } | ^ expected one of `=>`, `if`, or `|` error[E0586]: inclusive range with no end - --> $DIR/attr-stmt-expr-attr-bad.rs:97:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:91:35 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } | ^^^ help: use `..` instead @@ -361,19 +341,19 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) error: expected one of `=>`, `if`, or `|`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:97:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:91:38 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } | ^ expected one of `=>`, `if`, or `|` error: unexpected token: `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:100:39 + --> $DIR/attr-stmt-expr-attr-bad.rs:94:39 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } | ^ error[E0586]: inclusive range with no end - --> $DIR/attr-stmt-expr-attr-bad.rs:102:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:96:35 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } | ^^^ help: use `..` instead @@ -381,47 +361,47 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) error: expected one of `=>`, `if`, or `|`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:102:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:96:38 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } | ^ expected one of `=>`, `if`, or `|` error: unexpected token: `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:106:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:100:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } | ^ error: expected one of `.`, `;`, `?`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:106:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:100:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } | ^ expected one of `.`, `;`, `?`, or an operator error: unexpected token: `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:109:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:103:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } | ^ error: expected one of `.`, `;`, `?`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:109:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:103:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } | ^ expected one of `.`, `;`, `?`, or an operator error: expected statement after outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:114:44 + --> $DIR/attr-stmt-expr-attr-bad.rs:108:37 | LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } - | ^ + | ^^^^^^^ error: expected statement after outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:116:45 + --> $DIR/attr-stmt-expr-attr-bad.rs:110:37 | LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } } - | ^ + | ^^^^^^^ -error: aborting due to 57 previous errors +error: aborting due to 53 previous errors For more information about this error, try `rustc --explain E0586`. diff --git a/src/test/ui/parser/bad-interpolated-block.rs b/src/test/ui/parser/bad-interpolated-block.rs new file mode 100644 index 00000000000..38d53a14bc0 --- /dev/null +++ b/src/test/ui/parser/bad-interpolated-block.rs @@ -0,0 +1,15 @@ +#![feature(label_break_value)] + +fn main() {} + +macro_rules! m { + ($b:block) => { + 'lab: $b; //~ ERROR cannot use a `block` macro fragment here + unsafe $b; //~ ERROR cannot use a `block` macro fragment here + |x: u8| -> () $b; //~ ERROR cannot use a `block` macro fragment here + } +} + +fn foo() { + m!({}); +} diff --git a/src/test/ui/parser/bad-interpolated-block.stderr b/src/test/ui/parser/bad-interpolated-block.stderr new file mode 100644 index 00000000000..2cbb6a13e74 --- /dev/null +++ b/src/test/ui/parser/bad-interpolated-block.stderr @@ -0,0 +1,39 @@ +error: cannot use a `block` macro fragment here + --> $DIR/bad-interpolated-block.rs:7:15 + | +LL | 'lab: $b; + | ------^^ + | | + | the `block` fragment is within this context +... +LL | m!({}); + | ------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: cannot use a `block` macro fragment here + --> $DIR/bad-interpolated-block.rs:8:16 + | +LL | unsafe $b; + | -------^^ + | | + | the `block` fragment is within this context +... +LL | m!({}); + | ------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: cannot use a `block` macro fragment here + --> $DIR/bad-interpolated-block.rs:9:23 + | +LL | |x: u8| -> () $b; + | ^^ the `block` fragment is within this context +... +LL | m!({}); + | ------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/parser/block-no-opening-brace.rs b/src/test/ui/parser/block-no-opening-brace.rs new file mode 100644 index 00000000000..e4bb39f6836 --- /dev/null +++ b/src/test/ui/parser/block-no-opening-brace.rs @@ -0,0 +1,31 @@ +// edition:2018 + +#![feature(try_blocks)] + +fn main() {} + +fn f1() { + loop + let x = 0; //~ ERROR expected `{`, found keyword `let` + drop(0); + } + +fn f2() { + while true + let x = 0; //~ ERROR expected `{`, found keyword `let` + } + +fn f3() { + for x in 0..1 + let x = 0; //~ ERROR expected `{`, found keyword `let` + } + +fn f4() { + try //~ ERROR expected expression, found reserved keyword `try` + let x = 0; + } + +fn f5() { + async //~ ERROR async closures are unstable + let x = 0; //~ ERROR expected one of `move`, `|`, or `||`, found keyword `let` + } diff --git a/src/test/ui/parser/block-no-opening-brace.stderr b/src/test/ui/parser/block-no-opening-brace.stderr new file mode 100644 index 00000000000..a88e4ac44cf --- /dev/null +++ b/src/test/ui/parser/block-no-opening-brace.stderr @@ -0,0 +1,53 @@ +error: expected `{`, found keyword `let` + --> $DIR/block-no-opening-brace.rs:9:9 + | +LL | let x = 0; + | ^^^------- + | | + | expected `{` + | help: try placing this code inside a block: `{ let x = 0; }` + +error: expected `{`, found keyword `let` + --> $DIR/block-no-opening-brace.rs:15:9 + | +LL | let x = 0; + | ^^^------- + | | + | expected `{` + | help: try placing this code inside a block: `{ let x = 0; }` + +error: expected `{`, found keyword `let` + --> $DIR/block-no-opening-brace.rs:20:9 + | +LL | let x = 0; + | ^^^------- + | | + | expected `{` + | help: try placing this code inside a block: `{ let x = 0; }` + +error: expected expression, found reserved keyword `try` + --> $DIR/block-no-opening-brace.rs:24:5 + | +LL | try + | ^^^ expected expression + +error: expected one of `move`, `|`, or `||`, found keyword `let` + --> $DIR/block-no-opening-brace.rs:30:9 + | +LL | async + | - expected one of `move`, `|`, or `||` +LL | let x = 0; + | ^^^ unexpected token + +error[E0658]: async closures are unstable + --> $DIR/block-no-opening-brace.rs:29:5 + | +LL | async + | ^^^^^ + | + = note: see issue #62290 for more information + = help: add `#![feature(async_closure)]` to the crate attributes to enable + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/parser/bounds-lifetime.rs b/src/test/ui/parser/bounds-lifetime.rs index 9225cfce94e..c9251ac5321 100644 --- a/src/test/ui/parser/bounds-lifetime.rs +++ b/src/test/ui/parser/bounds-lifetime.rs @@ -6,6 +6,6 @@ type A = for<'a: 'b,> fn(); // OK(rejected later by ast_validation) type A = for<'a: 'b +> fn(); // OK (rejected later by ast_validation) type A = for<'a, T> fn(); // OK (rejected later by ast_validation) -type A = for<,> fn(); //~ ERROR expected one of `>`, `const`, identifier, or lifetime, found `,` +type A = for<,> fn(); //~ ERROR expected one of `#`, `>`, `const`, identifier, or lifetime fn main() {} diff --git a/src/test/ui/parser/bounds-lifetime.stderr b/src/test/ui/parser/bounds-lifetime.stderr index 12b9b61ebd1..e47a21d892b 100644 --- a/src/test/ui/parser/bounds-lifetime.stderr +++ b/src/test/ui/parser/bounds-lifetime.stderr @@ -1,8 +1,8 @@ -error: expected one of `>`, `const`, identifier, or lifetime, found `,` +error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,` --> $DIR/bounds-lifetime.rs:9:14 | LL | type A = for<,> fn(); - | ^ expected one of `>`, `const`, identifier, or lifetime + | ^ expected one of `#`, `>`, `const`, identifier, or lifetime error: aborting due to previous error diff --git a/src/test/ui/parser/circular_modules_main.rs b/src/test/ui/parser/circular_modules_main.rs index b85003bf091..1ae36a1f760 100644 --- a/src/test/ui/parser/circular_modules_main.rs +++ b/src/test/ui/parser/circular_modules_main.rs @@ -6,5 +6,5 @@ pub fn hi_str() -> String { } fn main() { - circular_modules_hello::say_hello(); + circular_modules_hello::say_hello(); //~ ERROR cannot find function `say_hello` in module } diff --git a/src/test/ui/parser/circular_modules_main.stderr b/src/test/ui/parser/circular_modules_main.stderr index 33865fb7bca..90f81c64835 100644 --- a/src/test/ui/parser/circular_modules_main.stderr +++ b/src/test/ui/parser/circular_modules_main.stderr @@ -1,8 +1,20 @@ error: circular modules: $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs - --> $DIR/circular_modules_main.rs:2:5 + --> $DIR/circular_modules_main.rs:2:1 | LL | mod circular_modules_hello; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0425]: cannot find function `say_hello` in module `circular_modules_hello` + --> $DIR/circular_modules_main.rs:9:29 + | +LL | circular_modules_hello::say_hello(); + | ^^^^^^^^^ not found in `circular_modules_hello` + | +help: possible candidate is found in another module, you can import it into scope + | +LL | use circular_modules_hello::say_hello; + | + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/parser/closure-return-syntax.rs b/src/test/ui/parser/closure-return-syntax.rs index 54eb791d2bc..c6a08abeff4 100644 --- a/src/test/ui/parser/closure-return-syntax.rs +++ b/src/test/ui/parser/closure-return-syntax.rs @@ -3,5 +3,5 @@ fn main() { let x = || -> i32 22; - //~^ ERROR expected one of `!`, `(`, `+`, `::`, `<`, or `{`, found `22` + //~^ ERROR expected `{`, found `22` } diff --git a/src/test/ui/parser/closure-return-syntax.stderr b/src/test/ui/parser/closure-return-syntax.stderr index bfb7f98c5f5..1ccdd977305 100644 --- a/src/test/ui/parser/closure-return-syntax.stderr +++ b/src/test/ui/parser/closure-return-syntax.stderr @@ -1,8 +1,11 @@ -error: expected one of `!`, `(`, `+`, `::`, `<`, or `{`, found `22` +error: expected `{`, found `22` --> $DIR/closure-return-syntax.rs:5:23 | LL | let x = || -> i32 22; - | ^^ expected one of `!`, `(`, `+`, `::`, `<`, or `{` + | ^^ + | | + | expected `{` + | help: try placing this code inside a block: `{ 22 }` error: aborting due to previous error diff --git a/src/test/ui/parser/column-offset-1-based.rs b/src/test/ui/parser/column-offset-1-based.rs index e158e5247db..0c24478c25c 100644 --- a/src/test/ui/parser/column-offset-1-based.rs +++ b/src/test/ui/parser/column-offset-1-based.rs @@ -1 +1 @@ -# //~ ERROR expected `[`, found `` +# //~ ERROR expected one of `!` or `[`, found `` diff --git a/src/test/ui/parser/column-offset-1-based.stderr b/src/test/ui/parser/column-offset-1-based.stderr index 5cbf3d3e959..766d72a0a5a 100644 --- a/src/test/ui/parser/column-offset-1-based.stderr +++ b/src/test/ui/parser/column-offset-1-based.stderr @@ -1,8 +1,8 @@ -error: expected `[`, found `` +error: expected one of `!` or `[`, found `` --> $DIR/column-offset-1-based.rs:1:1 | LL | # - | ^ expected `[` + | ^ expected one of `!` or `[` error: aborting due to previous error diff --git a/src/test/ui/parser/doc-before-semi.rs b/src/test/ui/parser/doc-before-semi.rs index c3f478fe420..405a7e1e2a3 100644 --- a/src/test/ui/parser/doc-before-semi.rs +++ b/src/test/ui/parser/doc-before-semi.rs @@ -3,6 +3,4 @@ fn main() { //~^ ERROR found a documentation comment that doesn't document anything //~| HELP maybe a comment was intended ; - //~^ WARNING unnecessary trailing semicolon - //~| HELP remove this semicolon } diff --git a/src/test/ui/parser/doc-before-semi.stderr b/src/test/ui/parser/doc-before-semi.stderr index b9ac30b09b2..e6bade18d0a 100644 --- a/src/test/ui/parser/doc-before-semi.stderr +++ b/src/test/ui/parser/doc-before-semi.stderr @@ -6,14 +6,6 @@ LL | /// hi | = help: doc comments must come before what they document, maybe a comment was intended with `//`? -warning: unnecessary trailing semicolon - --> $DIR/doc-before-semi.rs:5:5 - | -LL | ; - | ^ help: remove this semicolon - | - = note: `#[warn(redundant_semicolon)]` on by default - error: aborting due to previous error For more information about this error, try `rustc --explain E0585`. diff --git a/src/test/ui/parser/doc-comment-in-if-statement.rs b/src/test/ui/parser/doc-comment-in-if-statement.rs index c85fe25a7d0..343eac1b81f 100644 --- a/src/test/ui/parser/doc-comment-in-if-statement.rs +++ b/src/test/ui/parser/doc-comment-in-if-statement.rs @@ -1,4 +1,5 @@ fn main() { if true /*!*/ {} - //~^ ERROR expected `{`, found doc comment `/*!*/` + //~^ ERROR outer attributes are not allowed on + //~| ERROR expected outer doc comment } diff --git a/src/test/ui/parser/doc-comment-in-if-statement.stderr b/src/test/ui/parser/doc-comment-in-if-statement.stderr index a720dd68bd0..af21b78733f 100644 --- a/src/test/ui/parser/doc-comment-in-if-statement.stderr +++ b/src/test/ui/parser/doc-comment-in-if-statement.stderr @@ -1,10 +1,19 @@ -error: expected `{`, found doc comment `/*!*/` +error: expected outer doc comment --> $DIR/doc-comment-in-if-statement.rs:2:13 | LL | if true /*!*/ {} - | -- ^^^^^ expected `{` - | | - | this `if` expression has a condition, but no block + | ^^^^^ + | + = note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items + +error: outer attributes are not allowed on `if` and `else` branches + --> $DIR/doc-comment-in-if-statement.rs:2:13 + | +LL | if true /*!*/ {} + | -- ^^^^^ -- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `if` -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/fn-body-eq-expr-semi.rs b/src/test/ui/parser/fn-body-eq-expr-semi.rs new file mode 100644 index 00000000000..09444079365 --- /dev/null +++ b/src/test/ui/parser/fn-body-eq-expr-semi.rs @@ -0,0 +1,23 @@ +fn main() {} + +fn syntax() { + fn foo() = 42; //~ ERROR function body cannot be `= expression;` + fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;` +} + +extern { + fn foo() = 42; //~ ERROR function body cannot be `= expression;` + //~^ ERROR incorrect function inside `extern` block + fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;` + //~^ ERROR incorrect function inside `extern` block +} + +trait Foo { + fn foo() = 42; //~ ERROR function body cannot be `= expression;` + fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;` +} + +impl Foo for () { + fn foo() = 42; //~ ERROR function body cannot be `= expression;` + fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;` +} diff --git a/src/test/ui/parser/fn-body-eq-expr-semi.stderr b/src/test/ui/parser/fn-body-eq-expr-semi.stderr new file mode 100644 index 00000000000..739133e0b40 --- /dev/null +++ b/src/test/ui/parser/fn-body-eq-expr-semi.stderr @@ -0,0 +1,117 @@ +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:4:14 + | +LL | fn foo() = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn foo() { 42 } + | ^ ^ + +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:5:20 + | +LL | fn bar() -> u8 = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn bar() -> u8 { 42 } + | ^ ^ + +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:9:14 + | +LL | fn foo() = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn foo() { 42 } + | ^ ^ + +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:11:20 + | +LL | fn bar() -> u8 = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn bar() -> u8 { 42 } + | ^ ^ + +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:16:14 + | +LL | fn foo() = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn foo() { 42 } + | ^ ^ + +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:17:20 + | +LL | fn bar() -> u8 = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn bar() -> u8 { 42 } + | ^ ^ + +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:21:14 + | +LL | fn foo() = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn foo() { 42 } + | ^ ^ + +error: function body cannot be `= expression;` + --> $DIR/fn-body-eq-expr-semi.rs:22:20 + | +LL | fn bar() -> u8 = 42; + | ^^^^^ + | +help: surround the expression with `{` and `}` instead of `=` and `;` + | +LL | fn bar() -> u8 { 42 } + | ^ ^ + +error: incorrect function inside `extern` block + --> $DIR/fn-body-eq-expr-semi.rs:9:8 + | +LL | extern { + | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | fn foo() = 42; + | ^^^ ----- help: remove the invalid body: `;` + | | + | cannot have a body + | + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: incorrect function inside `extern` block + --> $DIR/fn-body-eq-expr-semi.rs:11:8 + | +LL | extern { + | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body +... +LL | fn bar() -> u8 = 42; + | ^^^ ----- help: remove the invalid body: `;` + | | + | cannot have a body + | + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/parser/fn-header-semantic-fail.stderr b/src/test/ui/parser/fn-header-semantic-fail.stderr index 1142cee9851..d6b36fbb714 100644 --- a/src/test/ui/parser/fn-header-semantic-fail.stderr +++ b/src/test/ui/parser/fn-header-semantic-fail.stderr @@ -2,7 +2,7 @@ error: functions cannot be both `const` and `async` --> $DIR/fn-header-semantic-fail.rs:13:5 | LL | const async unsafe extern "C" fn ff5() {} // OK. - | -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-^^^^^------------------------------ | | | | | `async` because of this | `const` because of this @@ -45,7 +45,7 @@ error: functions cannot be both `const` and `async` --> $DIR/fn-header-semantic-fail.rs:21:9 | LL | const async unsafe extern "C" fn ft5(); - | -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-^^^^^---------------------------- | | | | | `async` because of this | `const` because of this @@ -88,7 +88,7 @@ error: functions cannot be both `const` and `async` --> $DIR/fn-header-semantic-fail.rs:34:9 | LL | const async unsafe extern "C" fn ft5() {} - | -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-^^^^^------------------------------ | | | | | `async` because of this | `const` because of this @@ -97,7 +97,7 @@ error: functions cannot be both `const` and `async` --> $DIR/fn-header-semantic-fail.rs:46:9 | LL | const async unsafe extern "C" fn fi5() {} - | -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-^^^^^------------------------------ | | | | | `async` because of this | `const` because of this @@ -160,7 +160,7 @@ error: functions cannot be both `const` and `async` --> $DIR/fn-header-semantic-fail.rs:55:9 | LL | const async unsafe extern "C" fn fe5(); - | -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-^^^^^---------------------------- | | | | | `async` because of this | `const` because of this diff --git a/src/test/ui/parser/inner-attr-after-doc-comment.stderr b/src/test/ui/parser/inner-attr-after-doc-comment.stderr index b012abc25e7..c1e9e7a427f 100644 --- a/src/test/ui/parser/inner-attr-after-doc-comment.stderr +++ b/src/test/ui/parser/inner-attr-after-doc-comment.stderr @@ -7,7 +7,7 @@ LL | | */ | |___- previous doc comment LL | LL | #![recursion_limit="100"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attibute + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attribute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. diff --git a/src/test/ui/parser/inner-attr.stderr b/src/test/ui/parser/inner-attr.stderr index 070d9f47d96..e1bf2cca1c9 100644 --- a/src/test/ui/parser/inner-attr.stderr +++ b/src/test/ui/parser/inner-attr.stderr @@ -5,7 +5,7 @@ LL | #[feature(lang_items)] | ---------------------- previous outer attribute LL | LL | #![recursion_limit="100"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attibute + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attribute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. diff --git a/src/test/ui/parser/issue-1655.rs b/src/test/ui/parser/issue-1655.rs index 3d0bf3c1c7b..e9fc6f15346 100644 --- a/src/test/ui/parser/issue-1655.rs +++ b/src/test/ui/parser/issue-1655.rs @@ -1,6 +1,5 @@ -// error-pattern:expected `[`, found `vec` mod blade_runner { - #vec[doc( + #vec[doc( //~ ERROR expected one of `!` or `[`, found `vec` brief = "Blade Runner is probably the best movie ever", desc = "I like that in the world of Blade Runner it is always raining, and that it's always night time. And Aliens diff --git a/src/test/ui/parser/issue-1655.stderr b/src/test/ui/parser/issue-1655.stderr index 3f656b63cdb..0c390a0ec56 100644 --- a/src/test/ui/parser/issue-1655.stderr +++ b/src/test/ui/parser/issue-1655.stderr @@ -1,8 +1,8 @@ -error: expected `[`, found `vec` - --> $DIR/issue-1655.rs:3:6 +error: expected one of `!` or `[`, found `vec` + --> $DIR/issue-1655.rs:2:6 | LL | #vec[doc( - | ^^^ expected `[` + | ^^^ expected one of `!` or `[` error: aborting due to previous error diff --git a/src/test/ui/parser/issue-35813-postfix-after-cast.rs b/src/test/ui/parser/issue-35813-postfix-after-cast.rs new file mode 100644 index 00000000000..e725aa5d73d --- /dev/null +++ b/src/test/ui/parser/issue-35813-postfix-after-cast.rs @@ -0,0 +1,171 @@ +// edition:2018 +#![crate_type = "lib"] +#![feature(type_ascription)] +use std::future::Future; +use std::pin::Pin; + +// This tests the parser for "x as Y[z]". It errors, but we want to give useful +// errors and parse such that further code gives useful errors. +pub fn index_after_as_cast() { + vec![1, 2, 3] as Vec[0]; + //~^ ERROR: casts cannot be followed by indexing + vec![1, 2, 3]: Vec[0]; + //~^ ERROR: casts cannot be followed by indexing +} + +pub fn index_after_cast_to_index() { + (&[0]) as &[i32][0]; + //~^ ERROR: casts cannot be followed by indexing + (&[0i32]): &[i32; 1][0]; + //~^ ERROR: casts cannot be followed by indexing +} + +pub fn cast_after_cast() { + if 5u64 as i32 as u16 == 0u16 { + + } + if 5u64: u64: u64 == 0u64 { + + } + let _ = 5u64: u64: u64 as u8 as i8 == 9i8; + let _ = 0i32: i32: i32; + let _ = 0 as i32: i32; + let _ = 0i32: i32 as i32; + let _ = 0 as i32 as i32; + let _ = 0i32: i32: i32 as u32 as i32; +} + +pub fn cast_cast_method_call() { + let _ = 0i32: i32: i32.count_ones(); + //~^ ERROR: casts cannot be followed by a method call + let _ = 0 as i32: i32.count_ones(); + //~^ ERROR: casts cannot be followed by a method call + let _ = 0i32: i32 as i32.count_ones(); + //~^ ERROR: casts cannot be followed by a method call + let _ = 0 as i32 as i32.count_ones(); + //~^ ERROR: casts cannot be followed by a method call + let _ = 0i32: i32: i32 as u32 as i32.count_ones(); + //~^ ERROR: casts cannot be followed by a method call + let _ = 0i32: i32.count_ones(): u32; + //~^ ERROR: casts cannot be followed by a method call + let _ = 0 as i32.count_ones(): u32; + //~^ ERROR: casts cannot be followed by a method call + let _ = 0i32: i32.count_ones() as u32; + //~^ ERROR: casts cannot be followed by a method call + let _ = 0 as i32.count_ones() as u32; + //~^ ERROR: casts cannot be followed by a method call + let _ = 0i32: i32: i32.count_ones() as u32 as i32; + //~^ ERROR: casts cannot be followed by a method call +} + +pub fn multiline_error() { + let _ = 0 + as i32 + .count_ones(); + //~^^^ ERROR: casts cannot be followed by a method call +} + +// this tests that the precedence for `!x as Y.Z` is still what we expect +pub fn precedence() { + let x: i32 = &vec![1, 2, 3] as &Vec[0]; + //~^ ERROR: casts cannot be followed by indexing +} + +pub fn method_calls() { + 0 as i32.max(0); + //~^ ERROR: casts cannot be followed by a method call + 0: i32.max(0); + //~^ ERROR: casts cannot be followed by a method call +} + +pub fn complex() { + let _ = format!( + "{} and {}", + if true { 33 } else { 44 } as i32.max(0), + //~^ ERROR: casts cannot be followed by a method call + if true { 33 } else { 44 }: i32.max(0) + //~^ ERROR: casts cannot be followed by a method call + ); +} + +pub fn in_condition() { + if 5u64 as i32.max(0) == 0 { + //~^ ERROR: casts cannot be followed by a method call + } + if 5u64: u64.max(0) == 0 { + //~^ ERROR: casts cannot be followed by a method call + } +} + +pub fn inside_block() { + let _ = if true { + 5u64 as u32.max(0) == 0 + //~^ ERROR: casts cannot be followed by a method call + } else { false }; + let _ = if true { + 5u64: u64.max(0) == 0 + //~^ ERROR: casts cannot be followed by a method call + } else { false }; +} + +static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]); +//~^ ERROR: casts cannot be followed by indexing + +static bar2: &[i32] = &(&[1i32,2,3]: &[i32; 3][0..1]); +//~^ ERROR: casts cannot be followed by indexing + + +pub fn cast_then_try() -> Result { + Err(0u64) as Result?; + //~^ ERROR: casts cannot be followed by ? + Err(0u64): Result?; + //~^ ERROR: casts cannot be followed by ? + Ok(1) +} + + +pub fn cast_then_call() { + type F = fn(u8); + // type ascription won't actually do [unique drop fn type] -> fn(u8) casts. + let drop_ptr = drop as fn(u8); + drop as F(); + //~^ ERROR: parenthesized type parameters may only be used with a `Fn` trait [E0214] + drop_ptr: F(); + //~^ ERROR: parenthesized type parameters may only be used with a `Fn` trait [E0214] +} + +pub fn cast_to_fn_should_work() { + let drop_ptr = drop as fn(u8); + drop as fn(u8); + drop_ptr: fn(u8); +} + +pub fn parens_after_cast_error() { + let drop_ptr = drop as fn(u8); + drop as fn(u8)(0); + //~^ ERROR: casts cannot be followed by a function call + drop_ptr: fn(u8)(0); + //~^ ERROR: casts cannot be followed by a function call +} + +pub async fn cast_then_await() { + Box::pin(noop()) as Pin>>.await; + //~^ ERROR: casts cannot be followed by `.await` + + Box::pin(noop()): Pin>.await; + //~^ ERROR: casts cannot be followed by `.await` +} + +pub async fn noop() {} + +#[derive(Default)] +pub struct Foo { + pub bar: u32, +} + +pub fn struct_field() { + Foo::default() as Foo.bar; + //~^ ERROR: cannot be followed by a field access + Foo::default(): Foo.bar; + //~^ ERROR: cannot be followed by a field access +} diff --git a/src/test/ui/parser/issue-35813-postfix-after-cast.stderr b/src/test/ui/parser/issue-35813-postfix-after-cast.stderr new file mode 100644 index 00000000000..255e9f40921 --- /dev/null +++ b/src/test/ui/parser/issue-35813-postfix-after-cast.stderr @@ -0,0 +1,392 @@ +error: casts cannot be followed by indexing + --> $DIR/issue-35813-postfix-after-cast.rs:10:5 + | +LL | vec![1, 2, 3] as Vec[0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (vec![1, 2, 3] as Vec)[0]; + | ^ ^ + +error: casts cannot be followed by indexing + --> $DIR/issue-35813-postfix-after-cast.rs:12:5 + | +LL | vec![1, 2, 3]: Vec[0]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (vec![1, 2, 3]: Vec)[0]; + | ^ ^ + +error: casts cannot be followed by indexing + --> $DIR/issue-35813-postfix-after-cast.rs:17:5 + | +LL | (&[0]) as &[i32][0]; + | ^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | ((&[0]) as &[i32])[0]; + | ^ ^ + +error: casts cannot be followed by indexing + --> $DIR/issue-35813-postfix-after-cast.rs:19:5 + | +LL | (&[0i32]): &[i32; 1][0]; + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | ((&[0i32]): &[i32; 1])[0]; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:39:13 + | +LL | let _ = 0i32: i32: i32.count_ones(); + | ^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0i32: i32: i32).count_ones(); + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:41:13 + | +LL | let _ = 0 as i32: i32.count_ones(); + | ^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0 as i32: i32).count_ones(); + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:43:13 + | +LL | let _ = 0i32: i32 as i32.count_ones(); + | ^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0i32: i32 as i32).count_ones(); + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:45:13 + | +LL | let _ = 0 as i32 as i32.count_ones(); + | ^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0 as i32 as i32).count_ones(); + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:47:13 + | +LL | let _ = 0i32: i32: i32 as u32 as i32.count_ones(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0i32: i32: i32 as u32 as i32).count_ones(); + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:49:13 + | +LL | let _ = 0i32: i32.count_ones(): u32; + | ^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0i32: i32).count_ones(): u32; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:51:13 + | +LL | let _ = 0 as i32.count_ones(): u32; + | ^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0 as i32).count_ones(): u32; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:53:13 + | +LL | let _ = 0i32: i32.count_ones() as u32; + | ^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0i32: i32).count_ones() as u32; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:55:13 + | +LL | let _ = 0 as i32.count_ones() as u32; + | ^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0 as i32).count_ones() as u32; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:57:13 + | +LL | let _ = 0i32: i32: i32.count_ones() as u32 as i32; + | ^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0i32: i32: i32).count_ones() as u32 as i32; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:62:13 + | +LL | let _ = 0 + | _____________^ +LL | | as i32 + | |______________^ + | +help: try surrounding the expression in parentheses + | +LL | let _ = (0 +LL | as i32) + | + +error: casts cannot be followed by indexing + --> $DIR/issue-35813-postfix-after-cast.rs:70:18 + | +LL | let x: i32 = &vec![1, 2, 3] as &Vec[0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | let x: i32 = (&vec![1, 2, 3] as &Vec)[0]; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:75:5 + | +LL | 0 as i32.max(0); + | ^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (0 as i32).max(0); + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:77:5 + | +LL | 0: i32.max(0); + | ^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (0: i32).max(0); + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:92:8 + | +LL | if 5u64 as i32.max(0) == 0 { + | ^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | if (5u64 as i32).max(0) == 0 { + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:95:8 + | +LL | if 5u64: u64.max(0) == 0 { + | ^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | if (5u64: u64).max(0) == 0 { + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:102:9 + | +LL | 5u64 as u32.max(0) == 0 + | ^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (5u64 as u32).max(0) == 0 + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:106:9 + | +LL | 5u64: u64.max(0) == 0 + | ^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (5u64: u64).max(0) == 0 + | ^ ^ + +error: casts cannot be followed by indexing + --> $DIR/issue-35813-postfix-after-cast.rs:111:24 + | +LL | static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]); + | ^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | static bar: &[i32] = &((&[1,2,3] as &[i32])[0..1]); + | ^ ^ + +error: casts cannot be followed by indexing + --> $DIR/issue-35813-postfix-after-cast.rs:114:25 + | +LL | static bar2: &[i32] = &(&[1i32,2,3]: &[i32; 3][0..1]); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | static bar2: &[i32] = &((&[1i32,2,3]: &[i32; 3])[0..1]); + | ^ ^ + +error: casts cannot be followed by ? + --> $DIR/issue-35813-postfix-after-cast.rs:119:5 + | +LL | Err(0u64) as Result?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (Err(0u64) as Result)?; + | ^ ^ + +error: casts cannot be followed by ? + --> $DIR/issue-35813-postfix-after-cast.rs:121:5 + | +LL | Err(0u64): Result?; + | ^^^^^^^^^-^^^^^^^^^^^^^^^^ + | | + | help: maybe write a path separator here: `::` + | + = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` + = note: see issue #23416 for more information + +error: casts cannot be followed by a function call + --> $DIR/issue-35813-postfix-after-cast.rs:145:5 + | +LL | drop as fn(u8)(0); + | ^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (drop as fn(u8))(0); + | ^ ^ + +error: casts cannot be followed by a function call + --> $DIR/issue-35813-postfix-after-cast.rs:147:5 + | +LL | drop_ptr: fn(u8)(0); + | ^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (drop_ptr: fn(u8))(0); + | ^ ^ + +error: casts cannot be followed by `.await` + --> $DIR/issue-35813-postfix-after-cast.rs:152:5 + | +LL | Box::pin(noop()) as Pin>>.await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (Box::pin(noop()) as Pin>>).await; + | ^ ^ + +error: casts cannot be followed by `.await` + --> $DIR/issue-35813-postfix-after-cast.rs:155:5 + | +LL | Box::pin(noop()): Pin>.await; + | ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^ + | | + | help: maybe write a path separator here: `::` + | + = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` + = note: see issue #23416 for more information + +error: casts cannot be followed by a field access + --> $DIR/issue-35813-postfix-after-cast.rs:167:5 + | +LL | Foo::default() as Foo.bar; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (Foo::default() as Foo).bar; + | ^ ^ + +error: casts cannot be followed by a field access + --> $DIR/issue-35813-postfix-after-cast.rs:169:5 + | +LL | Foo::default(): Foo.bar; + | ^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (Foo::default(): Foo).bar; + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:84:9 + | +LL | if true { 33 } else { 44 } as i32.max(0), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (if true { 33 } else { 44 } as i32).max(0), + | ^ ^ + +error: casts cannot be followed by a method call + --> $DIR/issue-35813-postfix-after-cast.rs:86:9 + | +LL | if true { 33 } else { 44 }: i32.max(0) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (if true { 33 } else { 44 }: i32).max(0) + | ^ ^ + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/issue-35813-postfix-after-cast.rs:131:13 + | +LL | drop as F(); + | ^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/issue-35813-postfix-after-cast.rs:133:15 + | +LL | drop_ptr: F(); + | ^^^ only `Fn` traits may use parentheses + +error: aborting due to 36 previous errors + +For more information about this error, try `rustc --explain E0214`. diff --git a/src/test/ui/parser/issue-5806.stderr b/src/test/ui/parser/issue-5806.stderr index 6cf902ca86e..bdb5c91ff91 100644 --- a/src/test/ui/parser/issue-5806.stderr +++ b/src/test/ui/parser/issue-5806.stderr @@ -1,8 +1,8 @@ error: couldn't read $DIR/../parser: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE) - --> $DIR/issue-5806.rs:5:5 + --> $DIR/issue-5806.rs:5:1 | LL | mod foo; - | ^^^ + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/parser/issue-63116.stderr b/src/test/ui/parser/issue-63116.stderr index 2beb73d83d2..15cd3df860b 100644 --- a/src/test/ui/parser/issue-63116.stderr +++ b/src/test/ui/parser/issue-63116.stderr @@ -12,7 +12,7 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;` LL | impl W `, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, or lifetime, found `;` +error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;` --> $DIR/issue-63116.rs:3:15 | LL | impl W $DIR/issue-63135.rs:3:16 | LL | fn i(n{...,f # - | ^ expected `[` + | ^ expected one of `!` or `[` -error: expected one of `:` or `|`, found `)` - --> $DIR/issue-63135.rs:3:16 - | -LL | fn i(n{...,f # - | ^ expected one of `:` or `|` - -error: expected `;` or `{`, found `` - --> $DIR/issue-63135.rs:3:16 - | -LL | fn i(n{...,f # - | ^ expected `;` or `{` - -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors diff --git a/src/test/ui/parser/issue-68730.rs b/src/test/ui/parser/issue-68730.rs index b570e941775..20e18b4bcbb 100644 Binary files a/src/test/ui/parser/issue-68730.rs and b/src/test/ui/parser/issue-68730.rs differ diff --git a/src/test/ui/parser/issue-68730.stderr b/src/test/ui/parser/issue-68730.stderr index 090b41d839f..9f8833e17fe 100644 Binary files a/src/test/ui/parser/issue-68730.stderr and b/src/test/ui/parser/issue-68730.stderr differ diff --git a/src/test/ui/parser/issue-68890-2.rs b/src/test/ui/parser/issue-68890-2.rs new file mode 100644 index 00000000000..ae022460468 --- /dev/null +++ b/src/test/ui/parser/issue-68890-2.rs @@ -0,0 +1,6 @@ +fn main() {} + +type X<'a> = (?'a) +; +//~^ ERROR `?` may only modify trait bounds, not lifetime bounds +//~| ERROR at least one trait is required for an object type +//~| WARN trait objects without an explicit `dyn` are deprecated diff --git a/src/test/ui/parser/issue-68890-2.stderr b/src/test/ui/parser/issue-68890-2.stderr new file mode 100644 index 00000000000..d475c79cb27 --- /dev/null +++ b/src/test/ui/parser/issue-68890-2.stderr @@ -0,0 +1,22 @@ +error: `?` may only modify trait bounds, not lifetime bounds + --> $DIR/issue-68890-2.rs:3:15 + | +LL | type X<'a> = (?'a) +; + | ^ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/issue-68890-2.rs:3:14 + | +LL | type X<'a> = (?'a) +; + | ^^^^^^^ help: use `dyn`: `dyn (?'a) +` + | + = note: `#[warn(bare_trait_objects)]` on by default + +error[E0224]: at least one trait is required for an object type + --> $DIR/issue-68890-2.rs:3:14 + | +LL | type X<'a> = (?'a) +; + | ^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/issue-68890.rs b/src/test/ui/parser/issue-68890.rs index a7c5a5e1300..bab4ed7f800 100644 --- a/src/test/ui/parser/issue-68890.rs +++ b/src/test/ui/parser/issue-68890.rs @@ -1,4 +1,4 @@ enum e{A((?'a a+?+l))} //~^ ERROR `?` may only modify trait bounds, not lifetime bounds //~| ERROR expected one of `)`, `+`, or `,` -//~| ERROR expected trait bound, not lifetime bound +//~| ERROR expected item, found `)` diff --git a/src/test/ui/parser/issue-68890.stderr b/src/test/ui/parser/issue-68890.stderr index 9bb8761b67b..2a3bf6b41f0 100644 --- a/src/test/ui/parser/issue-68890.stderr +++ b/src/test/ui/parser/issue-68890.stderr @@ -10,11 +10,11 @@ error: expected one of `)`, `+`, or `,`, found `a` LL | enum e{A((?'a a+?+l))} | ^ expected one of `)`, `+`, or `,` -error: expected trait bound, not lifetime bound - --> $DIR/issue-68890.rs:1:11 +error: expected item, found `)` + --> $DIR/issue-68890.rs:1:21 | LL | enum e{A((?'a a+?+l))} - | ^^^ + | ^ expected item error: aborting due to 3 previous errors diff --git a/src/test/ui/parser/labeled-no-colon-expr.rs b/src/test/ui/parser/labeled-no-colon-expr.rs new file mode 100644 index 00000000000..db9ef52c1ae --- /dev/null +++ b/src/test/ui/parser/labeled-no-colon-expr.rs @@ -0,0 +1,17 @@ +#![feature(label_break_value)] + +fn main() { + 'l0 while false {} //~ ERROR labeled expression must be followed by `:` + 'l1 for _ in 0..1 {} //~ ERROR labeled expression must be followed by `:` + 'l2 loop {} //~ ERROR labeled expression must be followed by `:` + 'l3 {} //~ ERROR labeled expression must be followed by `:` + 'l4 0; //~ ERROR labeled expression must be followed by `:` + //~^ ERROR expected `while`, `for`, `loop` or `{` + + macro_rules! m { + ($b:block) => { + 'l5 $b; //~ ERROR cannot use a `block` macro fragment here + } + } + m!({}); //~ ERROR labeled expression must be followed by `:` +} diff --git a/src/test/ui/parser/labeled-no-colon-expr.stderr b/src/test/ui/parser/labeled-no-colon-expr.stderr new file mode 100644 index 00000000000..4f5e8f78aa0 --- /dev/null +++ b/src/test/ui/parser/labeled-no-colon-expr.stderr @@ -0,0 +1,89 @@ +error: labeled expression must be followed by `:` + --> $DIR/labeled-no-colon-expr.rs:4:5 + | +LL | 'l0 while false {} + | ----^^^^^^^^^^^^^^ + | | | + | | help: add `:` after the label + | the label + | + = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them + +error: labeled expression must be followed by `:` + --> $DIR/labeled-no-colon-expr.rs:5:5 + | +LL | 'l1 for _ in 0..1 {} + | ----^^^^^^^^^^^^^^^^ + | | | + | | help: add `:` after the label + | the label + | + = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them + +error: labeled expression must be followed by `:` + --> $DIR/labeled-no-colon-expr.rs:6:5 + | +LL | 'l2 loop {} + | ----^^^^^^^ + | | | + | | help: add `:` after the label + | the label + | + = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them + +error: labeled expression must be followed by `:` + --> $DIR/labeled-no-colon-expr.rs:7:5 + | +LL | 'l3 {} + | ----^^ + | | | + | | help: add `:` after the label + | the label + | + = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them + +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/labeled-no-colon-expr.rs:8:9 + | +LL | 'l4 0; + | ^ expected `while`, `for`, `loop` or `{` after a label + +error: labeled expression must be followed by `:` + --> $DIR/labeled-no-colon-expr.rs:8:9 + | +LL | 'l4 0; + | ----^ + | | | + | | help: add `:` after the label + | the label + | + = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them + +error: cannot use a `block` macro fragment here + --> $DIR/labeled-no-colon-expr.rs:13:17 + | +LL | 'l5 $b; + | ----^^ + | | + | the `block` fragment is within this context +... +LL | m!({}); + | ------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: labeled expression must be followed by `:` + --> $DIR/labeled-no-colon-expr.rs:16:8 + | +LL | 'l5 $b; + | ---- help: add `:` after the label + | | + | the label +... +LL | m!({}); + | ^^ + | + = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/parser/macro/trait-object-macro-matcher.rs b/src/test/ui/parser/macro/trait-object-macro-matcher.rs index 80d867d3b56..170ac22780b 100644 --- a/src/test/ui/parser/macro/trait-object-macro-matcher.rs +++ b/src/test/ui/parser/macro/trait-object-macro-matcher.rs @@ -2,9 +2,14 @@ // `ty` matcher in particular doesn't accept a single lifetime macro_rules! m { - ($t: ty) => ( let _: $t; ) + ($t: ty) => { + let _: $t; + }; } fn main() { - m!('static); //~ ERROR expected type, found `'static` + m!('static); + //~^ ERROR lifetime in trait object type must be followed by `+` + //~| ERROR at least one trait is required for an object type + //~| WARN trait objects without an explicit `dyn` are deprecated } diff --git a/src/test/ui/parser/macro/trait-object-macro-matcher.stderr b/src/test/ui/parser/macro/trait-object-macro-matcher.stderr index f02f60e4bfb..230733371dd 100644 --- a/src/test/ui/parser/macro/trait-object-macro-matcher.stderr +++ b/src/test/ui/parser/macro/trait-object-macro-matcher.stderr @@ -1,8 +1,22 @@ -error: expected type, found `'static` - --> $DIR/trait-object-macro-matcher.rs:9:8 +error: lifetime in trait object type must be followed by `+` + --> $DIR/trait-object-macro-matcher.rs:11:8 | LL | m!('static); - | ^^^^^^^ expected type + | ^^^^^^^ -error: aborting due to previous error +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/trait-object-macro-matcher.rs:11:8 + | +LL | m!('static); + | ^^^^^^^ help: use `dyn`: `dyn 'static` + | + = note: `#[warn(bare_trait_objects)]` on by default + +error[E0224]: at least one trait is required for an object type + --> $DIR/trait-object-macro-matcher.rs:11:8 + | +LL | m!('static); + | ^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr index 311f1768d82..f1be5dc5ba7 100644 --- a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr +++ b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr @@ -1,14 +1,8 @@ error: unexpected closing delimiter: `}` --> $DIR/mismatched-delim-brace-empty-block.rs:5:1 | -LL | fn main() { - | ___________- -LL | | -LL | | } - | |_- this block is empty, you might have not meant to close it -LL | let _ = (); -LL | } - | ^ unexpected closing delimiter +LL | } + | ^ unexpected closing delimiter error: aborting due to previous error diff --git a/src/test/ui/parser/mod_file_not_exist.rs b/src/test/ui/parser/mod_file_not_exist.rs index e662c707a38..f4a27b52ec5 100644 --- a/src/test/ui/parser/mod_file_not_exist.rs +++ b/src/test/ui/parser/mod_file_not_exist.rs @@ -1,8 +1,9 @@ // ignore-windows mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` -//~^ HELP name the file either not_a_real_file.rs or not_a_real_file/mod.rs inside the directory +//~^ HELP to create the module `not_a_real_file`, create file fn main() { assert_eq!(mod_file_aux::bar(), 10); + //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` } diff --git a/src/test/ui/parser/mod_file_not_exist.stderr b/src/test/ui/parser/mod_file_not_exist.stderr index dadf4b29dcf..087ae9fe3e0 100644 --- a/src/test/ui/parser/mod_file_not_exist.stderr +++ b/src/test/ui/parser/mod_file_not_exist.stderr @@ -1,11 +1,18 @@ error[E0583]: file not found for module `not_a_real_file` - --> $DIR/mod_file_not_exist.rs:3:5 + --> $DIR/mod_file_not_exist.rs:3:1 | LL | mod not_a_real_file; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | - = help: name the file either not_a_real_file.rs or not_a_real_file/mod.rs inside the directory "$DIR" + = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" -error: aborting due to previous error +error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` + --> $DIR/mod_file_not_exist.rs:7:16 + | +LL | assert_eq!(mod_file_aux::bar(), 10); + | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0583`. +Some errors have detailed explanations: E0433, E0583. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/parser/mod_file_not_exist_windows.rs b/src/test/ui/parser/mod_file_not_exist_windows.rs index 0cd9e9c799f..4b7d7a02bbe 100644 --- a/src/test/ui/parser/mod_file_not_exist_windows.rs +++ b/src/test/ui/parser/mod_file_not_exist_windows.rs @@ -1,8 +1,9 @@ // only-windows mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` -//~^ HELP name the file either not_a_real_file.rs or not_a_real_file\mod.rs inside the directory +//~^ HELP to create the module `not_a_real_file`, create file fn main() { assert_eq!(mod_file_aux::bar(), 10); + //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` } diff --git a/src/test/ui/parser/mod_file_not_exist_windows.stderr b/src/test/ui/parser/mod_file_not_exist_windows.stderr index 60ae00abab1..d67205cfdf1 100644 --- a/src/test/ui/parser/mod_file_not_exist_windows.stderr +++ b/src/test/ui/parser/mod_file_not_exist_windows.stderr @@ -1,11 +1,18 @@ error[E0583]: file not found for module `not_a_real_file` - --> $DIR/mod_file_not_exist_windows.rs:3:5 + --> $DIR/mod_file_not_exist_windows.rs:3:1 | LL | mod not_a_real_file; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | - = help: name the file either not_a_real_file.rs or not_a_real_file/mod.rs inside the directory "$DIR" + = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" -error: aborting due to previous error +error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` + --> $DIR/mod_file_not_exist_windows.rs:7:16 + | +LL | assert_eq!(mod_file_aux::bar(), 10); + | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0583`. +Some errors have detailed explanations: E0433, E0583. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/parser/mod_file_with_path_attr.stderr b/src/test/ui/parser/mod_file_with_path_attr.stderr index 004b5d7963a..cd1add73d58 100644 --- a/src/test/ui/parser/mod_file_with_path_attr.stderr +++ b/src/test/ui/parser/mod_file_with_path_attr.stderr @@ -1,8 +1,8 @@ error: couldn't read $DIR/not_a_real_file.rs: $FILE_NOT_FOUND_MSG (os error 2) - --> $DIR/mod_file_with_path_attr.rs:4:5 + --> $DIR/mod_file_with_path_attr.rs:4:1 | LL | mod m; - | ^ + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/parser/recovery-attr-on-if.rs b/src/test/ui/parser/recovery-attr-on-if.rs deleted file mode 100644 index 0d1f5be7b49..00000000000 --- a/src/test/ui/parser/recovery-attr-on-if.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - #[attr] if true {}; - //~^ ERROR cannot find attribute - //~| ERROR attributes are not yet allowed on `if` expressions - #[attr] if true {}; - //~^ ERROR cannot find attribute - //~| ERROR attributes are not yet allowed on `if` expressions - let _recovery_witness: () = 0; //~ ERROR mismatched types -} diff --git a/src/test/ui/parser/recovery-attr-on-if.stderr b/src/test/ui/parser/recovery-attr-on-if.stderr deleted file mode 100644 index a02846827c9..00000000000 --- a/src/test/ui/parser/recovery-attr-on-if.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: attributes are not yet allowed on `if` expressions - --> $DIR/recovery-attr-on-if.rs:2:5 - | -LL | #[attr] if true {}; - | ^^^^^^^ - -error: attributes are not yet allowed on `if` expressions - --> $DIR/recovery-attr-on-if.rs:5:5 - | -LL | #[attr] if true {}; - | ^^^^^^^ - -error: cannot find attribute `attr` in this scope - --> $DIR/recovery-attr-on-if.rs:5:7 - | -LL | #[attr] if true {}; - | ^^^^ - -error: cannot find attribute `attr` in this scope - --> $DIR/recovery-attr-on-if.rs:2:7 - | -LL | #[attr] if true {}; - | ^^^^ - -error[E0308]: mismatched types - --> $DIR/recovery-attr-on-if.rs:8:33 - | -LL | let _recovery_witness: () = 0; - | -- ^ expected `()`, found integer - | | - | expected due to this - -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/regions-out-of-scope-slice.rs b/src/test/ui/parser/regions-out-of-scope-slice.rs index 21369d0be61..d223619e1de 100644 --- a/src/test/ui/parser/regions-out-of-scope-slice.rs +++ b/src/test/ui/parser/regions-out-of-scope-slice.rs @@ -4,7 +4,7 @@ fn foo(cond: bool) { let mut x; if cond { - x = &'blk [1,2,3]; //~ ERROR expected `:`, found `[` + x = &'blk [1,2,3]; //~ ERROR borrow expressions cannot be annotated with lifetimes } } diff --git a/src/test/ui/parser/regions-out-of-scope-slice.stderr b/src/test/ui/parser/regions-out-of-scope-slice.stderr index 8d9bf0b7a04..bbc657ffd61 100644 --- a/src/test/ui/parser/regions-out-of-scope-slice.stderr +++ b/src/test/ui/parser/regions-out-of-scope-slice.stderr @@ -1,8 +1,11 @@ -error: expected `:`, found `[` - --> $DIR/regions-out-of-scope-slice.rs:7:19 +error: borrow expressions cannot be annotated with lifetimes + --> $DIR/regions-out-of-scope-slice.rs:7:13 | LL | x = &'blk [1,2,3]; - | ^ expected `:` + | ^----^^^^^^^^ + | | + | annotated with lifetime here + | help: remove the lifetime annotation error: aborting due to previous error diff --git a/src/test/ui/parser/stripped-nested-outline-mod-pass.rs b/src/test/ui/parser/stripped-nested-outline-mod-pass.rs new file mode 100644 index 00000000000..1b4669a439f --- /dev/null +++ b/src/test/ui/parser/stripped-nested-outline-mod-pass.rs @@ -0,0 +1,13 @@ +// Expansion drives parsing, so conditional compilation will strip +// out outline modules and we will never attempt parsing them. + +// check-pass + +fn main() {} + +#[cfg(FALSE)] +mod foo { + mod bar { + mod baz; // This was an error before. + } +} diff --git a/src/test/ui/parser/trait-object-lifetime-parens.rs b/src/test/ui/parser/trait-object-lifetime-parens.rs index c8b0eb684f3..5a5c19f32e8 100644 --- a/src/test/ui/parser/trait-object-lifetime-parens.rs +++ b/src/test/ui/parser/trait-object-lifetime-parens.rs @@ -6,9 +6,7 @@ fn f<'a, T: Trait + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not s fn check<'a>() { let _: Box; //~ ERROR parenthesized lifetime bounds are not supported - let _: Box<('a) + Trait>; - //~^ ERROR expected type, found `'a` - //~| ERROR expected `:`, found `)` + let _: Box<('a) + Trait>; //~ ERROR lifetime in trait object type must be followed by `+` } fn main() {} diff --git a/src/test/ui/parser/trait-object-lifetime-parens.stderr b/src/test/ui/parser/trait-object-lifetime-parens.stderr index 319a308c013..1289c248275 100644 --- a/src/test/ui/parser/trait-object-lifetime-parens.stderr +++ b/src/test/ui/parser/trait-object-lifetime-parens.stderr @@ -10,19 +10,11 @@ error: parenthesized lifetime bounds are not supported LL | let _: Box; | ^^^^ help: remove the parentheses -error: expected `:`, found `)` - --> $DIR/trait-object-lifetime-parens.rs:9:19 - | -LL | let _: Box<('a) + Trait>; - | ^ expected `:` - -error: expected type, found `'a` +error: lifetime in trait object type must be followed by `+` --> $DIR/trait-object-lifetime-parens.rs:9:17 | LL | let _: Box<('a) + Trait>; - | - ^^ expected type - | | - | while parsing the type for `_` + | ^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/parser/trait-object-trait-parens.rs b/src/test/ui/parser/trait-object-trait-parens.rs index a113de14b6f..9fbc938c4dc 100644 --- a/src/test/ui/parser/trait-object-trait-parens.rs +++ b/src/test/ui/parser/trait-object-trait-parens.rs @@ -1,15 +1,20 @@ trait Trait<'a> {} +trait Obj {} + fn f Trait<'a>)>() {} fn main() { - let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>; + let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; + //~^ ERROR `?Trait` is not permitted in trait object types + //~| ERROR only auto traits can be used as additional traits + //~| WARN trait objects without an explicit `dyn` are deprecated + let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>; //~^ ERROR `?Trait` is not permitted in trait object types + //~| ERROR only auto traits can be used as additional traits //~| WARN trait objects without an explicit `dyn` are deprecated - let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Copy)>; - //~^ WARN trait objects without an explicit `dyn` are deprecated - let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>; - //~^ ERROR use of undeclared lifetime name `'a` - //~| ERROR `?Trait` is not permitted in trait object types + let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>; + //~^ ERROR `?Trait` is not permitted in trait object types + //~| ERROR only auto traits can be used as additional traits //~| WARN trait objects without an explicit `dyn` are deprecated } diff --git a/src/test/ui/parser/trait-object-trait-parens.stderr b/src/test/ui/parser/trait-object-trait-parens.stderr index 4b9f49423cb..7022a66ca1a 100644 --- a/src/test/ui/parser/trait-object-trait-parens.stderr +++ b/src/test/ui/parser/trait-object-trait-parens.stderr @@ -1,44 +1,74 @@ error: `?Trait` is not permitted in trait object types - --> $DIR/trait-object-trait-parens.rs:6:25 + --> $DIR/trait-object-trait-parens.rs:8:24 | -LL | let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>; - | ^^^^^^^^ +LL | let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; + | ^^^^^^^^ error: `?Trait` is not permitted in trait object types - --> $DIR/trait-object-trait-parens.rs:11:47 + --> $DIR/trait-object-trait-parens.rs:12:17 | -LL | let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>; - | ^^^^^^^^ +LL | let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>; + | ^^^^^^ + +error: `?Trait` is not permitted in trait object types + --> $DIR/trait-object-trait-parens.rs:16:46 + | +LL | let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>; + | ^^^^^^^^ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/trait-object-trait-parens.rs:6:16 + --> $DIR/trait-object-trait-parens.rs:8:16 | -LL | let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (Copy) + (?Sized) + (for<'a> Trait<'a>)` +LL | let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (Obj) + (?Sized) + (for<'a> Trait<'a>)` | = note: `#[warn(bare_trait_objects)]` on by default warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/trait-object-trait-parens.rs:9:16 + --> $DIR/trait-object-trait-parens.rs:12:16 | -LL | let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Copy)>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (?Sized) + (for<'a> Trait<'a>) + (Copy)` +LL | let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (?Sized) + (for<'a> Trait<'a>) + (Obj)` warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/trait-object-trait-parens.rs:11:16 + --> $DIR/trait-object-trait-parens.rs:16:16 + | +LL | let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (for<'a> Trait<'a>) + (Obj) + (?Sized)` + +error[E0225]: only auto traits can be used as additional traits in a trait object + --> $DIR/trait-object-trait-parens.rs:8:35 + | +LL | let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; + | ----- ^^^^^^^^^^^^^^^^^^^ + | | | + | | additional non-auto trait + | | trait alias used in trait object type (additional use) + | first non-auto trait + | trait alias used in trait object type (first use) + +error[E0225]: only auto traits can be used as additional traits in a trait object + --> $DIR/trait-object-trait-parens.rs:12:49 | -LL | let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (for<'a> Trait<'a>) + (Copy) + (?Sized)` +LL | let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>; + | ------------------- ^^^^^ + | | | + | | additional non-auto trait + | | trait alias used in trait object type (additional use) + | first non-auto trait + | trait alias used in trait object type (first use) -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/trait-object-trait-parens.rs:11:31 +error[E0225]: only auto traits can be used as additional traits in a trait object + --> $DIR/trait-object-trait-parens.rs:16:38 | -LL | fn main() { - | - help: consider introducing lifetime `'a` here: `<'a>` -... -LL | let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>; - | ^^ undeclared lifetime +LL | let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>; + | ----------------- ^^^^^ + | | | + | | additional non-auto trait + | | trait alias used in trait object type (additional use) + | first non-auto trait + | trait alias used in trait object type (first use) -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0261`. +For more information about this error, try `rustc --explain E0225`. diff --git a/src/test/ui/pattern/bindings-after-at/box-patterns.rs b/src/test/ui/pattern/bindings-after-at/box-patterns.rs new file mode 100644 index 00000000000..ef9669a6b9e --- /dev/null +++ b/src/test/ui/pattern/bindings-after-at/box-patterns.rs @@ -0,0 +1,36 @@ +// Test bindings-after-at with box-patterns + +// run-pass + +#![feature(bindings_after_at)] +#![feature(box_patterns)] + +#[derive(Debug, PartialEq)] +enum MatchArm { + Arm(usize), + Wild, +} + +fn test(x: Option>) -> MatchArm { + match x { + ref bar @ Some(box n) if n > 0 => { + // bar is a &Option> + assert_eq!(bar, &x); + + MatchArm::Arm(0) + }, + Some(ref bar @ box n) if n < 0 => { + // bar is a &Box here + assert_eq!(**bar, n); + + MatchArm::Arm(1) + }, + _ => MatchArm::Wild, + } +} + +fn main() { + assert_eq!(test(Some(Box::new(2))), MatchArm::Arm(0)); + assert_eq!(test(Some(Box::new(-1))), MatchArm::Arm(1)); + assert_eq!(test(Some(Box::new(0))), MatchArm::Wild); +} diff --git a/src/test/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs b/src/test/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs new file mode 100644 index 00000000000..ca8826f03f1 --- /dev/null +++ b/src/test/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs @@ -0,0 +1,45 @@ +// Test bindings-after-at with or-patterns and box-patterns + +// run-pass + +#![feature(bindings_after_at)] +#![feature(or_patterns)] +#![feature(box_patterns)] + +#[derive(Debug, PartialEq)] +enum MatchArm { + Arm(usize), + Wild, +} + +#[derive(Debug, PartialEq)] +enum Test { + Foo, + Bar, + Baz, + Qux, +} + +fn test(foo: Option>) -> MatchArm { + match foo { + ref bar @ Some(box Test::Foo | box Test::Bar) => { + assert_eq!(bar, &foo); + + MatchArm::Arm(0) + }, + Some(ref bar @ box Test::Baz | ref bar @ box Test::Qux) => { + assert!(**bar == Test::Baz || **bar == Test::Qux); + + MatchArm::Arm(1) + }, + _ => MatchArm::Wild, + } +} + +fn main() { + assert_eq!(test(Some(Box::new(Test::Foo))), MatchArm::Arm(0)); + assert_eq!(test(Some(Box::new(Test::Bar))), MatchArm::Arm(0)); + assert_eq!(test(Some(Box::new(Test::Baz))), MatchArm::Arm(1)); + assert_eq!(test(Some(Box::new(Test::Qux))), MatchArm::Arm(1)); + assert_eq!(test(None), MatchArm::Wild); +} diff --git a/src/test/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs b/src/test/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs new file mode 100644 index 00000000000..65c2b3741b3 --- /dev/null +++ b/src/test/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs @@ -0,0 +1,56 @@ +// Test bindings-after-at with or-patterns and slice-patterns + +// run-pass + +#![feature(bindings_after_at)] +#![feature(or_patterns)] + +#[derive(Debug, PartialEq)] +enum MatchArm { + Arm(usize), + Wild, +} + +#[derive(Debug, PartialEq)] +enum Test { + Foo, + Bar, + Baz, + Qux, +} + +fn test(foo: &[Option]) -> MatchArm { + match foo { + bar @ [Some(Test::Foo), .., Some(Test::Qux | Test::Foo)] => { + assert_eq!(bar, foo); + + MatchArm::Arm(0) + }, + [.., bar @ Some(Test::Bar | Test::Qux), _] => { + assert!(bar == &Some(Test::Bar) || bar == &Some(Test::Qux)); + + MatchArm::Arm(1) + }, + _ => MatchArm::Wild, + } +} + +fn main() { + let foo = vec![ + Some(Test::Foo), + Some(Test::Bar), + Some(Test::Baz), + Some(Test::Qux), + ]; + + // path 1a + assert_eq!(test(&foo), MatchArm::Arm(0)); + // path 1b + assert_eq!(test(&[Some(Test::Foo), Some(Test::Bar), Some(Test::Foo)]), MatchArm::Arm(0)); + // path 2a + assert_eq!(test(&foo[..3]), MatchArm::Arm(1)); + // path 2b + assert_eq!(test(&[Some(Test::Bar), Some(Test::Qux), Some(Test::Baz)]), MatchArm::Arm(1)); + // path 3 + assert_eq!(test(&foo[1..2]), MatchArm::Wild); +} diff --git a/src/test/ui/pattern/bindings-after-at/or-patterns.rs b/src/test/ui/pattern/bindings-after-at/or-patterns.rs new file mode 100644 index 00000000000..a0e14004ab1 --- /dev/null +++ b/src/test/ui/pattern/bindings-after-at/or-patterns.rs @@ -0,0 +1,40 @@ +// Test bindings-after-at with or-patterns + +// run-pass + +#![feature(bindings_after_at)] +#![feature(or_patterns)] + +#[derive(Debug, PartialEq)] +enum MatchArm { + Arm(usize), + Wild, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum Test { + Foo, + Bar, + Baz, + Qux, +} + +fn test(foo: Option) -> MatchArm { + match foo { + bar @ Some(Test::Foo | Test::Bar) => { + assert!(bar == Some(Test::Foo) || bar == Some(Test::Bar)); + + MatchArm::Arm(0) + }, + Some(_) => MatchArm::Arm(1), + _ => MatchArm::Wild, + } +} + +fn main() { + assert_eq!(test(Some(Test::Foo)), MatchArm::Arm(0)); + assert_eq!(test(Some(Test::Bar)), MatchArm::Arm(0)); + assert_eq!(test(Some(Test::Baz)), MatchArm::Arm(1)); + assert_eq!(test(Some(Test::Qux)), MatchArm::Arm(1)); + assert_eq!(test(None), MatchArm::Wild); +} diff --git a/src/test/ui/pattern/bindings-after-at/slice-patterns.rs b/src/test/ui/pattern/bindings-after-at/slice-patterns.rs new file mode 100644 index 00000000000..7e50527af0b --- /dev/null +++ b/src/test/ui/pattern/bindings-after-at/slice-patterns.rs @@ -0,0 +1,40 @@ +// Test bindings-after-at with slice-patterns + +// run-pass + +#![feature(bindings_after_at)] + +#[derive(Debug, PartialEq)] +enum MatchArm { + Arm(usize), + Wild, +} + +fn test(foo: &[i32]) -> MatchArm { + match foo { + [bar @ .., n] if n == &5 => { + for i in bar { + assert!(i < &5); + } + + MatchArm::Arm(0) + }, + bar @ [x0, .., xn] => { + assert_eq!(x0, &1); + assert_eq!(x0, &1); + assert_eq!(xn, &4); + assert_eq!(bar, &[1, 2, 3, 4]); + + MatchArm::Arm(1) + }, + _ => MatchArm::Wild, + } +} + +fn main() { + let foo = vec![1, 2, 3, 4, 5]; + + assert_eq!(test(&foo), MatchArm::Arm(0)); + assert_eq!(test(&foo[..4]), MatchArm::Arm(1)); + assert_eq!(test(&foo[0..1]), MatchArm::Wild); +} diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs index 11eae2af9c9..7d1cac8a442 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs @@ -21,7 +21,7 @@ fn uninhab_union() -> Foo { fn match_on_uninhab() { match uninhab_ref() { - //~^ ERROR non-exhaustive patterns: type `&'static !` is non-empty + //~^ ERROR non-exhaustive patterns: type `&!` is non-empty } match uninhab_union() { diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr index 1b1096c977a..e1079f912d0 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -1,4 +1,4 @@ -error[E0004]: non-exhaustive patterns: type `&'static !` is non-empty +error[E0004]: non-exhaustive patterns: type `&!` is non-empty --> $DIR/always-inhabited-union-ref.rs:23:11 | LL | match uninhab_ref() { diff --git a/src/test/ui/privacy/privacy1.rs b/src/test/ui/privacy/privacy1.rs index fcf7b19572f..e28fd13b97f 100644 --- a/src/test/ui/privacy/privacy1.rs +++ b/src/test/ui/privacy/privacy1.rs @@ -74,7 +74,7 @@ fn test() { } self::baz::A; self::baz::A::foo(); - self::baz::A::bar(); //~ ERROR: method `bar` is private + self::baz::A::bar(); //~ ERROR: associated function `bar` is private self::baz::A.foo2(); // this used to cause an ICE in privacy traversal. @@ -92,21 +92,21 @@ pub fn gpub() {} fn lol() { bar::A; bar::A::foo(); - bar::A::bar(); //~ ERROR: method `bar` is private + bar::A::bar(); //~ ERROR: associated function `bar` is private bar::A.foo2(); } mod foo { fn test() { ::bar::A::foo(); - ::bar::A::bar(); //~ ERROR: method `bar` is private + ::bar::A::bar(); //~ ERROR: associated function `bar` is private ::bar::A.foo2(); ::bar::baz::A::foo(); //~ ERROR: module `baz` is private ::bar::baz::A::bar(); //~ ERROR: module `baz` is private - //~^ ERROR: method `bar` is private + //~^ ERROR: associated function `bar` is private ::bar::baz::A.foo2(); //~ ERROR: module `baz` is private ::bar::baz::A.bar2(); //~ ERROR: module `baz` is private - //~^ ERROR: method `bar2` is private + //~^ ERROR: associated function `bar2` is private let _: isize = ::bar::B::foo(); //~ ERROR: trait `B` is private diff --git a/src/test/ui/privacy/privacy1.stderr b/src/test/ui/privacy/privacy1.stderr index 215df0dc754..ec2bc0d84ac 100644 --- a/src/test/ui/privacy/privacy1.stderr +++ b/src/test/ui/privacy/privacy1.stderr @@ -154,31 +154,31 @@ note: the trait `B` is defined here LL | trait B { | ^^^^^^^ -error[E0624]: method `bar` is private +error[E0624]: associated function `bar` is private --> $DIR/privacy1.rs:77:9 | LL | self::baz::A::bar(); | ^^^^^^^^^^^^^^^^^ -error[E0624]: method `bar` is private +error[E0624]: associated function `bar` is private --> $DIR/privacy1.rs:95:5 | LL | bar::A::bar(); | ^^^^^^^^^^^ -error[E0624]: method `bar` is private +error[E0624]: associated function `bar` is private --> $DIR/privacy1.rs:102:9 | LL | ::bar::A::bar(); | ^^^^^^^^^^^^^ -error[E0624]: method `bar` is private +error[E0624]: associated function `bar` is private --> $DIR/privacy1.rs:105:9 | LL | ::bar::baz::A::bar(); | ^^^^^^^^^^^^^^^^^^ -error[E0624]: method `bar2` is private +error[E0624]: associated function `bar2` is private --> $DIR/privacy1.rs:108:23 | LL | ::bar::baz::A.bar2(); diff --git a/src/test/ui/privacy/privacy2.stderr b/src/test/ui/privacy/privacy2.stderr index 719dc27ccf4..b10c3a52659 100644 --- a/src/test/ui/privacy/privacy2.stderr +++ b/src/test/ui/privacy/privacy2.stderr @@ -10,11 +10,16 @@ error[E0603]: function import `foo` is private LL | use bar::glob::foo; | ^^^ this function import is private | -note: the function import `foo` is defined here +note: the function import `foo` is defined here... --> $DIR/privacy2.rs:10:13 | LL | use foo; | ^^^ +note: ...and refers to the function `foo` which is defined here + --> $DIR/privacy2.rs:14:1 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^ consider importing it directly error: requires `sized` lang_item diff --git a/src/test/ui/privacy/private-impl-method.rs b/src/test/ui/privacy/private-impl-method.rs index b5587920f1c..f7be6726c5e 100644 --- a/src/test/ui/privacy/private-impl-method.rs +++ b/src/test/ui/privacy/private-impl-method.rs @@ -17,5 +17,5 @@ fn bar(&self) {} // This should be visible outside `f` fn main() { let s = a::Foo { x: 1 }; s.bar(); - s.foo(); //~ ERROR method `foo` is private + s.foo(); //~ ERROR associated function `foo` is private } diff --git a/src/test/ui/privacy/private-impl-method.stderr b/src/test/ui/privacy/private-impl-method.stderr index e1da3f47a4e..6833cdb4df9 100644 --- a/src/test/ui/privacy/private-impl-method.stderr +++ b/src/test/ui/privacy/private-impl-method.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `foo` is private +error[E0624]: associated function `foo` is private --> $DIR/private-impl-method.rs:20:7 | LL | s.foo(); diff --git a/src/test/ui/privacy/private-method-cross-crate.rs b/src/test/ui/privacy/private-method-cross-crate.rs index 4da44e0682b..ab3bbdfe496 100644 --- a/src/test/ui/privacy/private-method-cross-crate.rs +++ b/src/test/ui/privacy/private-method-cross-crate.rs @@ -4,5 +4,5 @@ fn main() { let nyan : cat = cat(52, 99); - nyan.nap(); //~ ERROR method `nap` is private + nyan.nap(); //~ ERROR associated function `nap` is private } diff --git a/src/test/ui/privacy/private-method-cross-crate.stderr b/src/test/ui/privacy/private-method-cross-crate.stderr index 10e0bfe5b13..6b49063815a 100644 --- a/src/test/ui/privacy/private-method-cross-crate.stderr +++ b/src/test/ui/privacy/private-method-cross-crate.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `nap` is private +error[E0624]: associated function `nap` is private --> $DIR/private-method-cross-crate.rs:7:8 | LL | nyan.nap(); diff --git a/src/test/ui/privacy/private-method-inherited.rs b/src/test/ui/privacy/private-method-inherited.rs index bc27027e886..2f6454288ae 100644 --- a/src/test/ui/privacy/private-method-inherited.rs +++ b/src/test/ui/privacy/private-method-inherited.rs @@ -10,5 +10,5 @@ fn f(self) {} fn main() { let x = a::Foo; - x.f(); //~ ERROR method `f` is private + x.f(); //~ ERROR associated function `f` is private } diff --git a/src/test/ui/privacy/private-method-inherited.stderr b/src/test/ui/privacy/private-method-inherited.stderr index d2ba591ef0c..5551e1bd759 100644 --- a/src/test/ui/privacy/private-method-inherited.stderr +++ b/src/test/ui/privacy/private-method-inherited.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `f` is private +error[E0624]: associated function `f` is private --> $DIR/private-method-inherited.rs:13:7 | LL | x.f(); diff --git a/src/test/ui/privacy/private-method.rs b/src/test/ui/privacy/private-method.rs index a9bea520e75..76a642cde1a 100644 --- a/src/test/ui/privacy/private-method.rs +++ b/src/test/ui/privacy/private-method.rs @@ -19,5 +19,5 @@ pub fn cat(in_x : usize, in_y : isize) -> Cat { fn main() { let nyan : kitties::Cat = kitties::cat(52, 99); - nyan.nap(); //~ ERROR method `nap` is private + nyan.nap(); //~ ERROR associated function `nap` is private } diff --git a/src/test/ui/privacy/private-method.stderr b/src/test/ui/privacy/private-method.stderr index 61fc122e318..583dc123e24 100644 --- a/src/test/ui/privacy/private-method.stderr +++ b/src/test/ui/privacy/private-method.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `nap` is private +error[E0624]: associated function `nap` is private --> $DIR/private-method.rs:22:8 | LL | nyan.nap(); diff --git a/src/test/ui/privacy/restricted/test.stderr b/src/test/ui/privacy/restricted/test.stderr index aac444b8e3c..e73f723ed0a 100644 --- a/src/test/ui/privacy/restricted/test.stderr +++ b/src/test/ui/privacy/restricted/test.stderr @@ -52,13 +52,13 @@ error[E0616]: field `x` of struct `foo::bar::S` is private LL | S::default().x; | ^^^^^^^^^^^^^^ -error[E0624]: method `f` is private +error[E0624]: associated function `f` is private --> $DIR/test.rs:32:18 | LL | S::default().f(); | ^ -error[E0624]: method `g` is private +error[E0624]: associated function `g` is private --> $DIR/test.rs:33:5 | LL | S::g(); @@ -76,13 +76,13 @@ error[E0616]: field `z` of struct `pub_restricted::Universe` is private LL | let _ = u.z; | ^^^ -error[E0624]: method `g` is private +error[E0624]: associated function `g` is private --> $DIR/test.rs:45:7 | LL | u.g(); | ^ -error[E0624]: method `h` is private +error[E0624]: associated function `h` is private --> $DIR/test.rs:46:7 | LL | u.h(); diff --git a/src/test/ui/proc-macro/attribute-spans-preserved.stdout b/src/test/ui/proc-macro/attribute-spans-preserved.stdout index faf31712156..cf9a97491f0 100644 --- a/src/test/ui/proc-macro/attribute-spans-preserved.stdout +++ b/src/test/ui/proc-macro/attribute-spans-preserved.stdout @@ -1 +1 @@ -fn main () { let y : u32 = "z" ; { let x : u32 = "y" ; } } +fn main() { let y : u32 = "z" ; { let x : u32 = "y" ; } } diff --git a/src/test/ui/proc-macro/auxiliary/derive-unstable.rs b/src/test/ui/proc-macro/auxiliary/derive-unstable.rs index f702df66db1..2ccd3f88200 100644 --- a/src/test/ui/proc-macro/auxiliary/derive-unstable.rs +++ b/src/test/ui/proc-macro/auxiliary/derive-unstable.rs @@ -10,5 +10,5 @@ #[proc_macro_derive(Unstable)] pub fn derive(_input: TokenStream) -> TokenStream { - "unsafe fn foo() -> u32 { ::std::intrinsics::init() }".parse().unwrap() + "unsafe fn foo() -> u32 { ::std::intrinsics::abort() }".parse().unwrap() } diff --git a/src/test/ui/proc-macro/crt-static.rs b/src/test/ui/proc-macro/crt-static.rs new file mode 100644 index 00000000000..90e3d422b3c --- /dev/null +++ b/src/test/ui/proc-macro/crt-static.rs @@ -0,0 +1,16 @@ +// Test proc-macro crate can be built without addtional RUSTFLAGS +// on musl target +// override -Ctarget-feature=-crt-static from compiletest +// compile-flags: -Ctarget-feature= +// ignore-wasm32 +// build-pass +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(Foo)] +pub fn derive_foo(input: TokenStream) -> TokenStream { + input +} diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout index ea06f6c1aca..15433bebde9 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout @@ -1,4 +1,4 @@ -PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ; +PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct A(crate::S); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout index 619b2fd5321..73e407918ec 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout @@ -1,5 +1,5 @@ PRINT-ATTR INPUT (DISPLAY): struct A(identity!(crate :: S)); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A (identity ! ($crate :: S)) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A(identity ! ($crate :: S)) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -55,7 +55,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct B(identity!(::dollar_crate_external :: S)); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct B (identity ! ($crate :: S)) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct B(identity ! ($crate :: S)) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout index 5fdc6f8ee96..e125a3e7f17 100644 --- a/src/test/ui/proc-macro/dollar-crate.stdout +++ b/src/test/ui/proc-macro/dollar-crate.stdout @@ -1,4 +1,4 @@ -PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ; +PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct A(crate::S); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -80,7 +80,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): struct D(crate::S); -PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ; +PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D($crate :: S) ; PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -120,7 +120,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ span: #3 bytes(LO..HI), }, ] -PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ; +PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -161,7 +161,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -202,7 +202,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S); -PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ; +PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D($crate :: S) ; PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", diff --git a/src/test/ui/proc-macro/expand-to-unstable-2.stderr b/src/test/ui/proc-macro/expand-to-unstable-2.stderr index ff2e3af3777..19144b210a1 100644 --- a/src/test/ui/proc-macro/expand-to-unstable-2.stderr +++ b/src/test/ui/proc-macro/expand-to-unstable-2.stderr @@ -4,7 +4,6 @@ error[E0658]: attributes starting with `rustc` are reserved for use by the `rust LL | #[derive(Unstable)] | ^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/proc-macro/mixed-site-span.stderr b/src/test/ui/proc-macro/mixed-site-span.stderr index c344147ed93..30a4cd7c116 100644 --- a/src/test/ui/proc-macro/mixed-site-span.stderr +++ b/src/test/ui/proc-macro/mixed-site-span.stderr @@ -21,21 +21,10 @@ LL | local_def; | ^^^^^^^^^ not found in this scope error[E0412]: cannot find type `ItemUse` in crate `$crate` - --> $DIR/auxiliary/mixed-site-span.rs:14:1 - | -LL | / pub fn proc_macro_rules(input: TokenStream) -> TokenStream { -LL | | if input.is_empty() { -LL | | let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site())); -LL | | let item_def = id("ItemDef"); -... | -LL | | } -LL | | } - | |_^ not found in `$crate` - | - ::: $DIR/mixed-site-span.rs:26:1 - | -LL | pass_dollar_crate!(); - | --------------------- in this macro invocation + --> $DIR/mixed-site-span.rs:26:1 + | +LL | pass_dollar_crate!(); + | ^^^^^^^^^^^^^^^^^^^^^ not found in `$crate` | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: possible candidate is found in another module, you can import it into scope diff --git a/src/test/ui/proc-macro/multispan.stderr b/src/test/ui/proc-macro/multispan.stderr index c9390a04b9e..4405278528e 100644 --- a/src/test/ui/proc-macro/multispan.stderr +++ b/src/test/ui/proc-macro/multispan.stderr @@ -1,19 +1,8 @@ error: hello to you, too! - --> $DIR/auxiliary/multispan.rs:31:1 - | -LL | / pub fn hello(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | } -LL | | -LL | | TokenStream::new() -LL | | } - | |_^ - | - ::: $DIR/multispan.rs:14:5 - | -LL | hello!(hi); - | ----------- in this macro invocation + --> $DIR/multispan.rs:14:5 + | +LL | hello!(hi); + | ^^^^^^^^^^^ | note: found these 'hi's --> $DIR/multispan.rs:14:12 @@ -23,21 +12,10 @@ LL | hello!(hi); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/auxiliary/multispan.rs:31:1 - | -LL | / pub fn hello(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | } -LL | | -LL | | TokenStream::new() -LL | | } - | |_^ - | - ::: $DIR/multispan.rs:17:5 - | -LL | hello!(hi hi); - | -------------- in this macro invocation + --> $DIR/multispan.rs:17:5 + | +LL | hello!(hi hi); + | ^^^^^^^^^^^^^^ | note: found these 'hi's --> $DIR/multispan.rs:17:12 @@ -47,21 +25,10 @@ LL | hello!(hi hi); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/auxiliary/multispan.rs:31:1 - | -LL | / pub fn hello(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | } -LL | | -LL | | TokenStream::new() -LL | | } - | |_^ - | - ::: $DIR/multispan.rs:20:5 - | -LL | hello!(hi hi hi); - | ----------------- in this macro invocation + --> $DIR/multispan.rs:20:5 + | +LL | hello!(hi hi hi); + | ^^^^^^^^^^^^^^^^^ | note: found these 'hi's --> $DIR/multispan.rs:20:12 @@ -71,21 +38,10 @@ LL | hello!(hi hi hi); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/auxiliary/multispan.rs:31:1 - | -LL | / pub fn hello(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | } -LL | | -LL | | TokenStream::new() -LL | | } - | |_^ - | - ::: $DIR/multispan.rs:23:5 - | -LL | hello!(hi hey hi yo hi beep beep hi hi); - | ---------------------------------------- in this macro invocation + --> $DIR/multispan.rs:23:5 + | +LL | hello!(hi hey hi yo hi beep beep hi hi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's --> $DIR/multispan.rs:23:12 @@ -95,21 +51,10 @@ LL | hello!(hi hey hi yo hi beep beep hi hi); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/auxiliary/multispan.rs:31:1 - | -LL | / pub fn hello(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | } -LL | | -LL | | TokenStream::new() -LL | | } - | |_^ - | - ::: $DIR/multispan.rs:24:5 - | -LL | hello!(hi there, hi how are you? hi... hi.); - | -------------------------------------------- in this macro invocation + --> $DIR/multispan.rs:24:5 + | +LL | hello!(hi there, hi how are you? hi... hi.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's --> $DIR/multispan.rs:24:12 @@ -119,21 +64,10 @@ LL | hello!(hi there, hi how are you? hi... hi.); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/auxiliary/multispan.rs:31:1 - | -LL | / pub fn hello(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | } -LL | | -LL | | TokenStream::new() -LL | | } - | |_^ - | - ::: $DIR/multispan.rs:25:5 - | -LL | hello!(whoah. hi di hi di ho); - | ------------------------------ in this macro invocation + --> $DIR/multispan.rs:25:5 + | +LL | hello!(whoah. hi di hi di ho); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's --> $DIR/multispan.rs:25:19 @@ -143,21 +77,10 @@ LL | hello!(whoah. hi di hi di ho); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: hello to you, too! - --> $DIR/auxiliary/multispan.rs:31:1 - | -LL | / pub fn hello(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | } -LL | | -LL | | TokenStream::new() -LL | | } - | |_^ - | - ::: $DIR/multispan.rs:26:5 - | -LL | hello!(hi good hi and good bye); - | -------------------------------- in this macro invocation + --> $DIR/multispan.rs:26:5 + | +LL | hello!(hi good hi and good bye); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: found these 'hi's --> $DIR/multispan.rs:26:12 diff --git a/src/test/ui/proc-macro/span-api-tests.rs b/src/test/ui/proc-macro/span-api-tests.rs index 3667e14c9e0..5c0cbd77a8d 100644 --- a/src/test/ui/proc-macro/span-api-tests.rs +++ b/src/test/ui/proc-macro/span-api-tests.rs @@ -11,7 +11,9 @@ extern crate span_api_tests; -use span_api_tests::{reemit, assert_fake_source_file, assert_source_file, macro_stringify}; +// FIXME(69775): Investigate `assert_fake_source_file`. + +use span_api_tests::{reemit, assert_source_file, macro_stringify}; macro_rules! say_hello { ($macname:ident) => ( $macname! { "Hello, world!" }) @@ -25,7 +27,7 @@ macro_rules! say_hello { assert_source_file! { "Hello, world!" } } -say_hello_extern! { assert_fake_source_file } +say_hello_extern! { assert_source_file } reemit! { assert_source_file! { "Hello, world!" } diff --git a/src/test/ui/proc-macro/three-equals.stderr b/src/test/ui/proc-macro/three-equals.stderr index 82c4167262f..ca82a345345 100644 --- a/src/test/ui/proc-macro/three-equals.stderr +++ b/src/test/ui/proc-macro/three-equals.stderr @@ -1,19 +1,8 @@ error: found 2 equal signs, need exactly 3 - --> $DIR/auxiliary/three-equals.rs:42:1 - | -LL | / pub fn three_equals(input: TokenStream) -> TokenStream { -LL | | if let Err(diag) = parse(input) { -LL | | diag.emit(); -LL | | return TokenStream::new(); -... | -LL | | "3".parse().unwrap() -LL | | } - | |_^ - | - ::: $DIR/three-equals.rs:15:5 - | -LL | three_equals!(==); - | ------------------ in this macro invocation + --> $DIR/three-equals.rs:15:5 + | +LL | three_equals!(==); + | ^^^^^^^^^^^^^^^^^^ | = help: input must be: `===` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/qualified/qualified-path-params.rs b/src/test/ui/qualified/qualified-path-params.rs index b1b60b4b73f..65549d909d0 100644 --- a/src/test/ui/qualified/qualified-path-params.rs +++ b/src/test/ui/qualified/qualified-path-params.rs @@ -18,7 +18,7 @@ fn f() {} fn main() { match 10 { ::A::f:: => {} - //~^ ERROR expected unit struct, unit variant or constant, found method `<::A>::f` + //~^ ERROR expected unit struct, unit variant or constant, found associated function 0 ..= ::A::f:: => {} //~ ERROR only char and numeric types are allowed in range } } diff --git a/src/test/ui/qualified/qualified-path-params.stderr b/src/test/ui/qualified/qualified-path-params.stderr index 7a74a37021b..7ff43f4404c 100644 --- a/src/test/ui/qualified/qualified-path-params.stderr +++ b/src/test/ui/qualified/qualified-path-params.stderr @@ -1,4 +1,4 @@ -error[E0533]: expected unit struct, unit variant or constant, found method `<::A>::f` +error[E0533]: expected unit struct, unit variant or constant, found associated function `<::A>::f` --> $DIR/qualified-path-params.rs:20:9 | LL | ::A::f:: => {} diff --git a/src/test/ui/range/issue-54505-no-std.rs b/src/test/ui/range/issue-54505-no-std.rs index 22cf15fb2e4..c6a3cc346fc 100644 --- a/src/test/ui/range/issue-54505-no-std.rs +++ b/src/test/ui/range/issue-54505-no-std.rs @@ -15,10 +15,6 @@ #[lang = "eh_personality"] extern fn eh_personality() {} -#[cfg(target_os = "windows")] -#[lang = "eh_unwind_resume"] -extern fn eh_unwind_resume() {} - // take a reference to any built-in range fn take_range(_r: &impl RangeBounds) {} diff --git a/src/test/ui/range/issue-54505-no-std.stderr b/src/test/ui/range/issue-54505-no-std.stderr index aead80fa500..90934061132 100644 --- a/src/test/ui/range/issue-54505-no-std.stderr +++ b/src/test/ui/range/issue-54505-no-std.stderr @@ -1,7 +1,7 @@ error: `#[panic_handler]` function required, but not found error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:28:16 + --> $DIR/issue-54505-no-std.rs:24:16 | LL | take_range(0..1); | ^^^^ @@ -13,7 +13,7 @@ LL | take_range(0..1); found struct `core::ops::Range<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:33:16 + --> $DIR/issue-54505-no-std.rs:29:16 | LL | take_range(1..); | ^^^ @@ -25,7 +25,7 @@ LL | take_range(1..); found struct `core::ops::RangeFrom<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:38:16 + --> $DIR/issue-54505-no-std.rs:34:16 | LL | take_range(..); | ^^ @@ -37,7 +37,7 @@ LL | take_range(..); found struct `core::ops::RangeFull` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:43:16 + --> $DIR/issue-54505-no-std.rs:39:16 | LL | take_range(0..=1); | ^^^^^ @@ -49,7 +49,7 @@ LL | take_range(0..=1); found struct `core::ops::RangeInclusive<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:48:16 + --> $DIR/issue-54505-no-std.rs:44:16 | LL | take_range(..5); | ^^^ @@ -61,7 +61,7 @@ LL | take_range(..5); found struct `core::ops::RangeTo<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:53:16 + --> $DIR/issue-54505-no-std.rs:49:16 | LL | take_range(..=42); | ^^^^^ diff --git a/src/test/ui/realloc-16687.rs b/src/test/ui/realloc-16687.rs index 425aa83e70a..eb6224ad1bb 100644 --- a/src/test/ui/realloc-16687.rs +++ b/src/test/ui/realloc-16687.rs @@ -41,13 +41,13 @@ unsafe fn allocate(layout: Layout) -> *mut u8 { println!("allocate({:?})", layout); } - let ret = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + let (ptr, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); if PRINT { - println!("allocate({:?}) = {:?}", layout, ret); + println!("allocate({:?}) = {:?}", layout, ptr); } - ret.cast().as_ptr() + ptr.cast().as_ptr() } unsafe fn deallocate(ptr: *mut u8, layout: Layout) { @@ -63,16 +63,16 @@ unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr), old, new.size()) + let (ptr, _) = Global.realloc(NonNull::new_unchecked(ptr), old, new.size()) .unwrap_or_else(|_| handle_alloc_error( Layout::from_size_align_unchecked(new.size(), old.align()) )); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", - ptr, old, new, ret); + ptr, old, new, ptr); } - ret.cast().as_ptr() + ptr.cast().as_ptr() } fn idx_to_size(i: usize) -> usize { (i+1) * 10 } diff --git a/src/test/ui/recursion_limit/empty.rs b/src/test/ui/recursion_limit/empty.rs index 2a064f3e115..31ff9c1e3a7 100644 --- a/src/test/ui/recursion_limit/empty.rs +++ b/src/test/ui/recursion_limit/empty.rs @@ -1,6 +1,6 @@ // Test the parse error for an empty recursion_limit -#![recursion_limit = ""] //~ ERROR `recursion_limit` must be a non-negative integer - //~| `recursion_limit` must be a non-negative integer +#![recursion_limit = ""] //~ ERROR `limit` must be a non-negative integer + //~| `limit` must be a non-negative integer fn main() {} diff --git a/src/test/ui/recursion_limit/empty.stderr b/src/test/ui/recursion_limit/empty.stderr index 690c33a7463..bcd1d27e59b 100644 --- a/src/test/ui/recursion_limit/empty.stderr +++ b/src/test/ui/recursion_limit/empty.stderr @@ -1,10 +1,10 @@ -error: `recursion_limit` must be a non-negative integer +error: `limit` must be a non-negative integer --> $DIR/empty.rs:3:1 | LL | #![recursion_limit = ""] | ^^^^^^^^^^^^^^^^^^^^^--^ | | - | `recursion_limit` must be a non-negative integer + | `limit` must be a non-negative integer error: aborting due to previous error diff --git a/src/test/ui/recursion_limit/invalid_digit.rs b/src/test/ui/recursion_limit/invalid_digit.rs index 903d8040476..759d69d0af2 100644 --- a/src/test/ui/recursion_limit/invalid_digit.rs +++ b/src/test/ui/recursion_limit/invalid_digit.rs @@ -1,6 +1,6 @@ // Test the parse error for an invalid digit in recursion_limit -#![recursion_limit = "-100"] //~ ERROR `recursion_limit` must be a non-negative integer +#![recursion_limit = "-100"] //~ ERROR `limit` must be a non-negative integer //~| not a valid integer fn main() {} diff --git a/src/test/ui/recursion_limit/invalid_digit.stderr b/src/test/ui/recursion_limit/invalid_digit.stderr index 1dcfea547c0..e6fd6b72a09 100644 --- a/src/test/ui/recursion_limit/invalid_digit.stderr +++ b/src/test/ui/recursion_limit/invalid_digit.stderr @@ -1,4 +1,4 @@ -error: `recursion_limit` must be a non-negative integer +error: `limit` must be a non-negative integer --> $DIR/invalid_digit.rs:3:1 | LL | #![recursion_limit = "-100"] diff --git a/src/test/ui/recursion_limit/overflow.rs b/src/test/ui/recursion_limit/overflow.rs index 6487b1350aa..8eee2792b23 100644 --- a/src/test/ui/recursion_limit/overflow.rs +++ b/src/test/ui/recursion_limit/overflow.rs @@ -1,7 +1,7 @@ // Test the parse error for an overflowing recursion_limit #![recursion_limit = "999999999999999999999999"] -//~^ ERROR `recursion_limit` must be a non-negative integer -//~| `recursion_limit` is too large +//~^ ERROR `limit` must be a non-negative integer +//~| `limit` is too large fn main() {} diff --git a/src/test/ui/recursion_limit/overflow.stderr b/src/test/ui/recursion_limit/overflow.stderr index c3fc11989dc..f6ed76c1ebc 100644 --- a/src/test/ui/recursion_limit/overflow.stderr +++ b/src/test/ui/recursion_limit/overflow.stderr @@ -1,10 +1,10 @@ -error: `recursion_limit` must be a non-negative integer +error: `limit` must be a non-negative integer --> $DIR/overflow.rs:3:1 | LL | #![recursion_limit = "999999999999999999999999"] | ^^^^^^^^^^^^^^^^^^^^^--------------------------^ | | - | `recursion_limit` is too large + | `limit` is too large error: aborting due to previous error diff --git a/src/test/ui/recursion_limit/zero.rs b/src/test/ui/recursion_limit/zero.rs index f7199944e00..eb95d7babc6 100644 --- a/src/test/ui/recursion_limit/zero.rs +++ b/src/test/ui/recursion_limit/zero.rs @@ -1,4 +1,4 @@ -// Test that a `recursion_limit` of 0 is valid +// Test that a `limit` of 0 is valid #![recursion_limit = "0"] diff --git a/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr b/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr index a33d3583552..06e1b0f1ac2 100644 --- a/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr +++ b/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr @@ -1,32 +1,54 @@ -error[E0623]: lifetime mismatch +error[E0491]: in type `&'b &'a usize`, reference has a longer lifetime than the data it references --> $DIR/regions-free-region-ordering-caller.rs:11:12 | -LL | fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { - | --------- --------- - | | - | these two types are declared with different lifetimes... LL | let z: Option<&'b &'a usize> = None; - | ^^^^^^^^^^^^^^^^^^^^^ ...but data from `a` flows into `b` here + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'b` as defined on the function body at 10:14 + --> $DIR/regions-free-region-ordering-caller.rs:10:14 + | +LL | fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ +note: but the referenced data is only valid for the lifetime `'a` as defined on the function body at 10:10 + --> $DIR/regions-free-region-ordering-caller.rs:10:10 + | +LL | fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ -error[E0623]: lifetime mismatch +error[E0491]: in type `&'b Paramd<'a>`, reference has a longer lifetime than the data it references --> $DIR/regions-free-region-ordering-caller.rs:17:12 | -LL | fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { - | --------- --------- - | | - | these two types are declared with different lifetimes... -LL | let y: Paramd<'a> = Paramd { x: a }; LL | let z: Option<&'b Paramd<'a>> = None; - | ^^^^^^^^^^^^^^^^^^^^^^ ...but data from `a` flows into `b` here + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'b` as defined on the function body at 15:14 + --> $DIR/regions-free-region-ordering-caller.rs:15:14 + | +LL | fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ +note: but the referenced data is only valid for the lifetime `'a` as defined on the function body at 15:10 + --> $DIR/regions-free-region-ordering-caller.rs:15:10 + | +LL | fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ -error[E0623]: lifetime mismatch +error[E0491]: in type `&'a &'b usize`, reference has a longer lifetime than the data it references --> $DIR/regions-free-region-ordering-caller.rs:22:12 | -LL | fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { - | --------- --------- these two types are declared with different lifetimes... LL | let z: Option<&'a &'b usize> = None; - | ^^^^^^^^^^^^^^^^^^^^^ ...but data from `b` flows into `a` here + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'a` as defined on the function body at 21:10 + --> $DIR/regions-free-region-ordering-caller.rs:21:10 + | +LL | fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ +note: but the referenced data is only valid for the lifetime `'b` as defined on the function body at 21:14 + --> $DIR/regions-free-region-ordering-caller.rs:21:14 + | +LL | fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0623`. +For more information about this error, try `rustc --explain E0491`. diff --git a/src/test/ui/regions/regions-free-region-ordering-caller.rs b/src/test/ui/regions/regions-free-region-ordering-caller.rs index c0b12f23cdb..2bf4734cf73 100644 --- a/src/test/ui/regions/regions-free-region-ordering-caller.rs +++ b/src/test/ui/regions/regions-free-region-ordering-caller.rs @@ -8,18 +8,18 @@ struct Paramd<'a> { x: &'a usize } fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { - let z: Option<&'b &'a usize> = None;//[migrate]~ ERROR E0623 + let z: Option<&'b &'a usize> = None;//[migrate]~ ERROR E0491 //[nll]~^ ERROR lifetime may not live long enough } fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { let y: Paramd<'a> = Paramd { x: a }; - let z: Option<&'b Paramd<'a>> = None;//[migrate]~ ERROR E0623 + let z: Option<&'b Paramd<'a>> = None;//[migrate]~ ERROR E0491 //[nll]~^ ERROR lifetime may not live long enough } fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { - let z: Option<&'a &'b usize> = None;//[migrate]~ ERROR E0623 + let z: Option<&'a &'b usize> = None;//[migrate]~ ERROR E0491 //[nll]~^ ERROR lifetime may not live long enough } diff --git a/src/test/ui/regions/regions-mock-codegen.rs b/src/test/ui/regions/regions-mock-codegen.rs index f50b1c8b17f..fe3a864fe4b 100644 --- a/src/test/ui/regions/regions-mock-codegen.rs +++ b/src/test/ui/regions/regions-mock-codegen.rs @@ -24,29 +24,29 @@ struct Ccx { x: isize } -fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { +fn alloc(_bcx: &arena) -> &Bcx<'_> { unsafe { let layout = Layout::new::(); - let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + let (ptr, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); &*(ptr.as_ptr() as *const _) } } -fn h<'a>(bcx : &'a Bcx<'a>) -> &'a Bcx<'a> { +fn h<'a>(bcx: &'a Bcx<'a>) -> &'a Bcx<'a> { return alloc(bcx.fcx.arena); } -fn g(fcx : &Fcx) { - let bcx = Bcx { fcx: fcx }; +fn g(fcx: &Fcx) { + let bcx = Bcx { fcx }; let bcx2 = h(&bcx); unsafe { Global.dealloc(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::()); } } -fn f(ccx : &Ccx) { +fn f(ccx: &Ccx) { let a = arena(()); - let fcx = Fcx { arena: &a, ccx: ccx }; + let fcx = Fcx { arena: &a, ccx }; return g(&fcx); } diff --git a/src/test/ui/repr/repr-align-assign.stderr b/src/test/ui/repr/repr-align-assign.stderr index 192312d165b..b878ae0d173 100644 --- a/src/test/ui/repr/repr-align-assign.stderr +++ b/src/test/ui/repr/repr-align-assign.stderr @@ -24,3 +24,4 @@ LL | #[repr(align="8")] error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0693`. diff --git a/src/test/ui/reserved/reserved-attr-on-macro.stderr b/src/test/ui/reserved/reserved-attr-on-macro.stderr index 2870cb57e9c..c387bba0a13 100644 --- a/src/test/ui/reserved/reserved-attr-on-macro.stderr +++ b/src/test/ui/reserved/reserved-attr-on-macro.stderr @@ -4,7 +4,6 @@ error[E0658]: attributes starting with `rustc` are reserved for use by the `rust LL | #[rustc_attribute_should_be_reserved] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: cannot determine resolution for the macro `foo` diff --git a/src/test/ui/resolve/raw-ident-in-path.rs b/src/test/ui/resolve/raw-ident-in-path.rs new file mode 100644 index 00000000000..1bcbef59437 --- /dev/null +++ b/src/test/ui/resolve/raw-ident-in-path.rs @@ -0,0 +1,5 @@ +// Regression test for issue #63882. + +type A = crate::r#break; //~ ERROR cannot find type `r#break` in module `crate` + +fn main() {} diff --git a/src/test/ui/resolve/raw-ident-in-path.stderr b/src/test/ui/resolve/raw-ident-in-path.stderr new file mode 100644 index 00000000000..f2efcbc8e85 --- /dev/null +++ b/src/test/ui/resolve/raw-ident-in-path.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `r#break` in module `crate` + --> $DIR/raw-ident-in-path.rs:3:17 + | +LL | type A = crate::r#break; + | ^^^^^^^ not found in `crate` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/resolve/resolve-inconsistent-binding-mode.rs b/src/test/ui/resolve/resolve-inconsistent-binding-mode.rs index e9c4e47f887..43e9378b7d0 100644 --- a/src/test/ui/resolve/resolve-inconsistent-binding-mode.rs +++ b/src/test/ui/resolve/resolve-inconsistent-binding-mode.rs @@ -1,39 +1,40 @@ enum Opts { - A(isize), B(isize), C(isize) + A(isize), + B(isize), + C(isize), } fn matcher1(x: Opts) { match x { - Opts::A(ref i) | Opts::B(i) => {} - //~^ ERROR variable `i` is bound in inconsistent ways within the same match arm - //~^^ ERROR mismatched types - Opts::C(_) => {} + Opts::A(ref i) | Opts::B(i) => {} + //~^ ERROR variable `i` is bound inconsistently + //~^^ ERROR mismatched types + Opts::C(_) => {} } } fn matcher2(x: Opts) { match x { - Opts::A(ref i) | Opts::B(i) => {} - //~^ ERROR variable `i` is bound in inconsistent ways within the same match arm - //~^^ ERROR mismatched types - Opts::C(_) => {} + Opts::A(ref i) | Opts::B(i) => {} + //~^ ERROR variable `i` is bound inconsistently + //~^^ ERROR mismatched types + Opts::C(_) => {} } } fn matcher4(x: Opts) { match x { - Opts::A(ref mut i) | Opts::B(ref i) => {} - //~^ ERROR variable `i` is bound in inconsistent ways within the same match arm - //~^^ ERROR mismatched types - Opts::C(_) => {} + Opts::A(ref mut i) | Opts::B(ref i) => {} + //~^ ERROR variable `i` is bound inconsistently + //~^^ ERROR mismatched types + Opts::C(_) => {} } } - fn matcher5(x: Opts) { match x { - Opts::A(ref i) | Opts::B(ref i) => {} - Opts::C(_) => {} + Opts::A(ref i) | Opts::B(ref i) => {} + Opts::C(_) => {} } } diff --git a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr index 749ed131b20..c14dfa3601a 100644 --- a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr +++ b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr @@ -1,58 +1,58 @@ -error[E0409]: variable `i` is bound in inconsistent ways within the same match arm - --> $DIR/resolve-inconsistent-binding-mode.rs:7:32 +error[E0409]: variable `i` is bound inconsistently across alternatives separated by `|` + --> $DIR/resolve-inconsistent-binding-mode.rs:9:34 | -LL | Opts::A(ref i) | Opts::B(i) => {} - | - ^ bound in different ways - | | - | first binding +LL | Opts::A(ref i) | Opts::B(i) => {} + | - ^ bound in different ways + | | + | first binding -error[E0409]: variable `i` is bound in inconsistent ways within the same match arm - --> $DIR/resolve-inconsistent-binding-mode.rs:16:32 +error[E0409]: variable `i` is bound inconsistently across alternatives separated by `|` + --> $DIR/resolve-inconsistent-binding-mode.rs:18:34 | -LL | Opts::A(ref i) | Opts::B(i) => {} - | - ^ bound in different ways - | | - | first binding +LL | Opts::A(ref i) | Opts::B(i) => {} + | - ^ bound in different ways + | | + | first binding -error[E0409]: variable `i` is bound in inconsistent ways within the same match arm - --> $DIR/resolve-inconsistent-binding-mode.rs:25:40 +error[E0409]: variable `i` is bound inconsistently across alternatives separated by `|` + --> $DIR/resolve-inconsistent-binding-mode.rs:27:42 | -LL | Opts::A(ref mut i) | Opts::B(ref i) => {} - | - first binding ^ bound in different ways +LL | Opts::A(ref mut i) | Opts::B(ref i) => {} + | - first binding ^ bound in different ways error[E0308]: mismatched types - --> $DIR/resolve-inconsistent-binding-mode.rs:7:32 + --> $DIR/resolve-inconsistent-binding-mode.rs:9:34 | LL | match x { | - this expression has type `Opts` -LL | Opts::A(ref i) | Opts::B(i) => {} - | ----- ^ expected `&isize`, found `isize` - | | - | first introduced with type `&isize` here +LL | Opts::A(ref i) | Opts::B(i) => {} + | ----- ^ expected `&isize`, found `isize` + | | + | first introduced with type `&isize` here | = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/resolve-inconsistent-binding-mode.rs:16:32 + --> $DIR/resolve-inconsistent-binding-mode.rs:18:34 | LL | match x { | - this expression has type `Opts` -LL | Opts::A(ref i) | Opts::B(i) => {} - | ----- ^ expected `&isize`, found `isize` - | | - | first introduced with type `&isize` here +LL | Opts::A(ref i) | Opts::B(i) => {} + | ----- ^ expected `&isize`, found `isize` + | | + | first introduced with type `&isize` here | = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/resolve-inconsistent-binding-mode.rs:25:36 + --> $DIR/resolve-inconsistent-binding-mode.rs:27:38 | LL | match x { | - this expression has type `Opts` -LL | Opts::A(ref mut i) | Opts::B(ref i) => {} - | --------- ^^^^^ types differ in mutability - | | - | first introduced with type `&mut isize` here +LL | Opts::A(ref mut i) | Opts::B(ref i) => {} + | --------- ^^^^^ types differ in mutability + | | + | first introduced with type `&mut isize` here | = note: expected type `&mut isize` found type `&isize` diff --git a/src/test/ui/resolve/resolve-inconsistent-names.rs b/src/test/ui/resolve/resolve-inconsistent-names.rs index 2fb803c4b2a..b9202f556d1 100644 --- a/src/test/ui/resolve/resolve-inconsistent-names.rs +++ b/src/test/ui/resolve/resolve-inconsistent-names.rs @@ -19,7 +19,7 @@ fn main() { (A, B) | (ref B, c) | (c, A) => () //~^ ERROR variable `A` is not bound in all patterns //~| ERROR variable `B` is not bound in all patterns - //~| ERROR variable `B` is bound in inconsistent ways + //~| ERROR variable `B` is bound inconsistently //~| ERROR mismatched types //~| ERROR variable `c` is not bound in all patterns //~| HELP consider making the path in the pattern qualified: `?::A` diff --git a/src/test/ui/resolve/resolve-inconsistent-names.stderr b/src/test/ui/resolve/resolve-inconsistent-names.stderr index 1d3079c90ba..70e9c2e5bf5 100644 --- a/src/test/ui/resolve/resolve-inconsistent-names.stderr +++ b/src/test/ui/resolve/resolve-inconsistent-names.stderr @@ -47,7 +47,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () | | variable not in all patterns | pattern doesn't bind `c` -error[E0409]: variable `B` is bound in inconsistent ways within the same match arm +error[E0409]: variable `B` is bound inconsistently across alternatives separated by `|` --> $DIR/resolve-inconsistent-names.rs:19:23 | LL | (A, B) | (ref B, c) | (c, A) => () diff --git a/src/test/ui/rfc-2005-default-binding-mode/const.stderr b/src/test/ui/rfc-2005-default-binding-mode/const.stderr index 27efd450b94..10d30ec1a1b 100644 --- a/src/test/ui/rfc-2005-default-binding-mode/const.stderr +++ b/src/test/ui/rfc-2005-default-binding-mode/const.stderr @@ -1,10 +1,17 @@ error[E0308]: mismatched types --> $DIR/const.rs:14:9 | +LL | const FOO: Foo = Foo{bar: 5}; + | ----------------------------- constant defined here +... LL | match &f { | -- this expression has type `&Foo` LL | FOO => {}, - | ^^^ expected `&Foo`, found struct `Foo` + | ^^^ + | | + | expected `&Foo`, found struct `Foo` + | `FOO` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_foo` error: aborting due to previous error diff --git a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs index aa013d4bf35..b4a0d8145c1 100644 --- a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs +++ b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs @@ -4,7 +4,7 @@ pub fn main() { let x = &Some((3, 3)); let _: &i32 = match x { Some((x, 3)) | &Some((ref x, 5)) => x, - //~^ ERROR is bound in inconsistent ways + //~^ ERROR is bound inconsistently _ => &5i32, }; } diff --git a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr index ff8dce32b2a..e1e1bf7f6d9 100644 --- a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr +++ b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr @@ -1,4 +1,4 @@ -error[E0409]: variable `x` is bound in inconsistent ways within the same match arm +error[E0409]: variable `x` is bound inconsistently across alternatives separated by `|` --> $DIR/issue-44912-or.rs:6:35 | LL | Some((x, 3)) | &Some((ref x, 5)) => x, diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr index 7becb9c5b60..c2fb8fa1eb6 100644 --- a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr +++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr @@ -8,3 +8,4 @@ LL | struct S; error: aborting due to previous error +For more information about this error, try `rustc --explain E0739`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr index 3ea58a3728a..834f6a409f5 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr @@ -1,18 +1,18 @@ error: inherent impls cannot be `const` - --> $DIR/inherent-impl.rs:9:1 + --> $DIR/inherent-impl.rs:9:12 | LL | impl const S {} - | ^^^^^-----^^^^^ + | ----- ^ inherent impl for this type | | | `const` because of this | = note: only trait implementations may be annotated with `const` error: inherent impls cannot be `const` - --> $DIR/inherent-impl.rs:12:1 + --> $DIR/inherent-impl.rs:12:12 | LL | impl const T {} - | ^^^^^-----^^^^^ + | ----- ^ inherent impl for this type | | | `const` because of this | diff --git a/src/test/ui/rust-2018/macro-use-warned-against.rs b/src/test/ui/rust-2018/macro-use-warned-against.rs index 6cd54aa68ae..65400163ddd 100644 --- a/src/test/ui/rust-2018/macro-use-warned-against.rs +++ b/src/test/ui/rust-2018/macro-use-warned-against.rs @@ -4,7 +4,7 @@ #![warn(macro_use_extern_crate, unused)] -#[macro_use] //~ WARN should be replaced at use sites with a `use` statement +#[macro_use] //~ WARN should be replaced at use sites with a `use` item extern crate macro_use_warned_against; #[macro_use] //~ WARN unused `#[macro_use]` extern crate macro_use_warned_against2; diff --git a/src/test/ui/rust-2018/macro-use-warned-against.stderr b/src/test/ui/rust-2018/macro-use-warned-against.stderr index 611b9d5dac9..ef00b865815 100644 --- a/src/test/ui/rust-2018/macro-use-warned-against.stderr +++ b/src/test/ui/rust-2018/macro-use-warned-against.stderr @@ -1,4 +1,4 @@ -warning: deprecated `#[macro_use]` directive used to import macros should be replaced at use sites with a `use` statement to import the macro instead +warning: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead --> $DIR/macro-use-warned-against.rs:7:1 | LL | #[macro_use] diff --git a/src/test/ui/sanitize/address.rs b/src/test/ui/sanitize/address.rs index d27a30a2dc5..f8650cd86d5 100644 --- a/src/test/ui/sanitize/address.rs +++ b/src/test/ui/sanitize/address.rs @@ -1,16 +1,15 @@ // needs-sanitizer-support // only-x86_64 // -// compile-flags: -Z sanitizer=address -O +// compile-flags: -Z sanitizer=address -O -g // // run-fail // error-pattern: AddressSanitizer: stack-buffer-overflow -// error-pattern: 'xs' <== Memory access at offset +// error-pattern: 'xs' (line 15) <== Memory access at offset #![feature(test)] use std::hint::black_box; -use std::mem; fn main() { let xs = [0, 1, 2, 3]; diff --git a/src/test/ui/sanitize/badfree.rs b/src/test/ui/sanitize/badfree.rs new file mode 100644 index 00000000000..1ca082c8b47 --- /dev/null +++ b/src/test/ui/sanitize/badfree.rs @@ -0,0 +1,19 @@ +// needs-sanitizer-support +// only-x86_64 +// +// compile-flags: -Z sanitizer=address -O +// +// run-fail +// error-pattern: AddressSanitizer: SEGV + +use std::ffi::c_void; + +extern "C" { + fn free(ptr: *mut c_void); +} + +fn main() { + unsafe { + free(1 as *mut c_void); + } +} diff --git a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs new file mode 100644 index 00000000000..d0984bbe65f --- /dev/null +++ b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs @@ -0,0 +1,27 @@ +// Regression test for sanitizer function instrumentation passes not +// being run when compiling with new LLVM pass manager and ThinLTO. +// Note: The issue occurred only on non-zero opt-level. +// +// min-llvm-version 9.0 +// needs-sanitizer-support +// only-x86_64 +// +// no-prefer-dynamic +// revisions: opt0 opt1 +// compile-flags: -Znew-llvm-pass-manager=yes -Zsanitizer=address -Clto=thin +//[opt0]compile-flags: -Copt-level=0 +//[opt1]compile-flags: -Copt-level=1 +// run-fail +// error-pattern: ERROR: AddressSanitizer: stack-use-after-scope + +static mut P: *mut usize = std::ptr::null_mut(); + +fn main() { + unsafe { + { + let mut x = 0; + P = &mut x; + } + std::ptr::write_volatile(P, 123); + } +} diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.nll.stderr index 6afcf24cd3e..57374b7e3bb 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.nll.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.nll.stderr @@ -2,7 +2,7 @@ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:8:52 | LL | async fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } - | - - ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | - - ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | | | | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` @@ -11,7 +11,7 @@ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:11:75 | LL | async fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } - | - - ^^^^^^^^^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | - - ^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | | | | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` @@ -20,7 +20,7 @@ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:17:64 | LL | async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } - | -- - ^^^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` + | -- - ^^^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` | | | | | let's call the lifetime of this reference `'1` | lifetime `'a` defined here diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr index a659e448785..17099201d11 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr @@ -2,7 +2,7 @@ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:6:46 | LL | fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } - | - - ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | - - ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | | | | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` @@ -11,7 +11,7 @@ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:8:69 | LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } - | - - ^^^^^^^^^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | - - ^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | | | | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` @@ -20,7 +20,7 @@ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:13:58 | LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } - | -- ---- has type `std::pin::Pin<&'1 Foo>` ^^^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` + | -- ---- has type `std::pin::Pin<&'1 Foo>` ^^^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` | | | lifetime `'a` defined here diff --git a/src/test/ui/self/elision/lt-ref-self-async.nll.stderr b/src/test/ui/self/elision/lt-ref-self-async.nll.stderr index 57d0929c50a..c10b8824e6d 100644 --- a/src/test/ui/self/elision/lt-ref-self-async.nll.stderr +++ b/src/test/ui/self/elision/lt-ref-self-async.nll.stderr @@ -6,7 +6,7 @@ LL | async fn ref_self(&self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:19:9 @@ -16,7 +16,7 @@ LL | async fn ref_Self(self: &Self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:23:9 @@ -26,7 +26,7 @@ LL | async fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:27:9 @@ -36,7 +36,7 @@ LL | async fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:31:9 @@ -46,7 +46,7 @@ LL | async fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:35:9 @@ -56,7 +56,7 @@ LL | async fn box_pin_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 6 previous errors diff --git a/src/test/ui/self/elision/lt-ref-self.nll.stderr b/src/test/ui/self/elision/lt-ref-self.nll.stderr index b51b5a0ba38..e2de743b8f6 100644 --- a/src/test/ui/self/elision/lt-ref-self.nll.stderr +++ b/src/test/ui/self/elision/lt-ref-self.nll.stderr @@ -6,7 +6,7 @@ LL | fn ref_self(&self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self.rs:17:9 @@ -16,7 +16,7 @@ LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self.rs:21:9 @@ -26,7 +26,7 @@ LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self.rs:25:9 @@ -36,7 +36,7 @@ LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self.rs:29:9 @@ -46,7 +46,7 @@ LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/lt-ref-self.rs:33:9 @@ -56,7 +56,7 @@ LL | fn box_pin_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 6 previous errors diff --git a/src/test/ui/self/elision/ref-mut-self-async.nll.stderr b/src/test/ui/self/elision/ref-mut-self-async.nll.stderr index 46e828390b0..19496a5ef6d 100644 --- a/src/test/ui/self/elision/ref-mut-self-async.nll.stderr +++ b/src/test/ui/self/elision/ref-mut-self-async.nll.stderr @@ -6,7 +6,7 @@ LL | async fn ref_self(&mut self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:19:9 @@ -16,7 +16,7 @@ LL | async fn ref_Self(self: &mut Self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:23:9 @@ -26,7 +26,7 @@ LL | async fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:27:9 @@ -36,7 +36,7 @@ LL | async fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:31:9 @@ -46,7 +46,7 @@ LL | async fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:35:9 @@ -56,7 +56,7 @@ LL | async fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 6 previous errors diff --git a/src/test/ui/self/elision/ref-mut-self.nll.stderr b/src/test/ui/self/elision/ref-mut-self.nll.stderr index 6c8c030e5ff..94bfc5f4a81 100644 --- a/src/test/ui/self/elision/ref-mut-self.nll.stderr +++ b/src/test/ui/self/elision/ref-mut-self.nll.stderr @@ -6,7 +6,7 @@ LL | fn ref_self(&mut self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self.rs:17:9 @@ -16,7 +16,7 @@ LL | fn ref_Self(self: &mut Self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self.rs:21:9 @@ -26,7 +26,7 @@ LL | fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self.rs:25:9 @@ -36,7 +36,7 @@ LL | fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self.rs:29:9 @@ -46,7 +46,7 @@ LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-self.rs:33:9 @@ -56,7 +56,7 @@ LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 6 previous errors diff --git a/src/test/ui/self/elision/ref-mut-struct-async.nll.stderr b/src/test/ui/self/elision/ref-mut-struct-async.nll.stderr index 99340800790..94671c7c87a 100644 --- a/src/test/ui/self/elision/ref-mut-struct-async.nll.stderr +++ b/src/test/ui/self/elision/ref-mut-struct-async.nll.stderr @@ -6,7 +6,7 @@ LL | async fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:17:9 @@ -16,7 +16,7 @@ LL | async fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:21:9 @@ -26,7 +26,7 @@ LL | async fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:25:9 @@ -36,7 +36,7 @@ LL | async fn box_box_ref_Struct(self: Box>, f: &u32) -> &u | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:29:9 @@ -46,7 +46,7 @@ LL | async fn box_pin_ref_Struct(self: Box>, f: &u32) -> &u | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 5 previous errors diff --git a/src/test/ui/self/elision/ref-mut-struct.nll.stderr b/src/test/ui/self/elision/ref-mut-struct.nll.stderr index e3886444db2..c9e7479ea5d 100644 --- a/src/test/ui/self/elision/ref-mut-struct.nll.stderr +++ b/src/test/ui/self/elision/ref-mut-struct.nll.stderr @@ -6,7 +6,7 @@ LL | fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct.rs:15:9 @@ -16,7 +16,7 @@ LL | fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct.rs:19:9 @@ -26,7 +26,7 @@ LL | fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct.rs:23:9 @@ -36,7 +36,7 @@ LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-mut-struct.rs:27:9 @@ -46,7 +46,7 @@ LL | fn box_pin_ref_Struct(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 5 previous errors diff --git a/src/test/ui/self/elision/ref-self.nll.stderr b/src/test/ui/self/elision/ref-self.nll.stderr index ecac1ce3378..d1fd209102e 100644 --- a/src/test/ui/self/elision/ref-self.nll.stderr +++ b/src/test/ui/self/elision/ref-self.nll.stderr @@ -6,7 +6,7 @@ LL | fn ref_self(&self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-self.rs:27:9 @@ -16,7 +16,7 @@ LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-self.rs:31:9 @@ -26,7 +26,7 @@ LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-self.rs:35:9 @@ -36,7 +36,7 @@ LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-self.rs:39:9 @@ -46,7 +46,7 @@ LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-self.rs:43:9 @@ -56,7 +56,7 @@ LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-self.rs:47:9 @@ -66,7 +66,7 @@ LL | fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 7 previous errors diff --git a/src/test/ui/self/elision/ref-struct-async.nll.stderr b/src/test/ui/self/elision/ref-struct-async.nll.stderr index bcbf79bc039..9361b6f3f81 100644 --- a/src/test/ui/self/elision/ref-struct-async.nll.stderr +++ b/src/test/ui/self/elision/ref-struct-async.nll.stderr @@ -6,7 +6,7 @@ LL | async fn ref_Struct(self: &Struct, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:17:9 @@ -16,7 +16,7 @@ LL | async fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:21:9 @@ -26,7 +26,7 @@ LL | async fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:25:9 @@ -36,7 +36,7 @@ LL | async fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:29:9 @@ -46,7 +46,7 @@ LL | async fn box_pin_Struct(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 5 previous errors diff --git a/src/test/ui/self/elision/ref-struct.nll.stderr b/src/test/ui/self/elision/ref-struct.nll.stderr index 39e7631f31e..e1cc38b7c95 100644 --- a/src/test/ui/self/elision/ref-struct.nll.stderr +++ b/src/test/ui/self/elision/ref-struct.nll.stderr @@ -6,7 +6,7 @@ LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct.rs:15:9 @@ -16,7 +16,7 @@ LL | fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct.rs:19:9 @@ -26,7 +26,7 @@ LL | fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct.rs:23:9 @@ -36,7 +36,7 @@ LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> $DIR/ref-struct.rs:27:9 @@ -46,7 +46,7 @@ LL | fn box_pin_Struct(self: Box>, f: &u32) -> &u32 { | | | let's call the lifetime of this reference `'2` LL | f - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` error: aborting due to 5 previous errors diff --git a/src/test/ui/shadowed/shadowed-use-visibility.stderr b/src/test/ui/shadowed/shadowed-use-visibility.stderr index cd8ec13794c..2244f3a46b2 100644 --- a/src/test/ui/shadowed/shadowed-use-visibility.stderr +++ b/src/test/ui/shadowed/shadowed-use-visibility.stderr @@ -4,11 +4,16 @@ error[E0603]: module import `bar` is private LL | use foo::bar::f as g; | ^^^ this module import is private | -note: the module import `bar` is defined here +note: the module import `bar` is defined here... --> $DIR/shadowed-use-visibility.rs:4:9 | LL | use foo as bar; | ^^^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/shadowed-use-visibility.rs:1:1 + | +LL | mod foo { + | ^^^^^^^ error[E0603]: module import `f` is private --> $DIR/shadowed-use-visibility.rs:15:10 @@ -16,11 +21,16 @@ error[E0603]: module import `f` is private LL | use bar::f::f; | ^ this module import is private | -note: the module import `f` is defined here +note: the module import `f` is defined here... --> $DIR/shadowed-use-visibility.rs:11:9 | LL | use foo as f; | ^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/shadowed-use-visibility.rs:1:1 + | +LL | mod foo { + | ^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/span/auxiliary/transitive_dep_three.rs b/src/test/ui/span/auxiliary/transitive_dep_three.rs new file mode 100644 index 00000000000..99b51625ac3 --- /dev/null +++ b/src/test/ui/span/auxiliary/transitive_dep_three.rs @@ -0,0 +1,9 @@ +#[macro_export] +macro_rules! define_parse_error { + () => { + #[macro_export] + macro_rules! parse_error { + () => { parse error } + } + } +} diff --git a/src/test/ui/span/auxiliary/transitive_dep_two.rs b/src/test/ui/span/auxiliary/transitive_dep_two.rs new file mode 100644 index 00000000000..5110c42765b --- /dev/null +++ b/src/test/ui/span/auxiliary/transitive_dep_two.rs @@ -0,0 +1,3 @@ +extern crate transitive_dep_three; + +transitive_dep_three::define_parse_error!(); diff --git a/src/test/ui/span/transitive-dep-span.rs b/src/test/ui/span/transitive-dep-span.rs new file mode 100644 index 00000000000..b445d389c56 --- /dev/null +++ b/src/test/ui/span/transitive-dep-span.rs @@ -0,0 +1,13 @@ +// Tests that we properly serialize/deserialize spans from transitive dependencies +// (e.g. imported SourceFiles) +// +// The order of these next lines is important, since we need +// transitive_dep_two.rs to be able to reference transitive_dep_three.rs +// +// aux-build: transitive_dep_three.rs +// aux-build: transitive_dep_two.rs +// compile-flags: -Z macro-backtrace + +extern crate transitive_dep_two; + +transitive_dep_two::parse_error!(); //~ ERROR expected one of diff --git a/src/test/ui/span/transitive-dep-span.stderr b/src/test/ui/span/transitive-dep-span.stderr new file mode 100644 index 00000000000..68d8911a435 --- /dev/null +++ b/src/test/ui/span/transitive-dep-span.stderr @@ -0,0 +1,19 @@ +error: expected one of `!` or `::`, found `error` + --> $DIR/auxiliary/transitive_dep_three.rs:6:27 + | +LL | / macro_rules! parse_error { +LL | | () => { parse error } + | | ^^^^^ expected one of `!` or `::` +LL | | } + | |_________- in this expansion of `transitive_dep_two::parse_error!` + | + ::: $DIR/transitive-dep-span.rs:13:1 + | +LL | transitive_dep_two::parse_error!(); + | ----------------------------------- + | | + | in this macro invocation + | in this macro invocation + +error: aborting due to previous error + diff --git a/src/test/ui/span/type-annotations-needed-expr.stderr b/src/test/ui/span/type-annotations-needed-expr.stderr index 2b92f9b93bf..35d994e194f 100644 --- a/src/test/ui/span/type-annotations-needed-expr.stderr +++ b/src/test/ui/span/type-annotations-needed-expr.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let _ = (vec![1,2,3]).into_iter().sum() as f64; | ^^^ | | - | cannot infer type for type parameter `S` declared on the method `sum` + | cannot infer type for type parameter `S` declared on the associated function `sum` | help: consider specifying the type argument in the method call: `sum::` | = note: type must be known at this point diff --git a/src/test/ui/specialization/defaultimpl/validation.stderr b/src/test/ui/specialization/defaultimpl/validation.stderr index 03b1ef69ca0..6e19d79e48f 100644 --- a/src/test/ui/specialization/defaultimpl/validation.stderr +++ b/src/test/ui/specialization/defaultimpl/validation.stderr @@ -1,8 +1,8 @@ error: inherent impls cannot be `default` - --> $DIR/validation.rs:7:1 + --> $DIR/validation.rs:7:14 | LL | default impl S {} - | -------^^^^^^^ + | ------- ^ inherent impl for this type | | | `default` because of this | diff --git a/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs b/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs new file mode 100644 index 00000000000..6ec0d261d51 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs @@ -0,0 +1,6 @@ +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +pub trait SpecTrait { + fn method(&self); +} diff --git a/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.rs b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.rs new file mode 100644 index 00000000000..03cab00b0fb --- /dev/null +++ b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.rs @@ -0,0 +1,32 @@ +// Test that associated types in trait objects are not considered to be +// constrained. + +#![feature(min_specialization)] + +trait Specializable { + fn f(); +} + +trait B { + type Y; +} + +trait C { + type Y; +} + +impl Specializable for A { + default fn f() {} +} + +impl<'a, T> Specializable for dyn B + 'a { + //~^ ERROR specializing impl repeats parameter `T` + fn f() {} +} + +impl<'a, T> Specializable for dyn C + 'a { + //~^ ERROR specializing impl repeats parameter `T` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.stderr b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.stderr new file mode 100644 index 00000000000..6345cee2c37 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.stderr @@ -0,0 +1,20 @@ +error: specializing impl repeats parameter `T` + --> $DIR/dyn-trait-assoc-types.rs:22:1 + | +LL | / impl<'a, T> Specializable for dyn B + 'a { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: specializing impl repeats parameter `T` + --> $DIR/dyn-trait-assoc-types.rs:27:1 + | +LL | / impl<'a, T> Specializable for dyn C + 'a { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs b/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs new file mode 100644 index 00000000000..723ed71c3e9 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs @@ -0,0 +1,16 @@ +// Check that specialization traits can't be implemented without a feature. + +// gate-test-min_specialization + +// aux-build:specialization-trait.rs + +extern crate specialization_trait; + +struct A {} + +impl specialization_trait::SpecTrait for A { + //~^ ERROR implementing `rustc_specialization_trait` traits is unstable + fn method(&self) {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr b/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr new file mode 100644 index 00000000000..934103d49dc --- /dev/null +++ b/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr @@ -0,0 +1,10 @@ +error: implementing `rustc_specialization_trait` traits is unstable + --> $DIR/impl_specialization_trait.rs:11:1 + | +LL | impl specialization_trait::SpecTrait for A { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(min_specialization)]` to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs b/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs new file mode 100644 index 00000000000..98d7f919435 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs @@ -0,0 +1,30 @@ +// Test that specializing on the well-formed predicates of the trait and +// self-type of an impl is allowed. + +// check-pass + +#![feature(min_specialization)] + +struct OrdOnly(T); + +trait SpecTrait { + fn f(); +} + +impl SpecTrait for T { + default fn f() {} +} + +impl SpecTrait<()> for OrdOnly { + fn f() {} +} + +impl SpecTrait> for () { + fn f() {} +} + +impl SpecTrait<(OrdOnly, OrdOnly)> for &[OrdOnly] { + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeated_projection_type.rs b/src/test/ui/specialization/min_specialization/repeated_projection_type.rs new file mode 100644 index 00000000000..f21f39f0669 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeated_projection_type.rs @@ -0,0 +1,24 @@ +// Test that projection bounds can't be specialized on. + +#![feature(min_specialization)] + +trait X { + fn f(); +} +trait Id { + type This; +} +impl Id for T { + type This = T; +} + +impl X for T { + default fn f() {} +} + +impl> X for V { + //~^ ERROR cannot specialize on + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr new file mode 100644 index 00000000000..7cc4357a704 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on `Binder(ProjectionPredicate(ProjectionTy { substs: [V], item_def_id: DefId(0:6 ~ repeated_projection_type[317d]::Id[0]::This[0]) }, (I,)))` + --> $DIR/repeated_projection_type.rs:19:1 + | +LL | / impl> X for V { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/repeating_lifetimes.rs b/src/test/ui/specialization/min_specialization/repeating_lifetimes.rs new file mode 100644 index 00000000000..49bfacec0ae --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_lifetimes.rs @@ -0,0 +1,19 @@ +// Test that directly specializing on repeated lifetime parameters is not +// allowed. + +#![feature(min_specialization)] + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} + +impl<'a> X for (&'a u8, &'a u8) { + //~^ ERROR specializing impl repeats parameter `'a` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeating_lifetimes.stderr b/src/test/ui/specialization/min_specialization/repeating_lifetimes.stderr new file mode 100644 index 00000000000..ce9309f7012 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_lifetimes.stderr @@ -0,0 +1,11 @@ +error: specializing impl repeats parameter `'a` + --> $DIR/repeating_lifetimes.rs:14:1 + | +LL | / impl<'a> X for (&'a u8, &'a u8) { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/repeating_param.rs b/src/test/ui/specialization/min_specialization/repeating_param.rs new file mode 100644 index 00000000000..5a1c97fd321 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_param.rs @@ -0,0 +1,17 @@ +// Test that specializing on two type parameters being equal is not allowed. + +#![feature(min_specialization)] + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} +impl X for (T, T) { + //~^ ERROR specializing impl repeats parameter `T` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeating_param.stderr b/src/test/ui/specialization/min_specialization/repeating_param.stderr new file mode 100644 index 00000000000..8b4be1c4995 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_param.stderr @@ -0,0 +1,11 @@ +error: specializing impl repeats parameter `T` + --> $DIR/repeating_param.rs:12:1 + | +LL | / impl X for (T, T) { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/spec-iter.rs b/src/test/ui/specialization/min_specialization/spec-iter.rs new file mode 100644 index 00000000000..e17e9dd5f13 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/spec-iter.rs @@ -0,0 +1,20 @@ +// Check that we can specialize on a concrete iterator type. This requires us +// to consider which parameters in the parent impl are constrained. + +// check-pass + +#![feature(min_specialization)] + +trait SpecFromIter { + fn f(&self); +} + +impl<'a, T: 'a, I: Iterator> SpecFromIter for I { + default fn f(&self) {} +} + +impl<'a, T> SpecFromIter for std::slice::Iter<'a, T> { + fn f(&self) {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/spec-reference.rs b/src/test/ui/specialization/min_specialization/spec-reference.rs new file mode 100644 index 00000000000..377889e2cca --- /dev/null +++ b/src/test/ui/specialization/min_specialization/spec-reference.rs @@ -0,0 +1,19 @@ +// Check that lifetime parameters are allowed in specializing impls. + +// check-pass + +#![feature(min_specialization)] + +trait MySpecTrait { + fn f(); +} + +impl MySpecTrait for T { + default fn f() {} +} + +impl<'a, T: ?Sized> MySpecTrait for &'a T { + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_marker.rs b/src/test/ui/specialization/min_specialization/specialization_marker.rs new file mode 100644 index 00000000000..93462d02ea5 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_marker.rs @@ -0,0 +1,17 @@ +// Test that `rustc_unsafe_specialization_marker` is only allowed on marker traits. + +#![feature(rustc_attrs)] + +#[rustc_unsafe_specialization_marker] +trait SpecMarker { + fn f(); + //~^ ERROR marker traits +} + +#[rustc_unsafe_specialization_marker] +trait SpecMarker2 { + type X; + //~^ ERROR marker traits +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_marker.stderr b/src/test/ui/specialization/min_specialization/specialization_marker.stderr new file mode 100644 index 00000000000..ffeced19821 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_marker.stderr @@ -0,0 +1,15 @@ +error[E0714]: marker traits cannot have associated items + --> $DIR/specialization_marker.rs:7:5 + | +LL | fn f(); + | ^^^^^^^ + +error[E0714]: marker traits cannot have associated items + --> $DIR/specialization_marker.rs:13:5 + | +LL | type X; + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0714`. diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.rs b/src/test/ui/specialization/min_specialization/specialization_super_trait.rs new file mode 100644 index 00000000000..145f376edf9 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.rs @@ -0,0 +1,18 @@ +// Test that supertraits can't be assumed in impls of +// `rustc_specialization_trait`, as such impls would +// allow specializing on the supertrait. + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +trait SpecMarker: Default { + fn f(); +} + +impl SpecMarker for T { + //~^ ERROR cannot specialize + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr new file mode 100644 index 00000000000..154c839c6da --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on trait `std::default::Default` + --> $DIR/specialization_super_trait.rs:13:1 + | +LL | / impl SpecMarker for T { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/specialization_trait.rs b/src/test/ui/specialization/min_specialization/specialization_trait.rs new file mode 100644 index 00000000000..d597278d296 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_trait.rs @@ -0,0 +1,26 @@ +// Test that `rustc_specialization_trait` requires always applicable impls. + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +trait SpecMarker { + fn f(); +} + +impl SpecMarker for &'static u8 { + //~^ ERROR cannot specialize + fn f() {} +} + +impl SpecMarker for (T, T) { + //~^ ERROR specializing impl + fn f() {} +} + +impl SpecMarker for [T] { + //~^ ERROR cannot specialize + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_trait.stderr new file mode 100644 index 00000000000..4357d2318fc --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_trait.stderr @@ -0,0 +1,29 @@ +error: cannot specialize on `'static` lifetime + --> $DIR/specialization_trait.rs:11:1 + | +LL | / impl SpecMarker for &'static u8 { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: specializing impl repeats parameter `T` + --> $DIR/specialization_trait.rs:16:1 + | +LL | / impl SpecMarker for (T, T) { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: cannot specialize on trait `std::clone::Clone` + --> $DIR/specialization_trait.rs:21:1 + | +LL | / impl SpecMarker for [T] { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/specialization/min_specialization/specialize_on_marker.rs b/src/test/ui/specialization/min_specialization/specialize_on_marker.rs new file mode 100644 index 00000000000..4219bd13b18 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_marker.rs @@ -0,0 +1,24 @@ +// Test that specializing on a `rustc_unsafe_specialization_marker` trait is +// allowed. + +// check-pass + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_unsafe_specialization_marker] +trait SpecMarker {} + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} + +impl X for T { + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_spec_trait.rs b/src/test/ui/specialization/min_specialization/specialize_on_spec_trait.rs new file mode 100644 index 00000000000..abbab5c23db --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_spec_trait.rs @@ -0,0 +1,27 @@ +// Test that specializing on a `rustc_specialization_trait` trait is allowed. + +// check-pass + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +trait SpecTrait { + fn g(&self); +} + +trait X { + fn f(&self); +} + +impl X for T { + default fn f(&self) {} +} + +impl X for T { + fn f(&self) { + self.g(); + } +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_static.rs b/src/test/ui/specialization/min_specialization/specialize_on_static.rs new file mode 100644 index 00000000000..dd1b05401e6 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_static.rs @@ -0,0 +1,18 @@ +// Test that directly specializing on `'static` is not allowed. + +#![feature(min_specialization)] + +trait X { + fn f(); +} + +impl X for &'_ T { + default fn f() {} +} + +impl X for &'static u8 { + //~^ ERROR cannot specialize on `'static` lifetime + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_static.stderr b/src/test/ui/specialization/min_specialization/specialize_on_static.stderr new file mode 100644 index 00000000000..d1809d6dfbb --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_static.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on `'static` lifetime + --> $DIR/specialize_on_static.rs:13:1 + | +LL | / impl X for &'static u8 { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/specialize_on_trait.rs b/src/test/ui/specialization/min_specialization/specialize_on_trait.rs new file mode 100644 index 00000000000..0588442c320 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_trait.rs @@ -0,0 +1,20 @@ +// Test that specializing on a trait is not allowed in general. + +#![feature(min_specialization)] + +trait SpecMarker {} + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} + +impl X for T { + //~^ ERROR cannot specialize on trait `SpecMarker` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr b/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr new file mode 100644 index 00000000000..35445fd09b9 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on trait `SpecMarker` + --> $DIR/specialize_on_trait.rs:15:1 + | +LL | / impl X for T { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/static/static-method-privacy.rs b/src/test/ui/static/static-method-privacy.rs index b637037f60e..9ee59b320ed 100644 --- a/src/test/ui/static/static-method-privacy.rs +++ b/src/test/ui/static/static-method-privacy.rs @@ -6,5 +6,5 @@ fn new() -> S { S } } fn main() { - let _ = a::S::new(); //~ ERROR method `new` is private + let _ = a::S::new(); //~ ERROR associated function `new` is private } diff --git a/src/test/ui/static/static-method-privacy.stderr b/src/test/ui/static/static-method-privacy.stderr index 14ca9f58301..78d211438cc 100644 --- a/src/test/ui/static/static-method-privacy.stderr +++ b/src/test/ui/static/static-method-privacy.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `new` is private +error[E0624]: associated function `new` is private --> $DIR/static-method-privacy.rs:9:13 | LL | let _ = a::S::new(); diff --git a/src/test/ui/suggestions/attribute-typos.stderr b/src/test/ui/suggestions/attribute-typos.stderr index 10a119a628c..c7c257ba5fe 100644 --- a/src/test/ui/suggestions/attribute-typos.stderr +++ b/src/test/ui/suggestions/attribute-typos.stderr @@ -4,7 +4,6 @@ error[E0658]: attributes starting with `rustc` are reserved for use by the `rust LL | #[rustc_err] | ^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: cannot find attribute `rustc_err` in this scope diff --git a/src/test/ui/suggestions/const-in-struct-pat.rs b/src/test/ui/suggestions/const-in-struct-pat.rs new file mode 100644 index 00000000000..1cbba935402 --- /dev/null +++ b/src/test/ui/suggestions/const-in-struct-pat.rs @@ -0,0 +1,11 @@ +#[allow(non_camel_case_types)] +struct foo; +struct Thing { + foo: String, +} + +fn example(t: Thing) { + let Thing { foo } = t; //~ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/suggestions/const-in-struct-pat.stderr b/src/test/ui/suggestions/const-in-struct-pat.stderr new file mode 100644 index 00000000000..0a010dcab4c --- /dev/null +++ b/src/test/ui/suggestions/const-in-struct-pat.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/const-in-struct-pat.rs:8:17 + | +LL | struct foo; + | ----------- unit struct defined here +... +LL | let Thing { foo } = t; + | ^^^ - this expression has type `Thing` + | | + | expected struct `std::string::String`, found struct `foo` + | `foo` is interpreted as a unit struct, not a new binding + | help: bind the struct field to a different name instead: `foo: other_foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/const-no-type.rs b/src/test/ui/suggestions/const-no-type.rs index 99200a965dd..6b79697e983 100644 --- a/src/test/ui/suggestions/const-no-type.rs +++ b/src/test/ui/suggestions/const-no-type.rs @@ -43,4 +43,4 @@ fn main() {} static mut SM = "abc"; //~^ ERROR missing type for `static mut` item //~| HELP provide a type for the item -//~| SUGGESTION &'static str +//~| SUGGESTION &str diff --git a/src/test/ui/suggestions/const-no-type.stderr b/src/test/ui/suggestions/const-no-type.stderr index c4f17109dc5..a7b5aa5e5b1 100644 --- a/src/test/ui/suggestions/const-no-type.stderr +++ b/src/test/ui/suggestions/const-no-type.stderr @@ -14,7 +14,7 @@ error: missing type for `static mut` item --> $DIR/const-no-type.rs:43:12 | LL | static mut SM = "abc"; - | ^^ help: provide a type for the item: `SM: &'static str` + | ^^ help: provide a type for the item: `SM: &str` error: missing type for `const` item --> $DIR/const-no-type.rs:14:7 diff --git a/src/test/ui/suggestions/suggest-methods.stderr b/src/test/ui/suggestions/suggest-methods.stderr index a715c565946..c343071ac3e 100644 --- a/src/test/ui/suggestions/suggest-methods.stderr +++ b/src/test/ui/suggestions/suggest-methods.stderr @@ -5,19 +5,19 @@ LL | struct Foo; | ----------- method `bat` not found for this ... LL | f.bat(1.0); - | ^^^ help: there is a method with a similar name: `bar` + | ^^^ help: there is an associated function with a similar name: `bar` error[E0599]: no method named `is_emtpy` found for struct `std::string::String` in the current scope --> $DIR/suggest-methods.rs:21:15 | LL | let _ = s.is_emtpy(); - | ^^^^^^^^ help: there is a method with a similar name: `is_empty` + | ^^^^^^^^ help: there is an associated function with a similar name: `is_empty` error[E0599]: no method named `count_eos` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:25:19 | LL | let _ = 63u32.count_eos(); - | ^^^^^^^^^ help: there is a method with a similar name: `count_zeros` + | ^^^^^^^^^ help: there is an associated function with a similar name: `count_zeros` error[E0599]: no method named `count_o` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:28:19 diff --git a/src/test/ui/syntax-trait-polarity-feature-gate.stderr b/src/test/ui/syntax-trait-polarity-feature-gate.stderr index ed76377278b..5d4c1b354f7 100644 --- a/src/test/ui/syntax-trait-polarity-feature-gate.stderr +++ b/src/test/ui/syntax-trait-polarity-feature-gate.stderr @@ -1,8 +1,8 @@ error[E0658]: negative trait bounds are not yet fully implemented; use marker types for now - --> $DIR/syntax-trait-polarity-feature-gate.rs:7:1 + --> $DIR/syntax-trait-polarity-feature-gate.rs:7:6 | LL | impl !Send for TestType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = note: see issue #13231 for more information = help: add `#![feature(optin_builtin_traits)]` to the crate attributes to enable diff --git a/src/test/ui/syntax-trait-polarity.stderr b/src/test/ui/syntax-trait-polarity.stderr index fef3a650888..5777e0ade90 100644 --- a/src/test/ui/syntax-trait-polarity.stderr +++ b/src/test/ui/syntax-trait-polarity.stderr @@ -1,29 +1,35 @@ error: inherent impls cannot be negative - --> $DIR/syntax-trait-polarity.rs:7:1 + --> $DIR/syntax-trait-polarity.rs:7:7 | LL | impl !TestType {} - | ^^^^^^^^^^^^^^^^^ + | -^^^^^^^^ inherent impl for this type + | | + | negative because of this error[E0198]: negative impls cannot be unsafe - --> $DIR/syntax-trait-polarity.rs:12:1 + --> $DIR/syntax-trait-polarity.rs:12:13 | LL | unsafe impl !Send for TestType {} - | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | + | ------ -^^^^ + | | | + | | negative because of this | unsafe because of this error: inherent impls cannot be negative - --> $DIR/syntax-trait-polarity.rs:19:1 + --> $DIR/syntax-trait-polarity.rs:19:10 | LL | impl !TestType2 {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | -^^^^^^^^^^^^ inherent impl for this type + | | + | negative because of this error[E0198]: negative impls cannot be unsafe - --> $DIR/syntax-trait-polarity.rs:22:1 + --> $DIR/syntax-trait-polarity.rs:22:16 | LL | unsafe impl !Send for TestType2 {} - | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | + | ------ -^^^^ + | | | + | | negative because of this | unsafe because of this error[E0192]: negative impls are only allowed for auto traits (e.g., `Send` and `Sync`) diff --git a/src/test/ui/test-panic-while-printing.rs b/src/test/ui/test-panic-while-printing.rs new file mode 100644 index 00000000000..23f45407c1a --- /dev/null +++ b/src/test/ui/test-panic-while-printing.rs @@ -0,0 +1,24 @@ +// compile-flags:--test +// run-pass +// ignore-emscripten no subprocess support + +use std::fmt; +use std::fmt::{Display, Formatter}; + +pub struct A(Vec); + +impl Display for A { + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + self.0[0]; + Ok(()) + } +} + +#[test] +fn main() { + let result = std::panic::catch_unwind(|| { + let a = A(vec![]); + eprintln!("{}", a); + }); + assert!(result.is_err()); +} diff --git a/src/test/ui/tool-attributes/diagnostic_item.stderr b/src/test/ui/tool-attributes/diagnostic_item.stderr index d12834084e7..743e4b658c6 100644 --- a/src/test/ui/tool-attributes/diagnostic_item.stderr +++ b/src/test/ui/tool-attributes/diagnostic_item.stderr @@ -4,7 +4,6 @@ error[E0658]: diagnostic items compiler internal support for linting LL | #[rustc_diagnostic_item = "foomp"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #29642 for more information = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/traits/trait-item-privacy.rs b/src/test/ui/traits/trait-item-privacy.rs index 8507d8ef17e..1ea1d65df62 100644 --- a/src/test/ui/traits/trait-item-privacy.rs +++ b/src/test/ui/traits/trait-item-privacy.rs @@ -69,7 +69,7 @@ fn check_method() { S.c(); // OK // a, b, c are resolved as inherent items, their traits don't need to be in scope let c = &S as &dyn C; - c.a(); //~ ERROR method `a` is private + c.a(); //~ ERROR associated function `a` is private c.b(); // OK c.c(); // OK @@ -81,7 +81,7 @@ fn check_method() { //~^ ERROR no function or associated item named `b` found S::c(&S); // OK // a, b, c are resolved as inherent items, their traits don't need to be in scope - C::a(&S); //~ ERROR method `a` is private + C::a(&S); //~ ERROR associated function `a` is private C::b(&S); // OK C::c(&S); // OK } diff --git a/src/test/ui/traits/trait-item-privacy.stderr b/src/test/ui/traits/trait-item-privacy.stderr index 2c0591c95f6..4b40c6405c4 100644 --- a/src/test/ui/traits/trait-item-privacy.stderr +++ b/src/test/ui/traits/trait-item-privacy.stderr @@ -36,7 +36,7 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f LL | use method::B; | -error[E0624]: method `a` is private +error[E0624]: associated function `a` is private --> $DIR/trait-item-privacy.rs:72:7 | LL | c.a(); @@ -73,7 +73,7 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f LL | use method::B; | -error[E0624]: method `a` is private +error[E0624]: associated function `a` is private --> $DIR/trait-item-privacy.rs:84:5 | LL | C::a(&S); diff --git a/src/test/ui/traits/trait-method-private.stderr b/src/test/ui/traits/trait-method-private.stderr index 10552acb348..035c1ea092b 100644 --- a/src/test/ui/traits/trait-method-private.stderr +++ b/src/test/ui/traits/trait-method-private.stderr @@ -1,4 +1,4 @@ -error[E0624]: method `method` is private +error[E0624]: associated function `method` is private --> $DIR/trait-method-private.rs:19:9 | LL | foo.method(); diff --git a/src/test/ui/traits/trait-safety-inherent-impl.stderr b/src/test/ui/traits/trait-safety-inherent-impl.stderr index c398785d394..0738d2973e2 100644 --- a/src/test/ui/traits/trait-safety-inherent-impl.stderr +++ b/src/test/ui/traits/trait-safety-inherent-impl.stderr @@ -1,14 +1,10 @@ error[E0197]: inherent impls cannot be unsafe - --> $DIR/trait-safety-inherent-impl.rs:5:1 + --> $DIR/trait-safety-inherent-impl.rs:5:13 | -LL | unsafe impl SomeStruct { - | ^----- - | | - | _unsafe because of this +LL | unsafe impl SomeStruct { + | ------ ^^^^^^^^^^ inherent impl for this type | | -LL | | fn foo(self) { } -LL | | } - | |_^ + | unsafe because of this error: aborting due to previous error diff --git a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr index 63182a6bd95..a83ff370151 100644 --- a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr +++ b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr @@ -1,8 +1,10 @@ error[E0568]: auto traits cannot have super traits - --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:7:1 + --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:7:19 | LL | auto trait Magic: Copy {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ----- ^^^^ help: remove the super traits + | | + | auto trait cannot have super traits error[E0277]: the trait bound `NoClone: std::marker::Copy` is not satisfied --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:15:23 diff --git a/src/test/ui/transmute/main.stderr b/src/test/ui/transmute/main.stderr index c72876e050f..4e781318329 100644 --- a/src/test/ui/transmute/main.stderr +++ b/src/test/ui/transmute/main.stderr @@ -4,8 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | transmute(x) | ^^^^^^^^^ | - = note: source type: `>::T` (size can vary because of ::T) - = note: target type: `>::T` (size can vary because of ::T) + = note: `::T` does not have a fixed size error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/main.rs:20:17 diff --git a/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs new file mode 100644 index 00000000000..8e8508cdd6f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs @@ -0,0 +1,28 @@ +// check-pass +// Regression test for issue #55099 +// Tests that we don't incorrectly consider a lifetime to part +// of the concrete type + +#![feature(type_alias_impl_trait)] + +trait Future { +} + +struct AndThen(F); + +impl Future for AndThen { +} + +struct Foo<'a> { + x: &'a mut (), +} + +type F = impl Future; + +impl<'a> Foo<'a> { + fn reply(&mut self) -> F { + AndThen(|| ()) + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-65918.rs b/src/test/ui/type-alias-impl-trait/issue-65918.rs index 4ba778d53ac..af6d5010920 100644 --- a/src/test/ui/type-alias-impl-trait/issue-65918.rs +++ b/src/test/ui/type-alias-impl-trait/issue-65918.rs @@ -6,7 +6,7 @@ use std::marker::PhantomData; -/* copied Index and TryFrom for convinience (and simplicity) */ +/* copied Index and TryFrom for convenience (and simplicity) */ trait MyIndex { type O; fn my_index(self) -> Self::O; diff --git a/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs b/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs index 2f844b4a05f..7da0b049264 100644 --- a/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs +++ b/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs @@ -1,6 +1,6 @@ // check-pass // Regression test for issue #67844 -// Ensures that we properly handle nested TAIT occurences +// Ensures that we properly handle nested TAIT occurrences // with generic parameters #![feature(type_alias_impl_trait)] diff --git a/src/test/ui/type-inference/or_else-multiple-type-params.stderr b/src/test/ui/type-inference/or_else-multiple-type-params.stderr index b9258b20f5a..24122e65867 100644 --- a/src/test/ui/type-inference/or_else-multiple-type-params.stderr +++ b/src/test/ui/type-inference/or_else-multiple-type-params.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | .or_else(|err| { | ^^^^^^^ | | - | cannot infer type for type parameter `F` declared on the method `or_else` + | cannot infer type for type parameter `F` declared on the associated function `or_else` | help: consider specifying the type arguments in the method call: `or_else::` error: aborting due to previous error diff --git a/src/test/ui/type-inference/sort_by_key.stderr b/src/test/ui/type-inference/sort_by_key.stderr index e74c0dfa5e2..bb108adcd64 100644 --- a/src/test/ui/type-inference/sort_by_key.stderr +++ b/src/test/ui/type-inference/sort_by_key.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | lst.sort_by_key(|&(v, _)| v.iter().sum()); | ^^^^^^^^^^^ --- help: consider specifying the type argument in the method call: `sum::` | | - | cannot infer type for type parameter `K` declared on the method `sort_by_key` + | cannot infer type for type parameter `K` declared on the associated function `sort_by_key` error: aborting due to previous error diff --git a/src/test/ui/type/ascription/issue-54516.rs b/src/test/ui/type/ascription/issue-54516.rs index b53bfe5df03..8d6fd2abb6d 100644 --- a/src/test/ui/type/ascription/issue-54516.rs +++ b/src/test/ui/type/ascription/issue-54516.rs @@ -2,5 +2,7 @@ fn main() { println!("{}", std::mem:size_of::>()); - //~^ ERROR expected one of + //~^ ERROR casts cannot be followed by a function call + //~| ERROR expected value, found module `std::mem` [E0423] + //~| ERROR cannot find type `size_of` in this scope [E0412] } diff --git a/src/test/ui/type/ascription/issue-54516.stderr b/src/test/ui/type/ascription/issue-54516.stderr index 7127f67cd7d..fdf35700ef9 100644 --- a/src/test/ui/type/ascription/issue-54516.stderr +++ b/src/test/ui/type/ascription/issue-54516.stderr @@ -1,13 +1,31 @@ -error: expected one of `!`, `,`, or `::`, found `(` - --> $DIR/issue-54516.rs:4:58 +error: casts cannot be followed by a function call + --> $DIR/issue-54516.rs:4:20 | LL | println!("{}", std::mem:size_of::>()); - | - ^ expected one of `!`, `,`, or `::` + | ^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | help: maybe write a path separator here: `::` | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` = note: see issue #23416 for more information -error: aborting due to previous error +error[E0423]: expected value, found module `std::mem` + --> $DIR/issue-54516.rs:4:20 + | +LL | println!("{}", std::mem:size_of::>()); + | ^^^^^^^^- help: maybe you meant to write a path separator here: `::` + | | + | not a value + +error[E0412]: cannot find type `size_of` in this scope + --> $DIR/issue-54516.rs:4:29 + | +LL | println!("{}", std::mem:size_of::>()); + | -^^^^^^^ not found in this scope + | | + | help: maybe you meant to write a path separator here: `::` + +error: aborting due to 3 previous errors +Some errors have detailed explanations: E0412, E0423. +For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/type/ascription/issue-60933.rs b/src/test/ui/type/ascription/issue-60933.rs index 8fb06c887bd..bcf9f88cb41 100644 --- a/src/test/ui/type/ascription/issue-60933.rs +++ b/src/test/ui/type/ascription/issue-60933.rs @@ -1,4 +1,6 @@ fn main() { let u: usize = std::mem:size_of::(); - //~^ ERROR expected one of + //~^ ERROR casts cannot be followed by a function call + //~| ERROR expected value, found module `std::mem` [E0423] + //~| ERROR cannot find type `size_of` in this scope [E0412] } diff --git a/src/test/ui/type/ascription/issue-60933.stderr b/src/test/ui/type/ascription/issue-60933.stderr index 7130767b6c6..cd9ae8f49f4 100644 --- a/src/test/ui/type/ascription/issue-60933.stderr +++ b/src/test/ui/type/ascription/issue-60933.stderr @@ -1,13 +1,31 @@ -error: expected one of `!`, `::`, or `;`, found `(` - --> $DIR/issue-60933.rs:2:43 +error: casts cannot be followed by a function call + --> $DIR/issue-60933.rs:2:20 | LL | let u: usize = std::mem:size_of::(); - | - ^ expected one of `!`, `::`, or `;` + | ^^^^^^^^-^^^^^^^^^^^^^^ | | | help: maybe write a path separator here: `::` | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` = note: see issue #23416 for more information -error: aborting due to previous error +error[E0423]: expected value, found module `std::mem` + --> $DIR/issue-60933.rs:2:20 + | +LL | let u: usize = std::mem:size_of::(); + | ^^^^^^^^- help: maybe you meant to write a path separator here: `::` + | | + | not a value + +error[E0412]: cannot find type `size_of` in this scope + --> $DIR/issue-60933.rs:2:29 + | +LL | let u: usize = std::mem:size_of::(); + | -^^^^^^^ not found in this scope + | | + | help: maybe you meant to write a path separator here: `::` + +error: aborting due to 3 previous errors +Some errors have detailed explanations: E0412, E0423. +For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs index c57e8149574..7bf151514c3 100644 --- a/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs +++ b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs @@ -1,4 +1,4 @@ -// Fix issue 52082: Confusing error if accidentially defining a type paramter with the same name as +// Fix issue 52082: Confusing error if accidentally defining a type parameter with the same name as // an existing type // // To this end, make sure that when trying to retrieve a field of a (reference to) type parameter, diff --git a/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr b/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr index 8755bcded9d..e3976293277 100644 --- a/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr +++ b/src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr @@ -1,8 +1,10 @@ error[E0568]: auto traits cannot have super traits - --> $DIR/typeck-auto-trait-no-supertraits-2.rs:3:1 + --> $DIR/typeck-auto-trait-no-supertraits-2.rs:3:20 | LL | auto trait Magic : Sized where Option : Magic {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ----- ^^^^^ help: remove the super traits + | | + | auto trait cannot have super traits error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr b/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr index 5a388834909..b1602e3642e 100644 --- a/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr +++ b/src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr @@ -1,8 +1,10 @@ error[E0568]: auto traits cannot have super traits - --> $DIR/typeck-auto-trait-no-supertraits.rs:27:1 + --> $DIR/typeck-auto-trait-no-supertraits.rs:27:19 | LL | auto trait Magic: Copy {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ----- ^^^^ help: remove the super traits + | | + | auto trait cannot have super traits error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr index f2d02f70f4a..dc86ab30dfe 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr +++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr @@ -70,7 +70,7 @@ LL | static TEST3: _ = "test"; | ^ | | | not allowed in type signatures - | help: replace `_` with the correct type: `&'static str` + | help: replace `_` with the correct type: `&str` error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:15:15 @@ -232,7 +232,7 @@ LL | static FN_TEST3: _ = "test"; | ^ | | | not allowed in type signatures - | help: replace `_` with the correct type: `&'static str` + | help: replace `_` with the correct type: `&str` error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:88:22 diff --git a/src/test/ui/ufcs/ufcs-partially-resolved.rs b/src/test/ui/ufcs/ufcs-partially-resolved.rs index e8c767b13e9..e6470aa6d64 100644 --- a/src/test/ui/ufcs/ufcs-partially-resolved.rs +++ b/src/test/ui/ufcs/ufcs-partially-resolved.rs @@ -49,8 +49,8 @@ fn main() { ::NN; //~ ERROR cannot find method or associated constant `NN` in `Tr::Y` ::NN; //~ ERROR failed to resolve: `Y` is a variant, not a module - let _: ::Z; //~ ERROR expected associated type, found method `Dr::Z` + let _: ::Z; //~ ERROR expected associated type, found associated function `Dr::Z` ::X; //~ ERROR expected method or associated constant, found associated type `Dr::X` - let _: ::Z::N; //~ ERROR expected associated type, found method `Dr::Z` + let _: ::Z::N; //~ ERROR expected associated type, found associated function `Dr::Z` ::X::N; //~ ERROR no associated item named `N` found for type `u16` } diff --git a/src/test/ui/ufcs/ufcs-partially-resolved.stderr b/src/test/ui/ufcs/ufcs-partially-resolved.stderr index e5e6ed9fac9..7177ca49085 100644 --- a/src/test/ui/ufcs/ufcs-partially-resolved.stderr +++ b/src/test/ui/ufcs/ufcs-partially-resolved.stderr @@ -35,10 +35,10 @@ error[E0576]: cannot find method or associated constant `N` in trait `Tr` --> $DIR/ufcs-partially-resolved.rs:22:17 | LL | fn Y() {} - | --------- similarly named method `Y` defined here + | --------- similarly named associated function `Y` defined here ... LL | ::N; - | ^ help: a method with a similar name exists: `Y` + | ^ help: an associated function with a similar name exists: `Y` error[E0576]: cannot find method or associated constant `N` in enum `E` --> $DIR/ufcs-partially-resolved.rs:23:16 @@ -166,7 +166,7 @@ error[E0576]: cannot find method or associated constant `NN` in `Tr::Y` LL | ::NN; | ^^ not found in `Tr::Y` -error[E0575]: expected associated type, found method `Dr::Z` +error[E0575]: expected associated type, found associated function `Dr::Z` --> $DIR/ufcs-partially-resolved.rs:52:12 | LL | type X = u16; @@ -181,16 +181,16 @@ error[E0575]: expected method or associated constant, found associated type `Dr: --> $DIR/ufcs-partially-resolved.rs:53:5 | LL | fn Z() {} - | --------- similarly named method `Z` defined here + | --------- similarly named associated function `Z` defined here ... LL | ::X; | ^^^^^^^^^^^^- | | - | help: a method with a similar name exists: `Z` + | help: an associated function with a similar name exists: `Z` | = note: can't use a type alias as a constructor -error[E0575]: expected associated type, found method `Dr::Z` +error[E0575]: expected associated type, found associated function `Dr::Z` --> $DIR/ufcs-partially-resolved.rs:54:12 | LL | type X = u16; diff --git a/src/test/ui/unsafe/unsafe-block-without-braces.stderr b/src/test/ui/unsafe/unsafe-block-without-braces.stderr index 13e0c3681fa..895f33638f9 100644 --- a/src/test/ui/unsafe/unsafe-block-without-braces.stderr +++ b/src/test/ui/unsafe/unsafe-block-without-braces.stderr @@ -1,10 +1,11 @@ error: expected `{`, found `std` --> $DIR/unsafe-block-without-braces.rs:3:9 | -LL | unsafe //{ - | - expected `{` LL | std::mem::transmute::(1.0); - | ^^^ unexpected token + | ^^^---------------------------------- + | | + | expected `{` + | help: try placing this code inside a block: `{ std::mem::transmute::(1.0); }` error: aborting due to previous error diff --git a/src/test/ui/wf/wf-array-elem-sized.rs b/src/test/ui/wf/wf-array-elem-sized.rs index 41c2d3c43e9..34bf2203426 100644 --- a/src/test/ui/wf/wf-array-elem-sized.rs +++ b/src/test/ui/wf/wf-array-elem-sized.rs @@ -1,4 +1,4 @@ -// Check that array elemen types must be Sized. Issue #25692. +// Check that array element types must be Sized. Issue #25692. #![allow(dead_code)] diff --git a/src/test/ui/xc-private-method.rs b/src/test/ui/xc-private-method.rs index e95cab93d75..f05994646b3 100644 --- a/src/test/ui/xc-private-method.rs +++ b/src/test/ui/xc-private-method.rs @@ -4,8 +4,8 @@ fn main() { let _ = xc_private_method_lib::Struct::static_meth_struct(); - //~^ ERROR: method `static_meth_struct` is private + //~^ ERROR: associated function `static_meth_struct` is private let _ = xc_private_method_lib::Enum::static_meth_enum(); - //~^ ERROR: method `static_meth_enum` is private + //~^ ERROR: associated function `static_meth_enum` is private } diff --git a/src/test/ui/xc-private-method.stderr b/src/test/ui/xc-private-method.stderr index 91bec2551c1..6a68bef90ef 100644 --- a/src/test/ui/xc-private-method.stderr +++ b/src/test/ui/xc-private-method.stderr @@ -1,10 +1,10 @@ -error[E0624]: method `static_meth_struct` is private +error[E0624]: associated function `static_meth_struct` is private --> $DIR/xc-private-method.rs:6:13 | LL | let _ = xc_private_method_lib::Struct::static_meth_struct(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0624]: method `static_meth_enum` is private +error[E0624]: associated function `static_meth_enum` is private --> $DIR/xc-private-method.rs:9:13 | LL | let _ = xc_private_method_lib::Enum::static_meth_enum(); diff --git a/src/test/ui/xc-private-method2.rs b/src/test/ui/xc-private-method2.rs index f11b251082b..92946923f6e 100644 --- a/src/test/ui/xc-private-method2.rs +++ b/src/test/ui/xc-private-method2.rs @@ -4,8 +4,8 @@ fn main() { let _ = xc_private_method_lib::Struct{ x: 10 }.meth_struct(); - //~^ ERROR method `meth_struct` is private + //~^ ERROR associated function `meth_struct` is private let _ = xc_private_method_lib::Enum::Variant1(20).meth_enum(); - //~^ ERROR method `meth_enum` is private + //~^ ERROR associated function `meth_enum` is private } diff --git a/src/test/ui/xc-private-method2.stderr b/src/test/ui/xc-private-method2.stderr index 36ad850fb19..84a8b9817c0 100644 --- a/src/test/ui/xc-private-method2.stderr +++ b/src/test/ui/xc-private-method2.stderr @@ -1,10 +1,10 @@ -error[E0624]: method `meth_struct` is private +error[E0624]: associated function `meth_struct` is private --> $DIR/xc-private-method2.rs:6:52 | LL | let _ = xc_private_method_lib::Struct{ x: 10 }.meth_struct(); | ^^^^^^^^^^^ -error[E0624]: method `meth_enum` is private +error[E0624]: associated function `meth_enum` is private --> $DIR/xc-private-method2.rs:9:55 | LL | let _ = xc_private_method_lib::Enum::Variant1(20).meth_enum(); diff --git a/src/tools/cargo b/src/tools/cargo index e57bd02999c..7019b3ed3d5 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit e57bd02999c9f40d52116e0beca7d1dccb0643de +Subproject commit 7019b3ed3d539db7429d10a343b69be8c426b576 diff --git a/src/tools/clippy b/src/tools/clippy index fc5d0cc583c..23549a8c362 160000 --- a/src/tools/clippy +++ b/src/tools/clippy @@ -1 +1 @@ -Subproject commit fc5d0cc583cb1cd35d58fdb7f3e0cfa12dccd6c0 +Subproject commit 23549a8c362a403026432f65a6cb398cb10d44b7 diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index c3d699b3e23..8f685fb8559 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -125,7 +125,7 @@ fn parse_expected( let captures = RE.captures(line)?; match (cfg, captures.name("cfgs")) { - // Only error messages that contain our `cfg` betweeen the square brackets apply to us. + // Only error messages that contain our `cfg` between the square brackets apply to us. (Some(cfg), Some(filter)) if !filter.as_str().split(',').any(|s| s == cfg) => return None, (Some(_), Some(_)) => {} diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 38fa778219d..6c478f7e29d 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -109,6 +109,24 @@ fn no_system_llvm() { assert!(parse_rs(&config, "// no-system-llvm").ignore); } +#[test] +fn llvm_version() { + let mut config = config(); + + config.llvm_version = Some("8.1.2-rust".to_owned()); + assert!(parse_rs(&config, "// min-llvm-version 9.0").ignore); + + config.llvm_version = Some("9.0.1-rust-1.43.0-dev".to_owned()); + assert!(parse_rs(&config, "// min-llvm-version 9.2").ignore); + + config.llvm_version = Some("9.3.1-rust-1.43.0-dev".to_owned()); + assert!(!parse_rs(&config, "// min-llvm-version 9.2").ignore); + + // FIXME. + // config.llvm_version = Some("10.0.0-rust".to_owned()); + // assert!(!parse_rs(&config, "// min-llvm-version 9.0").ignore); +} + #[test] fn ignore_target() { let mut config = config(); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ac808b1f14e..b04012af515 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1487,7 +1487,7 @@ fn compile_test_general( // can turn it back on if needed. if !self.is_rustdoc() // Note that we use the local pass mode here as we don't want - // to set unused to allow if we've overriden the pass mode + // to set unused to allow if we've overridden the pass mode // via command line flags. && local_pm != Some(PassMode::Run) { @@ -2779,7 +2779,7 @@ fn run_js_doc_test(&self) { Command::new(&nodejs) .arg(root.join("src/tools/rustdoc-js/tester.js")) .arg(out_dir.parent().expect("no parent")) - .arg(&self.testpaths.file.file_stem().expect("couldn't get file stem")), + .arg(self.testpaths.file.with_extension("js")), ); if !res.status.success() { self.fatal_proc_rec("rustdoc-js test failed!", &res); @@ -3204,7 +3204,9 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S let json = cflags.contains("--error-format json") || cflags.contains("--error-format pretty-json") || cflags.contains("--error-format=json") - || cflags.contains("--error-format=pretty-json"); + || cflags.contains("--error-format=pretty-json") + || cflags.contains("--output-format json") + || cflags.contains("--output-format=json"); let mut normalized = output.to_string(); diff --git a/src/tools/miri b/src/tools/miri index 3c444bf6a6c..0ff05c4cfe5 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 3c444bf6a6cff3b9014005f21cc44995b34862ce +Subproject commit 0ff05c4cfe534321b194bf3bedf028df92ef519c diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index 5fbb986286a..b389cd0373c 100755 --- a/src/tools/publish_toolstate.py +++ b/src/tools/publish_toolstate.py @@ -1,11 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# This script publishes the new "current" toolstate in the toolstate repo (not to be -# confused with publishing the test results, which happens in -# `src/ci/docker/x86_64-gnu-tools/checktools.sh`). -# It is set as callback for `src/ci/docker/x86_64-gnu-tools/repo.sh` by the CI scripts -# when a new commit lands on `master` (i.e., after it passed all checks on `auto`). +# This script computes the new "current" toolstate for the toolstate repo (not to be +# confused with publishing the test results, which happens in `src/bootstrap/toolstate.rs`). +# It gets called from `src/ci/publish_toolstate.sh` when a new commit lands on `master` +# (i.e., after it passed all checks on `auto`). from __future__ import print_function diff --git a/src/tools/rustdoc-js-std/tester.js b/src/tools/rustdoc-js-std/tester.js index d5f0ab9f429..08930ff1227 100644 --- a/src/tools/rustdoc-js-std/tester.js +++ b/src/tools/rustdoc-js-std/tester.js @@ -1,6 +1,5 @@ const fs = require('fs'); - -const TEST_FOLDER = 'src/test/rustdoc-js-std/'; +const path = require('path'); function getNextStep(content, pos, stop) { while (pos < content.length && content[pos] !== stop && @@ -246,17 +245,16 @@ function readFileMatching(dir, name, extension) { } function main(argv) { - if (argv.length !== 3) { - console.error("Expected toolchain to check as argument (for example \ - 'x86_64-apple-darwin')"); + if (argv.length !== 4) { + console.error("USAGE: node tester.js STD_DOCS TEST_FOLDER"); return 1; } - var toolchain = argv[2]; + var std_docs = argv[2]; + var test_folder = argv[3]; - var mainJs = readFileMatching("build/" + toolchain + "/doc/", "main", ".js"); - var ALIASES = readFileMatching("build/" + toolchain + "/doc/", "aliases", ".js"); - var searchIndex = readFileMatching("build/" + toolchain + "/doc/", - "search-index", ".js").split("\n"); + var mainJs = readFileMatching(std_docs, "main", ".js"); + var ALIASES = readFileMatching(std_docs, "aliases", ".js"); + var searchIndex = readFileMatching(std_docs, "search-index", ".js").split("\n"); if (searchIndex[searchIndex.length - 1].length === 0) { searchIndex.pop(); } @@ -265,7 +263,7 @@ function main(argv) { finalJS = ""; var arraysToLoad = ["itemTypes"]; - var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", + var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", "NO_TYPE_FILTER", "GENERICS_DATA", "NAME", "INPUTS_DATA", "OUTPUT_DATA", "TY_PRIMITIVE", "TY_KEYWORD", "levenshtein_row2"]; @@ -287,8 +285,8 @@ function main(argv) { var errors = 0; - fs.readdirSync(TEST_FOLDER).forEach(function(file) { - var loadedFile = loadContent(readFile(TEST_FOLDER + file) + + fs.readdirSync(test_folder).forEach(function(file) { + var loadedFile = loadContent(readFile(path.join(test_folder, file)) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;'); const expected = loadedFile.EXPECTED; const query = loadedFile.QUERY; @@ -338,7 +336,7 @@ function main(argv) { console.log("OK"); } }); - return errors; + return errors > 0 ? 1 : 0; } process.exit(main(process.argv)); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 833ce5d1370..143e1a7480d 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -1,8 +1,7 @@ const fs = require('fs'); +const path = require('path'); const { spawnSync } = require('child_process'); -const TEST_FOLDER = 'src/test/rustdoc-js/'; - function getNextStep(content, pos, stop) { while (pos < content.length && content[pos] !== stop && (content[pos] === ' ' || content[pos] === '\t' || content[pos] === '\n')) { @@ -232,7 +231,7 @@ function load_files(out_folder, crate) { finalJS = ""; var arraysToLoad = ["itemTypes"]; - var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", + var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", "NO_TYPE_FILTER", "GENERICS_DATA", "NAME", "INPUTS_DATA", "OUTPUT_DATA", "TY_PRIMITIVE", "TY_KEYWORD", "levenshtein_row2"]; @@ -266,10 +265,11 @@ function main(argv) { var errors = 0; for (var j = 3; j < argv.length; ++j) { - const test_name = argv[j]; + const test_file = argv[j]; + const test_name = path.basename(test_file, ".js"); process.stdout.write('Checking "' + test_name + '" ... '); - if (!fs.existsSync(TEST_FOLDER + test_name + ".js")) { + if (!fs.existsSync(test_file)) { errors += 1; console.error("FAILED"); console.error("==> Missing '" + test_name + ".js' file..."); @@ -279,7 +279,7 @@ function main(argv) { const test_out_folder = out_folder + test_name; var [loaded, index] = load_files(test_out_folder, test_name); - var loadedFile = loadContent(readFile(TEST_FOLDER + test_name + ".js") + + var loadedFile = loadContent(readFile(test_file) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;'); const expected = loadedFile.EXPECTED; const query = loadedFile.QUERY; @@ -328,7 +328,7 @@ function main(argv) { console.log("OK"); } } - return errors; + return errors > 0 ? 1 : 0; } process.exit(main(process.argv)); diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index 43cae31f33f..f984e5b61a5 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -5,8 +5,7 @@ authors = ["Alex Crichton "] edition = "2018" [dependencies] +cargo_metadata = "0.9.1" regex = "1" -serde = { version = "1.0.8", features = ["derive"] } -serde_json = "1.0.2" lazy_static = "1" walkdir = "2" diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 7a20a96130c..1ffc415fb24 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -1,13 +1,11 @@ -//! Checks the licenses of third-party dependencies by inspecting vendors. +//! Checks the licenses of third-party dependencies. -use std::collections::{BTreeSet, HashMap, HashSet}; -use std::fs; +use cargo_metadata::{Metadata, Package, PackageId, Resolve}; +use std::collections::{BTreeSet, HashSet}; use std::path::Path; -use std::process::Command; - -use serde::Deserialize; -use serde_json; +/// These are licenses that are allowed for all crates, including the runtime, +/// rustc, tools, etc. const LICENSES: &[&str] = &[ "MIT/Apache-2.0", "MIT / Apache-2.0", @@ -25,261 +23,268 @@ /// should be considered bugs. Exceptions are only allowed in Rust /// tooling. It is _crucial_ that no exception crates be dependencies /// of the Rust runtime (std/test). -const EXCEPTIONS: &[&str] = &[ - "mdbook", // MPL2, mdbook - "openssl", // BSD+advertising clause, cargo, mdbook - "pest", // MPL2, mdbook via handlebars - "arrayref", // BSD-2-Clause, mdbook via handlebars via pest - "thread-id", // Apache-2.0, mdbook - "toml-query", // MPL-2.0, mdbook - "is-match", // MPL-2.0, mdbook - "cssparser", // MPL-2.0, rustdoc - "smallvec", // MPL-2.0, rustdoc - "rdrand", // ISC, mdbook, rustfmt - "fuchsia-cprng", // BSD-3-Clause, mdbook, rustfmt - "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo - "fuchsia-zircon", // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir) - "cssparser-macros", // MPL-2.0, rustdoc - "selectors", // MPL-2.0, rustdoc - "clippy_lints", // MPL-2.0, rls - "colored", // MPL-2.0, rustfmt - "ordslice", // Apache-2.0, rls - "cloudabi", // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5) - "ryu", // Apache-2.0, rls/cargo/... (because of serde) - "bytesize", // Apache-2.0, cargo - "im-rc", // MPL-2.0+, cargo - "adler32", // BSD-3-Clause AND Zlib, cargo dep that isn't used - "constant_time_eq", // CC0-1.0, rustfmt - "utf8parse", // Apache-2.0 OR MIT, cargo via strip-ansi-escapes - "vte", // Apache-2.0 OR MIT, cargo via strip-ansi-escapes - "sized-chunks", // MPL-2.0+, cargo via im-rc - "bitmaps", // MPL-2.0+, cargo via im-rc +const EXCEPTIONS: &[(&str, &str)] = &[ + ("mdbook", "MPL-2.0"), // mdbook + ("openssl", "Apache-2.0"), // cargo, mdbook + ("arrayref", "BSD-2-Clause"), // mdbook via handlebars via pest + ("toml-query", "MPL-2.0"), // mdbook + ("toml-query_derive", "MPL-2.0"), // mdbook + ("is-match", "MPL-2.0"), // mdbook + ("rdrand", "ISC"), // mdbook, rustfmt + ("fuchsia-cprng", "BSD-3-Clause"), // mdbook, rustfmt + ("fuchsia-zircon-sys", "BSD-3-Clause"), // rustdoc, rustc, cargo + ("fuchsia-zircon", "BSD-3-Clause"), // rustdoc, rustc, cargo (jobserver & tempdir) + ("colored", "MPL-2.0"), // rustfmt + ("ordslice", "Apache-2.0"), // rls + ("cloudabi", "BSD-2-Clause"), // (rls -> crossbeam-channel 0.2 -> rand 0.5) + ("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde) + ("bytesize", "Apache-2.0"), // cargo + ("im-rc", "MPL-2.0+"), // cargo + ("adler32", "BSD-3-Clause AND Zlib"), // cargo dep that isn't used + ("constant_time_eq", "CC0-1.0"), // rustfmt + ("sized-chunks", "MPL-2.0+"), // cargo via im-rc + ("bitmaps", "MPL-2.0+"), // cargo via im-rc // FIXME: this dependency violates the documentation comment above: - "fortanix-sgx-abi", // MPL-2.0+, libstd but only for `sgx` target - "dunce", // CC0-1.0 mdbook-linkcheck - "codespan-reporting", // Apache-2.0 mdbook-linkcheck - "codespan", // Apache-2.0 mdbook-linkcheck - "crossbeam-channel", // MIT/Apache-2.0 AND BSD-2-Clause, cargo + ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target + ("dunce", "CC0-1.0"), // mdbook-linkcheck + ("codespan-reporting", "Apache-2.0"), // mdbook-linkcheck + ("codespan", "Apache-2.0"), // mdbook-linkcheck + ("crossbeam-channel", "MIT/Apache-2.0 AND BSD-2-Clause"), // cargo ]; +/// These are the root crates that are part of the runtime. The licenses for +/// these and all their dependencies *must not* be in the exception list. +const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"]; + /// Which crates to check against the whitelist? -const WHITELIST_CRATES: &[CrateVersion<'_>] = - &[CrateVersion("rustc", "0.0.0"), CrateVersion("rustc_codegen_llvm", "0.0.0")]; +const WHITELIST_CRATES: &[&str] = &["rustc", "rustc_codegen_llvm"]; /// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible. -const WHITELIST: &[Crate<'_>] = &[ - Crate("adler32"), - Crate("aho-corasick"), - Crate("annotate-snippets"), - Crate("ansi_term"), - Crate("arrayvec"), - Crate("atty"), - Crate("autocfg"), - Crate("backtrace"), - Crate("backtrace-sys"), - Crate("bitflags"), - Crate("build_const"), - Crate("byteorder"), - Crate("c2-chacha"), - Crate("cc"), - Crate("cfg-if"), - Crate("chalk-engine"), - Crate("chalk-macros"), - Crate("cloudabi"), - Crate("cmake"), - Crate("compiler_builtins"), - Crate("crc"), - Crate("crc32fast"), - Crate("crossbeam-deque"), - Crate("crossbeam-epoch"), - Crate("crossbeam-queue"), - Crate("crossbeam-utils"), - Crate("datafrog"), - Crate("dlmalloc"), - Crate("either"), - Crate("ena"), - Crate("env_logger"), - Crate("filetime"), - Crate("flate2"), - Crate("fortanix-sgx-abi"), - Crate("fuchsia-zircon"), - Crate("fuchsia-zircon-sys"), - Crate("getopts"), - Crate("getrandom"), - Crate("hashbrown"), - Crate("humantime"), - Crate("indexmap"), - Crate("itertools"), - Crate("jobserver"), - Crate("kernel32-sys"), - Crate("lazy_static"), - Crate("libc"), - Crate("libz-sys"), - Crate("lock_api"), - Crate("log"), - Crate("log_settings"), - Crate("measureme"), - Crate("memchr"), - Crate("memmap"), - Crate("memoffset"), - Crate("miniz-sys"), - Crate("miniz_oxide"), - Crate("miniz_oxide_c_api"), - Crate("nodrop"), - Crate("num_cpus"), - Crate("owning_ref"), - Crate("parking_lot"), - Crate("parking_lot_core"), - Crate("pkg-config"), - Crate("polonius-engine"), - Crate("ppv-lite86"), - Crate("proc-macro2"), - Crate("punycode"), - Crate("quick-error"), - Crate("quote"), - Crate("rand"), - Crate("rand_chacha"), - Crate("rand_core"), - Crate("rand_hc"), - Crate("rand_isaac"), - Crate("rand_pcg"), - Crate("rand_xorshift"), - Crate("redox_syscall"), - Crate("redox_termios"), - Crate("regex"), - Crate("regex-syntax"), - Crate("remove_dir_all"), - Crate("rustc-demangle"), - Crate("rustc-hash"), - Crate("rustc-rayon"), - Crate("rustc-rayon-core"), - Crate("rustc_version"), - Crate("scoped-tls"), - Crate("scopeguard"), - Crate("semver"), - Crate("semver-parser"), - Crate("serde"), - Crate("serde_derive"), - Crate("smallvec"), - Crate("stable_deref_trait"), - Crate("syn"), - Crate("synstructure"), - Crate("tempfile"), - Crate("termcolor"), - Crate("terminon"), - Crate("termion"), - Crate("termize"), - Crate("thread_local"), - Crate("ucd-util"), - Crate("unicode-normalization"), - Crate("unicode-script"), - Crate("unicode-security"), - Crate("unicode-width"), - Crate("unicode-xid"), - Crate("unreachable"), - Crate("utf8-ranges"), - Crate("vcpkg"), - Crate("version_check"), - Crate("void"), - Crate("wasi"), - Crate("winapi"), - Crate("winapi-build"), - Crate("winapi-i686-pc-windows-gnu"), - Crate("winapi-util"), - Crate("winapi-x86_64-pc-windows-gnu"), - Crate("wincolor"), - Crate("hermit-abi"), +/// +/// This list is here to provide a speed-bump to adding a new dependency to +/// rustc. Please check with the compiler team before adding an entry. +const WHITELIST: &[&str] = &[ + "adler32", + "aho-corasick", + "annotate-snippets", + "ansi_term", + "arrayvec", + "atty", + "autocfg", + "backtrace", + "backtrace-sys", + "bitflags", + "byteorder", + "c2-chacha", + "cc", + "cfg-if", + "cloudabi", + "cmake", + "compiler_builtins", + "crc32fast", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", + "datafrog", + "dlmalloc", + "either", + "ena", + "env_logger", + "filetime", + "flate2", + "fortanix-sgx-abi", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "getopts", + "getrandom", + "hashbrown", + "hermit-abi", + "humantime", + "indexmap", + "itertools", + "jobserver", + "kernel32-sys", + "lazy_static", + "libc", + "libz-sys", + "lock_api", + "log", + "log_settings", + "measureme", + "memchr", + "memmap", + "memoffset", + "miniz_oxide", + "nodrop", + "num_cpus", + "parking_lot", + "parking_lot_core", + "pkg-config", + "polonius-engine", + "ppv-lite86", + "proc-macro2", + "punycode", + "quick-error", + "quote", + "rand", + "rand_chacha", + "rand_core", + "rand_hc", + "rand_isaac", + "rand_pcg", + "rand_xorshift", + "redox_syscall", + "redox_termios", + "regex", + "regex-syntax", + "remove_dir_all", + "rustc-demangle", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "rustc_version", + "scoped-tls", + "scopeguard", + "semver", + "semver-parser", + "serde", + "serde_derive", + "smallvec", + "stable_deref_trait", + "syn", + "synstructure", + "tempfile", + "termcolor", + "termion", + "termize", + "thread_local", + "ucd-util", + "unicode-normalization", + "unicode-script", + "unicode-security", + "unicode-width", + "unicode-xid", + "utf8-ranges", + "vcpkg", + "version_check", + "wasi", + "winapi", + "winapi-build", + "winapi-i686-pc-windows-gnu", + "winapi-util", + "winapi-x86_64-pc-windows-gnu", + "wincolor", ]; -// Some types for Serde to deserialize the output of `cargo metadata` to. - -#[derive(Deserialize)] -struct Output { - resolve: Resolve, -} - -#[derive(Deserialize)] -struct Resolve { - nodes: Vec, -} - -#[derive(Deserialize)] -struct ResolveNode { - id: String, - dependencies: Vec, -} - -/// A unique identifier for a crate. -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)] -struct Crate<'a>(&'a str); // (name) - -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)] -struct CrateVersion<'a>(&'a str, &'a str); // (name, version) - -impl Crate<'_> { - pub fn id_str(&self) -> String { - format!("{} ", self.0) - } -} - -impl<'a> CrateVersion<'a> { - /// Returns the struct and whether or not the dependency is in-tree. - pub fn from_str(s: &'a str) -> (Self, bool) { - let mut parts = s.split(' '); - let name = parts.next().unwrap(); - let version = parts.next().unwrap(); - let path = parts.next().unwrap(); - - let is_path_dep = path.starts_with("(path+"); - - (CrateVersion(name, version), is_path_dep) - } - - pub fn id_str(&self) -> String { - format!("{} {}", self.0, self.1) - } +/// Dependency checks. +/// +/// `path` is path to the `src` directory, `cargo` is path to the cargo executable. +pub fn check(path: &Path, cargo: &Path, bad: &mut bool) { + let mut cmd = cargo_metadata::MetadataCommand::new(); + cmd.cargo_path(cargo) + .manifest_path(path.parent().unwrap().join("Cargo.toml")) + .features(cargo_metadata::CargoOpt::AllFeatures); + let metadata = t!(cmd.exec()); + check_exceptions(&metadata, bad); + check_whitelist(&metadata, bad); + check_crate_duplicate(&metadata, bad); } -impl<'a> From> for Crate<'a> { - fn from(cv: CrateVersion<'a>) -> Crate<'a> { - Crate(cv.0) +/// Check that all licenses are in the valid list in `LICENSES`. +/// +/// Packages listed in `EXCEPTIONS` are allowed for tools. +fn check_exceptions(metadata: &Metadata, bad: &mut bool) { + // Validate the EXCEPTIONS list hasn't changed. + for (name, license) in EXCEPTIONS { + // Check that the package actually exists. + if !metadata.packages.iter().any(|p| p.name == *name) { + println!( + "could not find exception package `{}`\n\ + Remove from EXCEPTIONS list if it is no longer used.", + name + ); + *bad = true; + } + // Check that the license hasn't changed. + for pkg in metadata.packages.iter().filter(|p| p.name == *name) { + if pkg.name == "fuchsia-cprng" { + // This package doesn't declare a license expression. Manual + // inspection of the license file is necessary, which appears + // to be BSD-3-Clause. + assert!(pkg.license.is_none()); + continue; + } + match &pkg.license { + None => { + println!( + "dependency exception `{}` does not declare a license expression", + pkg.id + ); + *bad = true; + } + Some(pkg_license) => { + if pkg_license.as_str() != *license { + println!("dependency exception `{}` license has changed", name); + println!(" previously `{}` now `{}`", license, pkg_license); + println!(" update EXCEPTIONS for the new license"); + *bad = true; + } + } + } + } } -} -/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed. -/// -/// Specifically, this checks that the license is correct. -pub fn check(path: &Path, bad: &mut bool) { - // Check licences. - let path = path.join("../vendor"); - assert!(path.exists(), "vendor directory missing"); - let mut saw_dir = false; - for dir in t!(path.read_dir()) { - saw_dir = true; - let dir = t!(dir); + let exception_names: Vec<_> = EXCEPTIONS.iter().map(|(name, _license)| *name).collect(); + let runtime_ids = compute_runtime_crates(metadata); - // Skip our exceptions. - let is_exception = EXCEPTIONS.iter().any(|exception| { - dir.path().to_str().unwrap().contains(&format!("vendor/{}", exception)) - }); - if is_exception { + // Check if any package does not have a valid license. + for pkg in &metadata.packages { + if pkg.source.is_none() { + // No need to check local packages. continue; } - - let toml = dir.path().join("Cargo.toml"); - *bad = !check_license(&toml) || *bad; + if !runtime_ids.contains(&pkg.id) && exception_names.contains(&pkg.name.as_str()) { + continue; + } + let license = match &pkg.license { + Some(license) => license, + None => { + println!("dependency `{}` does not define a license expression", pkg.id,); + *bad = true; + continue; + } + }; + if !LICENSES.contains(&license.as_str()) { + if pkg.name == "fortanix-sgx-abi" { + // This is a specific exception because SGX is considered + // "third party". See + // https://github.com/rust-lang/rust/issues/62620 for more. In + // general, these should never be added. + continue; + } + println!("invalid license `{}` in `{}`", license, pkg.id); + *bad = true; + } } - assert!(saw_dir, "no vendored source"); } /// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a /// check failed. /// /// Specifically, this checks that the dependencies are on the `WHITELIST`. -pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) { - // Get dependencies from Cargo metadata. - let resolve = get_deps(path, cargo); - +fn check_whitelist(metadata: &Metadata, bad: &mut bool) { + // Check that the WHITELIST does not have unused entries. + for name in WHITELIST { + if !metadata.packages.iter().any(|p| p.name == *name) { + println!( + "could not find whitelisted package `{}`\n\ + Remove from WHITELIST list if it is no longer used.", + name + ); + *bad = true; + } + } // Get the whitelist in a convenient form. let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect(); @@ -287,122 +292,59 @@ pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) { let mut visited = BTreeSet::new(); let mut unapproved = BTreeSet::new(); for &krate in WHITELIST_CRATES.iter() { - let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false); + let pkg = pkg_from_name(metadata, krate); + let mut bad = check_crate_whitelist(&whitelist, metadata, &mut visited, pkg); unapproved.append(&mut bad); } if !unapproved.is_empty() { println!("Dependencies not on the whitelist:"); for dep in unapproved { - println!("* {}", dep.id_str()); + println!("* {}", dep); } *bad = true; } - - check_crate_duplicate(&resolve, bad); -} - -fn check_license(path: &Path) -> bool { - if !path.exists() { - panic!("{} does not exist", path.display()); - } - let contents = t!(fs::read_to_string(&path)); - - let mut found_license = false; - for line in contents.lines() { - if !line.starts_with("license") { - continue; - } - let license = extract_license(line); - if !LICENSES.contains(&&*license) { - println!("invalid license {} in {}", license, path.display()); - return false; - } - found_license = true; - break; - } - if !found_license { - println!("no license in {}", path.display()); - return false; - } - - true -} - -fn extract_license(line: &str) -> String { - let first_quote = line.find('"'); - let last_quote = line.rfind('"'); - if let (Some(f), Some(l)) = (first_quote, last_quote) { - let license = &line[f + 1..l]; - license.into() - } else { - "bad-license-parse".into() - } -} - -/// Gets the dependencies of the crate at the given path using `cargo metadata`. -fn get_deps(path: &Path, cargo: &Path) -> Resolve { - // Run `cargo metadata` to get the set of dependencies. - let output = Command::new(cargo) - .arg("metadata") - .arg("--format-version") - .arg("1") - .arg("--manifest-path") - .arg(path.join("../Cargo.toml")) - .output() - .expect("Unable to run `cargo metadata`") - .stdout; - let output = String::from_utf8_lossy(&output); - let output: Output = serde_json::from_str(&output).unwrap(); - - output.resolve } /// Checks the dependencies of the given crate from the given cargo metadata to see if they are on /// the whitelist. Returns a list of illegal dependencies. fn check_crate_whitelist<'a>( - whitelist: &'a HashSet>, - resolve: &'a Resolve, - visited: &mut BTreeSet>, - krate: CrateVersion<'a>, - must_be_on_whitelist: bool, -) -> BTreeSet> { + whitelist: &'a HashSet<&'static str>, + metadata: &'a Metadata, + visited: &mut BTreeSet<&'a PackageId>, + krate: &'a Package, +) -> BTreeSet<&'a PackageId> { // This will contain bad deps. let mut unapproved = BTreeSet::new(); // Check if we have already visited this crate. - if visited.contains(&krate) { + if visited.contains(&krate.id) { return unapproved; } - visited.insert(krate); + visited.insert(&krate.id); // If this path is in-tree, we don't require it to be on the whitelist. - if must_be_on_whitelist { + if krate.source.is_some() { // If this dependency is not on `WHITELIST`, add to bad set. - if !whitelist.contains(&krate.into()) { - unapproved.insert(krate.into()); + if !whitelist.contains(krate.name.as_str()) { + unapproved.insert(&krate.id); } } - // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!). - let to_check = resolve - .nodes - .iter() - .find(|n| n.id.starts_with(&krate.id_str())) - .expect("crate does not exist"); + // Do a DFS in the crate graph. + let to_check = deps_of(metadata, &krate.id); - for dep in to_check.dependencies.iter() { - let (krate, is_path_dep) = CrateVersion::from_str(dep); - - let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep); + for dep in to_check { + let mut bad = check_crate_whitelist(whitelist, metadata, visited, dep); unapproved.append(&mut bad); } unapproved } -fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) { +/// Prevents multiple versions of some expensive crates. +fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) { const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[ // These two crates take quite a long time to build, so don't allow two versions of them // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times @@ -410,19 +352,97 @@ fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) { "cargo", "rustc-ap-syntax", ]; - let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new(); - for node in resolve.nodes.iter() { - name_to_id.entry(node.id.split_whitespace().next().unwrap()).or_default().push(&node.id); - } - for name in FORBIDDEN_TO_HAVE_DUPLICATES { - if name_to_id[name].len() <= 1 { - continue; - } - println!("crate `{}` is duplicated in `Cargo.lock`", name); - for id in name_to_id[name].iter() { - println!(" * {}", id); + for &name in FORBIDDEN_TO_HAVE_DUPLICATES { + let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect(); + match matches.len() { + 0 => { + println!( + "crate `{}` is missing, update `check_crate_duplicate` \ + if it is no longer used", + name + ); + *bad = true; + } + 1 => {} + _ => { + println!( + "crate `{}` is duplicated in `Cargo.lock`, \ + it is too expensive to build multiple times, \ + so make sure only one version appears across all dependencies", + name + ); + for pkg in matches { + println!(" * {}", pkg.id); + } + *bad = true; + } } - *bad = true; + } +} + +/// Returns a list of dependencies for the given package. +fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> { + let resolve = metadata.resolve.as_ref().unwrap(); + let node = resolve + .nodes + .iter() + .find(|n| &n.id == pkg_id) + .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id)); + node.deps + .iter() + .map(|dep| { + metadata.packages.iter().find(|pkg| pkg.id == dep.pkg).unwrap_or_else(|| { + panic!("could not find dep `{}` for pkg `{}` in resolve", dep.pkg, pkg_id) + }) + }) + .collect() +} + +/// Finds a package with the given name. +fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package { + let mut i = metadata.packages.iter().filter(|p| p.name == name); + let result = + i.next().unwrap_or_else(|| panic!("could not find package `{}` in package list", name)); + assert!(i.next().is_none(), "more than one package found for `{}`", name); + result +} + +/// Finds all the packages that are in the rust runtime. +fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> { + let resolve = metadata.resolve.as_ref().unwrap(); + let mut result = HashSet::new(); + for name in RUNTIME_CRATES { + let id = &pkg_from_name(metadata, name).id; + normal_deps_of_r(resolve, id, &mut result); + } + result +} + +/// Recursively find all normal dependencies. +fn normal_deps_of_r<'a>( + resolve: &'a Resolve, + pkg_id: &'a PackageId, + result: &mut HashSet<&'a PackageId>, +) { + if !result.insert(pkg_id) { + return; + } + let node = resolve + .nodes + .iter() + .find(|n| &n.id == pkg_id) + .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id)); + // Don't care about dev-dependencies. + // Build dependencies *shouldn't* matter unless they do some kind of + // codegen. For now we'll assume they don't. + let deps = node.deps.iter().filter(|node_dep| { + node_dep + .dep_kinds + .iter() + .any(|kind_info| kind_info.kind == cargo_metadata::DependencyKind::Normal) + }); + for dep in deps { + normal_deps_of_r(resolve, &dep.pkg, result); } } diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 12f93a87cb1..d9320e9147c 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -63,20 +63,6 @@ pub struct CollectedFeatures { pub fn collect_lib_features(base_src_path: &Path) -> Features { let mut lib_features = Features::new(); - // This library feature is defined in the `compiler_builtins` crate, which - // has been moved out-of-tree. Now it can no longer be auto-discovered by - // `tidy`, because we need to filter out its (submodule) directory. Manually - // add it to the set of known library features so we can still generate docs. - lib_features.insert( - "compiler_builtins_lib".to_owned(), - Feature { - level: Status::Unstable, - since: None, - has_gate_test: false, - tracking_issue: None, - }, - ); - map_lib_features(base_src_path, &mut |res, _, _| { if let Ok((name, feature)) = res { lib_features.insert(name.to_owned(), feature); diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index ec8b14c288a..e2856c69055 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -1,8 +1,8 @@ //! Tidy checks source code in this repository. //! //! This program runs all of the various tidy checks for style, cleanliness, -//! etc. This is run by default on `make check` and as part of the auto -//! builders. +//! etc. This is run by default on `./x.py test` and as part of the auto +//! builders. The tidy checks can be executed with `./x.py test tidy`. #![deny(warnings)] @@ -30,10 +30,7 @@ fn main() { pal::check(&path, &mut bad); unstable_book::check(&path, collected, &mut bad); unit_tests::check(&path, &mut bad); - if !args.iter().any(|s| *s == "--no-vendor") { - deps::check(&path, &mut bad); - } - deps::check_whitelist(&path, &cargo, &mut bad); + deps::check(&path, &cargo, &mut bad); extdeps::check(&path, &mut bad); ui_tests::check(&path, &mut bad); error_codes_check::check(&path, &mut bad); diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index f11394bd95f..247e85603cf 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -41,7 +41,7 @@ "src/libpanic_unwind", "src/libunwind", // black_box implementation is LLVM-version specific and it uses - // target_os to tell targets with different LLVM-versions appart + // target_os to tell targets with different LLVM-versions apart // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`): "src/libcore/hint.rs", "src/libstd/sys/", // Platform-specific code for std lives here. @@ -59,6 +59,8 @@ "src/libstd/sys_common/mod.rs", "src/libstd/sys_common/net.rs", "src/libstd/sys_common/backtrace.rs", + // panic_unwind shims + "src/libstd/panicking.rs", "src/libterm", // Not sure how to make this crate portable, but test crate needs it. "src/libtest", // Probably should defer to unstable `std::sys` APIs. "src/libstd/sync/mpsc", // some tests are only run on non-emscripten diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs index 472d66459d7..7dfb6224d24 100644 --- a/src/tools/tidy/src/unstable_book.rs +++ b/src/tools/tidy/src/unstable_book.rs @@ -1,4 +1,4 @@ -use crate::features::{CollectedFeatures, Feature, Features, Status}; +use crate::features::{CollectedFeatures, Features, Status}; use std::collections::BTreeSet; use std::fs; use std::path::{Path, PathBuf}; @@ -73,26 +73,12 @@ fn collect_unstable_book_lib_features_section_file_names(base_src_path: &Path) - pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) { let lang_features = features.lang; - let mut lib_features = features + let lib_features = features .lib .into_iter() .filter(|&(ref name, _)| !lang_features.contains_key(name)) .collect::(); - // This library feature is defined in the `compiler_builtins` crate, which - // has been moved out-of-tree. Now it can no longer be auto-discovered by - // `tidy`, because we need to filter out its (submodule) directory. Manually - // add it to the set of known library features so we can still generate docs. - lib_features.insert( - "compiler_builtins_lib".to_owned(), - Feature { - level: Status::Unstable, - since: None, - has_gate_test: false, - tracking_issue: None, - }, - ); - // Library features let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features); let unstable_book_lib_features_section_file_names = diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index be8508e3973..839d914baa9 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -147,7 +147,7 @@ fn main() { eprintln!("Must provide path to write unicode tables to"); eprintln!( "e.g. {} src/libcore/unicode/unicode_data.rs", - std::env::args().nth(0).unwrap_or_default() + std::env::args().next().unwrap_or_default() ); std::process::exit(1); }); diff --git a/triagebot.toml b/triagebot.toml index a174dd1e7f3..2476f414e0e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -11,23 +11,25 @@ allow-unauthenticated = [ [assign] [ping.icebreakers-llvm] +alias = ["llvm", "llvms"] message = """\ Hey LLVM ICE-breakers! This bug has been identified as a good "LLVM ICE-breaking candidate". In case it's useful, here are some [instructions] for tackling these sorts of bugs. Maybe take a look? Thanks! <3 -[instructions]: https://rust-lang.github.io/rustc-guide/ice-breaker/llvm.html +[instructions]: https://rustc-dev-guide.rust-lang.org/ice-breaker/llvm.html """ label = "ICEBreaker-LLVM" [ping.icebreakers-cleanup-crew] +alias = ["cleanup", "cleanups", "cleanup-crew", "shrink", "reduce", "bisect"] message = """\ Hey Cleanup Crew ICE-breakers! This bug has been identified as a good "Cleanup ICE-breaking candidate". In case it's useful, here are some [instructions] for tackling these sorts of bugs. Maybe take a look? Thanks! <3 -[instructions]: https://rust-lang.github.io/rustc-guide/ice-breaker/cleanup-crew.html +[instructions]: https://rustc-dev-guide.rust-lang.org/ice-breaker/cleanup-crew.html """ label = "ICEBreaker-Cleanup-Crew"

(&self, value: P) -> Obligation<'tcx, P> { } impl<'tcx> FulfillmentError<'tcx> { - fn new( + pub fn new( obligation: PredicateObligation<'tcx>, code: FulfillmentErrorCode<'tcx>, ) -> FulfillmentError<'tcx> { - FulfillmentError { obligation: obligation, code: code, points_at_arg_span: false } + FulfillmentError { obligation, code, points_at_arg_span: false } } } impl<'tcx> TraitObligation<'tcx> { - fn self_ty(&self) -> ty::Binder> { + pub fn self_ty(&self) -> ty::Binder> { self.predicate.map_bound(|p| p.self_ty()) } } - -pub fn provide(providers: &mut ty::query::Providers<'_>) { - object_safety::provide(providers); - *providers = ty::query::Providers { - specialization_graph_of: specialize::specialization_graph_provider, - specializes: specialize::specializes, - codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, - vtable_methods, - substitute_normalize_and_test_predicates, - ..*providers - }; -} diff --git a/src/librustc_infer/traits/object_safety.rs b/src/librustc_infer/traits/object_safety.rs deleted file mode 100644 index f5bab7cfac9..00000000000 --- a/src/librustc_infer/traits/object_safety.rs +++ /dev/null @@ -1,777 +0,0 @@ -//! "Object safety" refers to the ability for a trait to be converted -//! to an object. In general, traits may only be converted to an -//! object if all of their methods meet certain criteria. In particular, -//! they must: -//! -//! - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version -//! that doesn't contain the vtable; -//! - not reference the erased type `Self` except for in this receiver; -//! - not have generic type parameters. - -use super::elaborate_predicates; - -use crate::infer::TyCtxtInferExt; -use crate::traits::{self, Obligation, ObligationCause}; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; -use rustc_span::symbol::Symbol; -use rustc_span::Span; -use smallvec::SmallVec; - -use std::iter; - -pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; - -/// Returns the object safety violations that affect -/// astconv -- currently, `Self` in supertraits. This is needed -/// because `object_safety_violations` can't be used during -/// type collection. -pub fn astconv_object_safety_violations( - tcx: TyCtxt<'_>, - trait_def_id: DefId, -) -> Vec { - debug_assert!(tcx.generics_of(trait_def_id).has_self); - let violations = traits::supertrait_def_ids(tcx, trait_def_id) - .map(|def_id| predicates_reference_self(tcx, def_id, true)) - .filter(|spans| !spans.is_empty()) - .map(|spans| ObjectSafetyViolation::SupertraitSelf(spans)) - .collect(); - - debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations); - - violations -} - -fn object_safety_violations(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Vec { - debug_assert!(tcx.generics_of(trait_def_id).has_self); - debug!("object_safety_violations: {:?}", trait_def_id); - - traits::supertrait_def_ids(tcx, trait_def_id) - .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)) - .collect() -} - -/// We say a method is *vtable safe* if it can be invoked on a trait -/// object. Note that object-safe traits can have some -/// non-vtable-safe methods, so long as they require `Self: Sized` or -/// otherwise ensure that they cannot be used when `Self = Trait`. -pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: &ty::AssocItem) -> bool { - debug_assert!(tcx.generics_of(trait_def_id).has_self); - debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method); - // Any method that has a `Self: Sized` bound cannot be called. - if generics_require_sized_self(tcx, method.def_id) { - return false; - } - - match virtual_call_violation_for_method(tcx, trait_def_id, method) { - None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true, - Some(_) => false, - } -} - -fn object_safety_violations_for_trait( - tcx: TyCtxt<'_>, - trait_def_id: DefId, -) -> Vec { - // Check methods for violations. - let mut violations: Vec<_> = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Method) - .filter_map(|item| { - object_safety_violation_for_method(tcx, trait_def_id, &item) - .map(|(code, span)| ObjectSafetyViolation::Method(item.ident.name, code, span)) - }) - .filter(|violation| { - if let ObjectSafetyViolation::Method( - _, - MethodViolationCode::WhereClauseReferencesSelf, - span, - ) = violation - { - // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. - // It's also hard to get a use site span, so we use the method definition span. - tcx.struct_span_lint_hir( - WHERE_CLAUSES_OBJECT_SAFETY, - hir::CRATE_HIR_ID, - *span, - |lint| { - let mut err = lint.build(&format!( - "the trait `{}` cannot be made into an object", - tcx.def_path_str(trait_def_id) - )); - let node = tcx.hir().get_if_local(trait_def_id); - let msg = if let Some(hir::Node::Item(item)) = node { - err.span_label( - item.ident.span, - "this trait cannot be made into an object...", - ); - format!("...because {}", violation.error_msg()) - } else { - format!( - "the trait cannot be made into an object because {}", - violation.error_msg() - ) - }; - err.span_label(*span, &msg); - match (node, violation.solution()) { - (Some(_), Some((note, None))) => { - err.help(¬e); - } - (Some(_), Some((note, Some((sugg, span))))) => { - err.span_suggestion( - span, - ¬e, - sugg, - Applicability::MachineApplicable, - ); - } - // Only provide the help if its a local trait, otherwise it's not actionable. - _ => {} - } - err.emit(); - }, - ); - false - } else { - true - } - }) - .collect(); - - // Check the trait itself. - if trait_has_sized_self(tcx, trait_def_id) { - // We don't want to include the requirement from `Sized` itself to be `Sized` in the list. - let spans = get_sized_bounds(tcx, trait_def_id); - violations.push(ObjectSafetyViolation::SizedSelf(spans)); - } - let spans = predicates_reference_self(tcx, trait_def_id, false); - if !spans.is_empty() { - violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); - } - - violations.extend( - tcx.associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Const) - .map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)), - ); - - debug!( - "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", - trait_def_id, violations - ); - - violations -} - -fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { - tcx.hir() - .get_if_local(trait_def_id) - .and_then(|node| match node { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(.., generics, bounds, _), - .. - }) => Some( - generics - .where_clause - .predicates - .iter() - .filter_map(|pred| { - match pred { - hir::WherePredicate::BoundPredicate(pred) - if pred.bounded_ty.hir_id.owner_def_id() == trait_def_id => - { - // Fetch spans for trait bounds that are Sized: - // `trait T where Self: Pred` - Some(pred.bounds.iter().filter_map(|b| match b { - hir::GenericBound::Trait( - trait_ref, - hir::TraitBoundModifier::None, - ) if trait_has_sized_self( - tcx, - trait_ref.trait_ref.trait_def_id(), - ) => - { - Some(trait_ref.span) - } - _ => None, - })) - } - _ => None, - } - }) - .flatten() - .chain(bounds.iter().filter_map(|b| match b { - hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None) - if trait_has_sized_self(tcx, trait_ref.trait_ref.trait_def_id()) => - { - // Fetch spans for supertraits that are `Sized`: `trait T: Super` - Some(trait_ref.span) - } - _ => None, - })) - .collect::>(), - ), - _ => None, - }) - .unwrap_or_else(SmallVec::new) -} - -fn predicates_reference_self( - tcx: TyCtxt<'_>, - trait_def_id: DefId, - supertraits_only: bool, -) -> SmallVec<[Span; 1]> { - let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); - let predicates = if supertraits_only { - tcx.super_predicates_of(trait_def_id) - } else { - tcx.predicates_of(trait_def_id) - }; - let self_ty = tcx.types.self_param; - let has_self_ty = |t: Ty<'_>| t.walk().any(|t| t == self_ty); - predicates - .predicates - .iter() - .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) - .filter_map(|(predicate, &sp)| { - match predicate { - ty::Predicate::Trait(ref data, _) => { - // In the case of a trait predicate, we can skip the "self" type. - if data.skip_binder().input_types().skip(1).any(has_self_ty) { - Some(sp) - } else { - None - } - } - ty::Predicate::Projection(ref data) => { - // And similarly for projections. This should be redundant with - // the previous check because any projection should have a - // matching `Trait` predicate with the same inputs, but we do - // the check to be safe. - // - // Note that we *do* allow projection *outputs* to contain - // `self` (i.e., `trait Foo: Bar { type Result; }`), - // we just require the user to specify *both* outputs - // in the object type (i.e., `dyn Foo`). - // - // This is ALT2 in issue #56288, see that for discussion of the - // possible alternatives. - if data - .skip_binder() - .projection_ty - .trait_ref(tcx) - .input_types() - .skip(1) - .any(has_self_ty) - { - Some(sp) - } else { - None - } - } - ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::TypeOutlives(..) - | ty::Predicate::RegionOutlives(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::ConstEvaluatable(..) => None, - } - }) - .collect() -} - -fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { - generics_require_sized_self(tcx, trait_def_id) -} - -fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let sized_def_id = match tcx.lang_items().sized_trait() { - Some(def_id) => def_id, - None => { - return false; /* No Sized trait, can't require it! */ - } - }; - - // Search for a predicate like `Self : Sized` amongst the trait bounds. - let predicates = tcx.predicates_of(def_id); - let predicates = predicates.instantiate_identity(tcx).predicates; - elaborate_predicates(tcx, predicates).any(|predicate| match predicate { - ty::Predicate::Trait(ref trait_pred, _) => { - trait_pred.def_id() == sized_def_id && trait_pred.skip_binder().self_ty().is_param(0) - } - ty::Predicate::Projection(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::RegionOutlives(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => false, - }) -} - -/// Returns `Some(_)` if this method makes the containing trait not object safe. -fn object_safety_violation_for_method( - tcx: TyCtxt<'_>, - trait_def_id: DefId, - method: &ty::AssocItem, -) -> Option<(MethodViolationCode, Span)> { - debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method); - // Any method that has a `Self : Sized` requisite is otherwise - // exempt from the regulations. - if generics_require_sized_self(tcx, method.def_id) { - return None; - } - - let violation = virtual_call_violation_for_method(tcx, trait_def_id, method); - // Get an accurate span depending on the violation. - violation.map(|v| { - let node = tcx.hir().get_if_local(method.def_id); - let span = match (v, node) { - (MethodViolationCode::ReferencesSelfInput(arg), Some(node)) => node - .fn_decl() - .and_then(|decl| decl.inputs.get(arg + 1)) - .map_or(method.ident.span, |arg| arg.span), - (MethodViolationCode::UndispatchableReceiver, Some(node)) => node - .fn_decl() - .and_then(|decl| decl.inputs.get(0)) - .map_or(method.ident.span, |arg| arg.span), - (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { - node.fn_decl().map_or(method.ident.span, |decl| decl.output.span()) - } - _ => method.ident.span, - }; - (v, span) - }) -} - -/// Returns `Some(_)` if this method cannot be called on a trait -/// object; this does not necessarily imply that the enclosing trait -/// is not object safe, because the method might have a where clause -/// `Self:Sized`. -fn virtual_call_violation_for_method<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - method: &ty::AssocItem, -) -> Option { - // The method's first parameter must be named `self` - if !method.method_has_self_argument { - // We'll attempt to provide a structured suggestion for `Self: Sized`. - let sugg = - tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map( - |generics| match generics.where_clause.predicates { - [] => (" where Self: Sized", generics.where_clause.span), - [.., pred] => (", Self: Sized", pred.span().shrink_to_hi()), - }, - ); - return Some(MethodViolationCode::StaticMethod(sugg)); - } - - let sig = tcx.fn_sig(method.def_id); - - for (i, input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { - if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) { - return Some(MethodViolationCode::ReferencesSelfInput(i)); - } - } - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output().skip_binder()) { - return Some(MethodViolationCode::ReferencesSelfOutput); - } - - // We can't monomorphize things like `fn foo(...)`. - let own_counts = tcx.generics_of(method.def_id).own_counts(); - if own_counts.types + own_counts.consts != 0 { - return Some(MethodViolationCode::Generic); - } - - if tcx - .predicates_of(method.def_id) - .predicates - .iter() - // A trait object can't claim to live more than the concrete type, - // so outlives predicates will always hold. - .cloned() - .filter(|(p, _)| p.to_opt_type_outlives().is_none()) - .collect::>() - // Do a shallow visit so that `contains_illegal_self_type_reference` - // may apply it's custom visiting. - .visit_tys_shallow(|t| contains_illegal_self_type_reference(tcx, trait_def_id, t)) - { - return Some(MethodViolationCode::WhereClauseReferencesSelf); - } - - let receiver_ty = - tcx.liberate_late_bound_regions(method.def_id, &sig.map_bound(|sig| sig.inputs()[0])); - - // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. - // However, this is already considered object-safe. We allow it as a special case here. - // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows - // `Receiver: Unsize dyn Trait]>`. - if receiver_ty != tcx.types.self_param { - if !receiver_is_dispatchable(tcx, method, receiver_ty) { - return Some(MethodViolationCode::UndispatchableReceiver); - } else { - // Do sanity check to make sure the receiver actually has the layout of a pointer. - - use rustc::ty::layout::Abi; - - let param_env = tcx.param_env(method.def_id); - - let abi_of_ty = |ty: Ty<'tcx>| -> &Abi { - match tcx.layout_of(param_env.and(ty)) { - Ok(layout) => &layout.abi, - Err(err) => bug!("error: {}\n while computing layout for type {:?}", err, ty), - } - }; - - // e.g., `Rc<()>` - let unit_receiver_ty = - receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id); - - match abi_of_ty(unit_receiver_ty) { - &Abi::Scalar(..) => (), - abi => { - tcx.sess.delay_span_bug( - tcx.def_span(method.def_id), - &format!( - "receiver when `Self = ()` should have a Scalar ABI; found {:?}", - abi - ), - ); - } - } - - let trait_object_ty = - object_ty_for_trait(tcx, trait_def_id, tcx.mk_region(ty::ReStatic)); - - // e.g., `Rc` - let trait_object_receiver = - receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id); - - match abi_of_ty(trait_object_receiver) { - &Abi::ScalarPair(..) => (), - abi => { - tcx.sess.delay_span_bug( - tcx.def_span(method.def_id), - &format!( - "receiver when `Self = {}` should have a ScalarPair ABI; \ - found {:?}", - trait_object_ty, abi - ), - ); - } - } - } - } - - None -} - -/// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`. -/// For example, for `receiver_ty = Rc` and `self_ty = Foo`, returns `Rc`. -fn receiver_for_self_ty<'tcx>( - tcx: TyCtxt<'tcx>, - receiver_ty: Ty<'tcx>, - self_ty: Ty<'tcx>, - method_def_id: DefId, -) -> Ty<'tcx> { - debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id); - let substs = InternalSubsts::for_item(tcx, method_def_id, |param, _| { - if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) } - }); - - let result = receiver_ty.subst(tcx, substs); - debug!( - "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}", - receiver_ty, self_ty, method_def_id, result - ); - result -} - -/// Creates the object type for the current trait. For example, -/// if the current trait is `Deref`, then this will be -/// `dyn Deref + 'static`. -fn object_ty_for_trait<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - lifetime: ty::Region<'tcx>, -) -> Ty<'tcx> { - debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id); - - let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); - - let trait_predicate = - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - - let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref)) - .flat_map(|super_trait_ref| { - tcx.associated_items(super_trait_ref.def_id()) - .in_definition_order() - .map(move |item| (super_trait_ref, item)) - }) - .filter(|(_, item)| item.kind == ty::AssocKind::Type) - .collect::>(); - - // existential predicates need to be in a specific order - associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id)); - - let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| { - // We *can* get bound lifetimes here in cases like - // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. - // - // binder moved to (*)... - let super_trait_ref = super_trait_ref.skip_binder(); - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - ty: tcx.mk_projection(item.def_id, super_trait_ref.substs), - item_def_id: item.def_id, - substs: super_trait_ref.substs, - }) - }); - - let existential_predicates = - tcx.mk_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); - - let object_ty = tcx.mk_dynamic( - // (*) ... binder re-introduced here - ty::Binder::bind(existential_predicates), - lifetime, - ); - - debug!("object_ty_for_trait: object_ty=`{}`", object_ty); - - object_ty -} - -/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a -/// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type -/// in the following way: -/// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc`, -/// - require the following bound: -/// -/// ``` -/// Receiver[Self => T]: DispatchFromDyn dyn Trait]> -/// ``` -/// -/// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`" -/// (substitution notation). -/// -/// Some examples of receiver types and their required obligation: -/// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`, -/// - `self: Rc` requires `Rc: DispatchFromDyn>`, -/// - `self: Pin>` requires `Pin>: DispatchFromDyn>>`. -/// -/// The only case where the receiver is not dispatchable, but is still a valid receiver -/// type (just not object-safe), is when there is more than one level of pointer indirection. -/// E.g., `self: &&Self`, `self: &Rc`, `self: Box>`. In these cases, there -/// is no way, or at least no inexpensive way, to coerce the receiver from the version where -/// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type -/// contained by the trait object, because the object that needs to be coerced is behind -/// a pointer. -/// -/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result -/// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch -/// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561). -/// Instead, we fudge a little by introducing a new type parameter `U` such that -/// `Self: Unsize` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. -/// Written as a chalk-style query: -/// -/// forall (U: Trait + ?Sized) { -/// if (Self: Unsize) { -/// Receiver: DispatchFromDyn U]> -/// } -/// } -/// -/// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>` -/// for `self: Rc`, this means `Rc: DispatchFromDyn>` -/// for `self: Pin>`, this means `Pin>: DispatchFromDyn>>` -// -// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this -// fallback query: `Receiver: Unsize U]>` to support receivers like -// `self: Wrapper`. -#[allow(dead_code)] -fn receiver_is_dispatchable<'tcx>( - tcx: TyCtxt<'tcx>, - method: &ty::AssocItem, - receiver_ty: Ty<'tcx>, -) -> bool { - debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty); - - let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait()); - let (unsize_did, dispatch_from_dyn_did) = if let (Some(u), Some(cu)) = traits { - (u, cu) - } else { - debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits"); - return false; - }; - - // the type `U` in the query - // use a bogus type parameter to mimick a forall(U) query using u32::MAX for now. - // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can - // replace this with `dyn Trait` - let unsized_self_ty: Ty<'tcx> = - tcx.mk_ty_param(::std::u32::MAX, Symbol::intern("RustaceansAreAwesome")); - - // `Receiver[Self => U]` - let unsized_receiver_ty = - receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id); - - // create a modified param env, with `Self: Unsize` and `U: Trait` added to caller bounds - // `U: ?Sized` is already implied here - let param_env = { - let mut param_env = tcx.param_env(method.def_id); - - // Self: Unsize - let unsize_predicate = ty::TraitRef { - def_id: unsize_did, - substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]), - } - .without_const() - .to_predicate(); - - // U: Trait - let trait_predicate = { - let substs = - InternalSubsts::for_item(tcx, method.container.assert_trait(), |param, _| { - if param.index == 0 { - unsized_self_ty.into() - } else { - tcx.mk_param_from_def(param) - } - }); - - ty::TraitRef { def_id: unsize_did, substs }.without_const().to_predicate() - }; - - let caller_bounds: Vec> = param_env - .caller_bounds - .iter() - .cloned() - .chain(iter::once(unsize_predicate)) - .chain(iter::once(trait_predicate)) - .collect(); - - param_env.caller_bounds = tcx.intern_predicates(&caller_bounds); - - param_env - }; - - // Receiver: DispatchFromDyn U]> - let obligation = { - let predicate = ty::TraitRef { - def_id: dispatch_from_dyn_did, - substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]), - } - .without_const() - .to_predicate(); - - Obligation::new(ObligationCause::dummy(), param_env, predicate) - }; - - tcx.infer_ctxt().enter(|ref infcx| { - // the receiver is dispatchable iff the obligation holds - infcx.predicate_must_hold_modulo_regions(&obligation) - }) -} - -fn contains_illegal_self_type_reference<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - ty: Ty<'tcx>, -) -> bool { - // This is somewhat subtle. In general, we want to forbid - // references to `Self` in the argument and return types, - // since the value of `Self` is erased. However, there is one - // exception: it is ok to reference `Self` in order to access - // an associated type of the current trait, since we retain - // the value of those associated types in the object type - // itself. - // - // ```rust - // trait SuperTrait { - // type X; - // } - // - // trait Trait : SuperTrait { - // type Y; - // fn foo(&self, x: Self) // bad - // fn foo(&self) -> Self // bad - // fn foo(&self) -> Option // bad - // fn foo(&self) -> Self::Y // OK, desugars to next example - // fn foo(&self) -> ::Y // OK - // fn foo(&self) -> Self::X // OK, desugars to next example - // fn foo(&self) -> ::X // OK - // } - // ``` - // - // However, it is not as simple as allowing `Self` in a projected - // type, because there are illegal ways to use `Self` as well: - // - // ```rust - // trait Trait : SuperTrait { - // ... - // fn foo(&self) -> ::X; - // } - // ``` - // - // Here we will not have the type of `X` recorded in the - // object type, and we cannot resolve `Self as SomeOtherTrait` - // without knowing what `Self` is. - - let mut supertraits: Option>> = None; - let mut error = false; - let self_ty = tcx.types.self_param; - ty.maybe_walk(|ty| { - match ty.kind { - ty::Param(_) => { - if ty == self_ty { - error = true; - } - - false // no contained types to walk - } - - ty::Projection(ref data) => { - // This is a projected type `::X`. - - // Compute supertraits of current trait lazily. - if supertraits.is_none() { - let trait_ref = ty::Binder::bind(ty::TraitRef::identity(tcx, trait_def_id)); - supertraits = Some(traits::supertraits(tcx, trait_ref).collect()); - } - - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let projection_trait_ref = ty::Binder::bind(data.trait_ref(tcx)); - let is_supertrait_of_current_trait = - supertraits.as_ref().unwrap().contains(&projection_trait_ref); - - if is_supertrait_of_current_trait { - false // do not walk contained types, do not report error, do collect $200 - } else { - true // DO walk contained types, POSSIBLY reporting an error - } - } - - _ => true, // walk contained types, if any - } - }); - - error -} - -pub fn provide(providers: &mut ty::query::Providers<'_>) { - *providers = ty::query::Providers { object_safety_violations, ..*providers }; -} diff --git a/src/librustc_infer/traits/on_unimplemented.rs b/src/librustc_infer/traits/on_unimplemented.rs deleted file mode 100644 index 19260293ee6..00000000000 --- a/src/librustc_infer/traits/on_unimplemented.rs +++ /dev/null @@ -1,383 +0,0 @@ -use fmt_macros::{Parser, Piece, Position}; - -use rustc::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc::util::common::ErrorReported; - -use rustc_ast::ast::{MetaItem, NestedMetaItem}; -use rustc_attr as attr; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::struct_span_err; -use rustc_hir::def_id::DefId; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; - -#[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString(Symbol); - -#[derive(Debug)] -pub struct OnUnimplementedDirective { - pub condition: Option, - pub subcommands: Vec, - pub message: Option, - pub label: Option, - pub note: Option, - pub enclosing_scope: Option, -} - -#[derive(Default)] -pub struct OnUnimplementedNote { - pub message: Option, - pub label: Option, - pub note: Option, - pub enclosing_scope: Option, -} - -fn parse_error( - tcx: TyCtxt<'_>, - span: Span, - message: &str, - label: &str, - note: Option<&str>, -) -> ErrorReported { - let mut diag = struct_span_err!(tcx.sess, span, E0232, "{}", message); - diag.span_label(span, label); - if let Some(note) = note { - diag.note(note); - } - diag.emit(); - ErrorReported -} - -impl<'tcx> OnUnimplementedDirective { - fn parse( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - items: &[NestedMetaItem], - span: Span, - is_root: bool, - ) -> Result { - let mut errored = false; - let mut item_iter = items.iter(); - - let condition = if is_root { - None - } else { - let cond = item_iter - .next() - .ok_or_else(|| { - parse_error( - tcx, - span, - "empty `on`-clause in `#[rustc_on_unimplemented]`", - "empty on-clause here", - None, - ) - })? - .meta_item() - .ok_or_else(|| { - parse_error( - tcx, - span, - "invalid `on`-clause in `#[rustc_on_unimplemented]`", - "invalid on-clause here", - None, - ) - })?; - attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true); - Some(cond.clone()) - }; - - let mut message = None; - let mut label = None; - let mut note = None; - let mut enclosing_scope = None; - let mut subcommands = vec![]; - - let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some) - }; - - for item in item_iter { - if item.check_name(sym::message) && message.is_none() { - if let Some(message_) = item.value_str() { - message = parse_value(message_)?; - continue; - } - } else if item.check_name(sym::label) && label.is_none() { - if let Some(label_) = item.value_str() { - label = parse_value(label_)?; - continue; - } - } else if item.check_name(sym::note) && note.is_none() { - if let Some(note_) = item.value_str() { - note = parse_value(note_)?; - continue; - } - } else if item.check_name(sym::enclosing_scope) && enclosing_scope.is_none() { - if let Some(enclosing_scope_) = item.value_str() { - enclosing_scope = parse_value(enclosing_scope_)?; - continue; - } - } else if item.check_name(sym::on) - && is_root - && message.is_none() - && label.is_none() - && note.is_none() - { - if let Some(items) = item.meta_item_list() { - if let Ok(subcommand) = - Self::parse(tcx, trait_def_id, &items, item.span(), false) - { - subcommands.push(subcommand); - } else { - errored = true; - } - continue; - } - } - - // nothing found - parse_error( - tcx, - item.span(), - "this attribute must have a valid value", - "expected value here", - Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#), - ); - } - - if errored { - Err(ErrorReported) - } else { - Ok(OnUnimplementedDirective { - condition, - subcommands, - message, - label, - note, - enclosing_scope, - }) - } - } - - pub fn of_item( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - impl_def_id: DefId, - ) -> Result, ErrorReported> { - let attrs = tcx.get_attrs(impl_def_id); - - let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) { - item - } else { - return Ok(None); - }; - - let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some) - } else if let Some(value) = attr.value_str() { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(OnUnimplementedFormatString::try_parse( - tcx, - trait_def_id, - value, - attr.span, - )?), - note: None, - enclosing_scope: None, - })) - } else { - return Err(ErrorReported); - }; - debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result); - result - } - - pub fn evaluate( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - options: &[(Symbol, Option)], - ) -> OnUnimplementedNote { - let mut message = None; - let mut label = None; - let mut note = None; - let mut enclosing_scope = None; - info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); - - for command in self.subcommands.iter().chain(Some(self)).rev() { - if let Some(ref condition) = command.condition { - if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| { - c.ident().map_or(false, |ident| { - options.contains(&(ident.name, c.value_str().map(|s| s.to_string()))) - }) - }) { - debug!("evaluate: skipping {:?} due to condition", command); - continue; - } - } - debug!("evaluate: {:?} succeeded", command); - if let Some(ref message_) = command.message { - message = Some(message_.clone()); - } - - if let Some(ref label_) = command.label { - label = Some(label_.clone()); - } - - if let Some(ref note_) = command.note { - note = Some(note_.clone()); - } - - if let Some(ref enclosing_scope_) = command.enclosing_scope { - enclosing_scope = Some(enclosing_scope_.clone()); - } - } - - let options: FxHashMap = - options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect(); - OnUnimplementedNote { - label: label.map(|l| l.format(tcx, trait_ref, &options)), - message: message.map(|m| m.format(tcx, trait_ref, &options)), - note: note.map(|n| n.format(tcx, trait_ref, &options)), - enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)), - } - } -} - -impl<'tcx> OnUnimplementedFormatString { - fn try_parse( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - from: Symbol, - err_sp: Span, - ) -> Result { - let result = OnUnimplementedFormatString(from); - result.verify(tcx, trait_def_id, err_sp)?; - Ok(result) - } - - fn verify( - &self, - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - span: Span, - ) -> Result<(), ErrorReported> { - let name = tcx.item_name(trait_def_id); - let generics = tcx.generics_of(trait_def_id); - let s = self.0.as_str(); - let parser = Parser::new(&s, None, vec![], false); - let mut result = Ok(()); - for token in parser { - match token { - Piece::String(_) => (), // Normal string, no need to check it - Piece::NextArgument(a) => match a.position { - // `{Self}` is allowed - Position::ArgumentNamed(s) if s == kw::SelfUpper => (), - // `{ThisTraitsName}` is allowed - Position::ArgumentNamed(s) if s == name => (), - // `{from_method}` is allowed - Position::ArgumentNamed(s) if s == sym::from_method => (), - // `{from_desugaring}` is allowed - Position::ArgumentNamed(s) if s == sym::from_desugaring => (), - // `{ItemContext}` is allowed - Position::ArgumentNamed(s) if s == sym::item_context => (), - // So is `{A}` if A is a type parameter - Position::ArgumentNamed(s) => { - match generics.params.iter().find(|param| param.name == s) { - Some(_) => (), - None => { - struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on trait `{}`", - s, - name - ) - .emit(); - result = Err(ErrorReported); - } - } - } - // `{:1}` and `{}` are not to be used - Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => { - struct_span_err!( - tcx.sess, - span, - E0231, - "only named substitution parameters are allowed" - ) - .emit(); - result = Err(ErrorReported); - } - }, - } - } - - result - } - - pub fn format( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - options: &FxHashMap, - ) -> String { - let name = tcx.item_name(trait_ref.def_id); - let trait_str = tcx.def_path_str(trait_ref.def_id); - let generics = tcx.generics_of(trait_ref.def_id); - let generic_map = generics - .params - .iter() - .filter_map(|param| { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize].to_string() - } - GenericParamDefKind::Lifetime => return None, - }; - let name = param.name; - Some((name, value)) - }) - .collect::>(); - let empty_string = String::new(); - - let s = self.0.as_str(); - let parser = Parser::new(&s, None, vec![], false); - let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string); - parser - .map(|p| match p { - Piece::String(s) => s, - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, - None => { - if let Some(val) = options.get(&s) { - val - } else if s == sym::from_desugaring || s == sym::from_method { - // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::item_context { - &item_context - } else { - bug!( - "broken on_unimplemented {:?} for {:?}: \ - no argument matching {:?}", - self.0, - trait_ref, - s - ) - } - } - }, - _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), - }, - }) - .collect() - } -} diff --git a/src/librustc_infer/traits/project.rs b/src/librustc_infer/traits/project.rs index cbe24320502..183e4be1890 100644 --- a/src/librustc_infer/traits/project.rs +++ b/src/librustc_infer/traits/project.rs @@ -1,398 +1,18 @@ //! Code for projecting associated types out of trait references. -use super::elaborate_predicates; -use super::specialization_graph; -use super::translate_substs; -use super::util; -use super::Obligation; -use super::ObligationCause; use super::PredicateObligation; -use super::Selection; -use super::SelectionContext; -use super::SelectionError; -use super::{VtableClosureData, VtableFnPointerData, VtableGeneratorData, VtableImplData}; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; -use rustc_ast::ast::Ident; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{self, Ty}; use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; -use rustc_hir::def_id::DefId; -use rustc_span::symbol::sym; -use rustc_span::DUMMY_SP; pub use rustc::traits::Reveal; -pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; - -pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<'tcx>>; - -pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>>; - -/// When attempting to resolve `::Name` ... -#[derive(Debug)] -pub enum ProjectionTyError<'tcx> { - /// ...we found multiple sources of information and couldn't resolve the ambiguity. - TooManyCandidates, - - /// ...an error occurred matching `T : TraitRef` - TraitSelectionError(SelectionError<'tcx>), -} - #[derive(Clone)] pub struct MismatchedProjectionTypes<'tcx> { pub err: ty::error::TypeError<'tcx>, } -#[derive(PartialEq, Eq, Debug)] -enum ProjectionTyCandidate<'tcx> { - // from a where-clause in the env or object type - ParamEnv(ty::PolyProjectionPredicate<'tcx>), - - // from the definition of `Trait` when you have something like <::B as Trait2>::C - TraitDef(ty::PolyProjectionPredicate<'tcx>), - - // from a "impl" (or a "pseudo-impl" returned by select) - Select(Selection<'tcx>), -} - -enum ProjectionTyCandidateSet<'tcx> { - None, - Single(ProjectionTyCandidate<'tcx>), - Ambiguous, - Error(SelectionError<'tcx>), -} - -impl<'tcx> ProjectionTyCandidateSet<'tcx> { - fn mark_ambiguous(&mut self) { - *self = ProjectionTyCandidateSet::Ambiguous; - } - - fn mark_error(&mut self, err: SelectionError<'tcx>) { - *self = ProjectionTyCandidateSet::Error(err); - } - - // Returns true if the push was successful, or false if the candidate - // was discarded -- this could be because of ambiguity, or because - // a higher-priority candidate is already there. - fn push_candidate(&mut self, candidate: ProjectionTyCandidate<'tcx>) -> bool { - use self::ProjectionTyCandidate::*; - use self::ProjectionTyCandidateSet::*; - - // This wacky variable is just used to try and - // make code readable and avoid confusing paths. - // It is assigned a "value" of `()` only on those - // paths in which we wish to convert `*self` to - // ambiguous (and return false, because the candidate - // was not used). On other paths, it is not assigned, - // and hence if those paths *could* reach the code that - // comes after the match, this fn would not compile. - let convert_to_ambiguous; - - match self { - None => { - *self = Single(candidate); - return true; - } - - Single(current) => { - // Duplicates can happen inside ParamEnv. In the case, we - // perform a lazy deduplication. - if current == &candidate { - return false; - } - - // Prefer where-clauses. As in select, if there are multiple - // candidates, we prefer where-clause candidates over impls. This - // may seem a bit surprising, since impls are the source of - // "truth" in some sense, but in fact some of the impls that SEEM - // applicable are not, because of nested obligations. Where - // clauses are the safer choice. See the comment on - // `select::SelectionCandidate` and #21974 for more details. - match (current, candidate) { - (ParamEnv(..), ParamEnv(..)) => convert_to_ambiguous = (), - (ParamEnv(..), _) => return false, - (_, ParamEnv(..)) => unreachable!(), - (_, _) => convert_to_ambiguous = (), - } - } - - Ambiguous | Error(..) => { - return false; - } - } - - // We only ever get here when we moved from a single candidate - // to ambiguous. - let () = convert_to_ambiguous; - *self = Ambiguous; - false - } -} - -/// Evaluates constraints of the form: -/// -/// for<...> ::U == V -/// -/// If successful, this may result in additional obligations. Also returns -/// the projection cache key used to track these additional obligations. -pub fn poly_project_and_unify_type<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &PolyProjectionObligation<'tcx>, -) -> Result>>, MismatchedProjectionTypes<'tcx>> { - debug!("poly_project_and_unify_type(obligation={:?})", obligation); - - let infcx = selcx.infcx(); - infcx.commit_if_ok(|snapshot| { - let (placeholder_predicate, placeholder_map) = - infcx.replace_bound_vars_with_placeholders(&obligation.predicate); - - let placeholder_obligation = obligation.with(placeholder_predicate); - let result = project_and_unify_type(selcx, &placeholder_obligation)?; - infcx - .leak_check(false, &placeholder_map, snapshot) - .map_err(|err| MismatchedProjectionTypes { err })?; - Ok(result) - }) -} - -/// Evaluates constraints of the form: -/// -/// ::U == V -/// -/// If successful, this may result in additional obligations. -fn project_and_unify_type<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionObligation<'tcx>, -) -> Result>>, MismatchedProjectionTypes<'tcx>> { - debug!("project_and_unify_type(obligation={:?})", obligation); - - let mut obligations = vec![]; - let normalized_ty = match opt_normalize_projection_type( - selcx, - obligation.param_env, - obligation.predicate.projection_ty, - obligation.cause.clone(), - obligation.recursion_depth, - &mut obligations, - ) { - Some(n) => n, - None => return Ok(None), - }; - - debug!( - "project_and_unify_type: normalized_ty={:?} obligations={:?}", - normalized_ty, obligations - ); - - let infcx = selcx.infcx(); - match infcx - .at(&obligation.cause, obligation.param_env) - .eq(normalized_ty, obligation.predicate.ty) - { - Ok(InferOk { obligations: inferred_obligations, value: () }) => { - obligations.extend(inferred_obligations); - Ok(Some(obligations)) - } - Err(err) => { - debug!("project_and_unify_type: equating types encountered error {:?}", err); - Err(MismatchedProjectionTypes { err }) - } - } -} - -/// Normalizes any associated type projections in `value`, replacing -/// them with a fully resolved type where possible. The return value -/// combines the normalized result and any additional obligations that -/// were incurred as result. -pub fn normalize<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - value: &T, -) -> Normalized<'tcx, T> -where - T: TypeFoldable<'tcx>, -{ - let mut obligations = Vec::new(); - let value = normalize_to(selcx, param_env, cause, value, &mut obligations); - Normalized { value, obligations } -} - -pub fn normalize_to<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - value: &T, - obligations: &mut Vec>, -) -> T -where - T: TypeFoldable<'tcx>, -{ - normalize_with_depth_to(selcx, param_env, cause, 0, value, obligations) -} - -/// As `normalize`, but with a custom depth. -pub fn normalize_with_depth<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: &T, -) -> Normalized<'tcx, T> -where - T: TypeFoldable<'tcx>, -{ - let mut obligations = Vec::new(); - let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); - Normalized { value, obligations } -} - -pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: &T, - obligations: &mut Vec>, -) -> T -where - T: TypeFoldable<'tcx>, -{ - debug!("normalize_with_depth(depth={}, value={:?})", depth, value); - let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); - let result = normalizer.fold(value); - debug!( - "normalize_with_depth: depth={} result={:?} with {} obligations", - depth, - result, - normalizer.obligations.len() - ); - debug!("normalize_with_depth: depth={} obligations={:?}", depth, normalizer.obligations); - result -} - -struct AssocTypeNormalizer<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - obligations: &'a mut Vec>, - depth: usize, -} - -impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { - fn new( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &'a mut Vec>, - ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - AssocTypeNormalizer { selcx, param_env, cause, obligations, depth } - } - - fn fold>(&mut self, value: &T) -> T { - let value = self.selcx.infcx().resolve_vars_if_possible(value); - - if !value.has_projections() { value } else { value.fold_with(self) } - } -} - -impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { - fn tcx<'c>(&'c self) -> TyCtxt<'tcx> { - self.selcx.tcx() - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !ty.has_projections() { - return ty; - } - // We don't want to normalize associated types that occur inside of region - // binders, because they may contain bound regions, and we can't cope with that. - // - // Example: - // - // for<'a> fn(>::A) - // - // Instead of normalizing `>::A` here, we'll - // normalize it when we instantiate those bound regions (which - // should occur eventually). - - let ty = ty.super_fold_with(self); - match ty.kind { - ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { - // (*) - // Only normalize `impl Trait` after type-checking, usually in codegen. - match self.param_env.reveal { - Reveal::UserFacing => ty, - - Reveal::All => { - let recursion_limit = *self.tcx().sess.recursion_limit.get(); - if self.depth >= recursion_limit { - let obligation = Obligation::with_depth( - self.cause.clone(), - recursion_limit, - self.param_env, - ty, - ); - self.selcx.infcx().report_overflow_error(&obligation, true); - } - - let generic_ty = self.tcx().type_of(def_id); - let concrete_ty = generic_ty.subst(self.tcx(), substs); - self.depth += 1; - let folded_ty = self.fold_ty(concrete_ty); - self.depth -= 1; - folded_ty - } - } - } - - ty::Projection(ref data) if !data.has_escaping_bound_vars() => { - // (*) - - // (*) This is kind of hacky -- we need to be able to - // handle normalization within binders because - // otherwise we wind up a need to normalize when doing - // trait matching (since you can have a trait - // obligation like `for<'a> T::B : Fn(&'a int)`), but - // we can't normalize with bound regions in scope. So - // far now we just ignore binders but only normalize - // if all bound regions are gone (and then we still - // have to renormalize whenever we instantiate a - // binder). It would be better to normalize in a - // binding-aware fashion. - - let normalized_ty = normalize_projection_type( - self.selcx, - self.param_env, - *data, - self.cause.clone(), - self.depth, - &mut self.obligations, - ); - debug!( - "AssocTypeNormalizer: depth={} normalized {:?} to {:?}, \ - now with {} obligations", - self.depth, - ty, - normalized_ty, - self.obligations.len() - ); - normalized_ty - } - - _ => ty, - } - } - - fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - constant.eval(self.selcx.tcx(), self.param_env) - } -} - #[derive(Clone, TypeFoldable)] pub struct Normalized<'tcx, T> { pub value: T, @@ -407,1107 +27,6 @@ pub fn with(self, value: U) -> Normalized<'tcx, U> { } } -/// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly -/// additional obligations). If ambiguity arises, which implies that -/// there are unresolved type variables in the projection, we will -/// substitute a fresh type variable `$X` and generate a new -/// obligation `::Item == $X` for later. -pub fn normalize_projection_type<'a, 'b, 'tcx>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &mut Vec>, -) -> Ty<'tcx> { - opt_normalize_projection_type( - selcx, - param_env, - projection_ty, - cause.clone(), - depth, - obligations, - ) - .unwrap_or_else(move || { - // if we bottom out in ambiguity, create a type variable - // and a deferred predicate to resolve this when more type - // information is available. - - let tcx = selcx.infcx().tcx; - let def_id = projection_ty.item_def_id; - let ty_var = selcx.infcx().next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, - span: tcx.def_span(def_id), - }); - let projection = ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, ty: ty_var }); - let obligation = - Obligation::with_depth(cause, depth + 1, param_env, projection.to_predicate()); - obligations.push(obligation); - ty_var - }) -} - -/// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly -/// additional obligations). Returns `None` in the case of ambiguity, -/// which indicates that there are unbound type variables. -/// -/// This function used to return `Option>`, which contains a -/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very -/// often immediately appended to another obligations vector. So now this -/// function takes an obligations vector and appends to it directly, which is -/// slightly uglier but avoids the need for an extra short-lived allocation. -fn opt_normalize_projection_type<'a, 'b, 'tcx>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &mut Vec>, -) -> Option> { - let infcx = selcx.infcx(); - - let projection_ty = infcx.resolve_vars_if_possible(&projection_ty); - let cache_key = ProjectionCacheKey { ty: projection_ty }; - - debug!( - "opt_normalize_projection_type(\ - projection_ty={:?}, \ - depth={})", - projection_ty, depth - ); - - // FIXME(#20304) For now, I am caching here, which is good, but it - // means we don't capture the type variables that are created in - // the case of ambiguity. Which means we may create a large stream - // of such variables. OTOH, if we move the caching up a level, we - // would not benefit from caching when proving `T: Trait` - // bounds. It might be the case that we want two distinct caches, - // or else another kind of cache entry. - - let cache_result = infcx.inner.borrow_mut().projection_cache.try_start(cache_key); - match cache_result { - Ok(()) => {} - Err(ProjectionCacheEntry::Ambiguous) => { - // If we found ambiguity the last time, that generally - // means we will continue to do so until some type in the - // key changes (and we know it hasn't, because we just - // fully resolved it). One exception though is closure - // types, which can transition from having a fixed kind to - // no kind with no visible change in the key. - // - // FIXME(#32286) refactor this so that closure type - // changes - debug!( - "opt_normalize_projection_type: \ - found cache entry: ambiguous" - ); - if !projection_ty.has_closure_types() { - return None; - } - } - Err(ProjectionCacheEntry::InProgress) => { - // If while normalized A::B, we are asked to normalize - // A::B, just return A::B itself. This is a conservative - // answer, in the sense that A::B *is* clearly equivalent - // to A::B, though there may be a better value we can - // find. - - // Under lazy normalization, this can arise when - // bootstrapping. That is, imagine an environment with a - // where-clause like `A::B == u32`. Now, if we are asked - // to normalize `A::B`, we will want to check the - // where-clauses in scope. So we will try to unify `A::B` - // with `A::B`, which can trigger a recursive - // normalization. In that case, I think we will want this code: - // - // ``` - // let ty = selcx.tcx().mk_projection(projection_ty.item_def_id, - // projection_ty.substs; - // return Some(NormalizedTy { value: v, obligations: vec![] }); - // ``` - - debug!( - "opt_normalize_projection_type: \ - found cache entry: in-progress" - ); - - // But for now, let's classify this as an overflow: - let recursion_limit = *selcx.tcx().sess.recursion_limit.get(); - let obligation = - Obligation::with_depth(cause, recursion_limit, param_env, projection_ty); - selcx.infcx().report_overflow_error(&obligation, false); - } - Err(ProjectionCacheEntry::NormalizedTy(ty)) => { - // This is the hottest path in this function. - // - // If we find the value in the cache, then return it along - // with the obligations that went along with it. Note - // that, when using a fulfillment context, these - // obligations could in principle be ignored: they have - // already been registered when the cache entry was - // created (and hence the new ones will quickly be - // discarded as duplicated). But when doing trait - // evaluation this is not the case, and dropping the trait - // evaluations can causes ICEs (e.g., #43132). - debug!( - "opt_normalize_projection_type: \ - found normalized ty `{:?}`", - ty - ); - - // Once we have inferred everything we need to know, we - // can ignore the `obligations` from that point on. - if infcx.unresolved_type_vars(&ty.value).is_none() { - infcx.inner.borrow_mut().projection_cache.complete_normalized(cache_key, &ty); - // No need to extend `obligations`. - } else { - obligations.extend(ty.obligations); - } - - obligations.push(get_paranoid_cache_value_obligation( - infcx, - param_env, - projection_ty, - cause, - depth, - )); - return Some(ty.value); - } - Err(ProjectionCacheEntry::Error) => { - debug!( - "opt_normalize_projection_type: \ - found error" - ); - let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); - obligations.extend(result.obligations); - return Some(result.value); - } - } - - let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty); - match project_type(selcx, &obligation) { - Ok(ProjectedTy::Progress(Progress { - ty: projected_ty, - obligations: mut projected_obligations, - })) => { - // if projection succeeded, then what we get out of this - // is also non-normalized (consider: it was derived from - // an impl, where-clause etc) and hence we must - // re-normalize it - - debug!( - "opt_normalize_projection_type: \ - projected_ty={:?} \ - depth={} \ - projected_obligations={:?}", - projected_ty, depth, projected_obligations - ); - - let result = if projected_ty.has_projections() { - let mut normalizer = AssocTypeNormalizer::new( - selcx, - param_env, - cause, - depth + 1, - &mut projected_obligations, - ); - let normalized_ty = normalizer.fold(&projected_ty); - - debug!( - "opt_normalize_projection_type: \ - normalized_ty={:?} depth={}", - normalized_ty, depth - ); - - Normalized { value: normalized_ty, obligations: projected_obligations } - } else { - Normalized { value: projected_ty, obligations: projected_obligations } - }; - - let cache_value = prune_cache_value_obligations(infcx, &result); - infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, cache_value); - obligations.extend(result.obligations); - Some(result.value) - } - Ok(ProjectedTy::NoProgress(projected_ty)) => { - debug!( - "opt_normalize_projection_type: \ - projected_ty={:?} no progress", - projected_ty - ); - let result = Normalized { value: projected_ty, obligations: vec![] }; - infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, result.clone()); - // No need to extend `obligations`. - Some(result.value) - } - Err(ProjectionTyError::TooManyCandidates) => { - debug!( - "opt_normalize_projection_type: \ - too many candidates" - ); - infcx.inner.borrow_mut().projection_cache.ambiguous(cache_key); - None - } - Err(ProjectionTyError::TraitSelectionError(_)) => { - debug!("opt_normalize_projection_type: ERROR"); - // if we got an error processing the `T as Trait` part, - // just return `ty::err` but add the obligation `T : - // Trait`, which when processed will cause the error to be - // reported later - - infcx.inner.borrow_mut().projection_cache.error(cache_key); - let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); - obligations.extend(result.obligations); - Some(result.value) - } - } -} - -/// If there are unresolved type variables, then we need to include -/// any subobligations that bind them, at least until those type -/// variables are fully resolved. -fn prune_cache_value_obligations<'a, 'tcx>( - infcx: &'a InferCtxt<'a, 'tcx>, - result: &NormalizedTy<'tcx>, -) -> NormalizedTy<'tcx> { - if infcx.unresolved_type_vars(&result.value).is_none() { - return NormalizedTy { value: result.value, obligations: vec![] }; - } - - let mut obligations: Vec<_> = result - .obligations - .iter() - .filter(|obligation| match obligation.predicate { - // We found a `T: Foo` predicate, let's check - // if `U` references any unresolved type - // variables. In principle, we only care if this - // projection can help resolve any of the type - // variables found in `result.value` -- but we just - // check for any type variables here, for fear of - // indirect obligations (e.g., we project to `?0`, - // but we have `T: Foo` and `?1: Bar`). - ty::Predicate::Projection(ref data) => infcx.unresolved_type_vars(&data.ty()).is_some(), - - // We are only interested in `T: Foo` predicates, whre - // `U` references one of `unresolved_type_vars`. =) - _ => false, - }) - .cloned() - .collect(); - - obligations.shrink_to_fit(); - - NormalizedTy { value: result.value, obligations } -} - -/// Whenever we give back a cache result for a projection like `::Item ==> X`, we *always* include the obligation to prove -/// that `T: Trait` (we may also include some other obligations). This -/// may or may not be necessary -- in principle, all the obligations -/// that must be proven to show that `T: Trait` were also returned -/// when the cache was first populated. But there are some vague concerns, -/// and so we take the precautionary measure of including `T: Trait` in -/// the result: -/// -/// Concern #1. The current setup is fragile. Perhaps someone could -/// have failed to prove the concerns from when the cache was -/// populated, but also not have used a snapshot, in which case the -/// cache could remain populated even though `T: Trait` has not been -/// shown. In this case, the "other code" is at fault -- when you -/// project something, you are supposed to either have a snapshot or -/// else prove all the resulting obligations -- but it's still easy to -/// get wrong. -/// -/// Concern #2. Even within the snapshot, if those original -/// obligations are not yet proven, then we are able to do projections -/// that may yet turn out to be wrong. This *may* lead to some sort -/// of trouble, though we don't have a concrete example of how that -/// can occur yet. But it seems risky at best. -fn get_paranoid_cache_value_obligation<'a, 'tcx>( - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, -) -> PredicateObligation<'tcx> { - let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); - Obligation { - cause, - recursion_depth: depth, - param_env, - predicate: trait_ref.without_const().to_predicate(), - } -} - -/// If we are projecting `::Item`, but `T: Trait` does not -/// hold. In various error cases, we cannot generate a valid -/// normalized projection. Therefore, we create an inference variable -/// return an associated obligation that, when fulfilled, will lead to -/// an error. -/// -/// Note that we used to return `Error` here, but that was quite -/// dubious -- the premise was that an error would *eventually* be -/// reported, when the obligation was processed. But in general once -/// you see a `Error` you are supposed to be able to assume that an -/// error *has been* reported, so that you can take whatever heuristic -/// paths you want to take. To make things worse, it was possible for -/// cycles to arise, where you basically had a setup like ` -/// as Trait>::Foo == $0`. Here, normalizing ` as -/// Trait>::Foo> to `[type error]` would lead to an obligation of -/// ` as Trait>::Foo`. We are supposed to report -/// an error for this obligation, but we legitimately should not, -/// because it contains `[type error]`. Yuck! (See issue #29857 for -/// one case where this arose.) -fn normalize_to_error<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, -) -> NormalizedTy<'tcx> { - let trait_ref = projection_ty.trait_ref(selcx.tcx()).to_poly_trait_ref(); - let trait_obligation = Obligation { - cause, - recursion_depth: depth, - param_env, - predicate: trait_ref.without_const().to_predicate(), - }; - let tcx = selcx.infcx().tcx; - let def_id = projection_ty.item_def_id; - let new_value = selcx.infcx().next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, - span: tcx.def_span(def_id), - }); - Normalized { value: new_value, obligations: vec![trait_obligation] } -} - -enum ProjectedTy<'tcx> { - Progress(Progress<'tcx>), - NoProgress(Ty<'tcx>), -} - -struct Progress<'tcx> { - ty: Ty<'tcx>, - obligations: Vec>, -} - -impl<'tcx> Progress<'tcx> { - fn error(tcx: TyCtxt<'tcx>) -> Self { - Progress { ty: tcx.types.err, obligations: vec![] } - } - - fn with_addl_obligations(mut self, mut obligations: Vec>) -> Self { - debug!( - "with_addl_obligations: self.obligations.len={} obligations.len={}", - self.obligations.len(), - obligations.len() - ); - - debug!( - "with_addl_obligations: self.obligations={:?} obligations={:?}", - self.obligations, obligations - ); - - self.obligations.append(&mut obligations); - self - } -} - -/// Computes the result of a projection type (if we can). -/// -/// IMPORTANT: -/// - `obligation` must be fully normalized -fn project_type<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, -) -> Result, ProjectionTyError<'tcx>> { - debug!("project(obligation={:?})", obligation); - - let recursion_limit = *selcx.tcx().sess.recursion_limit.get(); - if obligation.recursion_depth >= recursion_limit { - debug!("project: overflow!"); - return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow)); - } - - let obligation_trait_ref = &obligation.predicate.trait_ref(selcx.tcx()); - - debug!("project: obligation_trait_ref={:?}", obligation_trait_ref); - - if obligation_trait_ref.references_error() { - return Ok(ProjectedTy::Progress(Progress::error(selcx.tcx()))); - } - - let mut candidates = ProjectionTyCandidateSet::None; - - // Make sure that the following procedures are kept in order. ParamEnv - // needs to be first because it has highest priority, and Select checks - // the return value of push_candidate which assumes it's ran at last. - assemble_candidates_from_param_env(selcx, obligation, &obligation_trait_ref, &mut candidates); - - assemble_candidates_from_trait_def(selcx, obligation, &obligation_trait_ref, &mut candidates); - - assemble_candidates_from_impls(selcx, obligation, &obligation_trait_ref, &mut candidates); - - match candidates { - ProjectionTyCandidateSet::Single(candidate) => Ok(ProjectedTy::Progress( - confirm_candidate(selcx, obligation, &obligation_trait_ref, candidate), - )), - ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress( - selcx - .tcx() - .mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs), - )), - // Error occurred while trying to processing impls. - ProjectionTyCandidateSet::Error(e) => Err(ProjectionTyError::TraitSelectionError(e)), - // Inherent ambiguity that prevents us from even enumerating the - // candidates. - ProjectionTyCandidateSet::Ambiguous => Err(ProjectionTyError::TooManyCandidates), - } -} - -/// The first thing we have to do is scan through the parameter -/// environment to see whether there are any projection predicates -/// there that can answer this question. -fn assemble_candidates_from_param_env<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, -) { - debug!("assemble_candidates_from_param_env(..)"); - assemble_candidates_from_predicates( - selcx, - obligation, - obligation_trait_ref, - candidate_set, - ProjectionTyCandidate::ParamEnv, - obligation.param_env.caller_bounds.iter().cloned(), - ); -} - -/// In the case of a nested projection like <::FooT as Bar>::BarT, we may find -/// that the definition of `Foo` has some clues: -/// -/// ``` -/// trait Foo { -/// type FooT : Bar -/// } -/// ``` -/// -/// Here, for example, we could conclude that the result is `i32`. -fn assemble_candidates_from_trait_def<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, -) { - debug!("assemble_candidates_from_trait_def(..)"); - - let tcx = selcx.tcx(); - // Check whether the self-type is itself a projection. - let (def_id, substs) = match obligation_trait_ref.self_ty().kind { - ty::Projection(ref data) => (data.trait_ref(tcx).def_id, data.substs), - ty::Opaque(def_id, substs) => (def_id, substs), - ty::Infer(ty::TyVar(_)) => { - // If the self-type is an inference variable, then it MAY wind up - // being a projected type, so induce an ambiguity. - candidate_set.mark_ambiguous(); - return; - } - _ => return, - }; - - // If so, extract what we know from the trait and try to come up with a good answer. - let trait_predicates = tcx.predicates_of(def_id); - let bounds = trait_predicates.instantiate(tcx, substs); - let bounds = elaborate_predicates(tcx, bounds.predicates); - assemble_candidates_from_predicates( - selcx, - obligation, - obligation_trait_ref, - candidate_set, - ProjectionTyCandidate::TraitDef, - bounds, - ) -} - -fn assemble_candidates_from_predicates<'cx, 'tcx, I>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, - ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>, - env_predicates: I, -) where - I: IntoIterator>, -{ - debug!("assemble_candidates_from_predicates(obligation={:?})", obligation); - let infcx = selcx.infcx(); - for predicate in env_predicates { - debug!("assemble_candidates_from_predicates: predicate={:?}", predicate); - if let ty::Predicate::Projection(data) = predicate { - let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id; - - let is_match = same_def_id - && infcx.probe(|_| { - let data_poly_trait_ref = data.to_poly_trait_ref(infcx.tcx); - let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); - infcx - .at(&obligation.cause, obligation.param_env) - .sup(obligation_poly_trait_ref, data_poly_trait_ref) - .map(|InferOk { obligations: _, value: () }| { - // FIXME(#32730) -- do we need to take obligations - // into account in any way? At the moment, no. - }) - .is_ok() - }); - - debug!( - "assemble_candidates_from_predicates: candidate={:?} \ - is_match={} same_def_id={}", - data, is_match, same_def_id - ); - - if is_match { - candidate_set.push_candidate(ctor(data)); - } - } - } -} - -fn assemble_candidates_from_impls<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, -) { - // If we are resolving `>::Item == Type`, - // start out by selecting the predicate `T as TraitRef<...>`: - let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); - let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); - let _ = selcx.infcx().commit_if_ok(|_| { - let vtable = match selcx.select(&trait_obligation) { - Ok(Some(vtable)) => vtable, - Ok(None) => { - candidate_set.mark_ambiguous(); - return Err(()); - } - Err(e) => { - debug!("assemble_candidates_from_impls: selection error {:?}", e); - candidate_set.mark_error(e); - return Err(()); - } - }; - - let eligible = match &vtable { - super::VtableClosure(_) - | super::VtableGenerator(_) - | super::VtableFnPointer(_) - | super::VtableObject(_) - | super::VtableTraitAlias(_) => { - debug!("assemble_candidates_from_impls: vtable={:?}", vtable); - true - } - super::VtableImpl(impl_data) => { - // We have to be careful when projecting out of an - // impl because of specialization. If we are not in - // codegen (i.e., projection mode is not "any"), and the - // impl's type is declared as default, then we disable - // projection (even if the trait ref is fully - // monomorphic). In the case where trait ref is not - // fully monomorphic (i.e., includes type parameters), - // this is because those type parameters may - // ultimately be bound to types from other crates that - // may have specialized impls we can't see. In the - // case where the trait ref IS fully monomorphic, this - // is a policy decision that we made in the RFC in - // order to preserve flexibility for the crate that - // defined the specializable impl to specialize later - // for existing types. - // - // In either case, we handle this by not adding a - // candidate for an impl if it contains a `default` - // type. - // - // NOTE: This should be kept in sync with the similar code in - // `rustc::ty::instance::resolve_associated_item()`. - let node_item = - assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id); - - let is_default = if node_item.node.is_from_trait() { - // If true, the impl inherited a `type Foo = Bar` - // given in the trait, which is implicitly default. - // Otherwise, the impl did not specify `type` and - // neither did the trait: - // - // ```rust - // trait Foo { type T; } - // impl Foo for Bar { } - // ``` - // - // This is an error, but it will be - // reported in `check_impl_items_against_trait`. - // We accept it here but will flag it as - // an error when we confirm the candidate - // (which will ultimately lead to `normalize_to_error` - // being invoked). - false - } else { - // If we're looking at a trait *impl*, the item is - // specializable if the impl or the item are marked - // `default`. - node_item.item.defaultness.is_default() - || super::util::impl_is_default(selcx.tcx(), node_item.node.def_id()) - }; - - match is_default { - // Non-specializable items are always projectable - false => true, - - // Only reveal a specializable default if we're past type-checking - // and the obligation is monomorphic, otherwise passes such as - // transmute checking and polymorphic MIR optimizations could - // get a result which isn't correct for all monomorphizations. - true if obligation.param_env.reveal == Reveal::All => { - // NOTE(eddyb) inference variables can resolve to parameters, so - // assume `poly_trait_ref` isn't monomorphic, if it contains any. - let poly_trait_ref = - selcx.infcx().resolve_vars_if_possible(&poly_trait_ref); - !poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst() - } - - true => { - debug!( - "assemble_candidates_from_impls: not eligible due to default: \ - assoc_ty={} predicate={}", - selcx.tcx().def_path_str(node_item.item.def_id), - obligation.predicate, - ); - false - } - } - } - super::VtableParam(..) => { - // This case tell us nothing about the value of an - // associated type. Consider: - // - // ``` - // trait SomeTrait { type Foo; } - // fn foo(...) { } - // ``` - // - // If the user writes `::Foo`, then the `T - // : SomeTrait` binding does not help us decide what the - // type `Foo` is (at least, not more specifically than - // what we already knew). - // - // But wait, you say! What about an example like this: - // - // ``` - // fn bar>(...) { ... } - // ``` - // - // Doesn't the `T : Sometrait` predicate help - // resolve `T::Foo`? And of course it does, but in fact - // that single predicate is desugared into two predicates - // in the compiler: a trait predicate (`T : SomeTrait`) and a - // projection. And the projection where clause is handled - // in `assemble_candidates_from_param_env`. - false - } - super::VtableAutoImpl(..) | super::VtableBuiltin(..) => { - // These traits have no associated types. - span_bug!( - obligation.cause.span, - "Cannot project an associated type from `{:?}`", - vtable - ); - } - }; - - if eligible { - if candidate_set.push_candidate(ProjectionTyCandidate::Select(vtable)) { - Ok(()) - } else { - Err(()) - } - } else { - Err(()) - } - }); -} - -fn confirm_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, - candidate: ProjectionTyCandidate<'tcx>, -) -> Progress<'tcx> { - debug!("confirm_candidate(candidate={:?}, obligation={:?})", candidate, obligation); - - match candidate { - ProjectionTyCandidate::ParamEnv(poly_projection) - | ProjectionTyCandidate::TraitDef(poly_projection) => { - confirm_param_env_candidate(selcx, obligation, poly_projection) - } - - ProjectionTyCandidate::Select(vtable) => { - confirm_select_candidate(selcx, obligation, obligation_trait_ref, vtable) - } - } -} - -fn confirm_select_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, - vtable: Selection<'tcx>, -) -> Progress<'tcx> { - match vtable { - super::VtableImpl(data) => confirm_impl_candidate(selcx, obligation, data), - super::VtableGenerator(data) => confirm_generator_candidate(selcx, obligation, data), - super::VtableClosure(data) => confirm_closure_candidate(selcx, obligation, data), - super::VtableFnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data), - super::VtableObject(_) => confirm_object_candidate(selcx, obligation, obligation_trait_ref), - super::VtableAutoImpl(..) - | super::VtableParam(..) - | super::VtableBuiltin(..) - | super::VtableTraitAlias(..) => - // we don't create Select candidates with this kind of resolution - { - span_bug!( - obligation.cause.span, - "Cannot project an associated type from `{:?}`", - vtable - ) - } - } -} - -fn confirm_object_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, -) -> Progress<'tcx> { - let self_ty = obligation_trait_ref.self_ty(); - let object_ty = selcx.infcx().shallow_resolve(self_ty); - debug!("confirm_object_candidate(object_ty={:?})", object_ty); - let data = match object_ty.kind { - ty::Dynamic(ref data, ..) => data, - _ => span_bug!( - obligation.cause.span, - "confirm_object_candidate called with non-object: {:?}", - object_ty - ), - }; - let env_predicates = data - .projection_bounds() - .map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate()) - .collect(); - let env_predicate = { - let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates); - - // select only those projections that are actually projecting an - // item with the correct name - let env_predicates = env_predicates.filter_map(|p| match p { - ty::Predicate::Projection(data) => { - if data.projection_def_id() == obligation.predicate.item_def_id { - Some(data) - } else { - None - } - } - _ => None, - }); - - // select those with a relevant trait-ref - let mut env_predicates = env_predicates.filter(|data| { - let data_poly_trait_ref = data.to_poly_trait_ref(selcx.tcx()); - let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); - selcx.infcx().probe(|_| { - selcx - .infcx() - .at(&obligation.cause, obligation.param_env) - .sup(obligation_poly_trait_ref, data_poly_trait_ref) - .is_ok() - }) - }); - - // select the first matching one; there really ought to be one or - // else the object type is not WF, since an object type should - // include all of its projections explicitly - match env_predicates.next() { - Some(env_predicate) => env_predicate, - None => { - debug!( - "confirm_object_candidate: no env-predicate \ - found in object type `{:?}`; ill-formed", - object_ty - ); - return Progress::error(selcx.tcx()); - } - } - }; - - confirm_param_env_candidate(selcx, obligation, env_predicate) -} - -fn confirm_generator_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>, -) -> Progress<'tcx> { - let gen_sig = vtable.substs.as_generator().poly_sig(vtable.generator_def_id, selcx.tcx()); - let Normalized { value: gen_sig, obligations } = normalize_with_depth( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &gen_sig, - ); - - debug!( - "confirm_generator_candidate: obligation={:?},gen_sig={:?},obligations={:?}", - obligation, gen_sig, obligations - ); - - let tcx = selcx.tcx(); - - let gen_def_id = tcx.lang_items().gen_trait().unwrap(); - - let predicate = super::util::generator_trait_ref_and_outputs( - tcx, - gen_def_id, - obligation.predicate.self_ty(), - gen_sig, - ) - .map_bound(|(trait_ref, yield_ty, return_ty)| { - let name = tcx.associated_item(obligation.predicate.item_def_id).ident.name; - let ty = if name == sym::Return { - return_ty - } else if name == sym::Yield { - yield_ty - } else { - bug!() - }; - - ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { - substs: trait_ref.substs, - item_def_id: obligation.predicate.item_def_id, - }, - ty: ty, - } - }); - - confirm_param_env_candidate(selcx, obligation, predicate) - .with_addl_obligations(vtable.nested) - .with_addl_obligations(obligations) -} - -fn confirm_fn_pointer_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - fn_pointer_vtable: VtableFnPointerData<'tcx, PredicateObligation<'tcx>>, -) -> Progress<'tcx> { - let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty); - let sig = fn_type.fn_sig(selcx.tcx()); - let Normalized { value: sig, obligations } = normalize_with_depth( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &sig, - ); - - confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) - .with_addl_obligations(fn_pointer_vtable.nested) - .with_addl_obligations(obligations) -} - -fn confirm_closure_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>, -) -> Progress<'tcx> { - let tcx = selcx.tcx(); - let infcx = selcx.infcx(); - let closure_sig_ty = vtable.substs.as_closure().sig_ty(vtable.closure_def_id, tcx); - let closure_sig = infcx.shallow_resolve(closure_sig_ty).fn_sig(tcx); - let Normalized { value: closure_sig, obligations } = normalize_with_depth( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &closure_sig, - ); - - debug!( - "confirm_closure_candidate: obligation={:?},closure_sig={:?},obligations={:?}", - obligation, closure_sig, obligations - ); - - confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) - .with_addl_obligations(vtable.nested) - .with_addl_obligations(obligations) -} - -fn confirm_callable_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - fn_sig: ty::PolyFnSig<'tcx>, - flag: util::TupleArgumentsFlag, -) -> Progress<'tcx> { - let tcx = selcx.tcx(); - - debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig); - - // the `Output` associated type is declared on `FnOnce` - let fn_once_def_id = tcx.lang_items().fn_once_trait().unwrap(); - - let predicate = super::util::closure_trait_ref_and_return_type( - tcx, - fn_once_def_id, - obligation.predicate.self_ty(), - fn_sig, - flag, - ) - .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Ident::with_dummy_span(rustc_hir::FN_OUTPUT_NAME), - ), - ty: ret_type, - }); - - confirm_param_env_candidate(selcx, obligation, predicate) -} - -fn confirm_param_env_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, -) -> Progress<'tcx> { - let infcx = selcx.infcx(); - let cause = &obligation.cause; - let param_env = obligation.param_env; - - let (cache_entry, _) = infcx.replace_bound_vars_with_fresh_vars( - cause.span, - LateBoundRegionConversionTime::HigherRankedType, - &poly_cache_entry, - ); - - let cache_trait_ref = cache_entry.projection_ty.trait_ref(infcx.tcx); - let obligation_trait_ref = obligation.predicate.trait_ref(infcx.tcx); - match infcx.at(cause, param_env).eq(cache_trait_ref, obligation_trait_ref) { - Ok(InferOk { value: _, obligations }) => Progress { ty: cache_entry.ty, obligations }, - Err(e) => { - let msg = format!( - "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}", - obligation, poly_cache_entry, e, - ); - debug!("confirm_param_env_candidate: {}", msg); - infcx.tcx.sess.delay_span_bug(obligation.cause.span, &msg); - Progress { ty: infcx.tcx.types.err, obligations: vec![] } - } - } -} - -fn confirm_impl_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>, -) -> Progress<'tcx> { - let tcx = selcx.tcx(); - - let VtableImplData { impl_def_id, substs, nested } = impl_vtable; - let assoc_item_id = obligation.predicate.item_def_id; - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - - let param_env = obligation.param_env; - let assoc_ty = assoc_ty_def(selcx, impl_def_id, assoc_item_id); - - if !assoc_ty.item.defaultness.has_value() { - // This means that the impl is missing a definition for the - // associated type. This error will be reported by the type - // checker method `check_impl_items_against_trait`, so here we - // just return Error. - debug!( - "confirm_impl_candidate: no associated type {:?} for {:?}", - assoc_ty.item.ident, obligation.predicate - ); - return Progress { ty: tcx.types.err, obligations: nested }; - } - let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs); - let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node); - let ty = if let ty::AssocKind::OpaqueTy = assoc_ty.item.kind { - let item_substs = InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id); - tcx.mk_opaque(assoc_ty.item.def_id, item_substs) - } else { - tcx.type_of(assoc_ty.item.def_id) - }; - if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { - tcx.sess - .delay_span_bug(DUMMY_SP, "impl item and trait item have different parameter counts"); - Progress { ty: tcx.types.err, obligations: nested } - } else { - Progress { ty: ty.subst(tcx, substs), obligations: nested } - } -} - -/// Locate the definition of an associated type in the specialization hierarchy, -/// starting from the given impl. -/// -/// Based on the "projection mode", this lookup may in fact only examine the -/// topmost impl. See the comments for `Reveal` for more details. -fn assoc_ty_def( - selcx: &SelectionContext<'_, '_>, - impl_def_id: DefId, - assoc_ty_def_id: DefId, -) -> specialization_graph::NodeItem { - let tcx = selcx.tcx(); - let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident; - let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; - let trait_def = tcx.trait_def(trait_def_id); - - // This function may be called while we are still building the - // specialization graph that is queried below (via TraidDef::ancestors()), - // so, in order to avoid unnecessary infinite recursion, we manually look - // for the associated item at the given impl. - // If there is no such item in that impl, this function will fail with a - // cycle error if the specialization graph is currently being built. - let impl_node = specialization_graph::Node::Impl(impl_def_id); - for item in impl_node.items(tcx) { - if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy) - && tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id) - { - return specialization_graph::NodeItem { - node: specialization_graph::Node::Impl(impl_def_id), - item: *item, - }; - } - } - - if let Some(assoc_item) = - trait_def.ancestors(tcx, impl_def_id).leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) - { - assoc_item - } else { - // This is saying that neither the trait nor - // the impl contain a definition for this - // associated type. Normally this situation - // could only arise through a compiler bug -- - // if the user wrote a bad item name, it - // should have failed in astconv. - bug!("No associated type `{}` for {}", assoc_ty_name, tcx.def_path_str(impl_def_id)) - } -} - // # Cache /// The projection cache. Unlike the standard caches, this can include @@ -1549,26 +68,14 @@ pub struct ProjectionCacheKey<'tcx> { ty: ty::ProjectionTy<'tcx>, } -impl<'cx, 'tcx> ProjectionCacheKey<'tcx> { - pub fn from_poly_projection_predicate( - selcx: &mut SelectionContext<'cx, 'tcx>, - predicate: &ty::PolyProjectionPredicate<'tcx>, - ) -> Option { - let infcx = selcx.infcx(); - // We don't do cross-snapshot caching of obligations with escaping regions, - // so there's no cache key to use - predicate.no_bound_vars().map(|predicate| ProjectionCacheKey { - // We don't attempt to match up with a specific type-variable state - // from a specific call to `opt_normalize_projection_type` - if - // there's no precise match, the original cache entry is "stranded" - // anyway. - ty: infcx.resolve_vars_if_possible(&predicate.projection_ty), - }) +impl ProjectionCacheKey<'tcx> { + pub fn new(ty: ty::ProjectionTy<'tcx>) -> Self { + Self { ty } } } #[derive(Clone, Debug)] -enum ProjectionCacheEntry<'tcx> { +pub enum ProjectionCacheEntry<'tcx> { InProgress, Ambiguous, Error, @@ -1604,7 +111,7 @@ pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { /// Try to start normalize `key`; returns an error if /// normalization already occurred (this error corresponds to a /// cache hit, so it's actually a good thing). - fn try_start( + pub fn try_start( &mut self, key: ProjectionCacheKey<'tcx>, ) -> Result<(), ProjectionCacheEntry<'tcx>> { @@ -1617,7 +124,7 @@ fn try_start( } /// Indicates that `key` was normalized to `value`. - fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) { + pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) { debug!( "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", key, value @@ -1670,14 +177,14 @@ pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &Normal /// ambiguity. No point in trying it again then until we gain more /// type information (in which case, the "fully resolved" key will /// be different). - fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { + pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous); assert!(!fresh, "never started projecting `{:?}`", key); } /// Indicates that trying to normalize `key` resulted in /// error. - fn error(&mut self, key: ProjectionCacheKey<'tcx>) { + pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) { let fresh = self.map.insert(key, ProjectionCacheEntry::Error); assert!(!fresh, "never started projecting `{:?}`", key); } diff --git a/src/librustc_infer/traits/query/dropck_outlives.rs b/src/librustc_infer/traits/query/dropck_outlives.rs deleted file mode 100644 index a1d7a2836e4..00000000000 --- a/src/librustc_infer/traits/query/dropck_outlives.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::infer::at::At; -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferOk; - -use rustc::ty::subst::GenericArg; -use rustc::ty::{self, Ty, TyCtxt}; - -pub use rustc::traits::query::{DropckOutlivesResult, DtorckConstraint}; - -impl<'cx, 'tcx> At<'cx, 'tcx> { - /// Given a type `ty` of some value being dropped, computes a set - /// of "kinds" (types, regions) that must be outlive the execution - /// of the destructor. These basically correspond to data that the - /// destructor might access. This is used during regionck to - /// impose "outlives" constraints on any lifetimes referenced - /// within. - /// - /// The rules here are given by the "dropck" RFCs, notably [#1238] - /// and [#1327]. This is a fixed-point computation, where we - /// explore all the data that will be dropped (transitively) when - /// a value of type `ty` is dropped. For each type T that will be - /// dropped and which has a destructor, we must assume that all - /// the types/regions of T are live during the destructor, unless - /// they are marked with a special attribute (`#[may_dangle]`). - /// - /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md - /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md - pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { - debug!("dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env,); - - // Quick check: there are a number of cases that we know do not require - // any destructor. - let tcx = self.infcx.tcx; - if trivial_dropck_outlives(tcx, ty) { - return InferOk { value: vec![], obligations: vec![] }; - } - - let mut orig_values = OriginalQueryValues::default(); - let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values); - let span = self.cause.span; - debug!("c_ty = {:?}", c_ty); - if let Ok(result) = &tcx.dropck_outlives(c_ty) { - if result.is_proven() { - if let Ok(InferOk { value, obligations }) = - self.infcx.instantiate_query_response_and_region_obligations( - self.cause, - self.param_env, - &orig_values, - result, - ) - { - let ty = self.infcx.resolve_vars_if_possible(&ty); - let kinds = value.into_kinds_reporting_overflows(tcx, span, ty); - return InferOk { value: kinds, obligations }; - } - } - } - - // Errors and ambiuity in dropck occur in two cases: - // - unresolved inference variables at the end of typeck - // - non well-formed types where projections cannot be resolved - // Either of these should have created an error before. - tcx.sess.delay_span_bug(span, "dtorck encountered internal error"); - - InferOk { value: vec![], obligations: vec![] } - } -} - -/// This returns true if the type `ty` is "trivial" for -/// dropck-outlives -- that is, if it doesn't require any types to -/// outlive. This is similar but not *quite* the same as the -/// `needs_drop` test in the compiler already -- that is, for every -/// type T for which this function return true, needs-drop would -/// return `false`. But the reverse does not hold: in particular, -/// `needs_drop` returns false for `PhantomData`, but it is not -/// trivial for dropck-outlives. -/// -/// Note also that `needs_drop` requires a "global" type (i.e., one -/// with erased regions), but this function does not. -pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { - // None of these types have a destructor and hence they do not - // require anything in particular to outlive the dtor's - // execution. - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str - | ty::Foreign(..) - | ty::Error => true, - - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), - - // (T1..Tn) and closures have same properties as T1..Tn -- - // check if *any* of those are trivial. - ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), - ty::Closure(def_id, ref substs) => { - substs.as_closure().upvar_tys(def_id, tcx).all(|t| trivial_dropck_outlives(tcx, t)) - } - - ty::Adt(def, _) => { - if Some(def.did) == tcx.lang_items().manually_drop() { - // `ManuallyDrop` never has a dtor. - true - } else { - // Other types might. Moreover, PhantomData doesn't - // have a dtor, but it is considered to own its - // content, so it is non-trivial. Unions can have `impl Drop`, - // and hence are non-trivial as well. - false - } - } - - // The following *might* require a destructor: needs deeper inspection. - ty::Dynamic(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Generator(..) => false, - - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - } -} diff --git a/src/librustc_infer/traits/query/evaluate_obligation.rs b/src/librustc_infer/traits/query/evaluate_obligation.rs deleted file mode 100644 index b9ce3ccff27..00000000000 --- a/src/librustc_infer/traits/query/evaluate_obligation.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferCtxt; -use crate::traits::{ - EvaluationResult, OverflowError, PredicateObligation, SelectionContext, TraitQueryMode, -}; - -impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { - /// Evaluates whether the predicate can be satisfied (by any means) - /// in the given `ParamEnv`. - pub fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool { - self.evaluate_obligation_no_overflow(obligation).may_apply() - } - - /// Evaluates whether the predicate can be satisfied in the given - /// `ParamEnv`, and returns `false` if not certain. However, this is - /// not entirely accurate if inference variables are involved. - /// - /// This version may conservatively fail when outlives obligations - /// are required. - pub fn predicate_must_hold_considering_regions( - &self, - obligation: &PredicateObligation<'tcx>, - ) -> bool { - self.evaluate_obligation_no_overflow(obligation).must_apply_considering_regions() - } - - /// Evaluates whether the predicate can be satisfied in the given - /// `ParamEnv`, and returns `false` if not certain. However, this is - /// not entirely accurate if inference variables are involved. - /// - /// This version ignores all outlives constraints. - pub fn predicate_must_hold_modulo_regions( - &self, - obligation: &PredicateObligation<'tcx>, - ) -> bool { - self.evaluate_obligation_no_overflow(obligation).must_apply_modulo_regions() - } - - /// Evaluate a given predicate, capturing overflow and propagating it back. - pub fn evaluate_obligation( - &self, - obligation: &PredicateObligation<'tcx>, - ) -> Result { - let mut _orig_values = OriginalQueryValues::default(); - let c_pred = self - .canonicalize_query(&obligation.param_env.and(obligation.predicate), &mut _orig_values); - // Run canonical query. If overflow occurs, rerun from scratch but this time - // in standard trait query mode so that overflow is handled appropriately - // within `SelectionContext`. - self.tcx.evaluate_obligation(c_pred) - } - - // Helper function that canonicalizes and runs the query. If an - // overflow results, we re-run it in the local context so we can - // report a nice error. - crate fn evaluate_obligation_no_overflow( - &self, - obligation: &PredicateObligation<'tcx>, - ) -> EvaluationResult { - match self.evaluate_obligation(obligation) { - Ok(result) => result, - Err(OverflowError) => { - let mut selcx = SelectionContext::with_query_mode(&self, TraitQueryMode::Standard); - selcx.evaluate_root_obligation(obligation).unwrap_or_else(|r| { - span_bug!( - obligation.cause.span, - "Overflow should be caught earlier in standard query mode: {:?}, {:?}", - obligation, - r, - ) - }) - } - } - } -} diff --git a/src/librustc_infer/traits/query/method_autoderef.rs b/src/librustc_infer/traits/query/method_autoderef.rs deleted file mode 100644 index 80748c5ef38..00000000000 --- a/src/librustc_infer/traits/query/method_autoderef.rs +++ /dev/null @@ -1 +0,0 @@ -pub use rustc::traits::query::{CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult}; diff --git a/src/librustc_infer/traits/query/mod.rs b/src/librustc_infer/traits/query/mod.rs deleted file mode 100644 index 77b5ec669a0..00000000000 --- a/src/librustc_infer/traits/query/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Experimental types for the trait query interface. The methods -//! defined in this module are all based on **canonicalization**, -//! which makes a canonical query by replacing unbound inference -//! variables and regions, so that results can be reused more broadly. -//! The providers for the queries defined here can be found in -//! `librustc_traits`. - -pub mod dropck_outlives; -pub mod evaluate_obligation; -pub mod method_autoderef; -pub mod normalize; -pub mod outlives_bounds; -pub mod type_op; - -pub use rustc::traits::query::*; diff --git a/src/librustc_infer/traits/query/normalize.rs b/src/librustc_infer/traits/query/normalize.rs deleted file mode 100644 index 4577e3d2e1c..00000000000 --- a/src/librustc_infer/traits/query/normalize.rs +++ /dev/null @@ -1,189 +0,0 @@ -//! Code for the 'normalization' query. This consists of a wrapper -//! which folds deeply, invoking the underlying -//! `normalize_projection_ty` query when it encounters projections. - -use crate::infer::at::At; -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::{InferCtxt, InferOk}; -use crate::traits::project::Normalized; -use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; -use rustc::ty::fold::{TypeFoldable, TypeFolder}; -use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt}; - -use super::NoSolution; - -pub use rustc::traits::query::NormalizationResult; - -impl<'cx, 'tcx> At<'cx, 'tcx> { - /// Normalize `value` in the context of the inference context, - /// yielding a resulting type, or an error if `value` cannot be - /// normalized. If you don't care about regions, you should prefer - /// `normalize_erasing_regions`, which is more efficient. - /// - /// If the normalization succeeds and is unambiguous, returns back - /// the normalized value along with various outlives relations (in - /// the form of obligations that must be discharged). - /// - /// N.B., this will *eventually* be the main means of - /// normalizing, but for now should be used only when we actually - /// know that normalization will succeed, since error reporting - /// and other details are still "under development". - pub fn normalize(&self, value: &T) -> Result, NoSolution> - where - T: TypeFoldable<'tcx>, - { - debug!( - "normalize::<{}>(value={:?}, param_env={:?})", - ::std::any::type_name::(), - value, - self.param_env, - ); - if !value.has_projections() { - return Ok(Normalized { value: value.clone(), obligations: vec![] }); - } - - let mut normalizer = QueryNormalizer { - infcx: self.infcx, - cause: self.cause, - param_env: self.param_env, - obligations: vec![], - error: false, - anon_depth: 0, - }; - - let value1 = value.fold_with(&mut normalizer); - if normalizer.error { - Err(NoSolution) - } else { - Ok(Normalized { value: value1, obligations: normalizer.obligations }) - } - } -} - -struct QueryNormalizer<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, - cause: &'cx ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - obligations: Vec>, - error: bool, - anon_depth: usize, -} - -impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { - fn tcx<'c>(&'c self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !ty.has_projections() { - return ty; - } - - let ty = ty.super_fold_with(self); - match ty.kind { - ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { - // (*) - // Only normalize `impl Trait` after type-checking, usually in codegen. - match self.param_env.reveal { - Reveal::UserFacing => ty, - - Reveal::All => { - let recursion_limit = *self.tcx().sess.recursion_limit.get(); - if self.anon_depth >= recursion_limit { - let obligation = Obligation::with_depth( - self.cause.clone(), - recursion_limit, - self.param_env, - ty, - ); - self.infcx.report_overflow_error(&obligation, true); - } - - let generic_ty = self.tcx().type_of(def_id); - let concrete_ty = generic_ty.subst(self.tcx(), substs); - self.anon_depth += 1; - if concrete_ty == ty { - bug!( - "infinite recursion generic_ty: {:#?}, substs: {:#?}, \ - concrete_ty: {:#?}, ty: {:#?}", - generic_ty, - substs, - concrete_ty, - ty - ); - } - let folded_ty = self.fold_ty(concrete_ty); - self.anon_depth -= 1; - folded_ty - } - } - } - - ty::Projection(ref data) if !data.has_escaping_bound_vars() => { - // (*) - // (*) This is kind of hacky -- we need to be able to - // handle normalization within binders because - // otherwise we wind up a need to normalize when doing - // trait matching (since you can have a trait - // obligation like `for<'a> T::B : Fn(&'a int)`), but - // we can't normalize with bound regions in scope. So - // far now we just ignore binders but only normalize - // if all bound regions are gone (and then we still - // have to renormalize whenever we instantiate a - // binder). It would be better to normalize in a - // binding-aware fashion. - - let tcx = self.infcx.tcx; - - let mut orig_values = OriginalQueryValues::default(); - // HACK(matthewjasper) `'static` is special-cased in selection, - // so we cannot canonicalize it. - let c_data = self - .infcx - .canonicalize_hr_query_hack(&self.param_env.and(*data), &mut orig_values); - debug!("QueryNormalizer: c_data = {:#?}", c_data); - debug!("QueryNormalizer: orig_values = {:#?}", orig_values); - match tcx.normalize_projection_ty(c_data) { - Ok(result) => { - // We don't expect ambiguity. - if result.is_ambiguous() { - self.error = true; - return ty; - } - - match self.infcx.instantiate_query_response_and_region_obligations( - self.cause, - self.param_env, - &orig_values, - &result, - ) { - Ok(InferOk { value: result, obligations }) => { - debug!("QueryNormalizer: result = {:#?}", result); - debug!("QueryNormalizer: obligations = {:#?}", obligations); - self.obligations.extend(obligations); - return result.normalized_ty; - } - - Err(_) => { - self.error = true; - return ty; - } - } - } - - Err(NoSolution) => { - self.error = true; - ty - } - } - } - - _ => ty, - } - } - - fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - constant.eval(self.infcx.tcx, self.param_env) - } -} diff --git a/src/librustc_infer/traits/query/outlives_bounds.rs b/src/librustc_infer/traits/query/outlives_bounds.rs deleted file mode 100644 index eb32ebf5c4d..00000000000 --- a/src/librustc_infer/traits/query/outlives_bounds.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferCtxt; -use crate::traits::query::NoSolution; -use crate::traits::{FulfillmentContext, ObligationCause, TraitEngine, TraitEngineExt}; -use rustc::ty::{self, Ty}; -use rustc_hir as hir; -use rustc_span::source_map::Span; - -pub use rustc::traits::query::OutlivesBound; - -impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { - /// Implied bounds are region relationships that we deduce - /// automatically. The idea is that (e.g.) a caller must check that a - /// function's argument types are well-formed immediately before - /// calling that fn, and hence the *callee* can assume that its - /// argument types are well-formed. This may imply certain relationships - /// between generic parameters. For example: - /// - /// fn foo<'a,T>(x: &'a T) - /// - /// can only be called with a `'a` and `T` such that `&'a T` is WF. - /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. - /// - /// # Parameters - /// - /// - `param_env`, the where-clauses in scope - /// - `body_id`, the body-id to use when normalizing assoc types. - /// Note that this may cause outlives obligations to be injected - /// into the inference context with this body-id. - /// - `ty`, the type that we are supposed to assume is WF. - /// - `span`, a span to use when normalizing, hopefully not important, - /// might be useful if a `bug!` occurs. - pub fn implied_outlives_bounds( - &self, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - ty: Ty<'tcx>, - span: Span, - ) -> Vec> { - debug!("implied_outlives_bounds(ty = {:?})", ty); - - let mut orig_values = OriginalQueryValues::default(); - let key = self.canonicalize_query(¶m_env.and(ty), &mut orig_values); - let result = match self.tcx.implied_outlives_bounds(key) { - Ok(r) => r, - Err(NoSolution) => { - self.tcx.sess.delay_span_bug( - span, - "implied_outlives_bounds failed to solve all obligations", - ); - return vec![]; - } - }; - assert!(result.value.is_proven()); - - let result = self.instantiate_query_response_and_region_obligations( - &ObligationCause::misc(span, body_id), - param_env, - &orig_values, - &result, - ); - debug!("implied_outlives_bounds for {:?}: {:#?}", ty, result); - let result = match result { - Ok(v) => v, - Err(_) => { - self.tcx.sess.delay_span_bug(span, "implied_outlives_bounds failed to instantiate"); - return vec![]; - } - }; - - // Instantiation may have produced new inference variables and constraints on those - // variables. Process these constraints. - let mut fulfill_cx = FulfillmentContext::new(); - fulfill_cx.register_predicate_obligations(self, result.obligations); - if fulfill_cx.select_all_or_error(self).is_err() { - self.tcx.sess.delay_span_bug( - span, - "implied_outlives_bounds failed to solve obligations from instantiation", - ); - } - - result.value - } -} - -pub fn explicit_outlives_bounds<'tcx>( - param_env: ty::ParamEnv<'tcx>, -) -> impl Iterator> + 'tcx { - debug!("explicit_outlives_bounds()"); - param_env.caller_bounds.into_iter().filter_map(move |predicate| match predicate { - ty::Predicate::Projection(..) - | ty::Predicate::Trait(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, - ty::Predicate::RegionOutlives(ref data) => data - .no_bound_vars() - .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)), - }) -} diff --git a/src/librustc_infer/traits/query/type_op/ascribe_user_type.rs b/src/librustc_infer/traits/query/type_op/ascribe_user_type.rs deleted file mode 100644 index b14b79f0907..00000000000 --- a/src/librustc_infer/traits/query/type_op/ascribe_user_type.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; -use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, TyCtxt}; - -pub use rustc::traits::query::type_op::AscribeUserType; - -impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { - type QueryResponse = (); - - fn try_fast_path( - _tcx: TyCtxt<'tcx>, - _key: &ParamEnvAnd<'tcx, Self>, - ) -> Option { - None - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - tcx.type_op_ascribe_user_type(canonicalized) - } -} diff --git a/src/librustc_infer/traits/query/type_op/custom.rs b/src/librustc_infer/traits/query/type_op/custom.rs deleted file mode 100644 index c1c9030b888..00000000000 --- a/src/librustc_infer/traits/query/type_op/custom.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::infer::{InferCtxt, InferOk}; -use crate::traits::query::Fallible; -use std::fmt; - -use crate::infer::canonical::query_response; -use crate::infer::canonical::QueryRegionConstraints; -use crate::traits::{ObligationCause, TraitEngine, TraitEngineExt}; -use rustc_span::source_map::DUMMY_SP; -use std::rc::Rc; - -pub struct CustomTypeOp { - closure: F, - description: G, -} - -impl CustomTypeOp { - pub fn new<'tcx, R>(closure: F, description: G) -> Self - where - F: FnOnce(&InferCtxt<'_, 'tcx>) -> Fallible>, - G: Fn() -> String, - { - CustomTypeOp { closure, description } - } -} - -impl<'tcx, F, R, G> super::TypeOp<'tcx> for CustomTypeOp -where - F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'tcx>) -> Fallible>, - G: Fn() -> String, -{ - type Output = R; - - /// Processes the operation and all resulting obligations, - /// returning the final result along with any region constraints - /// (they will be given over to the NLL region solver). - fn fully_perform( - self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>)> { - if cfg!(debug_assertions) { - info!("fully_perform({:?})", self); - } - - scrape_region_constraints(infcx, || Ok((self.closure)(infcx)?)) - } -} - -impl fmt::Debug for CustomTypeOp -where - G: Fn() -> String, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", (self.description)()) - } -} - -/// Executes `op` and then scrapes out all the "old style" region -/// constraints that result, creating query-region-constraints. -fn scrape_region_constraints<'tcx, R>( - infcx: &InferCtxt<'_, 'tcx>, - op: impl FnOnce() -> Fallible>, -) -> Fallible<(R, Option>>)> { - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - let dummy_body_id = ObligationCause::dummy().body_id; - - // During NLL, we expect that nobody will register region - // obligations **except** as part of a custom type op (and, at the - // end of each custom type op, we scrape out the region - // obligations that resulted). So this vector should be empty on - // entry. - let pre_obligations = infcx.take_registered_region_obligations(); - assert!( - pre_obligations.is_empty(), - "scrape_region_constraints: incoming region obligations = {:#?}", - pre_obligations, - ); - - let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; - debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); - fulfill_cx.register_predicate_obligations(infcx, obligations); - if let Err(e) = fulfill_cx.select_all_or_error(infcx) { - infcx.tcx.sess.diagnostic().delay_span_bug( - DUMMY_SP, - &format!("errors selecting obligation during MIR typeck: {:?}", e), - ); - } - - let region_obligations = infcx.take_registered_region_obligations(); - - let region_constraint_data = infcx.take_and_reset_region_constraints(); - - let region_constraints = query_response::make_query_region_constraints( - infcx.tcx, - region_obligations - .iter() - .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)) - .map(|(ty, r)| (infcx.resolve_vars_if_possible(&ty), r)), - ®ion_constraint_data, - ); - - if region_constraints.is_empty() { - Ok((value, None)) - } else { - Ok((value, Some(Rc::new(region_constraints)))) - } -} diff --git a/src/librustc_infer/traits/query/type_op/eq.rs b/src/librustc_infer/traits/query/type_op/eq.rs deleted file mode 100644 index 3b6fbc7d8dd..00000000000 --- a/src/librustc_infer/traits/query/type_op/eq.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; -use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, TyCtxt}; - -pub use rustc::traits::query::type_op::Eq; - -impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { - type QueryResponse = (); - - fn try_fast_path( - _tcx: TyCtxt<'tcx>, - key: &ParamEnvAnd<'tcx, Eq<'tcx>>, - ) -> Option { - if key.value.a == key.value.b { Some(()) } else { None } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - tcx.type_op_eq(canonicalized) - } -} diff --git a/src/librustc_infer/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc_infer/traits/query/type_op/implied_outlives_bounds.rs deleted file mode 100644 index 3dad546872e..00000000000 --- a/src/librustc_infer/traits/query/type_op/implied_outlives_bounds.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; -use crate::traits::query::outlives_bounds::OutlivesBound; -use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; - -#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] -pub struct ImpliedOutlivesBounds<'tcx> { - pub ty: Ty<'tcx>, -} - -impl<'tcx> ImpliedOutlivesBounds<'tcx> { - pub fn new(ty: Ty<'tcx>) -> Self { - ImpliedOutlivesBounds { ty } - } -} - -impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { - type QueryResponse = Vec>; - - fn try_fast_path( - _tcx: TyCtxt<'tcx>, - _key: &ParamEnvAnd<'tcx, Self>, - ) -> Option { - None - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - // FIXME this `unchecked_map` is only necessary because the - // query is defined as taking a `ParamEnvAnd`; it should - // take a `ImpliedOutlivesBounds` instead - let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { - let ImpliedOutlivesBounds { ty } = value; - param_env.and(ty) - }); - - tcx.implied_outlives_bounds(canonicalized) - } -} diff --git a/src/librustc_infer/traits/query/type_op/mod.rs b/src/librustc_infer/traits/query/type_op/mod.rs deleted file mode 100644 index eb4c0a029e1..00000000000 --- a/src/librustc_infer/traits/query/type_op/mod.rs +++ /dev/null @@ -1,136 +0,0 @@ -use crate::infer::canonical::{ - Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, QueryRegionConstraints, -}; -use crate::infer::{InferCtxt, InferOk}; -use crate::traits::query::Fallible; -use crate::traits::ObligationCause; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{ParamEnvAnd, TyCtxt}; -use std::fmt; -use std::rc::Rc; - -pub mod ascribe_user_type; -pub mod custom; -pub mod eq; -pub mod implied_outlives_bounds; -pub mod normalize; -pub mod outlives; -pub mod prove_predicate; -use self::prove_predicate::ProvePredicate; -pub mod subtype; - -pub use rustc::traits::query::type_op::*; - -/// "Type ops" are used in NLL to perform some particular action and -/// extract out the resulting region constraints (or an error if it -/// cannot be completed). -pub trait TypeOp<'tcx>: Sized + fmt::Debug { - type Output; - - /// Processes the operation and all resulting obligations, - /// returning the final result along with any region constraints - /// (they will be given over to the NLL region solver). - fn fully_perform( - self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>)>; -} - -/// "Query type ops" are type ops that are implemented using a -/// [canonical query][c]. The `Self` type here contains the kernel of -/// information needed to do the operation -- `TypeOp` is actually -/// implemented for `ParamEnvAnd`, since we always need to bring -/// along a parameter environment as well. For query type-ops, we will -/// first canonicalize the key and then invoke the query on the tcx, -/// which produces the resulting query region constraints. -/// -/// [c]: https://rust-lang.github.io/rustc-guide/traits/canonicalization.html -pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx { - type QueryResponse: TypeFoldable<'tcx>; - - /// Give query the option for a simple fast path that never - /// actually hits the tcx cache lookup etc. Return `Some(r)` with - /// a final result or `None` to do the full path. - fn try_fast_path( - tcx: TyCtxt<'tcx>, - key: &ParamEnvAnd<'tcx, Self>, - ) -> Option; - - /// Performs the actual query with the canonicalized key -- the - /// real work happens here. This method is not given an `infcx` - /// because it shouldn't need one -- and if it had access to one, - /// it might do things like invoke `sub_regions`, which would be - /// bad, because it would create subregion relationships that are - /// not captured in the return value. - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible>; - - fn fully_perform_into( - query_key: ParamEnvAnd<'tcx, Self>, - infcx: &InferCtxt<'_, 'tcx>, - output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, - ) -> Fallible { - if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { - return Ok(result); - } - - // FIXME(#33684) -- We need to use - // `canonicalize_hr_query_hack` here because of things - // like the subtype query, which go awry around - // `'static` otherwise. - let mut canonical_var_values = OriginalQueryValues::default(); - let canonical_self = - infcx.canonicalize_hr_query_hack(&query_key, &mut canonical_var_values); - let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; - - let param_env = query_key.param_env; - - let InferOk { value, obligations } = infcx - .instantiate_nll_query_response_and_region_obligations( - &ObligationCause::dummy(), - param_env, - &canonical_var_values, - canonical_result, - output_query_region_constraints, - )?; - - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - for obligation in obligations { - let () = ProvePredicate::fully_perform_into( - obligation.param_env.and(ProvePredicate::new(obligation.predicate)), - infcx, - output_query_region_constraints, - )?; - } - - Ok(value) - } -} - -impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q> -where - Q: QueryTypeOp<'tcx>, -{ - type Output = Q::QueryResponse; - - fn fully_perform( - self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>)> { - let mut region_constraints = QueryRegionConstraints::default(); - let r = Q::fully_perform_into(self, infcx, &mut region_constraints)?; - - // Promote the final query-region-constraints into a - // (optional) ref-counted vector: - let opt_qrc = - if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) }; - - Ok((r, opt_qrc)) - } -} diff --git a/src/librustc_infer/traits/query/type_op/normalize.rs b/src/librustc_infer/traits/query/type_op/normalize.rs deleted file mode 100644 index d2eec53bf80..00000000000 --- a/src/librustc_infer/traits/query/type_op/normalize.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; -use crate::traits::query::Fallible; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt}; -use std::fmt; - -pub use rustc::traits::query::type_op::Normalize; - -impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize -where - T: Normalizable<'tcx> + 'tcx, -{ - type QueryResponse = T; - - fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { - if !key.value.value.has_projections() { Some(key.value.value) } else { None } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - T::type_op_method(tcx, canonicalized) - } -} - -pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'tcx> + Copy { - fn type_op_method( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible>; -} - -impl Normalizable<'tcx> for Ty<'tcx> { - fn type_op_method( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { - tcx.type_op_normalize_ty(canonicalized) - } -} - -impl Normalizable<'tcx> for ty::Predicate<'tcx> { - fn type_op_method( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { - tcx.type_op_normalize_predicate(canonicalized) - } -} - -impl Normalizable<'tcx> for ty::PolyFnSig<'tcx> { - fn type_op_method( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { - tcx.type_op_normalize_poly_fn_sig(canonicalized) - } -} - -impl Normalizable<'tcx> for ty::FnSig<'tcx> { - fn type_op_method( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { - tcx.type_op_normalize_fn_sig(canonicalized) - } -} diff --git a/src/librustc_infer/traits/query/type_op/outlives.rs b/src/librustc_infer/traits/query/type_op/outlives.rs deleted file mode 100644 index b94948cffd6..00000000000 --- a/src/librustc_infer/traits/query/type_op/outlives.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; -use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutlivesResult}; -use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; - -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, Lift)] -pub struct DropckOutlives<'tcx> { - dropped_ty: Ty<'tcx>, -} - -impl<'tcx> DropckOutlives<'tcx> { - pub fn new(dropped_ty: Ty<'tcx>) -> Self { - DropckOutlives { dropped_ty } - } -} - -impl super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { - type QueryResponse = DropckOutlivesResult<'tcx>; - - fn try_fast_path( - tcx: TyCtxt<'tcx>, - key: &ParamEnvAnd<'tcx, Self>, - ) -> Option { - if trivial_dropck_outlives(tcx, key.value.dropped_ty) { - Some(DropckOutlivesResult::default()) - } else { - None - } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - // Subtle: note that we are not invoking - // `infcx.at(...).dropck_outlives(...)` here, but rather the - // underlying `dropck_outlives` query. This same underlying - // query is also used by the - // `infcx.at(...).dropck_outlives(...)` fn. Avoiding the - // wrapper means we don't need an infcx in this code, which is - // good because the interface doesn't give us one (so that we - // know we are not registering any subregion relations or - // other things). - - // FIXME convert to the type expected by the `dropck_outlives` - // query. This should eventually be fixed by changing the - // *underlying query*. - let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { - let DropckOutlives { dropped_ty } = value; - param_env.and(dropped_ty) - }); - - tcx.dropck_outlives(canonicalized) - } -} diff --git a/src/librustc_infer/traits/query/type_op/prove_predicate.rs b/src/librustc_infer/traits/query/type_op/prove_predicate.rs deleted file mode 100644 index 8c68f7db9e5..00000000000 --- a/src/librustc_infer/traits/query/type_op/prove_predicate.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; -use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, Predicate, TyCtxt}; - -pub use rustc::traits::query::type_op::ProvePredicate; - -impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { - type QueryResponse = (); - - fn try_fast_path( - tcx: TyCtxt<'tcx>, - key: &ParamEnvAnd<'tcx, Self>, - ) -> Option { - // Proving Sized, very often on "obviously sized" types like - // `&T`, accounts for about 60% percentage of the predicates - // we have to prove. No need to canonicalize and all that for - // such cases. - if let Predicate::Trait(trait_ref, _) = key.value.predicate { - if let Some(sized_def_id) = tcx.lang_items().sized_trait() { - if trait_ref.def_id() == sized_def_id { - if trait_ref.skip_binder().self_ty().is_trivially_sized(tcx) { - return Some(()); - } - } - } - } - - None - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - tcx.type_op_prove_predicate(canonicalized) - } -} diff --git a/src/librustc_infer/traits/query/type_op/subtype.rs b/src/librustc_infer/traits/query/type_op/subtype.rs deleted file mode 100644 index 053411b0cac..00000000000 --- a/src/librustc_infer/traits/query/type_op/subtype.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; -use crate::traits::query::Fallible; -use rustc::ty::{ParamEnvAnd, TyCtxt}; - -pub use rustc::traits::query::type_op::Subtype; - -impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { - type QueryResponse = (); - - fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> { - if key.value.sub == key.value.sup { Some(()) } else { None } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - tcx.type_op_subtype(canonicalized) - } -} diff --git a/src/librustc_infer/traits/select.rs b/src/librustc_infer/traits/select.rs deleted file mode 100644 index a79cbfd5e17..00000000000 --- a/src/librustc_infer/traits/select.rs +++ /dev/null @@ -1,3827 +0,0 @@ -// ignore-tidy-filelength - -//! Candidate selection. See the [rustc guide] for more information on how this works. -//! -//! [rustc guide]: https://rust-lang.github.io/rustc-guide/traits/resolution.html#selection - -use self::EvaluationResult::*; -use self::SelectionCandidate::*; - -use super::coherence::{self, Conflict}; -use super::project; -use super::project::{ - normalize_with_depth, normalize_with_depth_to, Normalized, ProjectionCacheKey, -}; -use super::util; -use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; -use super::wf; -use super::DerivedObligationCause; -use super::Selection; -use super::SelectionResult; -use super::TraitNotObjectSafe; -use super::TraitQueryMode; -use super::{BuiltinDerivedObligation, ImplDerivedObligation, ObligationCauseCode}; -use super::{ObjectCastObligation, Obligation}; -use super::{ObligationCause, PredicateObligation, TraitObligation}; -use super::{OutputTypeParameterMismatch, Overflow, SelectionError, Unimplemented}; -use super::{ - VtableAutoImpl, VtableBuiltin, VtableClosure, VtableFnPointer, VtableGenerator, VtableImpl, - VtableObject, VtableParam, VtableTraitAlias, -}; -use super::{ - VtableAutoImplData, VtableBuiltinData, VtableClosureData, VtableFnPointerData, - VtableGeneratorData, VtableImplData, VtableObjectData, VtableTraitAliasData, -}; - -use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, PlaceholderMap, TypeFreshener}; -use rustc::dep_graph::{DepKind, DepNodeIndex}; -use rustc::middle::lang_items; -use rustc::ty::fast_reject; -use rustc::ty::relate::TypeRelation; -use rustc::ty::subst::{Subst, SubstsRef}; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc_ast::attr; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::GrowableBitSet; -use rustc_span::symbol::sym; -use rustc_target::spec::abi::Abi; - -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::fmt::{self, Display}; -use std::iter; -use std::rc::Rc; - -pub use rustc::traits::select::*; - -pub struct SelectionContext<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, - - /// Freshener used specifically for entries on the obligation - /// stack. This ensures that all entries on the stack at one time - /// will have the same set of placeholder entries, which is - /// important for checking for trait bounds that recursively - /// require themselves. - freshener: TypeFreshener<'cx, 'tcx>, - - /// If `true`, indicates that the evaluation should be conservative - /// and consider the possibility of types outside this crate. - /// This comes up primarily when resolving ambiguity. Imagine - /// there is some trait reference `$0: Bar` where `$0` is an - /// inference variable. If `intercrate` is true, then we can never - /// say for sure that this reference is not implemented, even if - /// there are *no impls at all for `Bar`*, because `$0` could be - /// bound to some type that in a downstream crate that implements - /// `Bar`. This is the suitable mode for coherence. Elsewhere, - /// though, we set this to false, because we are only interested - /// in types that the user could actually have written --- in - /// other words, we consider `$0: Bar` to be unimplemented if - /// there is no type that the user could *actually name* that - /// would satisfy it. This avoids crippling inference, basically. - intercrate: bool, - - intercrate_ambiguity_causes: Option>, - - /// Controls whether or not to filter out negative impls when selecting. - /// This is used in librustdoc to distinguish between the lack of an impl - /// and a negative impl - allow_negative_impls: bool, - - /// The mode that trait queries run in, which informs our error handling - /// policy. In essence, canonicalized queries need their errors propagated - /// rather than immediately reported because we do not have accurate spans. - query_mode: TraitQueryMode, -} - -#[derive(Clone, Debug)] -pub enum IntercrateAmbiguityCause { - DownstreamCrate { trait_desc: String, self_desc: Option }, - UpstreamCrateUpdate { trait_desc: String, self_desc: Option }, - ReservationImpl { message: String }, -} - -impl IntercrateAmbiguityCause { - /// Emits notes when the overlap is caused by complex intercrate ambiguities. - /// See #23980 for details. - pub fn add_intercrate_ambiguity_hint(&self, err: &mut rustc_errors::DiagnosticBuilder<'_>) { - err.note(&self.intercrate_ambiguity_hint()); - } - - pub fn intercrate_ambiguity_hint(&self) -> String { - match self { - &IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { - format!(" for type `{}`", ty) - } else { - String::new() - }; - format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) - } - &IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { - format!(" for type `{}`", ty) - } else { - String::new() - }; - format!( - "upstream crates may add a new impl of trait `{}`{} \ - in future versions", - trait_desc, self_desc - ) - } - &IntercrateAmbiguityCause::ReservationImpl { ref message } => message.clone(), - } - } -} - -// A stack that walks back up the stack frame. -struct TraitObligationStack<'prev, 'tcx> { - obligation: &'prev TraitObligation<'tcx>, - - /// The trait ref from `obligation` but "freshened" with the - /// selection-context's freshener. Used to check for recursion. - fresh_trait_ref: ty::PolyTraitRef<'tcx>, - - /// Starts out equal to `depth` -- if, during evaluation, we - /// encounter a cycle, then we will set this flag to the minimum - /// depth of that cycle for all participants in the cycle. These - /// participants will then forego caching their results. This is - /// not the most efficient solution, but it addresses #60010. The - /// problem we are trying to prevent: - /// - /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait` - /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok) - /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok) - /// - /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` - /// is `EvaluatedToOk`; this is because they were only considered - /// ok on the premise that if `A: AutoTrait` held, but we indeed - /// encountered a problem (later on) with `A: AutoTrait. So we - /// currently set a flag on the stack node for `B: AutoTrait` (as - /// well as the second instance of `A: AutoTrait`) to suppress - /// caching. - /// - /// This is a simple, targeted fix. A more-performant fix requires - /// deeper changes, but would permit more caching: we could - /// basically defer caching until we have fully evaluated the - /// tree, and then cache the entire tree at once. In any case, the - /// performance impact here shouldn't be so horrible: every time - /// this is hit, we do cache at least one trait, so we only - /// evaluate each member of a cycle up to N times, where N is the - /// length of the cycle. This means the performance impact is - /// bounded and we shouldn't have any terrible worst-cases. - reached_depth: Cell, - - previous: TraitObligationStackList<'prev, 'tcx>, - - /// The number of parent frames plus one (thus, the topmost frame has depth 1). - depth: usize, - - /// The depth-first number of this node in the search graph -- a - /// pre-order index. Basically, a freshly incremented counter. - dfn: usize, -} - -struct SelectionCandidateSet<'tcx> { - // A list of candidates that definitely apply to the current - // obligation (meaning: types unify). - vec: Vec>, - - // If `true`, then there were candidates that might or might - // not have applied, but we couldn't tell. This occurs when some - // of the input types are type variables, in which case there are - // various "builtin" rules that might or might not trigger. - ambiguous: bool, -} - -#[derive(PartialEq, Eq, Debug, Clone)] -struct EvaluatedCandidate<'tcx> { - candidate: SelectionCandidate<'tcx>, - evaluation: EvaluationResult, -} - -/// When does the builtin impl for `T: Trait` apply? -enum BuiltinImplConditions<'tcx> { - /// The impl is conditional on `T1, T2, ...: Trait`. - Where(ty::Binder>>), - /// There is no built-in impl. There may be some other - /// candidate (a where-clause or user-defined impl). - None, - /// It is unknown whether there is an impl. - Ambiguous, -} - -impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: true, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn with_negative( - infcx: &'cx InferCtxt<'cx, 'tcx>, - allow_negative_impls: bool, - ) -> SelectionContext<'cx, 'tcx> { - debug!("with_negative({:?})", allow_negative_impls); - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn with_query_mode( - infcx: &'cx InferCtxt<'cx, 'tcx>, - query_mode: TraitQueryMode, - ) -> SelectionContext<'cx, 'tcx> { - debug!("with_query_mode({:?})", query_mode); - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode, - } - } - - /// Enables tracking of intercrate ambiguity causes. These are - /// used in coherence to give improved diagnostics. We don't do - /// this until we detect a coherence error because it can lead to - /// false overflow results (#47139) and because it costs - /// computation time. - pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { - assert!(self.intercrate); - assert!(self.intercrate_ambiguity_causes.is_none()); - self.intercrate_ambiguity_causes = Some(vec![]); - debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); - } - - /// Gets the intercrate ambiguity causes collected since tracking - /// was enabled and disables tracking at the same time. If - /// tracking is not enabled, just returns an empty vector. - pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec { - assert!(self.intercrate); - self.intercrate_ambiguity_causes.take().unwrap_or(vec![]) - } - - pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { - self.infcx - } - - pub fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> { - self.infcx - } - - /////////////////////////////////////////////////////////////////////////// - // Selection - // - // The selection phase tries to identify *how* an obligation will - // be resolved. For example, it will identify which impl or - // parameter bound is to be used. The process can be inconclusive - // if the self type in the obligation is not fully inferred. Selection - // can result in an error in one of two ways: - // - // 1. If no applicable impl or parameter bound can be found. - // 2. If the output type parameters in the obligation do not match - // those specified by the impl/bound. For example, if the obligation - // is `Vec: Iterable`, but the impl specifies - // `impl Iterable for Vec`, than an error would result. - - /// Attempts to satisfy the obligation. If successful, this will affect the surrounding - /// type environment by performing unification. - pub fn select( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> SelectionResult<'tcx, Selection<'tcx>> { - debug!("select({:?})", obligation); - debug_assert!(!obligation.predicate.has_escaping_bound_vars()); - - let pec = &ProvisionalEvaluationCache::default(); - let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); - - let candidate = match self.candidate_from_obligation(&stack) { - Err(SelectionError::Overflow) => { - // In standard mode, overflow must have been caught and reported - // earlier. - assert!(self.query_mode == TraitQueryMode::Canonical); - return Err(SelectionError::Overflow); - } - Err(e) => { - return Err(e); - } - Ok(None) => { - return Ok(None); - } - Ok(Some(candidate)) => candidate, - }; - - match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => { - assert!(self.query_mode == TraitQueryMode::Canonical); - Err(SelectionError::Overflow) - } - Err(e) => Err(e), - Ok(candidate) => Ok(Some(candidate)), - } - } - - /////////////////////////////////////////////////////////////////////////// - // EVALUATION - // - // Tests whether an obligation can be selected or whether an impl - // can be applied to particular types. It skips the "confirmation" - // step and hence completely ignores output type parameters. - // - // The result is "true" if the obligation *may* hold and "false" if - // we can be sure it does not. - - /// Evaluates whether the obligation `obligation` can be satisfied (by any means). - pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { - debug!("predicate_may_hold_fatal({:?})", obligation); - - // This fatal query is a stopgap that should only be used in standard mode, - // where we do not expect overflow to be propagated. - assert!(self.query_mode == TraitQueryMode::Standard); - - self.evaluate_root_obligation(obligation) - .expect("Overflow should be caught earlier in standard query mode") - .may_apply() - } - - /// Evaluates whether the obligation `obligation` can be satisfied - /// and returns an `EvaluationResult`. This is meant for the - /// *initial* call. - pub fn evaluate_root_obligation( - &mut self, - obligation: &PredicateObligation<'tcx>, - ) -> Result { - self.evaluation_probe(|this| { - this.evaluate_predicate_recursively( - TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), - obligation.clone(), - ) - }) - } - - fn evaluation_probe( - &mut self, - op: impl FnOnce(&mut Self) -> Result, - ) -> Result { - self.infcx.probe(|snapshot| -> Result { - let result = op(self)?; - match self.infcx.region_constraints_added_in_snapshot(snapshot) { - None => Ok(result), - Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), - } - }) - } - - /// Evaluates the predicates in `predicates` recursively. Note that - /// this applies projections in the predicates, and therefore - /// is run within an inference probe. - fn evaluate_predicates_recursively<'o, I>( - &mut self, - stack: TraitObligationStackList<'o, 'tcx>, - predicates: I, - ) -> Result - where - I: IntoIterator>, - { - let mut result = EvaluatedToOk; - for obligation in predicates { - let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; - debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); - if let EvaluatedToErr = eval { - // fast-path - EvaluatedToErr is the top of the lattice, - // so we don't need to look on the other predicates. - return Ok(EvaluatedToErr); - } else { - result = cmp::max(result, eval); - } - } - Ok(result) - } - - fn evaluate_predicate_recursively<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - obligation: PredicateObligation<'tcx>, - ) -> Result { - debug!( - "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", - previous_stack.head(), - obligation - ); - - // `previous_stack` stores a `TraitObligatiom`, while `obligation` is - // a `PredicateObligation`. These are distinct types, so we can't - // use any `Option` combinator method that would force them to be - // the same. - match previous_stack.head() { - Some(h) => self.check_recursion_limit(&obligation, h.obligation)?, - None => self.check_recursion_limit(&obligation, &obligation)?, - } - - match obligation.predicate { - ty::Predicate::Trait(ref t, _) => { - debug_assert!(!t.has_escaping_bound_vars()); - let obligation = obligation.with(t.clone()); - self.evaluate_trait_predicate_recursively(previous_stack, obligation) - } - - ty::Predicate::Subtype(ref p) => { - // Does this code ever run? - match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { - Some(Ok(InferOk { mut obligations, .. })) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ) - } - Some(Err(_)) => Ok(EvaluatedToErr), - None => Ok(EvaluatedToAmbig), - } - } - - ty::Predicate::WellFormed(ty) => match wf::obligations( - self.infcx, - obligation.param_env, - obligation.cause.body_id, - ty, - obligation.cause.span, - ) { - Some(mut obligations) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively(previous_stack, obligations.into_iter()) - } - None => Ok(EvaluatedToAmbig), - }, - - ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => { - // We do not consider region relationships when evaluating trait matches. - Ok(EvaluatedToOkModuloRegions) - } - - ty::Predicate::ObjectSafe(trait_def_id) => { - if self.tcx().is_object_safe(trait_def_id) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } - } - - ty::Predicate::Projection(ref data) => { - let project_obligation = obligation.with(data.clone()); - match project::poly_project_and_unify_type(self, &project_obligation) { - Ok(Some(mut subobligations)) => { - self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); - let result = self.evaluate_predicates_recursively( - previous_stack, - subobligations.into_iter(), - ); - if let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate(self, data) - { - self.infcx.inner.borrow_mut().projection_cache.complete(key); - } - result - } - Ok(None) => Ok(EvaluatedToAmbig), - Err(_) => Ok(EvaluatedToErr), - } - } - - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - match self.infcx.closure_kind(closure_def_id, closure_substs) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } - } - None => Ok(EvaluatedToAmbig), - } - } - - ty::Predicate::ConstEvaluatable(def_id, substs) => { - match self.tcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - None, - ) { - Ok(_) => Ok(EvaluatedToOk), - Err(_) => Ok(EvaluatedToErr), - } - } - } - } - - fn evaluate_trait_predicate_recursively<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - mut obligation: TraitObligation<'tcx>, - ) -> Result { - debug!("evaluate_trait_predicate_recursively({:?})", obligation); - - if !self.intercrate - && obligation.is_global() - && obligation.param_env.caller_bounds.iter().all(|bound| bound.needs_subst()) - { - // If a param env has no global bounds, global obligations do not - // depend on its particular value in order to work, so we can clear - // out the param env and get better caching. - debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); - obligation.param_env = obligation.param_env.without_caller_bounds(); - } - - let stack = self.push_stack(previous_stack, &obligation); - let fresh_trait_ref = stack.fresh_trait_ref; - if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { - debug!("CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); - return Ok(result); - } - - if let Some(result) = stack.cache().get_provisional(fresh_trait_ref) { - debug!("PROVISIONAL CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); - stack.update_reached_depth(stack.cache().current_reached_depth()); - return Ok(result); - } - - // Check if this is a match for something already on the - // stack. If so, we don't want to insert the result into the - // main cache (it is cycle dependent) nor the provisional - // cache (which is meant for things that have completed but - // for a "backedge" -- this result *is* the backedge). - if let Some(cycle_result) = self.check_evaluation_cycle(&stack) { - return Ok(cycle_result); - } - - let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack)); - let result = result?; - - if !result.must_apply_modulo_regions() { - stack.cache().on_failure(stack.dfn); - } - - let reached_depth = stack.reached_depth.get(); - if reached_depth >= stack.depth { - debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result); - self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); - - stack.cache().on_completion(stack.depth, |fresh_trait_ref, provisional_result| { - self.insert_evaluation_cache( - obligation.param_env, - fresh_trait_ref, - dep_node, - provisional_result.max(result), - ); - }); - } else { - debug!("PROVISIONAL: {:?}={:?}", fresh_trait_ref, result); - debug!( - "evaluate_trait_predicate_recursively: caching provisionally because {:?} \ - is a cycle participant (at depth {}, reached depth {})", - fresh_trait_ref, stack.depth, reached_depth, - ); - - stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_ref, result); - } - - Ok(result) - } - - /// If there is any previous entry on the stack that precisely - /// matches this obligation, then we can assume that the - /// obligation is satisfied for now (still all other conditions - /// must be met of course). One obvious case this comes up is - /// marker traits like `Send`. Think of a linked list: - /// - /// struct List { data: T, next: Option>> } - /// - /// `Box>` will be `Send` if `T` is `Send` and - /// `Option>>` is `Send`, and in turn - /// `Option>>` is `Send` if `Box>` is - /// `Send`. - /// - /// Note that we do this comparison using the `fresh_trait_ref` - /// fields. Because these have all been freshened using - /// `self.freshener`, we can be sure that (a) this will not - /// affect the inferencer state and (b) that if we see two - /// fresh regions with the same index, they refer to the same - /// unbound type variable. - fn check_evaluation_cycle( - &mut self, - stack: &TraitObligationStack<'_, 'tcx>, - ) -> Option { - if let Some(cycle_depth) = stack - .iter() - .skip(1) // Skip top-most frame. - .find(|prev| { - stack.obligation.param_env == prev.obligation.param_env - && stack.fresh_trait_ref == prev.fresh_trait_ref - }) - .map(|stack| stack.depth) - { - debug!( - "evaluate_stack({:?}) --> recursive at depth {}", - stack.fresh_trait_ref, cycle_depth, - ); - - // If we have a stack like `A B C D E A`, where the top of - // the stack is the final `A`, then this will iterate over - // `A, E, D, C, B` -- i.e., all the participants apart - // from the cycle head. We mark them as participating in a - // cycle. This suppresses caching for those nodes. See - // `in_cycle` field for more details. - stack.update_reached_depth(cycle_depth); - - // Subtle: when checking for a coinductive cycle, we do - // not compare using the "freshened trait refs" (which - // have erased regions) but rather the fully explicit - // trait refs. This is important because it's only a cycle - // if the regions match exactly. - let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth); - let cycle = cycle.map(|stack| { - ty::Predicate::Trait(stack.obligation.predicate, hir::Constness::NotConst) - }); - if self.coinductive_match(cycle) { - debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref); - Some(EvaluatedToOk) - } else { - debug!("evaluate_stack({:?}) --> recursive, inductive", stack.fresh_trait_ref); - Some(EvaluatedToRecur) - } - } else { - None - } - } - - fn evaluate_stack<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> Result { - // In intercrate mode, whenever any of the types are unbound, - // there can always be an impl. Even if there are no impls in - // this crate, perhaps the type would be unified with - // something from another crate that does provide an impl. - // - // In intra mode, we must still be conservative. The reason is - // that we want to avoid cycles. Imagine an impl like: - // - // impl Eq for Vec - // - // and a trait reference like `$0 : Eq` where `$0` is an - // unbound variable. When we evaluate this trait-reference, we - // will unify `$0` with `Vec<$1>` (for some fresh variable - // `$1`), on the condition that `$1 : Eq`. We will then wind - // up with many candidates (since that are other `Eq` impls - // that apply) and try to winnow things down. This results in - // a recursive evaluation that `$1 : Eq` -- as you can - // imagine, this is just where we started. To avoid that, we - // check for unbound variables and return an ambiguous (hence possible) - // match if we've seen this trait before. - // - // This suffices to allow chains like `FnMut` implemented in - // terms of `Fn` etc, but we could probably make this more - // precise still. - let unbound_input_types = - stack.fresh_trait_ref.skip_binder().input_types().any(|ty| ty.is_fresh()); - // This check was an imperfect workaround for a bug in the old - // intercrate mode; it should be removed when that goes away. - if unbound_input_types && self.intercrate { - debug!( - "evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous", - stack.fresh_trait_ref - ); - // Heuristics: show the diagnostics when there are no candidates in crate. - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - if let Ok(candidate_set) = self.assemble_candidates(stack) { - if !candidate_set.ambiguous && candidate_set.vec.is_empty() { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let cause = IntercrateAmbiguityCause::DownstreamCrate { - trait_desc: trait_ref.print_only_trait_path().to_string(), - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(EvaluatedToAmbig); - } - if unbound_input_types - && stack.iter().skip(1).any(|prev| { - stack.obligation.param_env == prev.obligation.param_env - && self.match_fresh_trait_refs( - &stack.fresh_trait_ref, - &prev.fresh_trait_ref, - prev.obligation.param_env, - ) - }) - { - debug!( - "evaluate_stack({:?}) --> unbound argument, recursive --> giving up", - stack.fresh_trait_ref - ); - return Ok(EvaluatedToUnknown); - } - - match self.candidate_from_obligation(stack) { - Ok(Some(c)) => self.evaluate_candidate(stack, &c), - Ok(None) => Ok(EvaluatedToAmbig), - Err(Overflow) => Err(OverflowError), - Err(..) => Ok(EvaluatedToErr), - } - } - - /// For defaulted traits, we use a co-inductive strategy to solve, so - /// that recursion is ok. This routine returns `true` if the top of the - /// stack (`cycle[0]`): - /// - /// - is a defaulted trait, - /// - it also appears in the backtrace at some position `X`, - /// - all the predicates at positions `X..` between `X` and the top are - /// also defaulted traits. - pub fn coinductive_match(&mut self, cycle: I) -> bool - where - I: Iterator>, - { - let mut cycle = cycle; - cycle.all(|predicate| self.coinductive_predicate(predicate)) - } - - fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { - let result = match predicate { - ty::Predicate::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), - _ => false, - }; - debug!("coinductive_predicate({:?}) = {:?}", predicate, result); - result - } - - /// Further evaluates `candidate` to decide whether all type parameters match and whether nested - /// obligations are met. Returns whether `candidate` remains viable after this further - /// scrutiny. - fn evaluate_candidate<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - candidate: &SelectionCandidate<'tcx>, - ) -> Result { - debug!( - "evaluate_candidate: depth={} candidate={:?}", - stack.obligation.recursion_depth, candidate - ); - let result = self.evaluation_probe(|this| { - let candidate = (*candidate).clone(); - match this.confirm_candidate(stack.obligation, candidate) { - Ok(selection) => this.evaluate_predicates_recursively( - stack.list(), - selection.nested_obligations().into_iter(), - ), - Err(..) => Ok(EvaluatedToErr), - } - })?; - debug!( - "evaluate_candidate: depth={} result={:?}", - stack.obligation.recursion_depth, result - ); - Ok(result) - } - - fn check_evaluation_cache( - &self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Option { - let tcx = self.tcx(); - if self.can_use_global_caches(param_env) { - let cache = tcx.evaluation_cache.hashmap.borrow(); - if let Some(cached) = cache.get(¶m_env.and(trait_ref)) { - return Some(cached.get(tcx)); - } - } - self.infcx - .evaluation_cache - .hashmap - .borrow() - .get(¶m_env.and(trait_ref)) - .map(|v| v.get(tcx)) - } - - fn insert_evaluation_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - dep_node: DepNodeIndex, - result: EvaluationResult, - ) { - // Avoid caching results that depend on more than just the trait-ref - // - the stack can create recursion. - if result.is_stack_dependent() { - return; - } - - if self.can_use_global_caches(param_env) { - if !trait_ref.has_local_value() { - debug!( - "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, result, - ); - // This may overwrite the cache with the same value - // FIXME: Due to #50507 this overwrites the different values - // This should be changed to use HashMapExt::insert_same - // when that is fixed - self.tcx() - .evaluation_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); - return; - } - } - - debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); - self.infcx - .evaluation_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); - } - - /// For various reasons, it's possible for a subobligation - /// to have a *lower* recursion_depth than the obligation used to create it. - /// Projection sub-obligations may be returned from the projection cache, - /// which results in obligations with an 'old' `recursion_depth`. - /// Additionally, methods like `wf::obligations` and - /// `InferCtxt.subtype_predicate` produce subobligations without - /// taking in a 'parent' depth, causing the generated subobligations - /// to have a `recursion_depth` of `0`. - /// - /// To ensure that obligation_depth never decreasees, we force all subobligations - /// to have at least the depth of the original obligation. - fn add_depth>>( - &self, - it: I, - min_depth: usize, - ) { - it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); - } - - /// Checks that the recursion limit has not been exceeded. - /// - /// The weird return type of this function allows it to be used with the `try` (`?`) - /// operator within certain functions. - fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( - &self, - obligation: &Obligation<'tcx, T>, - error_obligation: &Obligation<'tcx, V>, - ) -> Result<(), OverflowError> { - let recursion_limit = *self.infcx.tcx.sess.recursion_limit.get(); - if obligation.recursion_depth >= recursion_limit { - match self.query_mode { - TraitQueryMode::Standard => { - self.infcx().report_overflow_error(error_obligation, true); - } - TraitQueryMode::Canonical => { - return Err(OverflowError); - } - } - } - Ok(()) - } - - /////////////////////////////////////////////////////////////////////////// - // CANDIDATE ASSEMBLY - // - // The selection process begins by examining all in-scope impls, - // caller obligations, and so forth and assembling a list of - // candidates. See the [rustc guide] for more details. - // - // [rustc guide]: - // https://rust-lang.github.io/rustc-guide/traits/resolution.html#candidate-assembly - - fn candidate_from_obligation<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - // Watch out for overflow. This intentionally bypasses (and does - // not update) the cache. - self.check_recursion_limit(&stack.obligation, &stack.obligation)?; - - // Check the cache. Note that we freshen the trait-ref - // separately rather than using `stack.fresh_trait_ref` -- - // this is because we want the unbound variables to be - // replaced with fresh types starting from index 0. - let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate.clone()); - debug!( - "candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})", - cache_fresh_trait_pred, stack - ); - debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); - - if let Some(c) = - self.check_candidate_cache(stack.obligation.param_env, &cache_fresh_trait_pred) - { - debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c); - return c; - } - - // If no match, compute result and insert into cache. - // - // FIXME(nikomatsakis) -- this cache is not taking into - // account cycles that may have occurred in forming the - // candidate. I don't know of any specific problems that - // result but it seems awfully suspicious. - let (candidate, dep_node) = - self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); - - debug!("CACHE MISS: SELECT({:?})={:?}", cache_fresh_trait_pred, candidate); - self.insert_candidate_cache( - stack.obligation.param_env, - cache_fresh_trait_pred, - dep_node, - candidate.clone(), - ); - candidate - } - - fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) - where - OP: FnOnce(&mut Self) -> R, - { - let (result, dep_node) = - self.tcx().dep_graph.with_anon_task(DepKind::TraitSelect, || op(self)); - self.tcx().dep_graph.read_index(dep_node); - (result, dep_node) - } - - // Treat negative impls as unimplemented, and reservation impls as ambiguity. - fn filter_negative_and_reservation_impls( - &mut self, - candidate: SelectionCandidate<'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let ImplCandidate(def_id) = candidate { - let tcx = self.tcx(); - match tcx.impl_polarity(def_id) { - ty::ImplPolarity::Negative if !self.allow_negative_impls => { - return Err(Unimplemented); - } - ty::ImplPolarity::Reservation => { - if let Some(intercrate_ambiguity_clauses) = - &mut self.intercrate_ambiguity_causes - { - let attrs = tcx.get_attrs(def_id); - let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl); - let value = attr.and_then(|a| a.value_str()); - if let Some(value) = value { - debug!( - "filter_negative_and_reservation_impls: \ - reservation impl ambiguity on {:?}", - def_id - ); - intercrate_ambiguity_clauses.push( - IntercrateAmbiguityCause::ReservationImpl { - message: value.to_string(), - }, - ); - } - } - return Ok(None); - } - _ => {} - }; - } - Ok(Some(candidate)) - } - - fn candidate_from_obligation_no_cache<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if stack.obligation.predicate.references_error() { - // If we encounter a `Error`, we generally prefer the - // most "optimistic" result in response -- that is, the - // one least likely to report downstream errors. But - // because this routine is shared by coherence and by - // trait selection, there isn't an obvious "right" choice - // here in that respect, so we opt to just return - // ambiguity and let the upstream clients sort it out. - return Ok(None); - } - - if let Some(conflict) = self.is_knowable(stack) { - debug!("coherence stage: not knowable"); - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - // Heuristics: show the diagnostics when there are no candidates in crate. - if let Ok(candidate_set) = self.assemble_candidates(stack) { - let mut no_candidates_apply = true; - { - let evaluated_candidates = - candidate_set.vec.iter().map(|c| self.evaluate_candidate(stack, &c)); - - for ec in evaluated_candidates { - match ec { - Ok(c) => { - if c.may_apply() { - no_candidates_apply = false; - break; - } - } - Err(e) => return Err(e.into()), - } - } - } - - if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; - let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } - } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(None); - } - - let candidate_set = self.assemble_candidates(stack)?; - - if candidate_set.ambiguous { - debug!("candidate set contains ambig"); - return Ok(None); - } - - let mut candidates = candidate_set.vec; - - debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - // At this point, we know that each of the entries in the - // candidate set is *individually* applicable. Now we have to - // figure out if they contain mutual incompatibilities. This - // frequently arises if we have an unconstrained input type -- - // for example, we are looking for `$0: Eq` where `$0` is some - // unconstrained type variable. In that case, we'll get a - // candidate which assumes $0 == int, one that assumes `$0 == - // usize`, etc. This spells an ambiguity. - - // If there is more than one candidate, first winnow them down - // by considering extra conditions (nested obligations and so - // forth). We don't winnow if there is exactly one - // candidate. This is a relatively minor distinction but it - // can lead to better inference and error-reporting. An - // example would be if there was an impl: - // - // impl Vec { fn push_clone(...) { ... } } - // - // and we were to see some code `foo.push_clone()` where `boo` - // is a `Vec` and `Bar` does not implement `Clone`. If - // we were to winnow, we'd wind up with zero candidates. - // Instead, we select the right impl now but report "`Bar` does - // not implement `Clone`". - if candidates.len() == 1 { - return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); - } - - // Winnow, but record the exact outcome of evaluation, which - // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates - .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { - Ok(eval) if eval.may_apply() => { - Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) - } - Ok(_) => Ok(None), - Err(OverflowError) => Err(Overflow), - }) - .flat_map(Result::transpose) - .collect::, _>>()?; - - debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - let needs_infer = stack.obligation.predicate.needs_infer(); - - // If there are STILL multiple candidates, we can further - // reduce the list by dropping duplicates -- including - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - needs_infer, - ) - }); - if is_dup { - debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - candidates.swap_remove(i); - } else { - debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Ok(None); - } - } - } - } - - // If there are *NO* candidates, then there are no impls -- - // that we know of, anyway. Note that in the case where there - // are unbound type variables within the obligation, it might - // be the case that you could still satisfy the obligation - // from another crate by instantiating the type variables with - // a type from another crate that does have an impl. This case - // is checked for in `evaluate_stack` (and hence users - // who might care about this case, like coherence, should use - // that function). - if candidates.is_empty() { - return Err(Unimplemented); - } - - // Just one candidate left. - self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) - } - - fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { - debug!("is_knowable(intercrate={:?})", self.intercrate); - - if !self.intercrate { - return None; - } - - let obligation = &stack.obligation; - let predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - - // Okay to skip binder because of the nature of the - // trait-ref-is-knowable check, which does not care about - // bound regions. - let trait_ref = predicate.skip_binder().trait_ref; - - coherence::trait_ref_is_knowable(self.tcx(), trait_ref) - } - - /// Returns `true` if the global caches can be used. - /// Do note that if the type itself is not in the - /// global tcx, the local caches will be used. - fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { - // If there are any e.g. inference variables in the `ParamEnv`, then we - // always use a cache local to this particular scope. Otherwise, we - // switch to a global cache. - if param_env.has_local_value() { - return false; - } - - // Avoid using the master cache during coherence and just rely - // on the local cache. This effectively disables caching - // during coherence. It is really just a simplification to - // avoid us having to fear that coherence results "pollute" - // the master cache. Since coherence executes pretty quickly, - // it's not worth going to more trouble to increase the - // hit-rate, I don't think. - if self.intercrate { - return false; - } - - // Otherwise, we can use the global cache. - true - } - - fn check_candidate_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>, - ) -> Option>> { - let tcx = self.tcx(); - let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref; - if self.can_use_global_caches(param_env) { - let cache = tcx.selection_cache.hashmap.borrow(); - if let Some(cached) = cache.get(¶m_env.and(*trait_ref)) { - return Some(cached.get(tcx)); - } - } - self.infcx - .selection_cache - .hashmap - .borrow() - .get(¶m_env.and(*trait_ref)) - .map(|v| v.get(tcx)) - } - - /// Determines whether can we safely cache the result - /// of selecting an obligation. This is almost always `true`, - /// except when dealing with certain `ParamCandidate`s. - /// - /// Ordinarily, a `ParamCandidate` will contain no inference variables, - /// since it was usually produced directly from a `DefId`. However, - /// certain cases (currently only librustdoc's blanket impl finder), - /// a `ParamEnv` may be explicitly constructed with inference types. - /// When this is the case, we do *not* want to cache the resulting selection - /// candidate. This is due to the fact that it might not always be possible - /// to equate the obligation's trait ref and the candidate's trait ref, - /// if more constraints end up getting added to an inference variable. - /// - /// Because of this, we always want to re-run the full selection - /// process for our obligation the next time we see it, since - /// we might end up picking a different `SelectionCandidate` (or none at all). - fn can_cache_candidate( - &self, - result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, - ) -> bool { - match result { - Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => { - !trait_ref.skip_binder().input_types().any(|t| t.walk().any(|t_| t_.is_ty_infer())) - } - _ => true, - } - } - - fn insert_candidate_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, - dep_node: DepNodeIndex, - candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, - ) { - let tcx = self.tcx(); - let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; - - if !self.can_cache_candidate(&candidate) { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ - candidate is not cacheable", - trait_ref, candidate - ); - return; - } - - if self.can_use_global_caches(param_env) { - if let Err(Overflow) = candidate { - // Don't cache overflow globally; we only produce this in certain modes. - } else if !trait_ref.has_local_value() { - if !candidate.has_local_value() { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, candidate, - ); - // This may overwrite the cache with the same value. - tcx.selection_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); - return; - } - } - } - - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", - trait_ref, candidate, - ); - self.infcx - .selection_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); - } - - fn assemble_candidates<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> Result, SelectionError<'tcx>> { - let TraitObligationStack { obligation, .. } = *stack; - let ref obligation = Obligation { - param_env: obligation.param_env, - cause: obligation.cause.clone(), - recursion_depth: obligation.recursion_depth, - predicate: self.infcx().resolve_vars_if_possible(&obligation.predicate), - }; - - if obligation.predicate.skip_binder().self_ty().is_ty_var() { - // Self is a type variable (e.g., `_: AsRef`). - // - // This is somewhat problematic, as the current scheme can't really - // handle it turning to be a projection. This does end up as truly - // ambiguous in most cases anyway. - // - // Take the fast path out - this also improves - // performance by preventing assemble_candidates_from_impls from - // matching every impl for this trait. - return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true }); - } - - let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - - self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?; - - // Other bounds. Consider both in-scope bounds from fn decl - // and applicable impls. There is a certain set of precedence rules here. - let def_id = obligation.predicate.def_id(); - let lang_items = self.tcx().lang_items(); - - if lang_items.copy_trait() == Some(def_id) { - debug!("obligation self ty is {:?}", obligation.predicate.skip_binder().self_ty()); - - // User-defined copy impls are permitted, but only for - // structs and enums. - self.assemble_candidates_from_impls(obligation, &mut candidates)?; - - // For other types, we'll use the builtin rules. - let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?; - } else if lang_items.sized_trait() == Some(def_id) { - // Sized is never implementable by end-users, it is - // always automatically computed. - let sized_conditions = self.sized_conditions(obligation); - self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; - } else if lang_items.unsize_trait() == Some(def_id) { - self.assemble_candidates_for_unsizing(obligation, &mut candidates); - } else { - if lang_items.clone_trait() == Some(def_id) { - // Same builtin conditions as `Copy`, i.e., every type which has builtin support - // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` - // types have builtin support for `Clone`. - let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; - } - - self.assemble_generator_candidates(obligation, &mut candidates)?; - self.assemble_closure_candidates(obligation, &mut candidates)?; - self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; - self.assemble_candidates_from_impls(obligation, &mut candidates)?; - self.assemble_candidates_from_object_ty(obligation, &mut candidates); - } - - self.assemble_candidates_from_projected_tys(obligation, &mut candidates); - self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; - // Auto implementations have lower priority, so we only - // consider triggering a default if there is no other impl that can apply. - if candidates.vec.is_empty() { - self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?; - } - debug!("candidate list size: {}", candidates.vec.len()); - Ok(candidates) - } - - fn assemble_candidates_from_projected_tys( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - debug!("assemble_candidates_for_projected_tys({:?})", obligation); - - // Before we go into the whole placeholder thing, just - // quickly check if the self-type is a projection at all. - match obligation.predicate.skip_binder().trait_ref.self_ty().kind { - ty::Projection(_) | ty::Opaque(..) => {} - ty::Infer(ty::TyVar(_)) => { - span_bug!( - obligation.cause.span, - "Self=_ should have been handled by assemble_candidates" - ); - } - _ => return, - } - - let result = self.infcx.probe(|snapshot| { - self.match_projection_obligation_against_definition_bounds(obligation, snapshot) - }); - - if result { - candidates.vec.push(ProjectionCandidate); - } - } - - fn match_projection_obligation_against_definition_bounds( - &mut self, - obligation: &TraitObligation<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> bool { - let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - let (placeholder_trait_predicate, placeholder_map) = - self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); - debug!( - "match_projection_obligation_against_definition_bounds: \ - placeholder_trait_predicate={:?}", - placeholder_trait_predicate, - ); - - let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().kind { - ty::Projection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs), - ty::Opaque(def_id, substs) => (def_id, substs), - _ => { - span_bug!( - obligation.cause.span, - "match_projection_obligation_against_definition_bounds() called \ - but self-ty is not a projection: {:?}", - placeholder_trait_predicate.trait_ref.self_ty() - ); - } - }; - debug!( - "match_projection_obligation_against_definition_bounds: \ - def_id={:?}, substs={:?}", - def_id, substs - ); - - let predicates_of = self.tcx().predicates_of(def_id); - let bounds = predicates_of.instantiate(self.tcx(), substs); - debug!( - "match_projection_obligation_against_definition_bounds: \ - bounds={:?}", - bounds - ); - - let elaborated_predicates = util::elaborate_predicates(self.tcx(), bounds.predicates); - let matching_bound = elaborated_predicates.filter_to_traits().find(|bound| { - self.infcx.probe(|_| { - self.match_projection( - obligation, - bound.clone(), - placeholder_trait_predicate.trait_ref.clone(), - &placeholder_map, - snapshot, - ) - }) - }); - - debug!( - "match_projection_obligation_against_definition_bounds: \ - matching_bound={:?}", - matching_bound - ); - match matching_bound { - None => false, - Some(bound) => { - // Repeat the successful match, if any, this time outside of a probe. - let result = self.match_projection( - obligation, - bound, - placeholder_trait_predicate.trait_ref.clone(), - &placeholder_map, - snapshot, - ); - - assert!(result); - true - } - } - } - - fn match_projection( - &mut self, - obligation: &TraitObligation<'tcx>, - trait_bound: ty::PolyTraitRef<'tcx>, - placeholder_trait_ref: ty::TraitRef<'tcx>, - placeholder_map: &PlaceholderMap<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> bool { - debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) - .is_ok() - && self.infcx.leak_check(false, placeholder_map, snapshot).is_ok() - } - - /// Given an obligation like ``, searches the obligations that the caller - /// supplied to find out whether it is listed among them. - /// - /// Never affects the inference environment. - fn assemble_candidates_from_caller_bounds<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - debug!("assemble_candidates_from_caller_bounds({:?})", stack.obligation); - - let all_bounds = stack - .obligation - .param_env - .caller_bounds - .iter() - .filter_map(|o| o.to_opt_poly_trait_ref()); - - // Micro-optimization: filter out predicates relating to different traits. - let matching_bounds = - all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id()); - - // Keep only those bounds which may apply, and propagate overflow if it occurs. - let mut param_candidates = vec![]; - for bound in matching_bounds { - let wc = self.evaluate_where_clause(stack, bound.clone())?; - if wc.may_apply() { - param_candidates.push(ParamCandidate(bound)); - } - } - - candidates.vec.extend(param_candidates); - - Ok(()) - } - - fn evaluate_where_clause<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - where_clause_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result { - self.evaluation_probe(|this| { - match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { - Ok(obligations) => { - this.evaluate_predicates_recursively(stack.list(), obligations.into_iter()) - } - Err(()) => Ok(EvaluatedToErr), - } - }) - } - - fn assemble_generator_candidates( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) { - return Ok(()); - } - - // Okay to skip binder because the substs on generator types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = *obligation.self_ty().skip_binder(); - match self_ty.kind { - ty::Generator(..) => { - debug!( - "assemble_generator_candidates: self_ty={:?} obligation={:?}", - self_ty, obligation - ); - - candidates.vec.push(GeneratorCandidate); - } - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_generator_candidates: ambiguous self-type"); - candidates.ambiguous = true; - } - _ => {} - } - - Ok(()) - } - - /// Checks for the artificial impl that the compiler will create for an obligation like `X : - /// FnMut<..>` where `X` is a closure type. - /// - /// Note: the type parameters on a closure candidate are modeled as *output* type - /// parameters and hence do not affect whether this trait is a match or not. They will be - /// unified during the confirmation step. - fn assemble_closure_candidates( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) { - Some(k) => k, - None => { - return Ok(()); - } - }; - - // Okay to skip binder because the substs on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters - match obligation.self_ty().skip_binder().kind { - ty::Closure(closure_def_id, closure_substs) => { - debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); - match self.infcx.closure_kind(closure_def_id, closure_substs) { - Some(closure_kind) => { - debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); - if closure_kind.extends(kind) { - candidates.vec.push(ClosureCandidate); - } - } - None => { - debug!("assemble_unboxed_candidates: closure_kind not yet known"); - candidates.vec.push(ClosureCandidate); - } - } - } - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_unboxed_closure_candidates: ambiguous self-type"); - candidates.ambiguous = true; - } - _ => {} - } - - Ok(()) - } - - /// Implements one of the `Fn()` family for a fn pointer. - fn assemble_fn_pointer_candidates( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - // We provide impl of all fn traits for fn pointers. - if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() { - return Ok(()); - } - - // Okay to skip binder because what we are inspecting doesn't involve bound regions. - let self_ty = *obligation.self_ty().skip_binder(); - match self_ty.kind { - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_fn_pointer_candidates: ambiguous self-type"); - candidates.ambiguous = true; // Could wind up being a fn() type. - } - // Provide an impl, but only for suitable `fn` pointers. - ty::FnDef(..) | ty::FnPtr(_) => { - if let ty::FnSig { - unsafety: hir::Unsafety::Normal, - abi: Abi::Rust, - c_variadic: false, - .. - } = self_ty.fn_sig(self.tcx()).skip_binder() - { - candidates.vec.push(FnPointerCandidate); - } - } - _ => {} - } - - Ok(()) - } - - /// Searches for impls that might apply to `obligation`. - fn assemble_candidates_from_impls( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - debug!("assemble_candidates_from_impls(obligation={:?})", obligation); - - self.tcx().for_each_relevant_impl( - obligation.predicate.def_id(), - obligation.predicate.skip_binder().trait_ref.self_ty(), - |impl_def_id| { - self.infcx.probe(|snapshot| { - if let Ok(_substs) = self.match_impl(impl_def_id, obligation, snapshot) { - candidates.vec.push(ImplCandidate(impl_def_id)); - } - }); - }, - ); - - Ok(()) - } - - fn assemble_candidates_from_auto_impls( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - // Okay to skip binder here because the tests we do below do not involve bound regions. - let self_ty = *obligation.self_ty().skip_binder(); - debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty); - - let def_id = obligation.predicate.def_id(); - - if self.tcx().trait_is_auto(def_id) { - match self_ty.kind { - ty::Dynamic(..) => { - // For object types, we don't know what the closed - // over types are. This means we conservatively - // say nothing; a candidate may be added by - // `assemble_candidates_from_object_ty`. - } - ty::Foreign(..) => { - // Since the contents of foreign types is unknown, - // we don't add any `..` impl. Default traits could - // still be provided by a manual implementation for - // this trait and type. - } - ty::Param(..) | ty::Projection(..) => { - // In these cases, we don't know what the actual - // type is. Therefore, we cannot break it down - // into its constituent types. So we don't - // consider the `..` impl but instead just add no - // candidates: this means that typeck will only - // succeed if there is another reason to believe - // that this obligation holds. That could be a - // where-clause or, in the case of an object type, - // it could be that the object type lists the - // trait (e.g., `Foo+Send : Send`). See - // `compile-fail/typeck-default-trait-impl-send-param.rs` - // for an example of a test case that exercises - // this path. - } - ty::Infer(ty::TyVar(_)) => { - // The auto impl might apply; we don't know. - candidates.ambiguous = true; - } - ty::Generator(_, _, movability) - if self.tcx().lang_items().unpin_trait() == Some(def_id) => - { - match movability { - hir::Movability::Static => { - // Immovable generators are never `Unpin`, so - // suppress the normal auto-impl candidate for it. - } - hir::Movability::Movable => { - // Movable generators are always `Unpin`, so add an - // unconditional builtin candidate. - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } - } - } - - _ => candidates.vec.push(AutoImplCandidate(def_id)), - } - } - - Ok(()) - } - - /// Searches for impls that might apply to `obligation`. - fn assemble_candidates_from_object_ty( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - debug!( - "assemble_candidates_from_object_ty(self_ty={:?})", - obligation.self_ty().skip_binder() - ); - - self.infcx.probe(|_snapshot| { - // The code below doesn't care about regions, and the - // self-ty here doesn't escape this probe, so just erase - // any LBR. - let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty()); - let poly_trait_ref = match self_ty.kind { - ty::Dynamic(ref data, ..) => { - if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { - debug!( - "assemble_candidates_from_object_ty: matched builtin bound, \ - pushing candidate" - ); - candidates.vec.push(BuiltinObjectCandidate); - return; - } - - if let Some(principal) = data.principal() { - if !self.infcx.tcx.features().object_safe_for_dispatch { - principal.with_self_ty(self.tcx(), self_ty) - } else if self.tcx().is_object_safe(principal.def_id()) { - principal.with_self_ty(self.tcx(), self_ty) - } else { - return; - } - } else { - // Only auto trait bounds exist. - return; - } - } - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_candidates_from_object_ty: ambiguous"); - candidates.ambiguous = true; // could wind up being an object type - return; - } - _ => return, - }; - - debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}", poly_trait_ref); - - // Count only those upcast versions that match the trait-ref - // we are looking for. Specifically, do not only check for the - // correct trait, but also the correct type parameters. - // For example, we may be trying to upcast `Foo` to `Bar`, - // but `Foo` is declared as `trait Foo: Bar`. - let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref) - .filter(|upcast_trait_ref| { - self.infcx - .probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok()) - }) - .count(); - - if upcast_trait_refs > 1 { - // Can be upcast in many ways; need more type information. - candidates.ambiguous = true; - } else if upcast_trait_refs == 1 { - candidates.vec.push(ObjectCandidate); - } - }) - } - - /// Searches for unsizing that might apply to `obligation`. - fn assemble_candidates_for_unsizing( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - // We currently never consider higher-ranked obligations e.g. - // `for<'a> &'a T: Unsize` to be implemented. This is not - // because they are a priori invalid, and we could potentially add support - // for them later, it's just that there isn't really a strong need for it. - // A `T: Unsize` obligation is always used as part of a `T: CoerceUnsize` - // impl, and those are generally applied to concrete types. - // - // That said, one might try to write a fn with a where clause like - // for<'a> Foo<'a, T>: Unsize> - // where the `'a` is kind of orthogonal to the relevant part of the `Unsize`. - // Still, you'd be more likely to write that where clause as - // T: Trait - // so it seems ok if we (conservatively) fail to accept that `Unsize` - // obligation above. Should be possible to extend this in the future. - let source = match obligation.self_ty().no_bound_vars() { - Some(t) => t, - None => { - // Don't add any candidates if there are bound regions. - return; - } - }; - let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); - - debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); - - let may_apply = match (&source.kind, &target.kind) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - // Upcasts permit two things: - // - // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` - // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` - // - // Note that neither of these changes requires any - // change at runtime. Eventually this will be - // generalized. - // - // We always upcast when we can because of reason - // #2 (region bounds). - data_a.principal_def_id() == data_b.principal_def_id() - && data_b - .auto_traits() - // All of a's auto traits need to be in b's auto traits. - .all(|b| data_a.auto_traits().any(|a| a == b)) - } - - // `T` -> `Trait` - (_, &ty::Dynamic(..)) => true, - - // Ambiguous handling is below `T` -> `Trait`, because inference - // variables can still implement `Unsize` and nested - // obligations will have the final say (likely deferred). - (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { - debug!("assemble_candidates_for_unsizing: ambiguous"); - candidates.ambiguous = true; - false - } - - // `[T; n]` -> `[T]` - (&ty::Array(..), &ty::Slice(_)) => true, - - // `Struct` -> `Struct` - (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { - def_id_a == def_id_b - } - - // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(), - - _ => false, - }; - - if may_apply { - candidates.vec.push(BuiltinUnsizeCandidate); - } - } - - fn assemble_candidates_for_trait_alias( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - // Okay to skip binder here because the tests we do below do not involve bound regions. - let self_ty = *obligation.self_ty().skip_binder(); - debug!("assemble_candidates_for_trait_alias(self_ty={:?})", self_ty); - - let def_id = obligation.predicate.def_id(); - - if self.tcx().is_trait_alias(def_id) { - candidates.vec.push(TraitAliasCandidate(def_id)); - } - - Ok(()) - } - - /////////////////////////////////////////////////////////////////////////// - // WINNOW - // - // Winnowing is the process of attempting to resolve ambiguity by - // probing further. During the winnowing process, we unify all - // type variables and then we also attempt to evaluate recursive - // bounds to see if they are satisfied. - - /// Returns `true` if `victim` should be dropped in favor of - /// `other`. Generally speaking we will drop duplicate - /// candidates and prefer where-clause candidates. - /// - /// See the comment for "SelectionCandidate" for more details. - fn candidate_should_be_dropped_in_favor_of( - &mut self, - victim: &EvaluatedCandidate<'tcx>, - other: &EvaluatedCandidate<'tcx>, - needs_infer: bool, - ) -> bool { - if victim.candidate == other.candidate { - return true; - } - - // Check if a bound would previously have been removed when normalizing - // the param_env so that it can be given the lowest priority. See - // #50825 for the motivation for this. - let is_global = - |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); - - match other.candidate { - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => true, - ParamCandidate(ref cand) => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => false, - ImplCandidate(..) - | ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => { - // Global bounds from the where clause should be ignored - // here (see issue #50825). Otherwise, we have a where - // clause so don't go around looking for impls. - !is_global(cand) - } - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - !is_global(cand) - } - ParamCandidate(..) => false, - }, - ObjectCandidate | ProjectionCandidate => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // Prefer `BuiltinCandidate { has_nested: false }` to anything else. - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - BuiltinCandidate { has_nested: false } => false, - ImplCandidate(..) - | ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => true, - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - true - } - ParamCandidate(ref cand) => is_global(cand), - }, - ImplCandidate(other_def) => { - // See if we can toss out `victim` based on specialization. - // This requires us to know *for sure* that the `other` impl applies - // i.e., `EvaluatedToOk`. - if other.evaluation.must_apply_modulo_regions() { - match victim.candidate { - ImplCandidate(victim_def) => { - let tcx = self.tcx(); - if tcx.specializes((other_def, victim_def)) { - return true; - } - return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { - Some(ty::ImplOverlapKind::Permitted { marker: true }) => { - // Subtle: If the predicate we are evaluating has inference - // variables, do *not* allow discarding candidates due to - // marker trait impls. - // - // Without this restriction, we could end up accidentally - // constrainting inference variables based on an arbitrarily - // chosen trait impl. - // - // Imagine we have the following code: - // - // ```rust - // #[marker] trait MyTrait {} - // impl MyTrait for u8 {} - // impl MyTrait for bool {} - // ``` - // - // And we are evaluating the predicate `<_#0t as MyTrait>`. - // - // During selection, we will end up with one candidate for each - // impl of `MyTrait`. If we were to discard one impl in favor - // of the other, we would be left with one candidate, causing - // us to "successfully" select the predicate, unifying - // _#0t with (for example) `u8`. - // - // However, we have no reason to believe that this unification - // is correct - we've essentially just picked an arbitrary - // *possibility* for _#0t, and required that this be the *only* - // possibility. - // - // Eventually, we will either: - // 1) Unify all inference variables in the predicate through - // some other means (e.g. type-checking of a function). We will - // then be in a position to drop marker trait candidates - // without constraining inference variables (since there are - // none left to constrin) - // 2) Be left with some unconstrained inference variables. We - // will then correctly report an inference error, since the - // existence of multiple marker trait impls tells us nothing - // about which one should actually apply. - !needs_infer - } - Some(_) => true, - None => false, - }; - } - ParamCandidate(ref cand) => { - // Prefer the impl to a global where clause candidate. - return is_global(cand); - } - _ => (), - } - } - - false - } - ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { has_nested: true } => { - match victim.candidate { - ParamCandidate(ref cand) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - is_global(cand) && other.evaluation.must_apply_modulo_regions() - } - _ => false, - } - } - _ => false, - } - } - - /////////////////////////////////////////////////////////////////////////// - // BUILTIN BOUNDS - // - // These cover the traits that are built-in to the language - // itself: `Copy`, `Clone` and `Sized`. - - fn assemble_builtin_bound_candidates( - &mut self, - conditions: BuiltinImplConditions<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - match conditions { - BuiltinImplConditions::Where(nested) => { - debug!("builtin_bound: nested={:?}", nested); - candidates - .vec - .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); - } - BuiltinImplConditions::None => {} - BuiltinImplConditions::Ambiguous => { - debug!("assemble_builtin_bound_candidates: ambiguous builtin"); - candidates.ambiguous = true; - } - } - - Ok(()) - } - - fn sized_conditions( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - match self_ty.kind { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::RawPtr(..) - | ty::Char - | ty::Ref(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Array(..) - | ty::Closure(..) - | ty::Never - | ty::Error => { - // safe for everything - Where(ty::Binder::dummy(Vec::new())) - } - - ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, - - ty::Tuple(tys) => { - Where(ty::Binder::bind(tys.last().into_iter().map(|k| k.expect_ty()).collect())) - } - - ty::Adt(def, substs) => { - let sized_crit = def.sized_constraint(self.tcx()); - // (*) binder moved here - Where(ty::Binder::bind( - sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect(), - )) - } - - ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, - ty::Infer(ty::TyVar(_)) => Ambiguous, - - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); - } - } - } - - fn copy_clone_conditions( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - match self_ty.kind { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Error => Where(ty::Binder::dummy(Vec::new())), - - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => { - // Implementations provided in libcore - None - } - - ty::Dynamic(..) - | ty::Str - | ty::Slice(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Foreign(..) - | ty::Ref(_, _, hir::Mutability::Mut) => None, - - ty::Array(element_ty, _) => { - // (*) binder moved here - Where(ty::Binder::bind(vec![element_ty])) - } - - ty::Tuple(tys) => { - // (*) binder moved here - Where(ty::Binder::bind(tys.iter().map(|k| k.expect_ty()).collect())) - } - - ty::Closure(def_id, substs) => { - // (*) binder moved here - Where(ty::Binder::bind(substs.as_closure().upvar_tys(def_id, self.tcx()).collect())) - } - - ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => { - // Fallback to whatever user-defined impls exist in this case. - None - } - - ty::Infer(ty::TyVar(_)) => { - // Unbound type variable. Might or might not have - // applicable impls and so forth, depending on what - // those type variables wind up being bound to. - Ambiguous - } - - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); - } - } - } - - /// For default impls, we need to break apart a type into its - /// "constituent types" -- meaning, the types that it contains. - /// - /// Here are some (simple) examples: - /// - /// ``` - /// (i32, u32) -> [i32, u32] - /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] - /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] - /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] - /// ``` - fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { - match t.kind { - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Str - | ty::Error - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Never - | ty::Char => Vec::new(), - - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) - | ty::Dynamic(..) - | ty::Param(..) - | ty::Foreign(..) - | ty::Projection(..) - | ty::Bound(..) - | ty::Infer(ty::TyVar(_)) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { - bug!("asked to assemble constituent types of unexpected type: {:?}", t); - } - - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - vec![element_ty] - } - - ty::Array(element_ty, _) | ty::Slice(element_ty) => vec![element_ty], - - ty::Tuple(ref tys) => { - // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - tys.iter().map(|k| k.expect_ty()).collect() - } - - ty::Closure(def_id, ref substs) => { - substs.as_closure().upvar_tys(def_id, self.tcx()).collect() - } - - ty::Generator(def_id, ref substs, _) => { - let witness = substs.as_generator().witness(def_id, self.tcx()); - substs - .as_generator() - .upvar_tys(def_id, self.tcx()) - .chain(iter::once(witness)) - .collect() - } - - ty::GeneratorWitness(types) => { - // This is sound because no regions in the witness can refer to - // the binder outside the witness. So we'll effectivly reuse - // the implicit binder around the witness. - types.skip_binder().to_vec() - } - - // For `PhantomData`, we pass `T`. - ty::Adt(def, substs) if def.is_phantom_data() => substs.types().collect(), - - ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect(), - - ty::Opaque(def_id, substs) => { - // We can resolve the `impl Trait` to its concrete type, - // which enforces a DAG between the functions requiring - // the auto trait bounds in question. - vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)] - } - } - } - - fn collect_predicates_for_types( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - trait_def_id: DefId, - types: ty::Binder>>, - ) -> Vec> { - // Because the types were potentially derived from - // higher-ranked obligations they may reference late-bound - // regions. For example, `for<'a> Foo<&'a int> : Copy` would - // yield a type like `for<'a> &'a int`. In general, we - // maintain the invariant that we never manipulate bound - // regions, so we have to process these bound regions somehow. - // - // The strategy is to: - // - // 1. Instantiate those regions to placeholder regions (e.g., - // `for<'a> &'a int` becomes `&0 int`. - // 2. Produce something like `&'0 int : Copy` - // 3. Re-bind the regions back to `for<'a> &'a int : Copy` - - types - .skip_binder() - .iter() - .flat_map(|ty| { - // binder moved -\ - let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ - - self.infcx.commit_unconditionally(|_| { - let (skol_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); - let Normalized { value: normalized_ty, mut obligations } = - project::normalize_with_depth( - self, - param_env, - cause.clone(), - recursion_depth, - &skol_ty, - ); - let skol_obligation = predicate_for_trait_def( - self.tcx(), - param_env, - cause.clone(), - trait_def_id, - recursion_depth, - normalized_ty, - &[], - ); - obligations.push(skol_obligation); - obligations - }) - }) - .collect() - } - - /////////////////////////////////////////////////////////////////////////// - // CONFIRMATION - // - // Confirmation unifies the output type parameters of the trait - // with the values found in the obligation, possibly yielding a - // type error. See the [rustc guide] for more details. - // - // [rustc guide]: - // https://rust-lang.github.io/rustc-guide/traits/resolution.html#confirmation - - fn confirm_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - candidate: SelectionCandidate<'tcx>, - ) -> Result, SelectionError<'tcx>> { - debug!("confirm_candidate({:?}, {:?})", obligation, candidate); - - match candidate { - BuiltinCandidate { has_nested } => { - let data = self.confirm_builtin_candidate(obligation, has_nested); - Ok(VtableBuiltin(data)) - } - - ParamCandidate(param) => { - let obligations = self.confirm_param_candidate(obligation, param); - Ok(VtableParam(obligations)) - } - - ImplCandidate(impl_def_id) => { - Ok(VtableImpl(self.confirm_impl_candidate(obligation, impl_def_id))) - } - - AutoImplCandidate(trait_def_id) => { - let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); - Ok(VtableAutoImpl(data)) - } - - ProjectionCandidate => { - self.confirm_projection_candidate(obligation); - Ok(VtableParam(Vec::new())) - } - - ClosureCandidate => { - let vtable_closure = self.confirm_closure_candidate(obligation)?; - Ok(VtableClosure(vtable_closure)) - } - - GeneratorCandidate => { - let vtable_generator = self.confirm_generator_candidate(obligation)?; - Ok(VtableGenerator(vtable_generator)) - } - - FnPointerCandidate => { - let data = self.confirm_fn_pointer_candidate(obligation)?; - Ok(VtableFnPointer(data)) - } - - TraitAliasCandidate(alias_def_id) => { - let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); - Ok(VtableTraitAlias(data)) - } - - ObjectCandidate => { - let data = self.confirm_object_candidate(obligation); - Ok(VtableObject(data)) - } - - BuiltinObjectCandidate => { - // This indicates something like `Trait + Send: Send`. In this case, we know that - // this holds because that's what the object type is telling us, and there's really - // no additional obligations to prove and no types in particular to unify, etc. - Ok(VtableParam(Vec::new())) - } - - BuiltinUnsizeCandidate => { - let data = self.confirm_builtin_unsize_candidate(obligation)?; - Ok(VtableBuiltin(data)) - } - } - } - - fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { - self.infcx.commit_unconditionally(|snapshot| { - let result = - self.match_projection_obligation_against_definition_bounds(obligation, snapshot); - assert!(result); - }) - } - - fn confirm_param_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - param: ty::PolyTraitRef<'tcx>, - ) -> Vec> { - debug!("confirm_param_candidate({:?},{:?})", obligation, param); - - // During evaluation, we already checked that this - // where-clause trait-ref could be unified with the obligation - // trait-ref. Repeat that unification now without any - // transactional boundary; it should not fail. - match self.match_where_clause_trait_ref(obligation, param.clone()) { - Ok(obligations) => obligations, - Err(()) => { - bug!( - "Where clause `{:?}` was applicable to `{:?}` but now is not", - param, - obligation - ); - } - } - } - - fn confirm_builtin_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - has_nested: bool, - ) -> VtableBuiltinData> { - debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested); - - let lang_items = self.tcx().lang_items(); - let obligations = if has_nested { - let trait_def = obligation.predicate.def_id(); - let conditions = if Some(trait_def) == lang_items.sized_trait() { - self.sized_conditions(obligation) - } else if Some(trait_def) == lang_items.copy_trait() { - self.copy_clone_conditions(obligation) - } else if Some(trait_def) == lang_items.clone_trait() { - self.copy_clone_conditions(obligation) - } else { - bug!("unexpected builtin trait {:?}", trait_def) - }; - let nested = match conditions { - BuiltinImplConditions::Where(nested) => nested, - _ => bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation), - }; - - let cause = obligation.derived_cause(BuiltinDerivedObligation); - self.collect_predicates_for_types( - obligation.param_env, - cause, - obligation.recursion_depth + 1, - trait_def, - nested, - ) - } else { - vec![] - }; - - debug!("confirm_builtin_candidate: obligations={:?}", obligations); - - VtableBuiltinData { nested: obligations } - } - - /// This handles the case where a `auto trait Foo` impl is being used. - /// The idea is that the impl applies to `X : Foo` if the following conditions are met: - /// - /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds - /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. - fn confirm_auto_impl_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - trait_def_id: DefId, - ) -> VtableAutoImplData> { - debug!("confirm_auto_impl_candidate({:?}, {:?})", obligation, trait_def_id); - - let types = obligation.predicate.map_bound(|inner| { - let self_ty = self.infcx.shallow_resolve(inner.self_ty()); - self.constituent_types_for_ty(self_ty) - }); - self.vtable_auto_impl(obligation, trait_def_id, types) - } - - /// See `confirm_auto_impl_candidate`. - fn vtable_auto_impl( - &mut self, - obligation: &TraitObligation<'tcx>, - trait_def_id: DefId, - nested: ty::Binder>>, - ) -> VtableAutoImplData> { - debug!("vtable_auto_impl: nested={:?}", nested); - - let cause = obligation.derived_cause(BuiltinDerivedObligation); - let mut obligations = self.collect_predicates_for_types( - obligation.param_env, - cause, - obligation.recursion_depth + 1, - trait_def_id, - nested, - ); - - let trait_obligations: Vec> = - self.infcx.commit_unconditionally(|_| { - let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); - let (trait_ref, _) = - self.infcx.replace_bound_vars_with_placeholders(&poly_trait_ref); - let cause = obligation.derived_cause(ImplDerivedObligation); - self.impl_or_trait_obligations( - cause, - obligation.recursion_depth + 1, - obligation.param_env, - trait_def_id, - &trait_ref.substs, - ) - }); - - // Adds the predicates from the trait. Note that this contains a `Self: Trait` - // predicate as usual. It won't have any effect since auto traits are coinductive. - obligations.extend(trait_obligations); - - debug!("vtable_auto_impl: obligations={:?}", obligations); - - VtableAutoImplData { trait_def_id, nested: obligations } - } - - fn confirm_impl_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - impl_def_id: DefId, - ) -> VtableImplData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_impl_candidate({:?},{:?})", obligation, impl_def_id); - - // First, create the substitutions by matching the impl again, - // this time not in a probe. - self.infcx.commit_unconditionally(|snapshot| { - let substs = self.rematch_impl(impl_def_id, obligation, snapshot); - debug!("confirm_impl_candidate: substs={:?}", substs); - let cause = obligation.derived_cause(ImplDerivedObligation); - self.vtable_impl( - impl_def_id, - substs, - cause, - obligation.recursion_depth + 1, - obligation.param_env, - ) - }) - } - - fn vtable_impl( - &mut self, - impl_def_id: DefId, - mut substs: Normalized<'tcx, SubstsRef<'tcx>>, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - ) -> VtableImplData<'tcx, PredicateObligation<'tcx>> { - debug!( - "vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})", - impl_def_id, substs, recursion_depth, - ); - - let mut impl_obligations = self.impl_or_trait_obligations( - cause, - recursion_depth, - param_env, - impl_def_id, - &substs.value, - ); - - debug!( - "vtable_impl: impl_def_id={:?} impl_obligations={:?}", - impl_def_id, impl_obligations - ); - - // Because of RFC447, the impl-trait-ref and obligations - // are sufficient to determine the impl substs, without - // relying on projections in the impl-trait-ref. - // - // e.g., `impl> Foo<::T> for V` - impl_obligations.append(&mut substs.obligations); - - VtableImplData { impl_def_id, substs: substs.value, nested: impl_obligations } - } - - fn confirm_object_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> VtableObjectData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_object_candidate({:?})", obligation); - - // FIXME(nmatsakis) skipping binder here seems wrong -- we should - // probably flatten the binder from the obligation and the binder - // from the object. Have to try to make a broken test case that - // results. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let poly_trait_ref = match self_ty.kind { - ty::Dynamic(ref data, ..) => data - .principal() - .unwrap_or_else(|| { - span_bug!(obligation.cause.span, "object candidate with no principal") - }) - .with_self_ty(self.tcx(), self_ty), - _ => span_bug!(obligation.cause.span, "object candidate with non-object"), - }; - - let mut upcast_trait_ref = None; - let mut nested = vec![]; - let vtable_base; - - { - let tcx = self.tcx(); - - // We want to find the first supertrait in the list of - // supertraits that we can unify with, and do that - // unification. We know that there is exactly one in the list - // where we can unify, because otherwise select would have - // reported an ambiguity. (When we do find a match, also - // record it for later.) - let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| { - match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) { - Ok(obligations) => { - upcast_trait_ref = Some(t); - nested.extend(obligations); - false - } - Err(_) => true, - } - }); - - // Additionally, for each of the non-matching predicates that - // we pass over, we sum up the set of number of vtable - // entries, so that we can compute the offset for the selected - // trait. - vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum(); - } - - VtableObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested } - } - - fn confirm_fn_pointer_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - debug!("confirm_fn_pointer_candidate({:?})", obligation); - - // Okay to skip binder; it is reintroduced below. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let sig = self_ty.fn_sig(self.tcx()); - let trait_ref = closure_trait_ref_and_return_type( - self.tcx(), - obligation.predicate.def_id(), - self_ty, - sig, - util::TupleArgumentsFlag::Yes, - ) - .map_bound(|(trait_ref, _)| trait_ref); - - let Normalized { value: trait_ref, obligations } = project::normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &trait_ref, - ); - - self.confirm_poly_trait_refs( - obligation.cause.clone(), - obligation.param_env, - obligation.predicate.to_poly_trait_ref(), - trait_ref, - )?; - Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations }) - } - - fn confirm_trait_alias_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - alias_def_id: DefId, - ) -> VtableTraitAliasData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_trait_alias_candidate({:?}, {:?})", obligation, alias_def_id); - - self.infcx.commit_unconditionally(|_| { - let (predicate, _) = - self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); - let trait_ref = predicate.trait_ref; - let trait_def_id = trait_ref.def_id; - let substs = trait_ref.substs; - - let trait_obligations = self.impl_or_trait_obligations( - obligation.cause.clone(), - obligation.recursion_depth, - obligation.param_env, - trait_def_id, - &substs, - ); - - debug!( - "confirm_trait_alias_candidate: trait_def_id={:?} trait_obligations={:?}", - trait_def_id, trait_obligations - ); - - VtableTraitAliasData { alias_def_id, substs: substs, nested: trait_obligations } - }) - } - - fn confirm_generator_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the substs on generator types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let (generator_def_id, substs) = match self_ty.kind { - ty::Generator(id, substs, _) => (id, substs), - _ => bug!("closure candidate for non-closure {:?}", obligation), - }; - - debug!("confirm_generator_candidate({:?},{:?},{:?})", obligation, generator_def_id, substs); - - let trait_ref = self.generator_trait_ref_unnormalized(obligation, generator_def_id, substs); - let Normalized { value: trait_ref, mut obligations } = normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &trait_ref, - ); - - debug!( - "confirm_generator_candidate(generator_def_id={:?}, \ - trait_ref={:?}, obligations={:?})", - generator_def_id, trait_ref, obligations - ); - - obligations.extend(self.confirm_poly_trait_refs( - obligation.cause.clone(), - obligation.param_env, - obligation.predicate.to_poly_trait_ref(), - trait_ref, - )?); - - Ok(VtableGeneratorData { generator_def_id, substs, nested: obligations }) - } - - fn confirm_closure_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - debug!("confirm_closure_candidate({:?})", obligation); - - let kind = self - .tcx() - .fn_trait_kind_from_lang_item(obligation.predicate.def_id()) - .unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation)); - - // Okay to skip binder because the substs on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let (closure_def_id, substs) = match self_ty.kind { - ty::Closure(id, substs) => (id, substs), - _ => bug!("closure candidate for non-closure {:?}", obligation), - }; - - let trait_ref = self.closure_trait_ref_unnormalized(obligation, closure_def_id, substs); - let Normalized { value: trait_ref, mut obligations } = normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &trait_ref, - ); - - debug!( - "confirm_closure_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})", - closure_def_id, trait_ref, obligations - ); - - obligations.extend(self.confirm_poly_trait_refs( - obligation.cause.clone(), - obligation.param_env, - obligation.predicate.to_poly_trait_ref(), - trait_ref, - )?); - - // FIXME: Chalk - - if !self.tcx().sess.opts.debugging_opts.chalk { - obligations.push(Obligation::new( - obligation.cause.clone(), - obligation.param_env, - ty::Predicate::ClosureKind(closure_def_id, substs, kind), - )); - } - - Ok(VtableClosureData { closure_def_id, substs: substs, nested: obligations }) - } - - /// In the case of closure types and fn pointers, - /// we currently treat the input type parameters on the trait as - /// outputs. This means that when we have a match we have only - /// considered the self type, so we have to go back and make sure - /// to relate the argument types too. This is kind of wrong, but - /// since we control the full set of impls, also not that wrong, - /// and it DOES yield better error messages (since we don't report - /// errors as if there is no applicable impl, but rather report - /// errors are about mismatched argument types. - /// - /// Here is an example. Imagine we have a closure expression - /// and we desugared it so that the type of the expression is - /// `Closure`, and `Closure` expects an int as argument. Then it - /// is "as if" the compiler generated this impl: - /// - /// impl Fn(int) for Closure { ... } - /// - /// Now imagine our obligation is `Fn(usize) for Closure`. So far - /// we have matched the self type `Closure`. At this point we'll - /// compare the `int` to `usize` and generate an error. - /// - /// Note that this checking occurs *after* the impl has selected, - /// because these output type parameters should not affect the - /// selection of the impl. Therefore, if there is a mismatch, we - /// report an error to the user. - fn confirm_poly_trait_refs( - &mut self, - obligation_cause: ObligationCause<'tcx>, - obligation_param_env: ty::ParamEnv<'tcx>, - obligation_trait_ref: ty::PolyTraitRef<'tcx>, - expected_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - self.infcx - .at(&obligation_cause, obligation_param_env) - .sup(obligation_trait_ref, expected_trait_ref) - .map(|InferOk { obligations, .. }| obligations) - .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) - } - - fn confirm_builtin_unsize_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - let tcx = self.tcx(); - - // `assemble_candidates_for_unsizing` should ensure there are no late-bound - // regions here. See the comment there for more details. - let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); - let target = self.infcx.shallow_resolve(target); - - debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); - - let mut nested = vec![]; - match (&source.kind, &target.kind) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { - // See `assemble_candidates_for_unsizing` for more info. - let existential_predicates = data_a.map_bound(|data_a| { - let iter = data_a - .principal() - .map(|x| ty::ExistentialPredicate::Trait(x)) - .into_iter() - .chain( - data_a - .projection_bounds() - .map(|x| ty::ExistentialPredicate::Projection(x)), - ) - .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); - tcx.mk_existential_predicates(iter) - }); - let source_trait = tcx.mk_dynamic(existential_predicates, r_b); - - // Require that the traits involved in this upcast are **equal**; - // only the **lifetime bound** is changed. - // - // FIXME: This condition is arguably too strong -- it would - // suffice for the source trait to be a *subtype* of the target - // trait. In particular, changing from something like - // `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be - // permitted. And, indeed, in the in commit - // 904a0bde93f0348f69914ee90b1f8b6e4e0d7cbc, this - // condition was loosened. However, when the leak check was - // added back, using subtype here actually guides the coercion - // code in such a way that it accepts `old-lub-glb-object.rs`. - // This is probably a good thing, but I've modified this to `.eq` - // because I want to continue rejecting that test (as we have - // done for quite some time) before we are firmly comfortable - // with what our behavior should be there. -nikomatsakis - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(target, source_trait) // FIXME -- see below - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - - // Register one obligation for 'a: 'b. - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(target), - ); - let outlives = ty::OutlivesPredicate(r_a, r_b); - nested.push(Obligation::with_depth( - cause, - obligation.recursion_depth + 1, - obligation.param_env, - ty::Binder::bind(outlives).to_predicate(), - )); - } - - // `T` -> `Trait` - (_, &ty::Dynamic(ref data, r)) => { - let mut object_dids = data.auto_traits().chain(data.principal_def_id()); - if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) { - return Err(TraitNotObjectSafe(did)); - } - - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(target), - ); - - let predicate_to_obligation = |predicate| { - Obligation::with_depth( - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - ) - }; - - // Create obligations: - // - Casting `T` to `Trait` - // - For all the various builtin bounds attached to the object cast. (In other - // words, if the object type is `Foo + Send`, this would create an obligation for - // the `Send` check.) - // - Projection predicates - nested.extend( - data.iter().map(|predicate| { - predicate_to_obligation(predicate.with_self_ty(tcx, source)) - }), - ); - - // We can only make objects from sized types. - let tr = ty::TraitRef::new( - tcx.require_lang_item(lang_items::SizedTraitLangItem, None), - tcx.mk_substs_trait(source, &[]), - ); - nested.push(predicate_to_obligation(tr.without_const().to_predicate())); - - // If the type is `Foo + 'a`, ensure that the type - // being cast to `Foo + 'a` outlives `'a`: - let outlives = ty::OutlivesPredicate(source, r); - nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate())); - } - - // `[T; n]` -> `[T]` - (&ty::Array(a, _), &ty::Slice(b)) => { - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(b, a) - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - } - - // `Struct` -> `Struct` - (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => { - let fields = - def.all_fields().map(|field| tcx.type_of(field.did)).collect::>(); - - // The last field of the structure has to exist and contain type parameters. - let field = if let Some(&field) = fields.last() { - field - } else { - return Err(Unimplemented); - }; - let mut ty_params = GrowableBitSet::new_empty(); - let mut found = false; - for ty in field.walk() { - if let ty::Param(p) = ty.kind { - ty_params.insert(p.index as usize); - found = true; - } - } - if !found { - return Err(Unimplemented); - } - - // Replace type parameters used in unsizing with - // Error and ensure they do not affect any other fields. - // This could be checked after type collection for any struct - // with a potentially unsized trailing field. - let params = substs_a - .iter() - .enumerate() - .map(|(i, &k)| if ty_params.contains(i) { tcx.types.err.into() } else { k }); - let substs = tcx.mk_substs(params); - for &ty in fields.split_last().unwrap().1 { - if ty.subst(tcx, substs).references_error() { - return Err(Unimplemented); - } - } - - // Extract `Field` and `Field` from `Struct` and `Struct`. - let inner_source = field.subst(tcx, substs_a); - let inner_target = field.subst(tcx, substs_b); - - // Check that the source struct with the target's - // unsized parameters is equal to the target. - let params = substs_a.iter().enumerate().map(|(i, &k)| { - if ty_params.contains(i) { substs_b.type_at(i).into() } else { k } - }); - let new_struct = tcx.mk_adt(def, tcx.mk_substs(params)); - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(target, new_struct) - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - - // Construct the nested `Field: Unsize>` predicate. - nested.push(predicate_for_trait_def( - tcx, - obligation.param_env, - obligation.cause.clone(), - obligation.predicate.def_id(), - obligation.recursion_depth + 1, - inner_source, - &[inner_target.into()], - )); - } - - // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { - assert_eq!(tys_a.len(), tys_b.len()); - - // The last field of the tuple has to exist. - let (&a_last, a_mid) = if let Some(x) = tys_a.split_last() { - x - } else { - return Err(Unimplemented); - }; - let &b_last = tys_b.last().unwrap(); - - // Check that the source tuple with the target's - // last element is equal to the target. - let new_tuple = tcx.mk_tup( - a_mid.iter().map(|k| k.expect_ty()).chain(iter::once(b_last.expect_ty())), - ); - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(target, new_tuple) - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - - // Construct the nested `T: Unsize` predicate. - nested.push(predicate_for_trait_def( - tcx, - obligation.param_env, - obligation.cause.clone(), - obligation.predicate.def_id(), - obligation.recursion_depth + 1, - a_last.expect_ty(), - &[b_last], - )); - } - - _ => bug!(), - }; - - Ok(VtableBuiltinData { nested }) - } - - /////////////////////////////////////////////////////////////////////////// - // Matching - // - // Matching is a common path used for both evaluation and - // confirmation. It basically unifies types that appear in impls - // and traits. This does affect the surrounding environment; - // therefore, when used during evaluation, match routines must be - // run inside of a `probe()` so that their side-effects are - // contained. - - fn rematch_impl( - &mut self, - impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> Normalized<'tcx, SubstsRef<'tcx>> { - match self.match_impl(impl_def_id, obligation, snapshot) { - Ok(substs) => substs, - Err(()) => { - bug!( - "Impl {:?} was matchable against {:?} but now is not", - impl_def_id, - obligation - ); - } - } - } - - fn match_impl( - &mut self, - impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> Result>, ()> { - let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); - - // Before we create the substitutions and everything, first - // consider a "quick reject". This avoids creating more types - // and so forth that we need to. - if self.fast_reject_trait_refs(obligation, &impl_trait_ref) { - return Err(()); - } - - let (skol_obligation, placeholder_map) = - self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); - let skol_obligation_trait_ref = skol_obligation.trait_ref; - - let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); - - let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); - - let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = - project::normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &impl_trait_ref, - ); - - debug!( - "match_impl(impl_def_id={:?}, obligation={:?}, \ - impl_trait_ref={:?}, skol_obligation_trait_ref={:?})", - impl_def_id, obligation, impl_trait_ref, skol_obligation_trait_ref - ); - - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(skol_obligation_trait_ref, impl_trait_ref) - .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; - nested_obligations.extend(obligations); - - if let Err(e) = self.infcx.leak_check(false, &placeholder_map, snapshot) { - debug!("match_impl: failed leak check due to `{}`", e); - return Err(()); - } - - if !self.intercrate - && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation - { - debug!("match_impl: reservation impls only apply in intercrate mode"); - return Err(()); - } - - debug!("match_impl: success impl_substs={:?}", impl_substs); - Ok(Normalized { value: impl_substs, obligations: nested_obligations }) - } - - fn fast_reject_trait_refs( - &mut self, - obligation: &TraitObligation<'_>, - impl_trait_ref: &ty::TraitRef<'_>, - ) -> bool { - // We can avoid creating type variables and doing the full - // substitution if we find that any of the input types, when - // simplified, do not match. - - obligation.predicate.skip_binder().input_types().zip(impl_trait_ref.input_types()).any( - |(obligation_ty, impl_ty)| { - let simplified_obligation_ty = - fast_reject::simplify_type(self.tcx(), obligation_ty, true); - let simplified_impl_ty = fast_reject::simplify_type(self.tcx(), impl_ty, false); - - simplified_obligation_ty.is_some() - && simplified_impl_ty.is_some() - && simplified_obligation_ty != simplified_impl_ty - }, - ) - } - - /// Normalize `where_clause_trait_ref` and try to match it against - /// `obligation`. If successful, return any predicates that - /// result from the normalization. Normalization is necessary - /// because where-clauses are stored in the parameter environment - /// unnormalized. - fn match_where_clause_trait_ref( - &mut self, - obligation: &TraitObligation<'tcx>, - where_clause_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, ()> { - self.match_poly_trait_ref(obligation, where_clause_trait_ref) - } - - /// Returns `Ok` if `poly_trait_ref` being true implies that the - /// obligation is satisfied. - fn match_poly_trait_ref( - &mut self, - obligation: &TraitObligation<'tcx>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, ()> { - debug!( - "match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}", - obligation, poly_trait_ref - ); - - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref) - .map(|InferOk { obligations, .. }| obligations) - .map_err(|_| ()) - } - - /////////////////////////////////////////////////////////////////////////// - // Miscellany - - fn match_fresh_trait_refs( - &self, - previous: &ty::PolyTraitRef<'tcx>, - current: &ty::PolyTraitRef<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - let mut matcher = ty::_match::Match::new(self.tcx(), param_env); - matcher.relate(previous, current).is_ok() - } - - fn push_stack<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - obligation: &'o TraitObligation<'tcx>, - ) -> TraitObligationStack<'o, 'tcx> { - let fresh_trait_ref = - obligation.predicate.to_poly_trait_ref().fold_with(&mut self.freshener); - - let dfn = previous_stack.cache.next_dfn(); - let depth = previous_stack.depth() + 1; - TraitObligationStack { - obligation, - fresh_trait_ref, - reached_depth: Cell::new(depth), - previous: previous_stack, - dfn, - depth, - } - } - - fn closure_trait_ref_unnormalized( - &mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> ty::PolyTraitRef<'tcx> { - debug!( - "closure_trait_ref_unnormalized(obligation={:?}, closure_def_id={:?}, substs={:?})", - obligation, closure_def_id, substs, - ); - let closure_type = self.infcx.closure_sig(closure_def_id, substs); - - debug!("closure_trait_ref_unnormalized: closure_type = {:?}", closure_type); - - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an unboxed closure type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. - closure_trait_ref_and_return_type( - self.tcx(), - obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) - closure_type, - util::TupleArgumentsFlag::No, - ) - .map_bound(|(trait_ref, _)| trait_ref) - } - - fn generator_trait_ref_unnormalized( - &mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> ty::PolyTraitRef<'tcx> { - let gen_sig = substs.as_generator().poly_sig(closure_def_id, self.tcx()); - - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an generator type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. - - super::util::generator_trait_ref_and_outputs( - self.tcx(), - obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) - gen_sig, - ) - .map_bound(|(trait_ref, ..)| trait_ref) - } - - /// Returns the obligations that are implied by instantiating an - /// impl or trait. The obligations are substituted and fully - /// normalized. This is used when confirming an impl or default - /// impl. - fn impl_or_trait_obligations( - &mut self, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, // of impl or trait - substs: SubstsRef<'tcx>, // for impl or trait - ) -> Vec> { - debug!("impl_or_trait_obligations(def_id={:?})", def_id); - let tcx = self.tcx(); - - // To allow for one-pass evaluation of the nested obligation, - // each predicate must be preceded by the obligations required - // to normalize it. - // for example, if we have: - // impl, V: Iterator> Foo for V - // the impl will have the following predicates: - // ::Item = U, - // U: Iterator, U: Sized, - // V: Iterator, V: Sized, - // ::Item: Copy - // When we substitute, say, `V => IntoIter, U => $0`, the last - // obligation will normalize to `<$0 as Iterator>::Item = $1` and - // `$1: Copy`, so we must ensure the obligations are emitted in - // that order. - let predicates = tcx.predicates_of(def_id); - assert_eq!(predicates.parent, None); - let mut obligations = Vec::with_capacity(predicates.predicates.len()); - for (predicate, _) in predicates.predicates { - let predicate = normalize_with_depth_to( - self, - param_env, - cause.clone(), - recursion_depth, - &predicate.subst(tcx, substs), - &mut obligations, - ); - obligations.push(Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }); - } - - // We are performing deduplication here to avoid exponential blowups - // (#38528) from happening, but the real cause of the duplication is - // unknown. What we know is that the deduplication avoids exponential - // amount of predicates being propagated when processing deeply nested - // types. - // - // This code is hot enough that it's worth avoiding the allocation - // required for the FxHashSet when possible. Special-casing lengths 0, - // 1 and 2 covers roughly 75-80% of the cases. - if obligations.len() <= 1 { - // No possibility of duplicates. - } else if obligations.len() == 2 { - // Only two elements. Drop the second if they are equal. - if obligations[0] == obligations[1] { - obligations.truncate(1); - } - } else { - // Three or more elements. Use a general deduplication process. - let mut seen = FxHashSet::default(); - obligations.retain(|i| seen.insert(i.clone())); - } - - obligations - } -} - -impl<'tcx> TraitObligation<'tcx> { - #[allow(unused_comparisons)] - pub fn derived_cause( - &self, - variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx> { - /*! - * Creates a cause for obligations that are derived from - * `obligation` by a recursive search (e.g., for a builtin - * bound, or eventually a `auto trait Foo`). If `obligation` - * is itself a derived obligation, this is just a clone, but - * otherwise we create a "derived obligation" cause so as to - * keep track of the original root obligation for error - * reporting. - */ - - let obligation = self; - - // NOTE(flaper87): As of now, it keeps track of the whole error - // chain. Ideally, we should have a way to configure this either - // by using -Z verbose or just a CLI argument. - let derived_cause = DerivedObligationCause { - parent_trait_ref: obligation.predicate.to_poly_trait_ref(), - parent_code: Rc::new(obligation.cause.code.clone()), - }; - let derived_code = variant(derived_cause); - ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code) - } -} - -impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { - fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList::with(self) - } - - fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> { - self.previous.cache - } - - fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> { - self.list() - } - - /// Indicates that attempting to evaluate this stack entry - /// required accessing something from the stack at depth `reached_depth`. - fn update_reached_depth(&self, reached_depth: usize) { - assert!( - self.depth > reached_depth, - "invoked `update_reached_depth` with something under this stack: \ - self.depth={} reached_depth={}", - self.depth, - reached_depth, - ); - debug!("update_reached_depth(reached_depth={})", reached_depth); - let mut p = self; - while reached_depth < p.depth { - debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref); - p.reached_depth.set(p.reached_depth.get().min(reached_depth)); - p = p.previous.head.unwrap(); - } - } -} - -/// The "provisional evaluation cache" is used to store intermediate cache results -/// when solving auto traits. Auto traits are unusual in that they can support -/// cycles. So, for example, a "proof tree" like this would be ok: -/// -/// - `Foo: Send` :- -/// - `Bar: Send` :- -/// - `Foo: Send` -- cycle, but ok -/// - `Baz: Send` -/// -/// Here, to prove `Foo: Send`, we have to prove `Bar: Send` and -/// `Baz: Send`. Proving `Bar: Send` in turn required `Foo: Send`. -/// For non-auto traits, this cycle would be an error, but for auto traits (because -/// they are coinductive) it is considered ok. -/// -/// However, there is a complication: at the point where we have -/// "proven" `Bar: Send`, we have in fact only proven it -/// *provisionally*. In particular, we proved that `Bar: Send` -/// *under the assumption* that `Foo: Send`. But what if we later -/// find out this assumption is wrong? Specifically, we could -/// encounter some kind of error proving `Baz: Send`. In that case, -/// `Bar: Send` didn't turn out to be true. -/// -/// In Issue #60010, we found a bug in rustc where it would cache -/// these intermediate results. This was fixed in #60444 by disabling -/// *all* caching for things involved in a cycle -- in our example, -/// that would mean we don't cache that `Bar: Send`. But this led -/// to large slowdowns. -/// -/// Specifically, imagine this scenario, where proving `Baz: Send` -/// first requires proving `Bar: Send` (which is true: -/// -/// - `Foo: Send` :- -/// - `Bar: Send` :- -/// - `Foo: Send` -- cycle, but ok -/// - `Baz: Send` -/// - `Bar: Send` -- would be nice for this to be a cache hit! -/// - `*const T: Send` -- but what if we later encounter an error? -/// -/// The *provisional evaluation cache* resolves this issue. It stores -/// cache results that we've proven but which were involved in a cycle -/// in some way. We track the minimal stack depth (i.e., the -/// farthest from the top of the stack) that we are dependent on. -/// The idea is that the cache results within are all valid -- so long as -/// none of the nodes in between the current node and the node at that minimum -/// depth result in an error (in which case the cached results are just thrown away). -/// -/// During evaluation, we consult this provisional cache and rely on -/// it. Accessing a cached value is considered equivalent to accessing -/// a result at `reached_depth`, so it marks the *current* solution as -/// provisional as well. If an error is encountered, we toss out any -/// provisional results added from the subtree that encountered the -/// error. When we pop the node at `reached_depth` from the stack, we -/// can commit all the things that remain in the provisional cache. -struct ProvisionalEvaluationCache<'tcx> { - /// next "depth first number" to issue -- just a counter - dfn: Cell, - - /// Stores the "coldest" depth (bottom of stack) reached by any of - /// the evaluation entries. The idea here is that all things in the provisional - /// cache are always dependent on *something* that is colder in the stack: - /// therefore, if we add a new entry that is dependent on something *colder still*, - /// we have to modify the depth for all entries at once. - /// - /// Example: - /// - /// Imagine we have a stack `A B C D E` (with `E` being the top of - /// the stack). We cache something with depth 2, which means that - /// it was dependent on C. Then we pop E but go on and process a - /// new node F: A B C D F. Now F adds something to the cache with - /// depth 1, meaning it is dependent on B. Our original cache - /// entry is also dependent on B, because there is a path from E - /// to C and then from C to F and from F to B. - reached_depth: Cell, - - /// Map from cache key to the provisionally evaluated thing. - /// The cache entries contain the result but also the DFN in which they - /// were added. The DFN is used to clear out values on failure. - /// - /// Imagine we have a stack like: - /// - /// - `A B C` and we add a cache for the result of C (DFN 2) - /// - Then we have a stack `A B D` where `D` has DFN 3 - /// - We try to solve D by evaluating E: `A B D E` (DFN 4) - /// - `E` generates various cache entries which have cyclic dependices on `B` - /// - `A B D E F` and so forth - /// - the DFN of `F` for example would be 5 - /// - then we determine that `E` is in error -- we will then clear - /// all cache values whose DFN is >= 4 -- in this case, that - /// means the cached value for `F`. - map: RefCell, ProvisionalEvaluation>>, -} - -/// A cache value for the provisional cache: contains the depth-first -/// number (DFN) and result. -#[derive(Copy, Clone, Debug)] -struct ProvisionalEvaluation { - from_dfn: usize, - result: EvaluationResult, -} - -impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { - fn default() -> Self { - Self { - dfn: Cell::new(0), - reached_depth: Cell::new(std::usize::MAX), - map: Default::default(), - } - } -} - -impl<'tcx> ProvisionalEvaluationCache<'tcx> { - /// Get the next DFN in sequence (basically a counter). - fn next_dfn(&self) -> usize { - let result = self.dfn.get(); - self.dfn.set(result + 1); - result - } - - /// Check the provisional cache for any result for - /// `fresh_trait_ref`. If there is a hit, then you must consider - /// it an access to the stack slots at depth - /// `self.current_reached_depth()` and above. - fn get_provisional(&self, fresh_trait_ref: ty::PolyTraitRef<'tcx>) -> Option { - debug!( - "get_provisional(fresh_trait_ref={:?}) = {:#?} with reached-depth {}", - fresh_trait_ref, - self.map.borrow().get(&fresh_trait_ref), - self.reached_depth.get(), - ); - Some(self.map.borrow().get(&fresh_trait_ref)?.result) - } - - /// Current value of the `reached_depth` counter -- all the - /// provisional cache entries are dependent on the item at this - /// depth. - fn current_reached_depth(&self) -> usize { - self.reached_depth.get() - } - - /// Insert a provisional result into the cache. The result came - /// from the node with the given DFN. It accessed a minimum depth - /// of `reached_depth` to compute. It evaluated `fresh_trait_ref` - /// and resulted in `result`. - fn insert_provisional( - &self, - from_dfn: usize, - reached_depth: usize, - fresh_trait_ref: ty::PolyTraitRef<'tcx>, - result: EvaluationResult, - ) { - debug!( - "insert_provisional(from_dfn={}, reached_depth={}, fresh_trait_ref={:?}, result={:?})", - from_dfn, reached_depth, fresh_trait_ref, result, - ); - let r_d = self.reached_depth.get(); - self.reached_depth.set(r_d.min(reached_depth)); - - debug!("insert_provisional: reached_depth={:?}", self.reached_depth.get()); - - self.map.borrow_mut().insert(fresh_trait_ref, ProvisionalEvaluation { from_dfn, result }); - } - - /// Invoked when the node with dfn `dfn` does not get a successful - /// result. This will clear out any provisional cache entries - /// that were added since `dfn` was created. This is because the - /// provisional entries are things which must assume that the - /// things on the stack at the time of their creation succeeded -- - /// since the failing node is presently at the top of the stack, - /// these provisional entries must either depend on it or some - /// ancestor of it. - fn on_failure(&self, dfn: usize) { - debug!("on_failure(dfn={:?})", dfn,); - self.map.borrow_mut().retain(|key, eval| { - if !eval.from_dfn >= dfn { - debug!("on_failure: removing {:?}", key); - false - } else { - true - } - }); - } - - /// Invoked when the node at depth `depth` completed without - /// depending on anything higher in the stack (if that completion - /// was a failure, then `on_failure` should have been invoked - /// already). The callback `op` will be invoked for each - /// provisional entry that we can now confirm. - fn on_completion( - &self, - depth: usize, - mut op: impl FnMut(ty::PolyTraitRef<'tcx>, EvaluationResult), - ) { - debug!("on_completion(depth={}, reached_depth={})", depth, self.reached_depth.get(),); - - if self.reached_depth.get() < depth { - debug!("on_completion: did not yet reach depth to complete"); - return; - } - - for (fresh_trait_ref, eval) in self.map.borrow_mut().drain() { - debug!("on_completion: fresh_trait_ref={:?} eval={:?}", fresh_trait_ref, eval,); - - op(fresh_trait_ref, eval.result); - } - - self.reached_depth.set(std::usize::MAX); - } -} - -#[derive(Copy, Clone)] -struct TraitObligationStackList<'o, 'tcx> { - cache: &'o ProvisionalEvaluationCache<'tcx>, - head: Option<&'o TraitObligationStack<'o, 'tcx>>, -} - -impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { - fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList { cache, head: None } - } - - fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList { cache: r.cache(), head: Some(r) } - } - - fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { - self.head - } - - fn depth(&self) -> usize { - if let Some(head) = self.head { head.depth } else { 0 } - } -} - -impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> { - type Item = &'o TraitObligationStack<'o, 'tcx>; - - fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { - match self.head { - Some(o) => { - *self = o.previous; - Some(o) - } - None => None, - } - } -} - -impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TraitObligationStack({:?})", self.obligation) - } -} diff --git a/src/librustc_infer/traits/specialize/mod.rs b/src/librustc_infer/traits/specialize/mod.rs deleted file mode 100644 index ee1c737c208..00000000000 --- a/src/librustc_infer/traits/specialize/mod.rs +++ /dev/null @@ -1,472 +0,0 @@ -//! Logic and data structures related to impl specialization, explained in -//! greater detail below. -//! -//! At the moment, this implementation support only the simple "chain" rule: -//! If any two impls overlap, one must be a strict subset of the other. -//! -//! See the [rustc guide] for a bit more detail on how specialization -//! fits together with the rest of the trait machinery. -//! -//! [rustc guide]: https://rust-lang.github.io/rustc-guide/traits/specialization.html - -pub mod specialization_graph; -use specialization_graph::GraphExt; - -use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; -use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; -use rustc::lint::LintDiagnosticBuilder; -use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc::ty::{self, TyCtxt, TypeFoldable}; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; -use rustc_hir::def_id::DefId; -use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; -use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; -use rustc_span::DUMMY_SP; - -use super::util::impl_trait_ref_and_oblig; -use super::{FulfillmentContext, SelectionContext}; - -/// Information pertinent to an overlapping impl error. -#[derive(Debug)] -pub struct OverlapError { - pub with_impl: DefId, - pub trait_desc: String, - pub self_desc: Option, - pub intercrate_ambiguity_causes: Vec, - pub involves_placeholder: bool, -} - -/// Given a subst for the requested impl, translate it to a subst -/// appropriate for the actual item definition (whether it be in that impl, -/// a parent impl, or the trait). -/// -/// When we have selected one impl, but are actually using item definitions from -/// a parent impl providing a default, we need a way to translate between the -/// type parameters of the two impls. Here the `source_impl` is the one we've -/// selected, and `source_substs` is a substitution of its generics. -/// And `target_node` is the impl/trait we're actually going to get the -/// definition from. The resulting substitution will map from `target_node`'s -/// generics to `source_impl`'s generics as instantiated by `source_subst`. -/// -/// For example, consider the following scenario: -/// -/// ```rust -/// trait Foo { ... } -/// impl Foo for (T, U) { ... } // target impl -/// impl Foo for (V, V) { ... } // source impl -/// ``` -/// -/// Suppose we have selected "source impl" with `V` instantiated with `u32`. -/// This function will produce a substitution with `T` and `U` both mapping to `u32`. -/// -/// where-clauses add some trickiness here, because they can be used to "define" -/// an argument indirectly: -/// -/// ```rust -/// impl<'a, I, T: 'a> Iterator for Cloned -/// where I: Iterator, T: Clone -/// ``` -/// -/// In a case like this, the substitution for `T` is determined indirectly, -/// through associated type projection. We deal with such cases by using -/// *fulfillment* to relate the two impls, requiring that all projections are -/// resolved. -pub fn translate_substs<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - source_impl: DefId, - source_substs: SubstsRef<'tcx>, - target_node: specialization_graph::Node, -) -> SubstsRef<'tcx> { - debug!( - "translate_substs({:?}, {:?}, {:?}, {:?})", - param_env, source_impl, source_substs, target_node - ); - let source_trait_ref = - infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs); - - // translate the Self and Param parts of the substitution, since those - // vary across impls - let target_substs = match target_node { - specialization_graph::Node::Impl(target_impl) => { - // no need to translate if we're targeting the impl we started with - if source_impl == target_impl { - return source_substs; - } - - fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( - |_| { - bug!( - "When translating substitutions for specialization, the expected \ - specialization failed to hold" - ) - }, - ) - } - specialization_graph::Node::Trait(..) => source_trait_ref.substs, - }; - - // directly inherent the method generics, since those do not vary across impls - source_substs.rebase_onto(infcx.tcx, source_impl, target_substs) -} - -/// Given a selected impl described by `impl_data`, returns the -/// definition and substitutions for the method with the name `name` -/// the kind `kind`, and trait method substitutions `substs`, in -/// that impl, a less specialized impl, or the trait default, -/// whichever applies. -pub fn find_associated_item<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - item: &ty::AssocItem, - substs: SubstsRef<'tcx>, - impl_data: &super::VtableImplData<'tcx, ()>, -) -> (DefId, SubstsRef<'tcx>) { - debug!("find_associated_item({:?}, {:?}, {:?}, {:?})", param_env, item, substs, impl_data); - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); - let trait_def = tcx.trait_def(trait_def_id); - - let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id); - match ancestors.leaf_def(tcx, item.ident, item.kind) { - Some(node_item) => { - let substs = tcx.infer_ctxt().enter(|infcx| { - let param_env = param_env.with_reveal_all(); - let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs); - let substs = translate_substs( - &infcx, - param_env, - impl_data.impl_def_id, - substs, - node_item.node, - ); - infcx.tcx.erase_regions(&substs) - }); - (node_item.item.def_id, substs) - } - None => bug!("{:?} not found in {:?}", item, impl_data.impl_def_id), - } -} - -/// Is `impl1` a specialization of `impl2`? -/// -/// Specialization is determined by the sets of types to which the impls apply; -/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies -/// to. -pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { - debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id); - - // The feature gate should prevent introducing new specializations, but not - // taking advantage of upstream ones. - if !tcx.features().specialization && (impl1_def_id.is_local() || impl2_def_id.is_local()) { - return false; - } - - // We determine whether there's a subset relationship by: - // - // - skolemizing impl1, - // - assuming the where clauses for impl1, - // - instantiating impl2 with fresh inference variables, - // - unifying, - // - attempting to prove the where clauses for impl2 - // - // The last three steps are encapsulated in `fulfill_implication`. - // - // See RFC 1210 for more details and justification. - - // Currently we do not allow e.g., a negative impl to specialize a positive one - if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) { - return false; - } - - // create a parameter environment corresponding to a (placeholder) instantiation of impl1 - let penv = tcx.param_env(impl1_def_id); - let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); - - // Create a infcx, taking the predicates of impl1 as assumptions: - tcx.infer_ctxt().enter(|infcx| { - // Normalize the trait reference. The WF rules ought to ensure - // that this always succeeds. - let impl1_trait_ref = match traits::fully_normalize( - &infcx, - FulfillmentContext::new(), - ObligationCause::dummy(), - penv, - &impl1_trait_ref, - ) { - Ok(impl1_trait_ref) => impl1_trait_ref, - Err(err) => { - bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err); - } - }; - - // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() - }) -} - -/// Attempt to fulfill all obligations of `target_impl` after unification with -/// `source_trait_ref`. If successful, returns a substitution for *all* the -/// generics of `target_impl`, including both those needed to unify with -/// `source_trait_ref` and those whose identity is determined via a where -/// clause in the impl. -fn fulfill_implication<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - source_trait_ref: ty::TraitRef<'tcx>, - target_impl: DefId, -) -> Result, ()> { - debug!( - "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", - param_env, source_trait_ref, target_impl - ); - - let selcx = &mut SelectionContext::new(&infcx); - let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); - let (target_trait_ref, mut obligations) = - impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs); - debug!( - "fulfill_implication: target_trait_ref={:?}, obligations={:?}", - target_trait_ref, obligations - ); - - // do the impls unify? If not, no specialization. - match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) { - Ok(InferOk { obligations: o, .. }) => { - obligations.extend(o); - } - Err(_) => { - debug!( - "fulfill_implication: {:?} does not unify with {:?}", - source_trait_ref, target_trait_ref - ); - return Err(()); - } - } - - // attempt to prove all of the predicates for impl2 given those for impl1 - // (which are packed up in penv) - - infcx.save_and_restore_in_snapshot_flag(|infcx| { - // If we came from `translate_substs`, we already know that the - // predicates for our impl hold (after all, we know that a more - // specialized impl holds, so our impl must hold too), and - // we only want to process the projections to determine the - // the types in our substs using RFC 447, so we can safely - // ignore region obligations, which allows us to avoid threading - // a node-id to assign them with. - // - // If we came from specialization graph construction, then - // we already make a mockery out of the region system, so - // why not ignore them a bit earlier? - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); - for oblig in obligations.into_iter() { - fulfill_cx.register_predicate_obligation(&infcx, oblig); - } - match fulfill_cx.select_all_or_error(infcx) { - Err(errors) => { - // no dice! - debug!( - "fulfill_implication: for impls on {:?} and {:?}, \ - could not fulfill: {:?} given {:?}", - source_trait_ref, target_trait_ref, errors, param_env.caller_bounds - ); - Err(()) - } - - Ok(()) => { - debug!( - "fulfill_implication: an impl for {:?} specializes {:?}", - source_trait_ref, target_trait_ref - ); - - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_vars_if_possible(&target_substs)) - } - } - }) -} - -// Query provider for `specialization_graph_of`. -pub(super) fn specialization_graph_provider( - tcx: TyCtxt<'_>, - trait_id: DefId, -) -> &specialization_graph::Graph { - let mut sg = specialization_graph::Graph::new(); - - let mut trait_impls = tcx.all_impls(trait_id); - - // The coherence checking implementation seems to rely on impls being - // iterated over (roughly) in definition order, so we are sorting by - // negated `CrateNum` (so remote definitions are visited first) and then - // by a flattened version of the `DefIndex`. - trait_impls - .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); - - for impl_def_id in trait_impls { - if impl_def_id.is_local() { - // This is where impl overlap checking happens: - let insert_result = sg.insert(tcx, impl_def_id); - // Report error if there was one. - let (overlap, used_to_be_allowed) = match insert_result { - Err(overlap) => (Some(overlap), None), - Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)), - Ok(None) => (None, None), - }; - - if let Some(overlap) = overlap { - let impl_span = - tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap()); - - // Work to be done after we've built the DiagnosticBuilder. We have to define it - // now because the struct_lint methods don't return back the DiagnosticBuilder - // that's passed in. - let decorate = |err: LintDiagnosticBuilder<'_>| { - let msg = format!( - "conflicting implementations of trait `{}`{}:{}", - overlap.trait_desc, - overlap - .self_desc - .clone() - .map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", - _ => "", - } - ); - let mut err = err.build(&msg); - match tcx.span_of_impl(overlap.with_impl) { - Ok(span) => { - err.span_label( - tcx.sess.source_map().def_span(span), - "first implementation here".to_string(), - ); - - err.span_label( - impl_span, - format!( - "conflicting implementation{}", - overlap - .self_desc - .map_or(String::new(), |ty| format!(" for `{}`", ty)) - ), - ); - } - Err(cname) => { - let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { - Some(s) => format!( - "conflicting implementation in crate `{}`:\n- {}", - cname, s - ), - None => format!("conflicting implementation in crate `{}`", cname), - }; - err.note(&msg); - } - } - - for cause in &overlap.intercrate_ambiguity_causes { - cause.add_intercrate_ambiguity_hint(&mut err); - } - - if overlap.involves_placeholder { - coherence::add_placeholder_note(&mut err); - } - err.emit() - }; - - match used_to_be_allowed { - None => { - let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); - decorate(LintDiagnosticBuilder::new(err)); - } - Some(kind) => { - let lint = match kind { - FutureCompatOverlapErrorKind::Issue33140 => { - ORDER_DEPENDENT_TRAIT_OBJECTS - } - FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, - }; - tcx.struct_span_lint_hir( - lint, - tcx.hir().as_local_hir_id(impl_def_id).unwrap(), - impl_span, - decorate, - ) - } - }; - } - } else { - let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); - sg.record_impl_from_cstore(tcx, parent, impl_def_id) - } - } - - tcx.arena.alloc(sg) -} - -/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a -/// string. -fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option { - use std::fmt::Write; - - let trait_ref = if let Some(tr) = tcx.impl_trait_ref(impl_def_id) { - tr - } else { - return None; - }; - - let mut w = "impl".to_owned(); - - let substs = InternalSubsts::identity_for_item(tcx, impl_def_id); - - // FIXME: Currently only handles ?Sized. - // Needs to support ?Move and ?DynSized when they are implemented. - let mut types_without_default_bounds = FxHashSet::default(); - let sized_trait = tcx.lang_items().sized_trait(); - - if !substs.is_noop() { - types_without_default_bounds.extend(substs.types()); - w.push('<'); - w.push_str( - &substs - .iter() - .map(|k| k.to_string()) - .filter(|k| k != "'_") - .collect::>() - .join(", "), - ); - w.push('>'); - } - - write!(w, " {} for {}", trait_ref.print_only_trait_path(), tcx.type_of(impl_def_id)).unwrap(); - - // The predicates will contain default bounds like `T: Sized`. We need to - // remove these bounds, and add `T: ?Sized` to any untouched type parameters. - let predicates = tcx.predicates_of(impl_def_id).predicates; - let mut pretty_predicates = - Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); - - for (p, _) in predicates { - if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { - if Some(poly_trait_ref.def_id()) == sized_trait { - types_without_default_bounds.remove(poly_trait_ref.self_ty()); - continue; - } - } - pretty_predicates.push(p.to_string()); - } - - pretty_predicates - .extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty))); - - if !pretty_predicates.is_empty() { - write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); - } - - w.push(';'); - Some(w) -} diff --git a/src/librustc_infer/traits/specialize/specialization_graph.rs b/src/librustc_infer/traits/specialize/specialization_graph.rs deleted file mode 100644 index 17d4a22b9dd..00000000000 --- a/src/librustc_infer/traits/specialize/specialization_graph.rs +++ /dev/null @@ -1,379 +0,0 @@ -use super::OverlapError; - -use crate::traits; -use rustc::ty::fast_reject::{self, SimplifiedType}; -use rustc::ty::{self, TyCtxt, TypeFoldable}; -use rustc_hir::def_id::DefId; - -pub use rustc::traits::specialization_graph::*; - -#[derive(Copy, Clone, Debug)] -pub enum FutureCompatOverlapErrorKind { - Issue33140, - LeakCheck, -} - -#[derive(Debug)] -pub struct FutureCompatOverlapError { - pub error: OverlapError, - pub kind: FutureCompatOverlapErrorKind, -} - -/// The result of attempting to insert an impl into a group of children. -enum Inserted { - /// The impl was inserted as a new child in this group of children. - BecameNewSibling(Option), - - /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc. - ReplaceChildren(Vec), - - /// The impl is a specialization of an existing child. - ShouldRecurseOn(DefId), -} - -trait ChildrenExt { - fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); - fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); - - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - simplified_self: Option, - ) -> Result; -} - -impl ChildrenExt for Children { - /// Insert an impl into this set of children without comparing to any existing impls. - fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); - self.nonblanket_impls.entry(st).or_default().push(impl_def_id) - } else { - debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id); - self.blanket_impls.push(impl_def_id) - } - } - - /// Removes an impl from this set of children. Used when replacing - /// an impl with a parent. The impl must be present in the list of - /// children already. - fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let vec: &mut Vec; - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); - vec = self.nonblanket_impls.get_mut(&st).unwrap(); - } else { - debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id); - vec = &mut self.blanket_impls; - } - - let index = vec.iter().position(|d| *d == impl_def_id).unwrap(); - vec.remove(index); - } - - /// Attempt to insert an impl into this set of children, while comparing for - /// specialization relationships. - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - simplified_self: Option, - ) -> Result { - let mut last_lint = None; - let mut replace_children = Vec::new(); - - debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,); - - let possible_siblings = match simplified_self { - Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)), - None => PotentialSiblings::Unfiltered(iter_children(self)), - }; - - for possible_sibling in possible_siblings { - debug!( - "insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}", - impl_def_id, simplified_self, possible_sibling, - ); - - let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| { - let trait_ref = overlap.impl_header.trait_ref.unwrap(); - let self_ty = trait_ref.self_ty(); - - OverlapError { - with_impl: possible_sibling, - trait_desc: trait_ref.print_only_trait_path().to_string(), - // Only report the `Self` type if it has at least - // some outer concrete shell; otherwise, it's - // not adding much information. - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, - involves_placeholder: overlap.involves_placeholder, - } - }; - - let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>, - last_lint: &mut _| { - // Found overlap, but no specialization; error out or report future-compat warning. - - // Do we *still* get overlap if we disable the future-incompatible modes? - let should_err = traits::overlapping_impls( - tcx, - possible_sibling, - impl_def_id, - traits::SkipLeakCheck::default(), - |_| true, - || false, - ); - - let error = create_overlap_error(overlap); - - if should_err { - Err(error) - } else { - *last_lint = Some(FutureCompatOverlapError { - error, - kind: FutureCompatOverlapErrorKind::LeakCheck, - }); - - Ok((false, false)) - } - }; - - let last_lint_mut = &mut last_lint; - let (le, ge) = traits::overlapping_impls( - tcx, - possible_sibling, - impl_def_id, - traits::SkipLeakCheck::Yes, - |overlap| { - if let Some(overlap_kind) = - tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) - { - match overlap_kind { - ty::ImplOverlapKind::Permitted { marker: _ } => {} - ty::ImplOverlapKind::Issue33140 => { - *last_lint_mut = Some(FutureCompatOverlapError { - error: create_overlap_error(overlap), - kind: FutureCompatOverlapErrorKind::Issue33140, - }); - } - } - - return Ok((false, false)); - } - - let le = tcx.specializes((impl_def_id, possible_sibling)); - let ge = tcx.specializes((possible_sibling, impl_def_id)); - - if le == ge { - report_overlap_error(overlap, last_lint_mut) - } else { - Ok((le, ge)) - } - }, - || Ok((false, false)), - )?; - - if le && !ge { - debug!( - "descending as child of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap() - ); - - // The impl specializes `possible_sibling`. - return Ok(Inserted::ShouldRecurseOn(possible_sibling)); - } else if ge && !le { - debug!( - "placing as parent of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap() - ); - - replace_children.push(possible_sibling); - } else { - // Either there's no overlap, or the overlap was already reported by - // `overlap_error`. - } - } - - if !replace_children.is_empty() { - return Ok(Inserted::ReplaceChildren(replace_children)); - } - - // No overlap with any potential siblings, so add as a new sibling. - debug!("placing as new sibling"); - self.insert_blindly(tcx, impl_def_id); - Ok(Inserted::BecameNewSibling(last_lint)) - } -} - -fn iter_children(children: &mut Children) -> impl Iterator + '_ { - let nonblanket = children.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter()); - children.blanket_impls.iter().chain(nonblanket).cloned() -} - -fn filtered_children( - children: &mut Children, - st: SimplifiedType, -) -> impl Iterator + '_ { - let nonblanket = children.nonblanket_impls.entry(st).or_default().iter(); - children.blanket_impls.iter().chain(nonblanket).cloned() -} - -// A custom iterator used by Children::insert -enum PotentialSiblings -where - I: Iterator, - J: Iterator, -{ - Unfiltered(I), - Filtered(J), -} - -impl Iterator for PotentialSiblings -where - I: Iterator, - J: Iterator, -{ - type Item = DefId; - - fn next(&mut self) -> Option { - match *self { - PotentialSiblings::Unfiltered(ref mut iter) => iter.next(), - PotentialSiblings::Filtered(ref mut iter) => iter.next(), - } - } -} - -pub trait GraphExt { - /// Insert a local impl into the specialization graph. If an existing impl - /// conflicts with it (has overlap, but neither specializes the other), - /// information about the area of overlap is returned in the `Err`. - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - ) -> Result, OverlapError>; - - /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId); -} - -impl GraphExt for Graph { - /// Insert a local impl into the specialization graph. If an existing impl - /// conflicts with it (has overlap, but neither specializes the other), - /// information about the area of overlap is returned in the `Err`. - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - ) -> Result, OverlapError> { - assert!(impl_def_id.is_local()); - - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let trait_def_id = trait_ref.def_id; - - debug!( - "insert({:?}): inserting TraitRef {:?} into specialization graph", - impl_def_id, trait_ref - ); - - // If the reference itself contains an earlier error (e.g., due to a - // resolution failure), then we just insert the impl at the top level of - // the graph and claim that there's no overlap (in order to suppress - // bogus errors). - if trait_ref.references_error() { - debug!( - "insert: inserting dummy node for erroneous TraitRef {:?}, \ - impl_def_id={:?}, trait_def_id={:?}", - trait_ref, impl_def_id, trait_def_id - ); - - self.parent.insert(impl_def_id, trait_def_id); - self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id); - return Ok(None); - } - - let mut parent = trait_def_id; - let mut last_lint = None; - let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false); - - // Descend the specialization tree, where `parent` is the current parent node. - loop { - use self::Inserted::*; - - let insert_result = - self.children.entry(parent).or_default().insert(tcx, impl_def_id, simplified)?; - - match insert_result { - BecameNewSibling(opt_lint) => { - last_lint = opt_lint; - break; - } - ReplaceChildren(grand_children_to_be) => { - // We currently have - // - // P - // | - // G - // - // and we are inserting the impl N. We want to make it: - // - // P - // | - // N - // | - // G - - // Adjust P's list of children: remove G and then add N. - { - let siblings = self.children.get_mut(&parent).unwrap(); - for &grand_child_to_be in &grand_children_to_be { - siblings.remove_existing(tcx, grand_child_to_be); - } - siblings.insert_blindly(tcx, impl_def_id); - } - - // Set G's parent to N and N's parent to P. - for &grand_child_to_be in &grand_children_to_be { - self.parent.insert(grand_child_to_be, impl_def_id); - } - self.parent.insert(impl_def_id, parent); - - // Add G as N's child. - for &grand_child_to_be in &grand_children_to_be { - self.children - .entry(impl_def_id) - .or_default() - .insert_blindly(tcx, grand_child_to_be); - } - break; - } - ShouldRecurseOn(new_parent) => { - parent = new_parent; - } - } - } - - self.parent.insert(impl_def_id, parent); - Ok(last_lint) - } - - /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) { - if self.parent.insert(child, parent).is_some() { - bug!( - "When recording an impl from the crate store, information about its parent \ - was already present." - ); - } - - self.children.entry(parent).or_default().insert_blindly(tcx, child); - } -} diff --git a/src/librustc_infer/traits/structural_match.rs b/src/librustc_infer/traits/structural_match.rs deleted file mode 100644 index 60682f58129..00000000000 --- a/src/librustc_infer/traits/structural_match.rs +++ /dev/null @@ -1,216 +0,0 @@ -use crate::infer::{InferCtxt, TyCtxtInferExt}; -use crate::traits::ObligationCause; -use crate::traits::{self, ConstPatternStructural, TraitEngine}; - -use rustc::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor}; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_span::Span; - -#[derive(Debug)] -pub enum NonStructuralMatchTy<'tcx> { - Adt(&'tcx AdtDef), - Param, -} - -/// This method traverses the structure of `ty`, trying to find an -/// instance of an ADT (i.e. struct or enum) that was declared without -/// the `#[structural_match]` attribute, or a generic type parameter -/// (which cannot be determined to be `structural_match`). -/// -/// The "structure of a type" includes all components that would be -/// considered when doing a pattern match on a constant of that -/// type. -/// -/// * This means this method descends into fields of structs/enums, -/// and also descends into the inner type `T` of `&T` and `&mut T` -/// -/// * The traversal doesn't dereference unsafe pointers (`*const T`, -/// `*mut T`), and it does not visit the type arguments of an -/// instantiated generic like `PhantomData`. -/// -/// The reason we do this search is Rust currently require all ADTs -/// reachable from a constant's type to be annotated with -/// `#[structural_match]`, an attribute which essentially says that -/// the implementation of `PartialEq::eq` behaves *equivalently* to a -/// comparison against the unfolded structure. -/// -/// For more background on why Rust has this requirement, and issues -/// that arose when the requirement was not enforced completely, see -/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -pub fn search_for_structural_match_violation<'tcx>( - id: hir::HirId, - span: Span, - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option> { - // FIXME: we should instead pass in an `infcx` from the outside. - tcx.infer_ctxt().enter(|infcx| { - let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() }; - ty.visit_with(&mut search); - search.found - }) -} - -/// This method returns true if and only if `adt_ty` itself has been marked as -/// eligible for structural-match: namely, if it implements both -/// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by -/// `#[derive(PartialEq)]` and `#[derive(Eq)]`). -/// -/// Note that this does *not* recursively check if the substructure of `adt_ty` -/// implements the traits. -pub fn type_marked_structural( - id: hir::HirId, - span: Span, - infcx: &InferCtxt<'_, 'tcx>, - adt_ty: Ty<'tcx>, -) -> bool { - let mut fulfillment_cx = traits::FulfillmentContext::new(); - let cause = ObligationCause::new(span, id, ConstPatternStructural); - // require `#[derive(PartialEq)]` - let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap(); - fulfillment_cx.register_bound( - infcx, - ty::ParamEnv::empty(), - adt_ty, - structural_peq_def_id, - cause, - ); - // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around - // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) - let cause = ObligationCause::new(span, id, ConstPatternStructural); - let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap(); - fulfillment_cx.register_bound( - infcx, - ty::ParamEnv::empty(), - adt_ty, - structural_teq_def_id, - cause, - ); - - // We deliberately skip *reporting* fulfillment errors (via - // `report_fulfillment_errors`), for two reasons: - // - // 1. The error messages would mention `std::marker::StructuralPartialEq` - // (a trait which is solely meant as an implementation detail - // for now), and - // - // 2. We are sometimes doing future-incompatibility lints for - // now, so we do not want unconditional errors here. - fulfillment_cx.select_all_or_error(infcx).is_ok() -} - -/// This implements the traversal over the structure of a given type to try to -/// find instances of ADTs (specifically structs or enums) that do not implement -/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). -struct Search<'a, 'tcx> { - id: hir::HirId, - span: Span, - - infcx: InferCtxt<'a, 'tcx>, - - /// Records first ADT that does not implement a structural-match trait. - found: Option>, - - /// Tracks ADTs previously encountered during search, so that - /// we will not recur on them again. - seen: FxHashSet, -} - -impl Search<'a, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { - type_marked_structural(self.id, self.span, &self.infcx, adt_ty) - } -} - -impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - debug!("Search visiting ty: {:?}", ty); - - let (adt_def, substs) = match ty.kind { - ty::Adt(adt_def, substs) => (adt_def, substs), - ty::Param(_) => { - self.found = Some(NonStructuralMatchTy::Param); - return true; // Stop visiting. - } - ty::RawPtr(..) => { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - - // (But still tell caller to continue search.) - return false; - } - ty::FnDef(..) | ty::FnPtr(..) => { - // types of formals and return in `fn(_) -> _` are also irrelevant; - // so we do not recur into them via `super_visit_with` - // - // (But still tell caller to continue search.) - return false; - } - ty::Array(_, n) - if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } => - { - // rust-lang/rust#62336: ignore type of contents - // for empty array. - return false; - } - _ => { - ty.super_visit_with(self); - return false; - } - }; - - if !self.seen.insert(adt_def.did) { - debug!("Search already seen adt_def: {:?}", adt_def); - // let caller continue its search - return false; - } - - if !self.type_marked_structural(ty) { - debug!("Search found ty: {:?}", ty); - self.found = Some(NonStructuralMatchTy::Adt(&adt_def)); - return true; // Halt visiting! - } - - // structural-match does not care about the - // instantiation of the generics in an ADT (it - // instead looks directly at its fields outside - // this match), so we skip super_visit_with. - // - // (Must not recur on substs for `PhantomData` cf - // rust-lang/rust#55028 and rust-lang/rust#55837; but also - // want to skip substs when only uses of generic are - // behind unsafe pointers `*const T`/`*mut T`.) - - // even though we skip super_visit_with, we must recur on - // fields of ADT. - let tcx = self.tcx(); - for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) { - if field_ty.visit_with(self) { - // found an ADT without structural-match; halt visiting! - assert!(self.found.is_some()); - return true; - } - } - - // Even though we do not want to recur on substs, we do - // want our caller to continue its own search. - false - } -} diff --git a/src/librustc_infer/traits/util.rs b/src/librustc_infer/traits/util.rs index 1dca01b3468..a7c02671115 100644 --- a/src/librustc_infer/traits/util.rs +++ b/src/librustc_infer/traits/util.rs @@ -1,16 +1,8 @@ -use rustc_errors::DiagnosticBuilder; -use rustc_span::Span; use smallvec::smallvec; -use smallvec::SmallVec; use rustc::ty::outlives::Component; -use rustc::ty::subst::{GenericArg, Subst, SubstsRef}; -use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc::ty::{self, ToPolyTraitRef, TyCtxt}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; - -use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; fn anonymize_predicate<'tcx>(tcx: TyCtxt<'tcx>, pred: &ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { match *pred { @@ -96,21 +88,6 @@ pub struct Elaborator<'tcx> { visited: PredicateSet<'tcx>, } -pub fn elaborate_trait_ref<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> Elaborator<'tcx> { - elaborate_predicates(tcx, vec![trait_ref.without_const().to_predicate()]) -} - -pub fn elaborate_trait_refs<'tcx>( - tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator>, -) -> Elaborator<'tcx> { - let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate()).collect(); - elaborate_predicates(tcx, predicates) -} - pub fn elaborate_predicates<'tcx>( tcx: TyCtxt<'tcx>, mut predicates: Vec>, @@ -121,10 +98,6 @@ pub fn elaborate_predicates<'tcx>( } impl Elaborator<'tcx> { - pub fn filter_to_traits(self) -> FilterToTraits { - FilterToTraits::new(self) - } - fn elaborate(&mut self, predicate: &ty::Predicate<'tcx>) { let tcx = self.visited.tcx; match *predicate { @@ -250,426 +223,3 @@ fn next(&mut self) -> Option> { } } } - -/////////////////////////////////////////////////////////////////////////// -// Supertrait iterator -/////////////////////////////////////////////////////////////////////////// - -pub type Supertraits<'tcx> = FilterToTraits>; - -pub fn supertraits<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> Supertraits<'tcx> { - elaborate_trait_ref(tcx, trait_ref).filter_to_traits() -} - -pub fn transitive_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - bounds: impl Iterator>, -) -> Supertraits<'tcx> { - elaborate_trait_refs(tcx, bounds).filter_to_traits() -} - -/////////////////////////////////////////////////////////////////////////// -// `TraitAliasExpander` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Trait alias expansion" is the process of expanding a sequence of trait -/// references into another sequence by transitively following all trait -/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias -/// `trait Foo = Bar + Sync;`, and another trait alias -/// `trait Bar = Read + Write`, then the bounds would expand to -/// `Read + Write + Sync + Send`. -/// Expansion is done via a DFS (depth-first search), and the `visited` field -/// is used to avoid cycles. -pub struct TraitAliasExpander<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec>, -} - -/// Stores information about the expansion of a trait via a path of zero or more trait aliases. -#[derive(Debug, Clone)] -pub struct TraitAliasExpansionInfo<'tcx> { - pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, -} - -impl<'tcx> TraitAliasExpansionInfo<'tcx> { - fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - Self { path: smallvec![(trait_ref, span)] } - } - - /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate - /// trait aliases. - pub fn label_with_exp_info( - &self, - diag: &mut DiagnosticBuilder<'_>, - top_label: &str, - use_desc: &str, - ) { - diag.span_label(self.top().1, top_label); - if self.path.len() > 1 { - for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { - diag.span_label(*sp, format!("referenced here ({})", use_desc)); - } - } - diag.span_label( - self.bottom().1, - format!("trait alias used in trait object type ({})", use_desc), - ); - } - - pub fn trait_ref(&self) -> &ty::PolyTraitRef<'tcx> { - &self.top().0 - } - - pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.last().unwrap() - } - - pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.first().unwrap() - } - - fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - let mut path = self.path.clone(); - path.push((trait_ref, span)); - - Self { path } - } -} - -pub fn expand_trait_aliases<'tcx>( - tcx: TyCtxt<'tcx>, - trait_refs: impl IntoIterator, Span)>, -) -> TraitAliasExpander<'tcx> { - let items: Vec<_> = trait_refs - .into_iter() - .map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)) - .collect(); - TraitAliasExpander { tcx, stack: items } -} - -impl<'tcx> TraitAliasExpander<'tcx> { - /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` - /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. - /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a - /// trait alias. - /// The return value indicates whether `item` should be yielded to the user. - fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { - let tcx = self.tcx; - let trait_ref = item.trait_ref(); - let pred = trait_ref.without_const().to_predicate(); - - debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); - - // Don't recurse if this bound is not a trait alias. - let is_alias = tcx.is_trait_alias(trait_ref.def_id()); - if !is_alias { - return true; - } - - // Don't recurse if this trait alias is already on the stack for the DFS search. - let anon_pred = anonymize_predicate(tcx, &pred); - if item.path.iter().rev().skip(1).any(|(tr, _)| { - anonymize_predicate(tcx, &tr.without_const().to_predicate()) == anon_pred - }) { - return false; - } - - // Get components of trait alias. - let predicates = tcx.super_predicates_of(trait_ref.def_id()); - - let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { - pred.subst_supertrait(tcx, &trait_ref) - .to_opt_poly_trait_ref() - .map(|trait_ref| item.clone_and_push(trait_ref, *span)) - }); - debug!("expand_trait_aliases: items={:?}", items.clone()); - - self.stack.extend(items); - - false - } -} - -impl<'tcx> Iterator for TraitAliasExpander<'tcx> { - type Item = TraitAliasExpansionInfo<'tcx>; - - fn size_hint(&self) -> (usize, Option) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option> { - while let Some(item) = self.stack.pop() { - if self.expand(&item) { - return Some(item); - } - } - None - } -} - -/////////////////////////////////////////////////////////////////////////// -// Iterator over def-IDs of supertraits -/////////////////////////////////////////////////////////////////////////// - -pub struct SupertraitDefIds<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec, - visited: FxHashSet, -} - -pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> { - SupertraitDefIds { - tcx, - stack: vec![trait_def_id], - visited: Some(trait_def_id).into_iter().collect(), - } -} - -impl Iterator for SupertraitDefIds<'tcx> { - type Item = DefId; - - fn next(&mut self) -> Option { - let def_id = self.stack.pop()?; - let predicates = self.tcx.super_predicates_of(def_id); - let visited = &mut self.visited; - self.stack.extend( - predicates - .predicates - .iter() - .filter_map(|(pred, _)| pred.to_opt_poly_trait_ref()) - .map(|trait_ref| trait_ref.def_id()) - .filter(|&super_def_id| visited.insert(super_def_id)), - ); - Some(def_id) - } -} - -/////////////////////////////////////////////////////////////////////////// -// Other -/////////////////////////////////////////////////////////////////////////// - -/// A filter around an iterator of predicates that makes it yield up -/// just trait references. -pub struct FilterToTraits { - base_iterator: I, -} - -impl FilterToTraits { - fn new(base: I) -> FilterToTraits { - FilterToTraits { base_iterator: base } - } -} - -impl<'tcx, I: Iterator>> Iterator for FilterToTraits { - type Item = ty::PolyTraitRef<'tcx>; - - fn next(&mut self) -> Option> { - while let Some(pred) = self.base_iterator.next() { - if let ty::Predicate::Trait(data, _) = pred { - return Some(data.to_poly_trait_ref()); - } - } - None - } - - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.base_iterator.size_hint(); - (0, upper) - } -} - -/////////////////////////////////////////////////////////////////////////// -// Other -/////////////////////////////////////////////////////////////////////////// - -/// Instantiate all bound parameters of the impl with the given substs, -/// returning the resulting trait ref and all obligations that arise. -/// The obligations are closed under normalization. -pub fn impl_trait_ref_and_oblig<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: DefId, - impl_substs: SubstsRef<'tcx>, -) -> (ty::TraitRef<'tcx>, Vec>) { - let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); - let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs); - let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = - super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref); - - let predicates = selcx.tcx().predicates_of(impl_def_id); - let predicates = predicates.instantiate(selcx.tcx(), impl_substs); - let Normalized { value: predicates, obligations: normalization_obligations2 } = - super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates); - let impl_obligations = - predicates_for_generics(ObligationCause::dummy(), 0, param_env, &predicates); - - let impl_obligations: Vec<_> = impl_obligations - .into_iter() - .chain(normalization_obligations1) - .chain(normalization_obligations2) - .collect(); - - (impl_trait_ref, impl_obligations) -} - -/// See [`super::obligations_for_generics`]. -pub fn predicates_for_generics<'tcx>( - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - generic_bounds: &ty::InstantiatedPredicates<'tcx>, -) -> Vec> { - debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); - - generic_bounds - .predicates - .iter() - .map(|&predicate| Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }) - .collect() -} - -pub fn predicate_for_trait_ref<'tcx>( - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - recursion_depth: usize, -) -> PredicateObligation<'tcx> { - Obligation { - cause, - param_env, - recursion_depth, - predicate: trait_ref.without_const().to_predicate(), - } -} - -pub fn predicate_for_trait_def( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - trait_def_id: DefId, - recursion_depth: usize, - self_ty: Ty<'tcx>, - params: &[GenericArg<'tcx>], -) -> PredicateObligation<'tcx> { - let trait_ref = - ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) }; - predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth) -} - -/// Casts a trait reference into a reference to one of its super -/// traits; returns `None` if `target_trait_def_id` is not a -/// supertrait. -pub fn upcast_choices( - tcx: TyCtxt<'tcx>, - source_trait_ref: ty::PolyTraitRef<'tcx>, - target_trait_def_id: DefId, -) -> Vec> { - if source_trait_ref.def_id() == target_trait_def_id { - return vec![source_trait_ref]; // Shortcut the most common case. - } - - supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() -} - -/// Given a trait `trait_ref`, returns the number of vtable entries -/// that come from `trait_ref`, excluding its supertraits. Used in -/// computing the vtable base for an upcast trait of a trait object. -pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize { - let mut entries = 0; - // Count number of methods and add them to the total offset. - // Skip over associated types and constants. - for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() { - if trait_item.kind == ty::AssocKind::Method { - entries += 1; - } - } - entries -} - -/// Given an upcast trait object described by `object`, returns the -/// index of the method `method_def_id` (which should be part of -/// `object.upcast_trait_ref`) within the vtable for `object`. -pub fn get_vtable_index_of_object_method( - tcx: TyCtxt<'tcx>, - object: &super::VtableObjectData<'tcx, N>, - method_def_id: DefId, -) -> usize { - // Count number of methods preceding the one we are selecting and - // add them to the total offset. - // Skip over associated types and constants. - let mut entries = object.vtable_base; - for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() { - if trait_item.def_id == method_def_id { - // The item with the ID we were given really ought to be a method. - assert_eq!(trait_item.kind, ty::AssocKind::Method); - return entries; - } - if trait_item.kind == ty::AssocKind::Method { - entries += 1; - } - } - - bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id); -} - -pub fn closure_trait_ref_and_return_type( - tcx: TyCtxt<'tcx>, - fn_trait_def_id: DefId, - self_ty: Ty<'tcx>, - sig: ty::PolyFnSig<'tcx>, - tuple_arguments: TupleArgumentsFlag, -) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> { - let arguments_tuple = match tuple_arguments { - TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], - TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()), - }; - let trait_ref = ty::TraitRef { - def_id: fn_trait_def_id, - substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]), - }; - ty::Binder::bind((trait_ref, sig.skip_binder().output())) -} - -pub fn generator_trait_ref_and_outputs( - tcx: TyCtxt<'tcx>, - fn_trait_def_id: DefId, - self_ty: Ty<'tcx>, - sig: ty::PolyGenSig<'tcx>, -) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { - let trait_ref = ty::TraitRef { - def_id: fn_trait_def_id, - substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]), - }; - ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty)) -} - -pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool { - match tcx.hir().as_local_hir_id(node_item_def_id) { - Some(hir_id) => { - let item = tcx.hir().expect_item(hir_id); - if let hir::ItemKind::Impl { defaultness, .. } = item.kind { - defaultness.is_default() - } else { - false - } - } - None => tcx.impl_defaultness(node_item_def_id).is_default(), - } -} - -pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { - assoc_item.defaultness.is_final() && !impl_is_default(tcx, assoc_item.container.id()) -} - -pub enum TupleArgumentsFlag { - Yes, - No, -} diff --git a/src/librustc_infer/traits/wf.rs b/src/librustc_infer/traits/wf.rs deleted file mode 100644 index 980a3f04781..00000000000 --- a/src/librustc_infer/traits/wf.rs +++ /dev/null @@ -1,753 +0,0 @@ -use crate::infer::opaque_types::required_region_bounds; -use crate::infer::InferCtxt; -use crate::traits::{self, AssocTypeBoundData}; -use rustc::middle::lang_items; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::symbol::{kw, Ident}; -use rustc_span::Span; - -/// Returns the set of obligations needed to make `ty` well-formed. -/// If `ty` contains unresolved inference variables, this may include -/// further WF obligations. However, if `ty` IS an unresolved -/// inference variable, returns `None`, because we are not able to -/// make any progress at all. This is to prevent "livelock" where we -/// say "$0 is WF if $0 is WF". -pub fn obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - ty: Ty<'tcx>, - span: Span, -) -> Option>> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; - if wf.compute(ty) { - debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); - - let result = wf.normalize(); - debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result); - Some(result) - } else { - None // no progress made, return None - } -} - -/// Returns the obligations that make this trait reference -/// well-formed. For example, if there is a trait `Set` defined like -/// `trait Set`, then the trait reference `Foo: Set` is WF -/// if `Bar: Eq`. -pub fn trait_obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - trait_ref: &ty::TraitRef<'tcx>, - span: Span, - item: Option<&'tcx hir::Item<'tcx>>, -) -> Vec> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item }; - wf.compute_trait_ref(trait_ref, Elaborate::All); - wf.normalize() -} - -pub fn predicate_obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - predicate: &ty::Predicate<'tcx>, - span: Span, -) -> Vec> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; - - // (*) ok to skip binders, because wf code is prepared for it - match *predicate { - ty::Predicate::Trait(ref t, _) => { - wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*) - } - ty::Predicate::RegionOutlives(..) => {} - ty::Predicate::TypeOutlives(ref t) => { - wf.compute(t.skip_binder().0); - } - ty::Predicate::Projection(ref t) => { - let t = t.skip_binder(); // (*) - wf.compute_projection(t.projection_ty); - wf.compute(t.ty); - } - ty::Predicate::WellFormed(t) => { - wf.compute(t); - } - ty::Predicate::ObjectSafe(_) => {} - ty::Predicate::ClosureKind(..) => {} - ty::Predicate::Subtype(ref data) => { - wf.compute(data.skip_binder().a); // (*) - wf.compute(data.skip_binder().b); // (*) - } - ty::Predicate::ConstEvaluatable(def_id, substs) => { - let obligations = wf.nominal_obligations(def_id, substs); - wf.out.extend(obligations); - - for ty in substs.types() { - wf.compute(ty); - } - } - } - - wf.normalize() -} - -struct WfPredicates<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - span: Span, - out: Vec>, - item: Option<&'tcx hir::Item<'tcx>>, -} - -/// Controls whether we "elaborate" supertraits and so forth on the WF -/// predicates. This is a kind of hack to address #43784. The -/// underlying problem in that issue was a trait structure like: -/// -/// ``` -/// trait Foo: Copy { } -/// trait Bar: Foo { } -/// impl Foo for T { } -/// impl Bar for T { } -/// ``` -/// -/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but -/// we decide that this is true because `T: Bar` is in the -/// where-clauses (and we can elaborate that to include `T: -/// Copy`). This wouldn't be a problem, except that when we check the -/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo` -/// impl. And so nowhere did we check that `T: Copy` holds! -/// -/// To resolve this, we elaborate the WF requirements that must be -/// proven when checking impls. This means that (e.g.) the `impl Bar -/// for T` will be forced to prove not only that `T: Foo` but also `T: -/// Copy` (which it won't be able to do, because there is no `Copy` -/// impl for `T`). -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -enum Elaborate { - All, - None, -} - -impl<'a, 'tcx> WfPredicates<'a, 'tcx> { - fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { - traits::ObligationCause::new(self.span, self.body_id, code) - } - - fn normalize(&mut self) -> Vec> { - let cause = self.cause(traits::MiscObligation); - let infcx = &mut self.infcx; - let param_env = self.param_env; - let mut obligations = Vec::with_capacity(self.out.len()); - for pred in &self.out { - assert!(!pred.has_escaping_bound_vars()); - let mut selcx = traits::SelectionContext::new(infcx); - let i = obligations.len(); - let value = - traits::normalize_to(&mut selcx, param_env, cause.clone(), pred, &mut obligations); - obligations.insert(i, value); - } - obligations - } - - /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. - fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) { - let tcx = self.infcx.tcx; - let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); - - let cause = self.cause(traits::MiscObligation); - let param_env = self.param_env; - - let item = &self.item; - let extend_cause_with_original_assoc_item_obligation = - |cause: &mut traits::ObligationCause<'_>, - pred: &ty::Predicate<'_>, - trait_assoc_items: &[ty::AssocItem]| { - let trait_item = tcx - .hir() - .as_local_hir_id(trait_ref.def_id) - .and_then(|trait_id| tcx.hir().find(trait_id)); - let (trait_name, trait_generics) = match trait_item { - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Trait(.., generics, _, _), - .. - })) - | Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::TraitAlias(generics, _), - .. - })) => (Some(ident), Some(generics)), - _ => (None, None), - }; - - let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span)); - match pred { - ty::Predicate::Projection(proj) => { - // The obligation comes not from the current `impl` nor the `trait` being - // implemented, but rather from a "second order" obligation, like in - // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`: - // - // error[E0271]: type mismatch resolving `::Ok == ()` - // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 - // | - // LL | type Ok; - // | -- associated type defined here - // ... - // LL | impl Bar for Foo { - // | ---------------- in this `impl` item - // LL | type Ok = (); - // | ^^^^^^^^^^^^^ expected `u32`, found `()` - // | - // = note: expected type `u32` - // found type `()` - // - // FIXME: we would want to point a span to all places that contributed to this - // obligation. In the case above, it should be closer to: - // - // error[E0271]: type mismatch resolving `::Ok == ()` - // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 - // | - // LL | type Ok; - // | -- associated type defined here - // LL | type Sibling: Bar2; - // | -------------------------------- obligation set here - // ... - // LL | impl Bar for Foo { - // | ---------------- in this `impl` item - // LL | type Ok = (); - // | ^^^^^^^^^^^^^ expected `u32`, found `()` - // ... - // LL | impl Bar2 for Foo2 { - // | ---------------- in this `impl` item - // LL | type Ok = u32; - // | -------------- obligation set here - // | - // = note: expected type `u32` - // found type `()` - if let Some(hir::ItemKind::Impl { items, .. }) = item.map(|i| &i.kind) { - let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); - if let Some(impl_item) = - items.iter().find(|item| item.ident == trait_assoc_item.ident) - { - cause.span = impl_item.span; - cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData { - impl_span: item_span, - original: trait_assoc_item.ident.span, - bounds: vec![], - })); - } - } - } - ty::Predicate::Trait(proj, _) => { - // An associated item obligation born out of the `trait` failed to be met. - // Point at the `impl` that failed the obligation, the associated item that - // needed to meet the obligation, and the definition of that associated item, - // which should hold the obligation in most cases. An example can be seen in - // `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`: - // - // error[E0277]: the trait bound `bool: Bar` is not satisfied - // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 - // | - // LL | type Assoc: Bar; - // | ----- associated type defined here - // ... - // LL | impl Foo for () { - // | --------------- in this `impl` item - // LL | type Assoc = bool; - // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` - // - // If the obligation comes from the where clause in the `trait`, we point at it: - // - // error[E0277]: the trait bound `bool: Bar` is not satisfied - // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 - // | - // | trait Foo where >::Assoc: Bar { - // | -------------------------- restricted in this bound - // LL | type Assoc; - // | ----- associated type defined here - // ... - // LL | impl Foo for () { - // | --------------- in this `impl` item - // LL | type Assoc = bool; - // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` - if let ( - ty::Projection(ty::ProjectionTy { item_def_id, .. }), - Some(hir::ItemKind::Impl { items, .. }), - ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) - { - if let Some((impl_item, trait_assoc_item)) = trait_assoc_items - .iter() - .find(|i| i.def_id == *item_def_id) - .and_then(|trait_assoc_item| { - items - .iter() - .find(|i| i.ident == trait_assoc_item.ident) - .map(|impl_item| (impl_item, trait_assoc_item)) - }) - { - let bounds = trait_generics - .map(|generics| { - get_generic_bound_spans( - &generics, - trait_name, - trait_assoc_item.ident, - ) - }) - .unwrap_or_else(Vec::new); - cause.span = impl_item.span; - cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData { - impl_span: item_span, - original: trait_assoc_item.ident.span, - bounds, - })); - } - } - } - _ => {} - } - }; - - if let Elaborate::All = elaborate { - // FIXME: Make `extend_cause_with_original_assoc_item_obligation` take an iterator - // instead of a slice. - let trait_assoc_items: Vec<_> = - tcx.associated_items(trait_ref.def_id).in_definition_order().copied().collect(); - - let predicates = obligations.iter().map(|obligation| obligation.predicate).collect(); - let implied_obligations = traits::elaborate_predicates(tcx, predicates); - let implied_obligations = implied_obligations.map(|pred| { - let mut cause = cause.clone(); - extend_cause_with_original_assoc_item_obligation( - &mut cause, - &pred, - &*trait_assoc_items, - ); - traits::Obligation::new(cause, param_env, pred) - }); - self.out.extend(implied_obligations); - } - - self.out.extend(obligations); - - self.out.extend(trait_ref.substs.types().filter(|ty| !ty.has_escaping_bound_vars()).map( - |ty| traits::Obligation::new(cause.clone(), param_env, ty::Predicate::WellFormed(ty)), - )); - } - - /// Pushes the obligations required for `trait_ref::Item` to be WF - /// into `self.out`. - fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) { - // A projection is well-formed if (a) the trait ref itself is - // WF and (b) the trait-ref holds. (It may also be - // normalizable and be WF that way.) - let trait_ref = data.trait_ref(self.infcx.tcx); - self.compute_trait_ref(&trait_ref, Elaborate::None); - - if !data.has_escaping_bound_vars() { - let predicate = trait_ref.without_const().to_predicate(); - let cause = self.cause(traits::ProjectionWf(data)); - self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); - } - } - - /// Pushes the obligations required for an array length to be WF - /// into `self.out`. - fn compute_array_len(&mut self, constant: ty::Const<'tcx>) { - if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.val { - assert!(promoted.is_none()); - - let obligations = self.nominal_obligations(def_id, substs); - self.out.extend(obligations); - - let predicate = ty::Predicate::ConstEvaluatable(def_id, substs); - let cause = self.cause(traits::MiscObligation); - self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); - } - } - - fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { - if !subty.has_escaping_bound_vars() { - let cause = self.cause(cause); - let trait_ref = ty::TraitRef { - def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None), - substs: self.infcx.tcx.mk_substs_trait(subty, &[]), - }; - self.out.push(traits::Obligation::new( - cause, - self.param_env, - trait_ref.without_const().to_predicate(), - )); - } - } - - /// Pushes new obligations into `out`. Returns `true` if it was able - /// to generate all the predicates needed to validate that `ty0` - /// is WF. Returns false if `ty0` is an unresolved type variable, - /// in which case we are not able to simplify at all. - fn compute(&mut self, ty0: Ty<'tcx>) -> bool { - let mut subtys = ty0.walk(); - let param_env = self.param_env; - while let Some(ty) = subtys.next() { - match ty.kind { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Error - | ty::Str - | ty::GeneratorWitness(..) - | ty::Never - | ty::Param(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Foreign(..) => { - // WfScalar, WfParameter, etc - } - - ty::Slice(subty) => { - self.require_sized(subty, traits::SliceOrArrayElem); - } - - ty::Array(subty, len) => { - self.require_sized(subty, traits::SliceOrArrayElem); - self.compute_array_len(*len); - } - - ty::Tuple(ref tys) => { - if let Some((_last, rest)) = tys.split_last() { - for elem in rest { - self.require_sized(elem.expect_ty(), traits::TupleElem); - } - } - } - - ty::RawPtr(_) => { - // simple cases that are WF if their type args are WF - } - - ty::Projection(data) => { - subtys.skip_current_subtree(); // subtree handled by compute_projection - self.compute_projection(data); - } - - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - - ty::Adt(def, substs) => { - // WfNominalType - let obligations = self.nominal_obligations(def.did, substs); - self.out.extend(obligations); - } - - ty::FnDef(did, substs) => { - let obligations = self.nominal_obligations(did, substs); - self.out.extend(obligations); - } - - ty::Ref(r, rty, _) => { - // WfReference - if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { - let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); - self.out.push(traits::Obligation::new( - cause, - param_env, - ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate( - rty, r, - ))), - )); - } - } - - ty::Generator(..) => { - // Walk ALL the types in the generator: this will - // include the upvar types as well as the yield - // type. Note that this is mildly distinct from - // the closure case, where we have to be careful - // about the signature of the closure. We don't - // have the problem of implied bounds here since - // generators don't take arguments. - } - - ty::Closure(def_id, substs) => { - // Only check the upvar types for WF, not the rest - // of the types within. This is needed because we - // capture the signature and it may not be WF - // without the implied bounds. Consider a closure - // like `|x: &'a T|` -- it may be that `T: 'a` is - // not known to hold in the creator's context (and - // indeed the closure may not be invoked by its - // creator, but rather turned to someone who *can* - // verify that). - // - // The special treatment of closures here really - // ought not to be necessary either; the problem - // is related to #25860 -- there is no way for us - // to express a fn type complete with the implied - // bounds that it is assuming. I think in reality - // the WF rules around fn are a bit messed up, and - // that is the rot problem: `fn(&'a T)` should - // probably always be WF, because it should be - // shorthand for something like `where(T: 'a) { - // fn(&'a T) }`, as discussed in #25860. - // - // Note that we are also skipping the generic - // types. This is consistent with the `outlives` - // code, but anyway doesn't matter: within the fn - // body where they are created, the generics will - // always be WF, and outside of that fn body we - // are not directly inspecting closure types - // anyway, except via auto trait matching (which - // only inspects the upvar types). - subtys.skip_current_subtree(); // subtree handled by compute_projection - for upvar_ty in substs.as_closure().upvar_tys(def_id, self.infcx.tcx) { - self.compute(upvar_ty); - } - } - - ty::FnPtr(_) => { - // let the loop iterate into the argument/return - // types appearing in the fn signature - } - - ty::Opaque(did, substs) => { - // all of the requirements on type parameters - // should've been checked by the instantiation - // of whatever returned this exact `impl Trait`. - - // for named opaque `impl Trait` types we still need to check them - if ty::is_impl_trait_defn(self.infcx.tcx, did).is_none() { - let obligations = self.nominal_obligations(did, substs); - self.out.extend(obligations); - } - } - - ty::Dynamic(data, r) => { - // WfObject - // - // Here, we defer WF checking due to higher-ranked - // regions. This is perhaps not ideal. - self.from_object_ty(ty, data, r); - - // FIXME(#27579) RFC also considers adding trait - // obligations that don't refer to Self and - // checking those - - let defer_to_coercion = self.infcx.tcx.features().object_safe_for_dispatch; - - if !defer_to_coercion { - let cause = self.cause(traits::MiscObligation); - let component_traits = data.auto_traits().chain(data.principal_def_id()); - self.out.extend(component_traits.map(|did| { - traits::Obligation::new( - cause.clone(), - param_env, - ty::Predicate::ObjectSafe(did), - ) - })); - } - } - - // Inference variables are the complicated case, since we don't - // know what type they are. We do two things: - // - // 1. Check if they have been resolved, and if so proceed with - // THAT type. - // 2. If not, check whether this is the type that we - // started with (ty0). In that case, we've made no - // progress at all, so return false. Otherwise, - // we've at least simplified things (i.e., we went - // from `Vec<$0>: WF` to `$0: WF`, so we can - // register a pending obligation and keep - // moving. (Goal is that an "inductive hypothesis" - // is satisfied to ensure termination.) - ty::Infer(_) => { - let ty = self.infcx.shallow_resolve(ty); - if let ty::Infer(_) = ty.kind { - // not yet resolved... - if ty == ty0 { - // ...this is the type we started from! no progress. - return false; - } - - let cause = self.cause(traits::MiscObligation); - self.out.push( - // ...not the type we started from, so we made progress. - traits::Obligation::new( - cause, - self.param_env, - ty::Predicate::WellFormed(ty), - ), - ); - } else { - // Yes, resolved, proceed with the - // result. Should never return false because - // `ty` is not a Infer. - assert!(self.compute(ty)); - } - } - } - } - - // if we made it through that loop above, we made progress! - return true; - } - - fn nominal_obligations( - &mut self, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Vec> { - let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs); - let cause = self.cause(traits::ItemObligation(def_id)); - predicates - .predicates - .into_iter() - .map(|pred| traits::Obligation::new(cause.clone(), self.param_env, pred)) - .filter(|pred| !pred.has_escaping_bound_vars()) - .collect() - } - - fn from_object_ty( - &mut self, - ty: Ty<'tcx>, - data: ty::Binder<&'tcx ty::List>>, - region: ty::Region<'tcx>, - ) { - // Imagine a type like this: - // - // trait Foo { } - // trait Bar<'c> : 'c { } - // - // &'b (Foo+'c+Bar<'d>) - // ^ - // - // In this case, the following relationships must hold: - // - // 'b <= 'c - // 'd <= 'c - // - // The first conditions is due to the normal region pointer - // rules, which say that a reference cannot outlive its - // referent. - // - // The final condition may be a bit surprising. In particular, - // you may expect that it would have been `'c <= 'd`, since - // usually lifetimes of outer things are conservative - // approximations for inner things. However, it works somewhat - // differently with trait objects: here the idea is that if the - // user specifies a region bound (`'c`, in this case) it is the - // "master bound" that *implies* that bounds from other traits are - // all met. (Remember that *all bounds* in a type like - // `Foo+Bar+Zed` must be met, not just one, hence if we write - // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and - // 'y.) - // - // Note: in fact we only permit builtin traits, not `Bar<'d>`, I - // am looking forward to the future here. - if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { - let implicit_bounds = object_region_bounds(self.infcx.tcx, data); - - let explicit_bound = region; - - self.out.reserve(implicit_bounds.len()); - for implicit_bound in implicit_bounds { - let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound)); - let outlives = - ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); - self.out.push(traits::Obligation::new( - cause, - self.param_env, - outlives.to_predicate(), - )); - } - } - } -} - -/// Given an object type like `SomeTrait + Send`, computes the lifetime -/// bounds that must hold on the elided self type. These are derived -/// from the declarations of `SomeTrait`, `Send`, and friends -- if -/// they declare `trait SomeTrait : 'static`, for example, then -/// `'static` would appear in the list. The hard work is done by -/// `infer::required_region_bounds`, see that for more information. -pub fn object_region_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - existential_predicates: ty::Binder<&'tcx ty::List>>, -) -> Vec> { - // Since we don't actually *know* the self type for an object, - // this "open(err)" serves as a kind of dummy standin -- basically - // a placeholder type. - let open_ty = tcx.mk_ty_infer(ty::FreshTy(0)); - - let predicates = existential_predicates - .iter() - .filter_map(|predicate| { - if let ty::ExistentialPredicate::Projection(_) = *predicate.skip_binder() { - None - } else { - Some(predicate.with_self_ty(tcx, open_ty)) - } - }) - .collect(); - - required_region_bounds(tcx, open_ty, predicates) -} - -/// Find the span of a generic bound affecting an associated type. -fn get_generic_bound_spans( - generics: &hir::Generics<'_>, - trait_name: Option<&Ident>, - assoc_item_name: Ident, -) -> Vec { - let mut bounds = vec![]; - for clause in generics.where_clause.predicates.iter() { - if let hir::WherePredicate::BoundPredicate(pred) = clause { - match &pred.bounded_ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => { - let mut s = path.segments.iter(); - if let (a, Some(b), None) = (s.next(), s.next(), s.next()) { - if a.map(|s| &s.ident) == trait_name - && b.ident == assoc_item_name - && is_self_path(&ty.kind) - { - // `::Bar` - bounds.push(pred.span); - } - } - } - hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => { - if segment.ident == assoc_item_name { - if is_self_path(&ty.kind) { - // `Self::Bar` - bounds.push(pred.span); - } - } - } - _ => {} - } - } - } - bounds -} - -fn is_self_path(kind: &hir::TyKind<'_>) -> bool { - match kind { - hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { - let mut s = path.segments.iter(); - if let (Some(segment), None) = (s.next(), s.next()) { - if segment.ident.name == kw::SelfUpper { - // `type(Self)` - return true; - } - } - } - _ => {} - } - false -} diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index b29480a8810..e84181f1d75 100644 --- a/src/librustc_interface/Cargo.toml +++ b/src/librustc_interface/Cargo.toml @@ -42,6 +42,7 @@ rustc_errors = { path = "../librustc_errors" } rustc_plugin_impl = { path = "../librustc_plugin_impl" } rustc_privacy = { path = "../librustc_privacy" } rustc_resolve = { path = "../librustc_resolve" } +rustc_trait_selection = { path = "../librustc_trait_selection" } rustc_ty = { path = "../librustc_ty" } tempfile = "3.0.5" once_cell = "1" diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs index e15217dfa67..c5ebcf0696f 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs @@ -1,10 +1,6 @@ pub use crate::passes::BoxedResolver; use crate::util; -use rustc::lint; -use rustc::session::config::{self, ErrorOutputType, Input, OutputFilenames}; -use rustc::session::early_error; -use rustc::session::{DiagnosticOutput, Session}; use rustc::ty; use rustc::util::common::ErrorReported; use rustc_ast::ast::{self, MetaItemKind}; @@ -16,7 +12,11 @@ use rustc_errors::registry::Registry; use rustc_lint::LintStore; use rustc_parse::new_parser_from_source_str; +use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames}; +use rustc_session::early_error; +use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; +use rustc_session::{DiagnosticOutput, Session}; use rustc_span::edition; use rustc_span::source_map::{FileLoader, FileName, SourceMap}; use std::path::PathBuf; diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 1e9b76c3e14..ee323b204b7 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -5,14 +5,9 @@ use log::{info, log_enabled, warn}; use rustc::arena::Arena; use rustc::dep_graph::DepGraph; -use rustc::hir::map; -use rustc::lint; +use rustc::hir::map::Definitions; use rustc::middle; use rustc::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; -use rustc::session::config::{self, CrateType, Input, OutputFilenames, OutputType}; -use rustc::session::config::{PpMode, PpSourceMode}; -use rustc::session::search_paths::PathKind; -use rustc::session::Session; use rustc::ty::steal::Steal; use rustc::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; use rustc::util::common::ErrorReported; @@ -27,7 +22,6 @@ use rustc_expand::base::ExtCtxt; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::Crate; -use rustc_infer::traits; use rustc_lint::LintStore; use rustc_mir as mir; use rustc_mir_build as mir_build; @@ -35,8 +29,14 @@ use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::{Resolver, ResolverArenas}; +use rustc_session::config::{self, CrateType, Input, OutputFilenames, OutputType}; +use rustc_session::config::{PpMode, PpSourceMode}; +use rustc_session::lint; +use rustc_session::search_paths::PathKind; +use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_span::FileName; +use rustc_trait_selection::traits; use rustc_typeck as typeck; use rustc_serialize::json; @@ -189,7 +189,7 @@ pub fn register_plugins<'a>( } sess.time("recursion_limit", || { - middle::recursion_limit::update_limits(sess, &krate); + middle::limits::update_limits(sess, &krate); }); let mut lint_store = rustc_lint::new_lint_store( @@ -210,14 +210,7 @@ pub fn register_plugins<'a>( Ok((krate, Lrc::new(lint_store))) } -fn configure_and_expand_inner<'a>( - sess: &'a Session, - lint_store: &'a LintStore, - mut krate: ast::Crate, - crate_name: &str, - resolver_arenas: &'a ResolverArenas<'a>, - metadata_loader: &'a MetadataLoaderDyn, -) -> Result<(ast::Crate, Resolver<'a>)> { +fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) { sess.time("pre_AST_expansion_lint_checks", || { rustc_lint::check_ast_crate( sess, @@ -228,6 +221,17 @@ fn configure_and_expand_inner<'a>( rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), ); }); +} + +fn configure_and_expand_inner<'a>( + sess: &'a Session, + lint_store: &'a LintStore, + mut krate: ast::Crate, + crate_name: &str, + resolver_arenas: &'a ResolverArenas<'a>, + metadata_loader: &'a MetadataLoaderDyn, +) -> Result<(ast::Crate, Resolver<'a>)> { + pre_expansion_lint(sess, lint_store, &krate); let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas); rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition()); @@ -291,7 +295,8 @@ fn configure_and_expand_inner<'a>( ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; - let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver); + let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k); + let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver, Some(&extern_mod_loaded)); // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); @@ -713,10 +718,7 @@ pub fn create_global_ctxt<'tcx>( arena: &'tcx WorkerLocal>, ) -> QueryContext<'tcx> { let sess = &compiler.session(); - let defs = mem::take(&mut resolver_outputs.definitions); - - // Construct the HIR map. - let hir_map = map::map_crate(sess, &*resolver_outputs.cstore, krate, dep_graph, defs); + let defs: &'tcx Definitions = arena.alloc(mem::take(&mut resolver_outputs.definitions)); let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess); @@ -742,7 +744,9 @@ pub fn create_global_ctxt<'tcx>( extern_providers, arena, resolver_outputs, - hir_map, + krate, + defs, + dep_graph, query_result_on_disk_cache, &crate_name, &outputs, @@ -763,6 +767,8 @@ pub fn create_global_ctxt<'tcx>( fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { assert_eq!(cnum, LOCAL_CRATE); + rustc::hir::map::check_crate(tcx); + let sess = tcx.sess; let mut entry_point = None; diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs index 426d97cc09b..3514829dca7 100644 --- a/src/librustc_interface/queries.rs +++ b/src/librustc_interface/queries.rs @@ -3,8 +3,6 @@ use rustc::arena::Arena; use rustc::dep_graph::DepGraph; -use rustc::session::config::{OutputFilenames, OutputType}; -use rustc::session::Session; use rustc::ty::steal::Steal; use rustc::ty::{GlobalCtxt, ResolverOutputs}; use rustc::util::common::ErrorReported; @@ -15,6 +13,8 @@ use rustc_hir::Crate; use rustc_incremental::DepGraphFuture; use rustc_lint::LintStore; +use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_session::Session; use std::any::Any; use std::cell::{Ref, RefCell, RefMut}; use std::mem; diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 1b80cf4e3db..db5ada92914 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -2,16 +2,16 @@ use crate::interface::parse_cfgspecs; -use rustc::lint::Level; use rustc::middle::cstore; -use rustc::session::config::{build_configuration, build_session_options, to_crate_config}; -use rustc::session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; -use rustc::session::config::{ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; -use rustc::session::config::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; -use rustc::session::search_paths::SearchPath; -use rustc::session::{build_session, Session}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; +use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; +use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; +use rustc_session::config::{ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; +use rustc_session::config::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; +use rustc_session::lint::Level; +use rustc_session::search_paths::SearchPath; +use rustc_session::{build_session, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel}; diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index 10a8c0a63f1..5c4de9e7155 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -1,5 +1,4 @@ use log::info; -use rustc::lint; use rustc::ty; use rustc_ast::ast::{AttrVec, BlockCheckMode}; use rustc_ast::mut_visit::{visit_clobber, MutVisitor, *}; @@ -18,7 +17,7 @@ use rustc_resolve::{self, Resolver}; use rustc_session as session; use rustc_session::config::{ErrorOutputType, Input, OutputFilenames}; -use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; +use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; use rustc_session::parse::CrateConfig; use rustc_session::CrateDisambiguator; use rustc_session::{config, early_error, filesearch, DiagnosticOutput, Session}; @@ -49,7 +48,7 @@ pub fn add_configuration( cfg.extend(codegen_backend.target_features(sess).into_iter().map(|feat| (tf, Some(feat)))); - if sess.crt_static_feature() { + if sess.crt_static_feature(None) { cfg.insert((tf, Some(Symbol::intern("crt-static")))); } } @@ -426,7 +425,7 @@ pub(crate) fn check_attr_crate_type(attrs: &[ast::Attribute], lint_buffer: &mut for a in attrs.iter() { if a.check_name(sym::crate_type) { if let Some(n) = a.value_str() { - if let Some(_) = categorize_crate_type(n) { + if categorize_crate_type(n).is_some() { return; } @@ -780,7 +779,7 @@ fn block_to_stmt(b: ast::Block, resolver: &mut Resolver<'_>) -> ast::Stmt { // in general the pretty printer processes unexpanded code, so // we override the default `visit_mac` method which panics. - fn visit_mac(&mut self, mac: &mut ast::Mac) { + fn visit_mac(&mut self, mac: &mut ast::MacCall) { noop_visit_mac(mac, self) } } diff --git a/src/librustc_lexer/src/unescape.rs b/src/librustc_lexer/src/unescape.rs index 4f5e03d008c..c51bf735fa7 100644 --- a/src/librustc_lexer/src/unescape.rs +++ b/src/librustc_lexer/src/unescape.rs @@ -17,7 +17,7 @@ pub enum EscapeError { /// Escaped '\' character without continuation. LoneSlash, - /// Invalid escape characted (e.g. '\z'). + /// Invalid escape character (e.g. '\z'). InvalidEscape, /// Raw '\r' encountered. BareCarriageReturn, @@ -43,7 +43,7 @@ pub enum EscapeError { UnclosedUnicodeEscape, /// '\u{_12}' LeadingUnderscoreUnicodeEscape, - /// More than 6 charactes in '\u{..}', e.g. '\u{10FFFF_FF}' + /// More than 6 characters in '\u{..}', e.g. '\u{10FFFF_FF}' OverlongUnicodeEscape, /// Invalid in-bound unicode character code, e.g. '\u{DFFF}'. LoneSurrogateUnicodeEscape, diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 6470d25fe0a..9785af5eab2 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -24,3 +24,4 @@ rustc_feature = { path = "../librustc_feature" } rustc_index = { path = "../librustc_index" } rustc_session = { path = "../librustc_session" } rustc_infer = { path = "../librustc_infer" } +rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 3eecd2a54e3..408031028b1 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -22,7 +22,6 @@ //! `late_lint_methods!` invocation in `lib.rs`. use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc::hir::map::Map; use rustc::lint::LintDiagnosticBuilder; use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt}; use rustc_ast::ast::{self, Expr}; @@ -39,12 +38,12 @@ use rustc_hir::def_id::DefId; use rustc_hir::{GenericParamKind, PatKind}; use rustc_hir::{HirIdSet, Node}; -use rustc_infer::traits::misc::can_type_implement_copy; use rustc_session::lint::FutureIncompatibleInfo; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{BytePos, Span}; +use rustc_trait_selection::traits::misc::can_type_implement_copy; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -399,7 +398,7 @@ fn exit_lint_attrs(&mut self, _: &LateContext<'_, '_>, _attrs: &[ast::Attribute] } fn check_crate(&mut self, cx: &LateContext<'_, '_>, krate: &hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate"); + self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "crate"); for macro_def in krate.exported_macros { let has_doc = macro_def.attrs.iter().any(|a| has_doc(a)); @@ -465,7 +464,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'_, '_>, trait_item: &hir::Trait let desc = match trait_item.kind { hir::TraitItemKind::Const(..) => "an associated constant", - hir::TraitItemKind::Method(..) => "a trait method", + hir::TraitItemKind::Fn(..) => "a trait method", hir::TraitItemKind::Type(..) => "an associated type", }; @@ -486,7 +485,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'_, '_>, impl_item: &hir::ImplIte let desc = match impl_item.kind { hir::ImplItemKind::Const(..) => "an associated constant", - hir::ImplItemKind::Method(..) => "a method", + hir::ImplItemKind::Fn(..) => "a method", hir::ImplItemKind::TyAlias(_) => "an associated type", hir::ImplItemKind::OpaqueTy(_) => "an associated `impl Trait` type", }; @@ -775,7 +774,10 @@ fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { ast::StmtKind::Local(..) => "statements", ast::StmtKind::Item(..) => "inner items", // expressions will be reported by `check_expr`. - ast::StmtKind::Semi(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Mac(..) => return, + ast::StmtKind::Empty + | ast::StmtKind::Semi(_) + | ast::StmtKind::Expr(_) + | ast::StmtKind::MacCall(_) => return, }; warn_if_doc(cx, stmt.span, kind, stmt.kind.attrs()); @@ -1068,9 +1070,9 @@ struct WalkAssocTypes<'a, 'db> { err: &'a mut DiagnosticBuilder<'db>, } impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } @@ -1475,7 +1477,7 @@ impl EarlyLintPass for KeywordIdents { fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) { self.check_tokens(cx, mac_def.body.inner_tokens()); } - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) { + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { self.check_tokens(cx, mac.args.inner_tokens()); } fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) { diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs index 29a6b8c693f..5b7b73b48ec 100644 --- a/src/librustc_lint/context.rs +++ b/src/librustc_lint/context.rs @@ -335,7 +335,7 @@ pub fn check_lint_name( lint_name.to_string() }; // If the lint was scoped with `tool::` check if the tool lint exists - if let Some(_) = tool_name { + if tool_name.is_some() { match self.by_name.get(&complete_name) { None => match self.lint_groups.get(&*complete_name) { None => return CheckLintNameResult::Tool(Err((None, String::new()))), @@ -369,7 +369,7 @@ pub fn check_lint_name( return if *silent { CheckLintNameResult::Ok(&lint_ids) } else { - CheckLintNameResult::Tool(Err((Some(&lint_ids), name.to_string()))) + CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) }; } CheckLintNameResult::Ok(&lint_ids) @@ -404,7 +404,7 @@ fn check_tool_name_for_backwards_compat( return if *silent { CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) } else { - CheckLintNameResult::Tool(Err((Some(&lint_ids), name.to_string()))) + CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) }; } CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) diff --git a/src/librustc_lint/early.rs b/src/librustc_lint/early.rs index ff6e9e000b0..34da29c9747 100644 --- a/src/librustc_lint/early.rs +++ b/src/librustc_lint/early.rs @@ -18,7 +18,7 @@ use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast::ast; use rustc_ast::visit as ast_visit; -use rustc_session::lint::{LintBuffer, LintPass}; +use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::Span; @@ -37,13 +37,7 @@ struct EarlyContextAndPass<'a, T: EarlyLintPass> { impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { - let rustc_session::lint::BufferedEarlyLint { - span, - msg, - node_id: _, - lint_id, - diagnostic, - } = early_lint; + let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; self.context.lookup_with_diagnostics( lint_id.lint, Some(span), @@ -249,7 +243,7 @@ fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { self.check_id(id); } - fn visit_mac(&mut self, mac: &'a ast::Mac) { + fn visit_mac(&mut self, mac: &'a ast::MacCall) { // FIXME(#54110): So, this setup isn't really right. I think // that (a) the librustc_ast visitor ought to be doing this as // part of `walk_mac`, and (b) we should be calling @@ -326,11 +320,9 @@ pub fn check_ast_crate( lint_buffer: Option, builtin_lints: T, ) { - let mut passes: Vec<_> = if pre_expansion { - lint_store.pre_expansion_passes.iter().map(|p| (p)()).collect() - } else { - lint_store.early_passes.iter().map(|p| (p)()).collect() - }; + let passes = + if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; + let mut passes: Vec<_> = passes.iter().map(|p| (p)()).collect(); let mut buffered = lint_buffer.unwrap_or_default(); if !sess.opts.debugging_opts.no_interleave_lints { diff --git a/src/librustc_lint/internal.rs b/src/librustc_lint/internal.rs index db109aef6eb..d8c685f2b22 100644 --- a/src/librustc_lint/internal.rs +++ b/src/librustc_lint/internal.rs @@ -140,7 +140,7 @@ fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &'tcx Ty<'tcx>) { } } TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => { - if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner_def_id()) { + if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { if cx.tcx.impl_trait_ref(impl_did).is_some() { return; } diff --git a/src/librustc_lint/late.rs b/src/librustc_lint/late.rs index ee03232a0ce..d2cc5510603 100644 --- a/src/librustc_lint/late.rs +++ b/src/librustc_lint/late.rs @@ -33,7 +33,7 @@ /// Extract the `LintStore` from the query context. /// This function exists because we've erased `LintStore` as `dyn Any` in the context. -crate fn unerased_lint_store<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx LintStore { +crate fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { let store: &dyn Any = &*tcx.lint_store; store.downcast_ref().unwrap() } @@ -99,8 +99,8 @@ impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> hir_visit::Visitor<'tcx> /// Because lints are scoped lexically, we want to walk nested /// items in the context of the outer item, so enable /// deep-walking. - fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, Self::Map> { - hir_visit::NestedVisitorMap::All(&self.context.tcx.hir()) + fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { + hir_visit::NestedVisitorMap::All(self.context.tcx.hir()) } fn visit_nested_body(&mut self, body: hir::BodyId) { @@ -419,7 +419,7 @@ fn late_lint_pass_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tc let mut cx = LateContextAndPass { context, pass }; // Visit the whole crate. - cx.with_lint_attrs(hir::CRATE_HIR_ID, &krate.attrs, |cx| { + cx.with_lint_attrs(hir::CRATE_HIR_ID, &krate.item.attrs, |cx| { // since the root module isn't visited as an item (because it isn't an // item), warn for it here. lint_callback!(cx, check_crate, krate); diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 7da69f3ed26..2062f9499ae 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -29,7 +29,7 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap { let mut builder = LintLevelMapBuilder { levels, tcx, store }; let krate = tcx.hir().krate(); - let push = builder.levels.push(&krate.attrs, &store); + let push = builder.levels.push(&krate.item.attrs, &store); builder.levels.register_id(hir::CRATE_HIR_ID); for macro_def in krate.exported_macros { builder.levels.register_id(macro_def.hir_id); @@ -377,10 +377,10 @@ pub fn push(&mut self, attrs: &[ast::Attribute], store: &LintStore) -> BuilderPu let prev = self.cur; if !specs.is_empty() { self.cur = self.sets.list.len() as u32; - self.sets.list.push(LintSet::Node { specs: specs, parent: prev }); + self.sets.list.push(LintSet::Node { specs, parent: prev }); } - BuilderPush { prev: prev, changed: prev != self.cur } + BuilderPush { prev, changed: prev != self.cur } } /// Called after `push` when the scope of a set of attributes are exited. @@ -438,8 +438,8 @@ fn with_lint_attrs(&mut self, id: hir::HirId, attrs: &[ast::Attribute], f: F) impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { type Map = Map<'tcx>; - fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, Self::Map> { - intravisit::NestedVisitorMap::All(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.tcx.hir()) } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 895c42a84d8..825ac04bc09 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -113,7 +113,7 @@ macro_rules! early_lint_passes { WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, IncompleteFeatures: IncompleteFeatures, - RedundantSemicolon: RedundantSemicolon, + RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, ] ); @@ -274,7 +274,8 @@ macro_rules! register_passes { UNUSED_EXTERN_CRATES, UNUSED_FEATURES, UNUSED_LABELS, - UNUSED_PARENS + UNUSED_PARENS, + REDUNDANT_SEMICOLONS ); add_lint_group!( @@ -307,6 +308,7 @@ macro_rules! register_passes { store.register_renamed("unused_doc_comment", "unused_doc_comments"); store.register_renamed("async_idents", "keyword_idents"); store.register_renamed("exceeding_bitshifts", "arithmetic_overflow"); + store.register_renamed("redundant_semicolon", "redundant_semicolons"); store.register_removed("unknown_features", "replaced by an error"); store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); store.register_removed("negate_unsigned", "cast a signed value instead"); diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs index b0560dc9fdf..afab55358d9 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/src/librustc_lint/nonstandard_style.rs @@ -343,7 +343,7 @@ fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item<'_>) { } fn check_trait_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Method(_, hir::TraitMethod::Required(pnames)) = item.kind { + if let hir::TraitItemKind::Fn(_, hir::TraitFn::Required(pnames)) = item.kind { self.check_snake_case(cx, "trait method", &item.ident); for param_name in pnames { self.check_snake_case(cx, "variable", param_name); diff --git a/src/librustc_lint/passes.rs b/src/librustc_lint/passes.rs index 813be2a032f..ace15471445 100644 --- a/src/librustc_lint/passes.rs +++ b/src/librustc_lint/passes.rs @@ -198,7 +198,7 @@ fn check_fn_post( fn check_path(a: &ast::Path, b: ast::NodeId); fn check_attribute(a: &ast::Attribute); fn check_mac_def(a: &ast::MacroDef, b: ast::NodeId); - fn check_mac(a: &ast::Mac); + fn check_mac(a: &ast::MacCall); /// Called when entering a syntax node that can have lint attributes such /// as `#[allow(...)]`. Called with *all* the attributes of that node. diff --git a/src/librustc_lint/redundant_semicolon.rs b/src/librustc_lint/redundant_semicolon.rs index 0b66fd92430..0f807cd497e 100644 --- a/src/librustc_lint/redundant_semicolon.rs +++ b/src/librustc_lint/redundant_semicolon.rs @@ -1,50 +1,41 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_ast::ast::{ExprKind, Stmt, StmtKind}; +use rustc_ast::ast::{Block, StmtKind}; use rustc_errors::Applicability; +use rustc_span::Span; declare_lint! { - pub REDUNDANT_SEMICOLON, + pub REDUNDANT_SEMICOLONS, Warn, "detects unnecessary trailing semicolons" } -declare_lint_pass!(RedundantSemicolon => [REDUNDANT_SEMICOLON]); +declare_lint_pass!(RedundantSemicolons => [REDUNDANT_SEMICOLONS]); -impl EarlyLintPass for RedundantSemicolon { - fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) { - if let StmtKind::Semi(expr) = &stmt.kind { - if let ExprKind::Tup(ref v) = &expr.kind { - if v.is_empty() { - // Strings of excess semicolons are encoded as empty tuple expressions - // during the parsing stage, so we check for empty tuple expressions - // which span only semicolons - if let Ok(source_str) = cx.sess().source_map().span_to_snippet(stmt.span) { - if source_str.chars().all(|c| c == ';') { - let multiple = (stmt.span.hi() - stmt.span.lo()).0 > 1; - let msg = if multiple { - "unnecessary trailing semicolons" - } else { - "unnecessary trailing semicolon" - }; - cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, |lint| { - let mut err = lint.build(&msg); - let suggest_msg = if multiple { - "remove these semicolons" - } else { - "remove this semicolon" - }; - err.span_suggestion( - stmt.span, - &suggest_msg, - String::new(), - Applicability::MaybeIncorrect, - ); - err.emit(); - }); - } - } - } +impl EarlyLintPass for RedundantSemicolons { + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + let mut seq = None; + for stmt in block.stmts.iter() { + match (&stmt.kind, &mut seq) { + (StmtKind::Empty, None) => seq = Some((stmt.span, false)), + (StmtKind::Empty, Some(seq)) => *seq = (seq.0.to(stmt.span), true), + (_, seq) => maybe_lint_redundant_semis(cx, seq), } } + maybe_lint_redundant_semis(cx, &mut seq); + } +} + +fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) { + if let Some((span, multiple)) = seq.take() { + cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| { + let (msg, rem) = if multiple { + ("unnecessary trailing semicolons", "remove these semicolons") + } else { + ("unnecessary trailing semicolon", "remove this semicolon") + }; + lint.build(msg) + .span_suggestion(span, rem, String::new(), Applicability::MaybeIncorrect) + .emit(); + }); } } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 86d93612e99..4949c93d45e 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -165,7 +165,7 @@ fn report_bin_hex_error( let mut err = lint.build(&format!("literal out of range for {}", t)); err.note(&format!( "the literal `{}` (decimal `{}`) does not fit into \ - an `{}` and will become `{}{}`", + the type `{}` and will become `{}{}`", repr_str, val, t, actually, t )); if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) @@ -242,7 +242,7 @@ fn lint_int_literal<'a, 'tcx>( v: u128, ) { let int_type = t.normalize(cx.sess().target.ptr_width); - let (_, max) = int_ty_range(int_type); + let (min, max) = int_ty_range(int_type); let max = max as u128; let negative = type_limits.negated_expr_id == e.hir_id; @@ -267,7 +267,19 @@ fn lint_int_literal<'a, 'tcx>( } cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { - lint.build(&format!("literal out of range for `{}`", t.name_str())).emit() + lint.build(&format!("literal out of range for `{}`", t.name_str())) + .note(&format!( + "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`", + cx.sess() + .source_map() + .span_to_snippet(lit.span) + .ok() + .expect("must get snippet from literal"), + t.name_str(), + min, + max, + )) + .emit(); }); } } @@ -320,7 +332,19 @@ fn lint_uint_literal<'a, 'tcx>( return; } cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { - lint.build(&format!("literal out of range for `{}`", t.name_str())).emit() + lint.build(&format!("literal out of range for `{}`", t.name_str())) + .note(&format!( + "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`", + cx.sess() + .source_map() + .span_to_snippet(lit.span) + .ok() + .expect("must get snippet from literal"), + t.name_str(), + min, + max, + )) + .emit() }); } } @@ -352,7 +376,17 @@ fn lint_literal<'a, 'tcx>( }; if is_infinite == Ok(true) { cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { - lint.build(&format!("literal out of range for `{}`", t.name_str())).emit() + lint.build(&format!("literal out of range for `{}`", t.name_str())) + .note(&format!( + "the literal `{}` does not fit into the type `{}` and will be converted to `std::{}::INFINITY`", + cx.sess() + .source_map() + .span_to_snippet(lit.span) + .expect("must get snippet from literal"), + t.name_str(), + t.name_str(), + )) + .emit(); }); } } @@ -998,10 +1032,8 @@ fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item<'_>) { let ty = cx.tcx.erase_regions(&t); let layout = match cx.layout_of(ty) { Ok(layout) => layout, - Err(ty::layout::LayoutError::Unknown(_)) => return, - Err(err @ ty::layout::LayoutError::SizeOverflow(_)) => { - bug!("failed to get layout for `{}`: {}", t, err); - } + Err(ty::layout::LayoutError::Unknown(_)) + | Err(ty::layout::LayoutError::SizeOverflow(_)) => return, }; let (variants, tag) = match layout.variants { layout::Variants::Multiple { diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 02f04b23459..229740615f7 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -54,7 +54,7 @@ fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) { match callee.kind { hir::ExprKind::Path(ref qpath) => { match cx.tables.qpath_res(qpath, callee.hir_id) { - Res::Def(DefKind::Fn, def_id) | Res::Def(DefKind::Method, def_id) => { + Res::Def(DefKind::Fn, def_id) | Res::Def(DefKind::AssocFn, def_id) => { Some(def_id) } // `Res::Local` if it was a closure, for which we @@ -124,7 +124,12 @@ fn check_must_use_ty<'tcx>( descr_post: &str, plural_len: usize, ) -> bool { - if ty.is_unit() || cx.tcx.is_ty_uninhabited_from(cx.tcx.parent_module(expr.hir_id), ty) + if ty.is_unit() + || cx.tcx.is_ty_uninhabited_from( + cx.tcx.parent_module(expr.hir_id).to_def_id(), + ty, + cx.param_env, + ) { return true; } @@ -538,7 +543,7 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) // The other cases do not contain sub-patterns. - | Wild | Rest | Lit(..) | Mac(..) | Range(..) | Ident(.., None) | Path(..) => return, + | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => return, // These are list-like patterns; parens can always be removed. TupleStruct(_, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false); diff --git a/src/librustc_macros/src/query.rs b/src/librustc_macros/src/query.rs index 6362f3c2c49..56b7be2f7e2 100644 --- a/src/librustc_macros/src/query.rs +++ b/src/librustc_macros/src/query.rs @@ -51,13 +51,10 @@ enum QueryModifier { /// Don't hash the result, instead just mark a query red if it runs NoHash, - /// Don't force the query - NoForce, - /// Generate a dep node based on the dependencies of the query Anon, - /// Always evaluate the query, ignoring its depdendencies + /// Always evaluate the query, ignoring its dependencies EvalAlways, } @@ -118,8 +115,6 @@ fn parse(input: ParseStream<'_>) -> Result { Ok(QueryModifier::CycleDelayBug) } else if modifier == "no_hash" { Ok(QueryModifier::NoHash) - } else if modifier == "no_force" { - Ok(QueryModifier::NoForce) } else if modifier == "anon" { Ok(QueryModifier::Anon) } else if modifier == "eval_always" { @@ -222,13 +217,10 @@ struct QueryModifiers { /// Don't hash the result, instead just mark a query red if it runs no_hash: bool, - /// Don't force the query - no_force: bool, - /// Generate a dep node based on the dependencies of the query anon: bool, - // Always evaluate the query, ignoring its depdendencies + // Always evaluate the query, ignoring its dependencies eval_always: bool, } @@ -241,7 +233,6 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { let mut fatal_cycle = false; let mut cycle_delay_bug = false; let mut no_hash = false; - let mut no_force = false; let mut anon = false; let mut eval_always = false; for modifier in query.modifiers.0.drain(..) { @@ -288,12 +279,6 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { } no_hash = true; } - QueryModifier::NoForce => { - if no_force { - panic!("duplicate modifier `no_force` for query `{}`", query.name); - } - no_force = true; - } QueryModifier::Anon => { if anon { panic!("duplicate modifier `anon` for query `{}`", query.name); @@ -316,7 +301,6 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { fatal_cycle, cycle_delay_bug, no_hash, - no_force, anon, eval_always, } @@ -425,7 +409,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut dep_node_def_stream = quote! {}; let mut dep_node_force_stream = quote! {}; let mut try_load_from_on_disk_cache_stream = quote! {}; - let mut no_force_queries = Vec::new(); let mut cached_queries = quote! {}; for group in groups.0 { @@ -444,19 +427,19 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { cached_queries.extend(quote! { #name, }); - } - if modifiers.cache.is_some() && !modifiers.no_force { try_load_from_on_disk_cache_stream.extend(quote! { DepKind::#name => { - debug_assert!(tcx.dep_graph - .node_color(self) - .map(|c| c.is_green()) - .unwrap_or(false)); - - let key = RecoverKey::recover(tcx, self).unwrap(); - if queries::#name::cache_on_disk(tcx, key, None) { - let _ = tcx.#name(key); + if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY { + debug_assert!($tcx.dep_graph + .node_color($dep_node) + .map(|c| c.is_green()) + .unwrap_or(false)); + + let key = <#arg as DepNodeParams>::recover($tcx, $dep_node).unwrap(); + if queries::#name::cache_on_disk($tcx, key, None) { + let _ = $tcx.#name(key); + } } } }); @@ -501,24 +484,21 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { [#attribute_stream] #name(#arg), }); - if modifiers.no_force { - no_force_queries.push(name.clone()); - } else { - // Add a match arm to force the query given the dep node - dep_node_force_stream.extend(quote! { - DepKind::#name => { - if let Some(key) = RecoverKey::recover($tcx, $dep_node) { + // Add a match arm to force the query given the dep node + dep_node_force_stream.extend(quote! { + DepKind::#name => { + if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY { + if let Some(key) = <#arg as DepNodeParams>::recover($tcx, $dep_node) { $tcx.force_query::>( key, DUMMY_SP, *$dep_node ); - } else { - return false; + return true; } } - }); - } + } + }); add_query_description_impl(&query, modifiers, &mut query_description_stream); } @@ -528,12 +508,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { }); } - // Add an arm for the no force queries to panic when trying to force them - for query in no_force_queries { - dep_node_force_stream.extend(quote! { - DepKind::#query | - }); - } dep_node_force_stream.extend(quote! { DepKind::Null => { bug!("Cannot force dep node: {:?}", $dep_node) @@ -577,14 +551,9 @@ macro_rules! rustc_cached_queries { #query_description_stream - impl DepNode { - /// Check whether the query invocation corresponding to the given - /// DepNode is eligible for on-disk-caching. If so, this is method - /// will execute the query corresponding to the given DepNode. - /// Also, as a sanity check, it expects that the corresponding query - /// invocation has been marked as green already. - pub fn try_load_from_on_disk_cache(&self, tcx: TyCtxt<'_>) { - match self.kind { + macro_rules! rustc_dep_node_try_load_from_on_disk_cache { + ($dep_node:expr, $tcx:expr) => { + match $dep_node.kind { #try_load_from_on_disk_cache_stream _ => (), } diff --git a/src/librustc_macros/src/symbols.rs b/src/librustc_macros/src/symbols.rs index feddcd5f994..0f8bc432307 100644 --- a/src/librustc_macros/src/symbols.rs +++ b/src/librustc_macros/src/symbols.rs @@ -103,6 +103,7 @@ pub fn symbols(input: TokenStream) -> TokenStream { #value, }); keyword_stream.extend(quote! { + #[allow(non_upper_case_globals)] pub const #name: Symbol = Symbol::new(#counter); }); counter += 1; @@ -120,6 +121,8 @@ pub fn symbols(input: TokenStream) -> TokenStream { #value, }); symbols_stream.extend(quote! { + #[allow(rustc::default_hash_types)] + #[allow(non_upper_case_globals)] pub const #name: Symbol = Symbol::new(#counter); }); counter += 1; @@ -149,6 +152,7 @@ macro_rules! symbols { () => { #symbols_stream + #[allow(non_upper_case_globals)] pub const digits_array: &[Symbol; 10] = &[ #digits_stream ]; diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml index 8abfb20965e..088cba83ef9 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -15,7 +15,6 @@ log = "0.4" memmap = "0.7" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc = { path = "../librustc" } -rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } @@ -26,8 +25,8 @@ rustc_serialize = { path = "../libserialize", package = "serialize" } stable_deref_trait = "1.0.0" rustc_ast = { path = "../librustc_ast" } rustc_expand = { path = "../librustc_expand" } -rustc_parse = { path = "../librustc_parse" } rustc_span = { path = "../librustc_span" } +rustc_session = { path = "../librustc_session" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "libloaderapi"] } diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 293a353a092..f20cdfcba15 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -6,19 +6,18 @@ use rustc::hir::map::Definitions; use rustc::middle::cstore::DepKind; use rustc::middle::cstore::{CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn}; -use rustc::session::config; -use rustc::session::search_paths::PathKind; -use rustc::session::{CrateDisambiguator, Session}; use rustc::ty::TyCtxt; -use rustc_ast::ast; -use rustc_ast::attr; use rustc_ast::expand::allocator::{global_allocator_spans, AllocatorKind}; +use rustc_ast::{ast, attr}; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; use rustc_expand::base::SyntaxExtension; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_index::vec::IndexVec; +use rustc_session::config; +use rustc_session::search_paths::PathKind; +use rustc_session::{CrateDisambiguator, Session}; use rustc_span::edition::Edition; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -76,6 +75,21 @@ fn report(self) -> ! { } } +/// A reference to `CrateMetadata` that can also give access to whole crate store when necessary. +#[derive(Clone, Copy)] +crate struct CrateMetadataRef<'a> { + pub cdata: &'a CrateMetadata, + pub cstore: &'a CStore, +} + +impl std::ops::Deref for CrateMetadataRef<'_> { + type Target = CrateMetadata; + + fn deref(&self) -> &Self::Target { + self.cdata + } +} + fn dump_crates(cstore: &CStore) { info!("resolved crates:"); cstore.iter_crate_data(|cnum, data| { @@ -100,10 +114,11 @@ fn alloc_new_crate_num(&mut self) -> CrateNum { CrateNum::new(self.metas.len() - 1) } - crate fn get_crate_data(&self, cnum: CrateNum) -> &CrateMetadata { - self.metas[cnum] + crate fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { + let cdata = self.metas[cnum] .as_ref() - .unwrap_or_else(|| panic!("Failed to get crate data for {:?}", cnum)) + .unwrap_or_else(|| panic!("Failed to get crate data for {:?}", cnum)); + CrateMetadataRef { cdata, cstore: self } } fn set_crate_data(&mut self, cnum: CrateNum, data: CrateMetadata) { @@ -217,7 +232,7 @@ fn existing_match(&self, name: Symbol, hash: Option, kind: PathKind) -> Opt // We're also sure to compare *paths*, not actual byte slices. The // `source` stores paths which are normalized which may be different // from the strings on the command line. - let source = self.cstore.get_crate_data(cnum).source(); + let source = self.cstore.get_crate_data(cnum).cdata.source(); if let Some(entry) = self.sess.opts.externs.get(&name.as_str()) { // Only use `--extern crate_name=path` here, not `--extern crate_name`. if let Some(mut files) = entry.files() { @@ -463,7 +478,7 @@ fn maybe_resolve_crate<'b>( self.load(&mut locator) .map(|r| (r, None)) .or_else(|| { - dep_kind = DepKind::UnexportedMacrosOnly; + dep_kind = DepKind::MacrosOnly; self.load_proc_macro(&mut locator, path_kind) }) .ok_or_else(move || LoadError::LocatorError(locator))? @@ -473,7 +488,7 @@ fn maybe_resolve_crate<'b>( (LoadResult::Previous(cnum), None) => { let data = self.cstore.get_crate_data(cnum); if data.is_proc_macro_crate() { - dep_kind = DepKind::UnexportedMacrosOnly; + dep_kind = DepKind::MacrosOnly; } data.update_dep_kind(|data_dep_kind| cmp::max(data_dep_kind, dep_kind)); Ok(cnum) @@ -547,9 +562,6 @@ fn resolve_crate_deps( "resolving dep crate {} hash: `{}` extra filename: `{}`", dep.name, dep.hash, dep.extra_filename ); - if dep.kind == DepKind::UnexportedMacrosOnly { - return krate; - } let dep_kind = match dep_kind { DepKind::MacrosOnly => DepKind::MacrosOnly, _ => dep.kind, @@ -850,7 +862,7 @@ pub fn process_extern_crate( None => item.ident.name, }; let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) { - DepKind::UnexportedMacrosOnly + DepKind::MacrosOnly } else { DepKind::Explicit }; @@ -858,11 +870,11 @@ pub fn process_extern_crate( let cnum = self.resolve_crate(name, item.span, dep_kind, None); let def_id = definitions.opt_local_def_id(item.id).unwrap(); - let path_len = definitions.def_path(def_id.index).data.len(); + let path_len = definitions.def_path(def_id).data.len(); self.update_extern_crate( cnum, ExternCrate { - src: ExternCrateSource::Extern(def_id), + src: ExternCrateSource::Extern(def_id.to_def_id()), span: item.span, path_len, dependency_of: LOCAL_CRATE, diff --git a/src/librustc_metadata/dependency_format.rs b/src/librustc_metadata/dependency_format.rs index 9e71839dbfd..4cfaf03b7a5 100644 --- a/src/librustc_metadata/dependency_format.rs +++ b/src/librustc_metadata/dependency_format.rs @@ -56,10 +56,10 @@ use rustc::middle::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; use rustc::middle::cstore::{self, DepKind}; use rustc::middle::dependency_format::{Dependencies, DependencyList, Linkage}; -use rustc::session::config; use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::CrateNum; +use rustc_session::config; use rustc_target::spec::PanicStrategy; crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { @@ -97,7 +97,9 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList { // If the global prefer_dynamic switch is turned off, or the final // executable will be statically linked, prefer static crate linkage. - config::CrateType::Executable if !sess.opts.cg.prefer_dynamic || sess.crt_static() => { + config::CrateType::Executable + if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => + { Linkage::Static } config::CrateType::Executable => Linkage::Dynamic, @@ -129,7 +131,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList { // If any are not found, generate some nice pretty errors. if ty == config::CrateType::Staticlib || (ty == config::CrateType::Executable - && sess.crt_static() + && sess.crt_static(Some(ty)) && !sess.target.target.options.crt_static_allows_dylibs) { for &cnum in tcx.crates().iter() { diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index d4cc3c32616..e401dc0f6e7 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -31,7 +31,7 @@ pub mod locator; pub fn validate_crate_name( - sess: Option<&rustc::session::Session>, + sess: Option<&rustc_session::Session>, s: &str, sp: Option, ) { diff --git a/src/librustc_metadata/link_args.rs b/src/librustc_metadata/link_args.rs index 8d018b9bb94..13668b2423f 100644 --- a/src/librustc_metadata/link_args.rs +++ b/src/librustc_metadata/link_args.rs @@ -8,7 +8,7 @@ let mut collector = Collector { args: Vec::new() }; tcx.hir().krate().visit_all_item_likes(&mut collector); - for attr in tcx.hir().krate().attrs.iter() { + for attr in tcx.hir().krate().item.attrs.iter() { if attr.has_name(sym::link_args) { if let Some(linkarg) = attr.value_str() { collector.add_link_args(&linkarg.as_str()); diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index 2157b8ce159..1ede629e7ef 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -216,13 +216,13 @@ use crate::rmeta::{rustc_version, MetadataBlob, METADATA_HEADER}; use rustc::middle::cstore::{CrateSource, MetadataLoader}; -use rustc::session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; -use rustc::session::search_paths::PathKind; -use rustc::session::{config, CrateDisambiguator, Session}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; use rustc_errors::{struct_span_err, DiagnosticBuilder}; +use rustc_session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; +use rustc_session::search_paths::PathKind; +use rustc_session::{config, CrateDisambiguator, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::{Target, TargetTriple}; @@ -711,7 +711,7 @@ fn extract_one( // See also #68149 which provides more detail on why emitting the // dependency on the rlib is a bad thing. // - // We currenty do not verify that these other sources are even in sync, + // We currently do not verify that these other sources are even in sync, // and this is arguably a bug (see #10786), but because reading metadata // is quite slow (especially from dylibs) we currently do not read it // from the other crate sources. diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index 2fa9cb099dd..64bbf393ba0 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -1,12 +1,12 @@ use rustc::middle::cstore::{self, NativeLibrary}; -use rustc::session::parse::feature_err; -use rustc::session::Session; use rustc::ty::TyCtxt; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_session::parse::feature_err; +use rustc_session::Session; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_target::spec::abi::Abi; diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index a72ee0cbe47..2bf74fe272e 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -1,9 +1,10 @@ // Decoding metadata from a single crate's metadata +use crate::creader::CrateMetadataRef; use crate::rmeta::table::{FixedSizeEncoding, Table}; use crate::rmeta::*; -use rustc::dep_graph::{self, DepNodeIndex}; +use rustc::dep_graph::{self, DepNode, DepNodeIndex}; use rustc::hir::exports::Export; use rustc::hir::map::definitions::DefPathTable; use rustc::hir::map::{DefKey, DefPath, DefPathData, DefPathHash}; @@ -13,36 +14,35 @@ use rustc::middle::lang_items; use rustc::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc::mir::{self, interpret, BodyAndCache, Promoted}; -use rustc::session::Session; use rustc::ty::codec::TyDecoder; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::common::record_time; +use rustc_ast::ast::{self, Ident}; +use rustc_attr as attr; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, Once}; +use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_serialize::{opaque, Decodable, Decoder, SpecializedDecoder}; +use rustc_session::Session; +use rustc_span::source_map::{self, respan, Spanned}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{self, hygiene::MacroKind, BytePos, Pos, Span, DUMMY_SP}; +use log::debug; +use proc_macro::bridge::client::ProcMacro; use std::io; use std::mem; use std::num::NonZeroUsize; use std::u32; -use log::debug; -use proc_macro::bridge::client::ProcMacro; -use rustc_ast::ast::{self, Ident}; -use rustc_attr as attr; -use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; -use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; -use rustc_serialize::{opaque, Decodable, Decoder, SpecializedDecoder}; -use rustc_span::source_map::{self, respan, Spanned}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{self, hygiene::MacroKind, BytePos, Pos, Span, DUMMY_SP}; - pub use cstore_impl::{provide, provide_extern}; mod cstore_impl; @@ -125,7 +125,7 @@ struct ImportedSourceFile { pub(super) struct DecodeContext<'a, 'tcx> { opaque: opaque::Decoder<'a>, - cdata: Option<&'a CrateMetadata>, + cdata: Option>, sess: Option<&'tcx Session>, tcx: Option>, @@ -141,7 +141,7 @@ pub(super) struct DecodeContext<'a, 'tcx> { /// Abstract over the various ways one can create metadata decoders. pub(super) trait Metadata<'a, 'tcx>: Copy { fn raw_bytes(self) -> &'a [u8]; - fn cdata(self) -> Option<&'a CrateMetadata> { + fn cdata(self) -> Option> { None } fn sess(self) -> Option<&'tcx Session> { @@ -162,7 +162,7 @@ fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> { lazy_state: LazyState::NoNode, alloc_decoding_session: self .cdata() - .map(|cdata| cdata.alloc_decoding_state.new_decoding_session()), + .map(|cdata| cdata.cdata.alloc_decoding_state.new_decoding_session()), } } } @@ -185,33 +185,33 @@ fn sess(self) -> Option<&'tcx Session> { } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for &'a CrateMetadata { +impl<'a, 'tcx> Metadata<'a, 'tcx> for &'a CrateMetadataRef<'a> { fn raw_bytes(self) -> &'a [u8] { self.blob.raw_bytes() } - fn cdata(self) -> Option<&'a CrateMetadata> { - Some(self) + fn cdata(self) -> Option> { + Some(*self) } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadata, &'tcx Session) { +impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, &'tcx Session) { fn raw_bytes(self) -> &'a [u8] { self.0.raw_bytes() } - fn cdata(self) -> Option<&'a CrateMetadata> { - Some(self.0) + fn cdata(self) -> Option> { + Some(*self.0) } fn sess(self) -> Option<&'tcx Session> { Some(&self.1) } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadata, TyCtxt<'tcx>) { +impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, TyCtxt<'tcx>) { fn raw_bytes(self) -> &'a [u8] { self.0.raw_bytes() } - fn cdata(self) -> Option<&'a CrateMetadata> { - Some(self.0) + fn cdata(self) -> Option> { + Some(*self.0) } fn tcx(self) -> Option> { Some(self.1) @@ -242,7 +242,7 @@ fn tcx(&self) -> TyCtxt<'tcx> { self.tcx.expect("missing TyCtxt in DecodeContext") } - fn cdata(&self) -> &'a CrateMetadata { + fn cdata(&self) -> CrateMetadataRef<'a> { self.cdata.expect("missing CrateMetadata in DecodeContext") } @@ -364,7 +364,7 @@ fn specialized_decode(&mut self) -> Result { impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { #[inline] fn specialized_decode(&mut self) -> Result { - self.specialized_decode().map(|i| LocalDefId::from_def_id(i)) + Ok(DefId::decode(self)?.expect_local()) } } @@ -386,7 +386,7 @@ fn specialized_decode(&mut self) -> Result { return Ok(DUMMY_SP); } - debug_assert_eq!(tag, TAG_VALID_SPAN); + debug_assert!(tag == TAG_VALID_SPAN_LOCAL || tag == TAG_VALID_SPAN_FOREIGN); let lo = BytePos::decode(self)?; let len = BytePos::decode(self)?; @@ -398,7 +398,68 @@ fn specialized_decode(&mut self) -> Result { bug!("Cannot decode Span without Session.") }; - let imported_source_files = self.cdata().imported_source_files(&sess.source_map()); + // There are two possibilities here: + // 1. This is a 'local span', which is located inside a `SourceFile` + // that came from this crate. In this case, we use the source map data + // encoded in this crate. This branch should be taken nearly all of the time. + // 2. This is a 'foreign span', which is located inside a `SourceFile` + // that came from a *different* crate (some crate upstream of the one + // whose metadata we're looking at). For example, consider this dependency graph: + // + // A -> B -> C + // + // Suppose that we're currently compiling crate A, and start deserializing + // metadata from crate B. When we deserialize a Span from crate B's metadata, + // there are two posibilites: + // + // 1. The span references a file from crate B. This makes it a 'local' span, + // which means that we can use crate B's serialized source map information. + // 2. The span references a file from crate C. This makes it a 'foreign' span, + // which means we need to use Crate *C* (not crate B) to determine the source + // map information. We only record source map information for a file in the + // crate that 'owns' it, so deserializing a Span may require us to look at + // a transitive dependency. + // + // When we encode a foreign span, we adjust its 'lo' and 'high' values + // to be based on the *foreign* crate (e.g. crate C), not the crate + // we are writing metadata for (e.g. crate B). This allows us to + // treat the 'local' and 'foreign' cases almost identically during deserialization: + // we can call `imported_source_files` for the proper crate, and binary search + // through the returned slice using our span. + let imported_source_files = if tag == TAG_VALID_SPAN_LOCAL { + self.cdata().imported_source_files(sess.source_map()) + } else { + // FIXME: We don't decode dependencies of proc-macros. + // Remove this once #69976 is merged + if self.cdata().root.is_proc_macro_crate() { + debug!( + "SpecializedDecoder::specialized_decode: skipping span for proc-macro crate {:?}", + self.cdata().cnum + ); + // Decode `CrateNum` as u32 - using `CrateNum::decode` will ICE + // since we don't have `cnum_map` populated. + // This advances the decoder position so that we can continue + // to read metadata. + let _ = u32::decode(self)?; + return Ok(DUMMY_SP); + } + // tag is TAG_VALID_SPAN_FOREIGN, checked by `debug_assert` above + let cnum = CrateNum::decode(self)?; + debug!( + "SpecializedDecoder::specialized_decode: loading source files from cnum {:?}", + cnum + ); + + // Decoding 'foreign' spans should be rare enough that it's + // not worth it to maintain a per-CrateNum cache for `last_source_file_index`. + // We just set it to 0, to ensure that we don't try to access something out + // of bounds for our initial 'guess' + self.last_source_file_index = 0; + + let foreign_data = self.cdata().cstore.get_crate_data(cnum); + foreign_data.imported_source_files(sess.source_map()) + }; + let source_file = { // Optimize for the case that most spans within a translated item // originate from the same source_file. @@ -412,16 +473,32 @@ fn specialized_decode(&mut self) -> Result { .binary_search_by_key(&lo, |source_file| source_file.original_start_pos) .unwrap_or_else(|index| index - 1); - self.last_source_file_index = index; + // Don't try to cache the index for foreign spans, + // as this would require a map from CrateNums to indices + if tag == TAG_VALID_SPAN_LOCAL { + self.last_source_file_index = index; + } &imported_source_files[index] } }; // Make sure our binary search above is correct. - debug_assert!(lo >= source_file.original_start_pos && lo <= source_file.original_end_pos); + debug_assert!( + lo >= source_file.original_start_pos && lo <= source_file.original_end_pos, + "Bad binary search: lo={:?} source_file.original_start_pos={:?} source_file.original_end_pos={:?}", + lo, + source_file.original_start_pos, + source_file.original_end_pos + ); // Make sure we correctly filtered out invalid spans during encoding - debug_assert!(hi >= source_file.original_start_pos && hi <= source_file.original_end_pos); + debug_assert!( + hi >= source_file.original_start_pos && hi <= source_file.original_end_pos, + "Bad binary search: hi={:?} source_file.original_start_pos={:?} source_file.original_end_pos={:?}", + hi, + source_file.original_start_pos, + source_file.original_end_pos + ); let lo = (lo + source_file.translated_source_file.start_pos) - source_file.original_start_pos; @@ -504,7 +581,7 @@ fn def_kind(&self) -> Option { EntryKind::Struct(_, _) => DefKind::Struct, EntryKind::Union(_, _) => DefKind::Union, EntryKind::Fn(_) | EntryKind::ForeignFn(_) => DefKind::Fn, - EntryKind::Method(_) => DefKind::Method, + EntryKind::AssocFn(_) => DefKind::AssocFn, EntryKind::Type => DefKind::TyAlias, EntryKind::TypeParam => DefKind::TyParam, EntryKind::ConstParam => DefKind::ConstParam, @@ -558,50 +635,7 @@ impl CrateRoot<'_> { } } -impl<'a, 'tcx> CrateMetadata { - crate fn new( - sess: &Session, - blob: MetadataBlob, - root: CrateRoot<'static>, - raw_proc_macros: Option<&'static [ProcMacro]>, - cnum: CrateNum, - cnum_map: CrateNumMap, - dep_kind: DepKind, - source: CrateSource, - private_dep: bool, - host_hash: Option, - ) -> CrateMetadata { - let def_path_table = record_time(&sess.perf_stats.decode_def_path_tables_time, || { - root.def_path_table.decode((&blob, sess)) - }); - let trait_impls = root - .impls - .decode((&blob, sess)) - .map(|trait_impls| (trait_impls.trait_id, trait_impls.impls)) - .collect(); - let alloc_decoding_state = - AllocDecodingState::new(root.interpret_alloc_index.decode(&blob).collect()); - let dependencies = Lock::new(cnum_map.iter().cloned().collect()); - CrateMetadata { - blob, - root, - def_path_table, - trait_impls, - raw_proc_macros, - source_map_import_info: Once::new(), - alloc_decoding_state, - dep_node_index: AtomicCell::new(DepNodeIndex::INVALID), - cnum, - cnum_map, - dependencies, - dep_kind: Lock::new(dep_kind), - source, - private_dep, - host_hash, - extern_crate: Lock::new(None), - } - } - +impl<'a, 'tcx> CrateMetadataRef<'a> { fn is_proc_macro(&self, id: DefIndex) -> bool { self.root.proc_macro_data.and_then(|data| data.decode(self).find(|x| *x == id)).is_some() } @@ -622,10 +656,6 @@ fn kind(&self, item_id: DefIndex) -> EntryKind { }) } - fn local_def_id(&self, index: DefIndex) -> DefId { - DefId { krate: self.cnum, index } - } - fn raw_proc_macro(&self, id: DefIndex) -> &ProcMacro { // DefIndex's in root.proc_macro_data have a one-to-one correspondence // with items in 'raw_proc_macros'. @@ -697,6 +727,7 @@ fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { data.paren_sugar, data.has_auto_impl, data.is_marker, + data.specialization_kind, self.def_path_table.def_path_hash(item_id), ) } @@ -706,6 +737,7 @@ fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { false, false, false, + ty::trait_def::TraitSpecializationKind::None, self.def_path_table.def_path_hash(item_id), ), _ => bug!("def-index does not refer to trait or trait alias"), @@ -1113,7 +1145,7 @@ fn get_associated_item(&self, id: DefIndex) -> ty::AssocItem { let (kind, container, has_self) = match self.kind(id) { EntryKind::AssocConst(container, _, _) => (ty::AssocKind::Const, container, false), - EntryKind::Method(data) => { + EntryKind::AssocFn(data) => { let data = data.decode(self); (ty::AssocKind::Method, data.container, data.has_self) } @@ -1191,18 +1223,6 @@ fn get_struct_field_names(&self, id: DefIndex, sess: &Session) -> Vec Option { - for (local, &global) in self.cnum_map.iter_enumerated() { - if global == did.krate { - return Some(DefId { krate: local, index: did.index }); - } - } - - None - } - fn get_inherent_implementations_for_type( &self, tcx: TyCtxt<'tcx>, @@ -1307,7 +1327,7 @@ fn get_missing_lang_items(&self, tcx: TyCtxt<'tcx>) -> &'tcx [lang_items::LangIt fn get_fn_param_names(&self, id: DefIndex) -> Vec { let param_names = match self.kind(id) { EntryKind::Fn(data) | EntryKind::ForeignFn(data) => data.decode(self).param_names, - EntryKind::Method(data) => data.decode(self).fn_data.param_names, + EntryKind::AssocFn(data) => data.decode(self).fn_data.param_names, _ => Lazy::empty(), }; param_names.decode(self).collect() @@ -1333,9 +1353,9 @@ fn get_rendered_const(&self, id: DefIndex) -> String { } } - fn get_macro(&self, id: DefIndex) -> MacroDef { + fn get_macro(&self, id: DefIndex, sess: &Session) -> MacroDef { match self.kind(id) { - EntryKind::MacroDef(macro_def) => macro_def.decode(self), + EntryKind::MacroDef(macro_def) => macro_def.decode((self, sess)), _ => bug!(), } } @@ -1344,7 +1364,7 @@ fn get_macro(&self, id: DefIndex) -> MacroDef { // don't serialize constness for tuple variant and tuple struct constructors. fn is_const_fn_raw(&self, id: DefIndex) -> bool { let constness = match self.kind(id) { - EntryKind::Method(data) => data.decode(self).fn_data.constness, + EntryKind::AssocFn(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, // Some intrinsics can be const fn. While we could recompute this (at least until we // stop having hardcoded whitelists and move to stability attributes), it seems cleaner @@ -1359,7 +1379,7 @@ fn is_const_fn_raw(&self, id: DefIndex) -> bool { fn asyncness(&self, id: DefIndex) -> hir::IsAsync { match self.kind(id) { EntryKind::Fn(data) => data.decode(self).asyncness, - EntryKind::Method(data) => data.decode(self).fn_data.asyncness, + EntryKind::AssocFn(data) => data.decode(self).fn_data.asyncness, EntryKind::ForeignFn(data) => data.decode(self).asyncness, _ => bug!("asyncness: expected function kind"), } @@ -1409,11 +1429,6 @@ fn def_path(&self, id: DefIndex) -> DefPath { DefPath::make(self.cnum, id, |parent| self.def_key(parent)) } - #[inline] - fn def_path_hash(&self, index: DefIndex) -> DefPathHash { - self.def_path_table.def_path_hash(index) - } - /// Imports the source_map from an external crate into the source_map of the crate /// currently being compiled (the "local crate"). /// @@ -1440,10 +1455,10 @@ fn def_path_hash(&self, index: DefIndex) -> DefPathHash { /// Proc macro crates don't currently export spans, so this function does not have /// to work for them. fn imported_source_files( - &'a self, + &self, local_source_map: &source_map::SourceMap, - ) -> &[ImportedSourceFile] { - self.source_map_import_info.init_locking(|| { + ) -> &'a [ImportedSourceFile] { + self.cdata.source_map_import_info.init_locking(|| { let external_source_map = self.root.source_map.decode(self); external_source_map @@ -1487,14 +1502,16 @@ fn imported_source_files( let local_version = local_source_map.new_imported_source_file( name, name_was_remapped, - self.cnum.as_u32(), src_hash, name_hash, source_length, + self.cnum, lines, multibyte_chars, non_narrow_chars, normalized_pos, + start_pos, + end_pos, ); debug!( "CrateMetaData::imported_source_files alloc \ @@ -1516,29 +1533,50 @@ fn imported_source_files( .collect() }) } +} - /// Get the `DepNodeIndex` corresponding this crate. The result of this - /// method is cached in the `dep_node_index` field. - fn get_crate_dep_node_index(&self, tcx: TyCtxt<'tcx>) -> DepNodeIndex { - let mut dep_node_index = self.dep_node_index.load(); - - if unlikely!(dep_node_index == DepNodeIndex::INVALID) { - // We have not cached the DepNodeIndex for this upstream crate yet, - // so use the dep-graph to find it out and cache it. - // Note that multiple threads can enter this block concurrently. - // That is fine because the DepNodeIndex remains constant - // throughout the whole compilation session, and multiple stores - // would always write the same value. - - let def_path_hash = self.def_path_hash(CRATE_DEF_INDEX); - let dep_node = def_path_hash.to_dep_node(dep_graph::DepKind::CrateMetadata); - - dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node); - assert!(dep_node_index != DepNodeIndex::INVALID); - self.dep_node_index.store(dep_node_index); +impl CrateMetadata { + crate fn new( + sess: &Session, + blob: MetadataBlob, + root: CrateRoot<'static>, + raw_proc_macros: Option<&'static [ProcMacro]>, + cnum: CrateNum, + cnum_map: CrateNumMap, + dep_kind: DepKind, + source: CrateSource, + private_dep: bool, + host_hash: Option, + ) -> CrateMetadata { + let def_path_table = record_time(&sess.perf_stats.decode_def_path_tables_time, || { + root.def_path_table.decode((&blob, sess)) + }); + let trait_impls = root + .impls + .decode((&blob, sess)) + .map(|trait_impls| (trait_impls.trait_id, trait_impls.impls)) + .collect(); + let alloc_decoding_state = + AllocDecodingState::new(root.interpret_alloc_index.decode(&blob).collect()); + let dependencies = Lock::new(cnum_map.iter().cloned().collect()); + CrateMetadata { + blob, + root, + def_path_table, + trait_impls, + raw_proc_macros, + source_map_import_info: Once::new(), + alloc_decoding_state, + dep_node_index: AtomicCell::new(DepNodeIndex::INVALID), + cnum, + cnum_map, + dependencies, + dep_kind: Lock::new(dep_kind), + source, + private_dep, + host_hash, + extern_crate: Lock::new(None), } - - dep_node_index } crate fn dependencies(&self) -> LockGuard<'_, Vec> { @@ -1613,6 +1651,52 @@ fn get_crate_dep_node_index(&self, tcx: TyCtxt<'tcx>) -> DepNodeIndex { crate fn hash(&self) -> Svh { self.root.hash } + + fn local_def_id(&self, index: DefIndex) -> DefId { + DefId { krate: self.cnum, index } + } + + // Translate a DefId from the current compilation environment to a DefId + // for an external crate. + fn reverse_translate_def_id(&self, did: DefId) -> Option { + for (local, &global) in self.cnum_map.iter_enumerated() { + if global == did.krate { + return Some(DefId { krate: local, index: did.index }); + } + } + + None + } + + #[inline] + fn def_path_hash(&self, index: DefIndex) -> DefPathHash { + self.def_path_table.def_path_hash(index) + } + + /// Get the `DepNodeIndex` corresponding this crate. The result of this + /// method is cached in the `dep_node_index` field. + fn get_crate_dep_node_index(&self, tcx: TyCtxt<'tcx>) -> DepNodeIndex { + let mut dep_node_index = self.dep_node_index.load(); + + if unlikely!(dep_node_index == DepNodeIndex::INVALID) { + // We have not cached the DepNodeIndex for this upstream crate yet, + // so use the dep-graph to find it out and cache it. + // Note that multiple threads can enter this block concurrently. + // That is fine because the DepNodeIndex remains constant + // throughout the whole compilation session, and multiple stores + // would always write the same value. + + let def_path_hash = self.def_path_hash(CRATE_DEF_INDEX); + let dep_node = + DepNode::from_def_path_hash(def_path_hash, dep_graph::DepKind::CrateMetadata); + + dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node); + assert!(dep_node_index != DepNodeIndex::INVALID); + self.dep_node_index.store(dep_node_index); + } + + dep_node_index + } } // Cannot be implemented on 'ProcMacro', as libproc_macro diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs index 50ab3811f52..cc2bd51f92f 100644 --- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs +++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs @@ -7,34 +7,27 @@ use rustc::hir::exports::Export; use rustc::hir::map::definitions::DefPathTable; use rustc::hir::map::{DefKey, DefPath, DefPathHash}; -use rustc::middle::cstore::{CrateSource, CrateStore, DepKind, EncodedMetadata, NativeLibraryKind}; +use rustc::middle::cstore::{CrateSource, CrateStore, EncodedMetadata, NativeLibraryKind}; use rustc::middle::exported_symbols::ExportedSymbol; use rustc::middle::stability::DeprecationEntry; -use rustc::session::{CrateDisambiguator, Session}; use rustc::ty::query::Providers; use rustc::ty::query::QueryConfig; use rustc::ty::{self, TyCtxt}; +use rustc_ast::ast; +use rustc_ast::attr; +use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_parse::parser::emit_unclosed_delims; -use rustc_parse::source_file_to_stream; +use rustc_session::{CrateDisambiguator, Session}; +use rustc_span::source_map::{self, Span, Spanned}; +use rustc_span::symbol::Symbol; use rustc_data_structures::sync::Lrc; use smallvec::SmallVec; use std::any::Any; use std::sync::Arc; -use rustc_ast::ast; -use rustc_ast::attr; -use rustc_ast::expand::allocator::AllocatorKind; -use rustc_ast::ptr::P; -use rustc_ast::tokenstream::DelimSpan; -use rustc_span::source_map; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; -use rustc_span::{FileName, Span}; - macro_rules! provide { (<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $($name:ident => $compute:block)*) => { @@ -147,7 +140,7 @@ fn into_args(self) -> (DefId, DefId) { cdata.get_deprecation(def_id.index).map(DeprecationEntry::external) } item_attrs => { cdata.get_item_attrs(def_id.index, tcx.sess) } - // FIXME(#38501) We've skipped a `read` on the `HirBody` of + // FIXME(#38501) We've skipped a `read` on the `hir_owner_items` of // a `fn` when encoding, so the dep-tracking wouldn't work. // This is only used by rustdoc anyway, which shouldn't have // incremental recompilation ever enabled. @@ -393,14 +386,6 @@ pub fn provide(providers: &mut Providers<'_>) { } impl CStore { - pub fn export_macros_untracked(&self, cnum: CrateNum) { - let data = self.get_crate_data(cnum); - let mut dep_kind = data.dep_kind.lock(); - if *dep_kind == DepKind::UnexportedMacrosOnly { - *dep_kind = DepKind::MacrosOnly; - } - } - pub fn struct_field_names_untracked(&self, def: DefId, sess: &Session) -> Vec> { self.get_crate_data(def.krate).get_struct_field_names(def.index, sess) } @@ -427,15 +412,7 @@ pub fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro { return LoadedMacro::ProcMacro(data.load_proc_macro(id.index, sess)); } - let def = data.get_macro(id.index); - let macro_full_name = data.def_path(id.index).to_string_friendly(|_| data.root.name); - let source_name = FileName::Macros(macro_full_name); - - let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body); - let local_span = Span::with_root_ctxt(source_file.start_pos, source_file.end_pos); - let dspan = DelimSpan::from_single(local_span); - let (body, mut errors) = source_file_to_stream(&sess.parse_sess, source_file, None); - emit_unclosed_delims(&mut errors, &sess.parse_sess); + let span = data.get_span(id.index, sess); // Mark the attrs as used let attrs = data.get_item_attrs(id.index, sess); @@ -443,28 +420,22 @@ pub fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro { attr::mark_used(attr); } - let name = data + let ident = data .def_key(id.index) .disambiguated_data .data .get_opt_name() + .map(ast::Ident::with_dummy_span) // FIXME: cross-crate hygiene .expect("no name in load_macro"); - sess.imported_macro_spans - .borrow_mut() - .insert(local_span, (name.to_string(), data.get_span(id.index, sess))); LoadedMacro::MacroDef( ast::Item { - // FIXME: cross-crate hygiene - ident: ast::Ident::with_dummy_span(name), + ident, id: ast::DUMMY_NODE_ID, - span: local_span, + span, attrs: attrs.iter().cloned().collect(), - kind: ast::ItemKind::MacroDef(ast::MacroDef { - body: P(ast::MacArgs::Delimited(dspan, ast::MacDelimiter::Brace, body)), - legacy: def.legacy, - }), - vis: source_map::respan(local_span.shrink_to_lo(), ast::VisibilityKind::Inherited), + kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)), + vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), tokens: None, }, data.root.edition, @@ -525,7 +496,7 @@ fn def_path_hash(&self, def: DefId) -> DefPathHash { } fn def_path_table(&self, cnum: CrateNum) -> &DefPathTable { - &self.get_crate_data(cnum).def_path_table + &self.get_crate_data(cnum).cdata.def_path_table } fn crates_untracked(&self) -> Vec { diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index ea0cc2f0c8b..6280fd62de9 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -1,6 +1,7 @@ use crate::rmeta::table::FixedSizeEncoding; use crate::rmeta::*; +use log::{debug, trace}; use rustc::hir::map::definitions::DefPathTable; use rustc::hir::map::Map; use rustc::middle::cstore::{EncodedMetadata, ForeignModule, LinkagePreference, NativeLibrary}; @@ -12,33 +13,29 @@ use rustc::ty::codec::{self as ty_codec, TyEncoder}; use rustc::ty::layout::VariantIdx; use rustc::ty::{self, SymbolName, Ty, TyCtxt}; +use rustc_ast::ast; +use rustc_ast::attr; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::sync::Lrc; +use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{AnonConst, GenericParamKind}; use rustc_index::vec::Idx; - -use rustc::session::config::{self, CrateType}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::Lrc; use rustc_serialize::{opaque, Encodable, Encoder, SpecializedEncoder}; - -use log::{debug, trace}; -use rustc_ast::ast; -use rustc_ast::attr; +use rustc_session::config::{self, CrateType}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, FileName, SourceFile, Span}; +use rustc_span::{self, ExternalSource, FileName, SourceFile, Span}; use std::hash::Hash; use std::num::NonZeroUsize; use std::path::Path; use std::u32; -use rustc_hir as hir; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; - struct EncodeContext<'tcx> { opaque: opaque::Encoder, tcx: TyCtxt<'tcx>, @@ -167,20 +164,56 @@ fn specialized_encode(&mut self, span: &Span) -> Result<(), Self::Error> { return TAG_INVALID_SPAN.encode(self); } - // HACK(eddyb) there's no way to indicate which crate a Span is coming - // from right now, so decoding would fail to find the SourceFile if - // it's not local to the crate the Span is found in. - if self.source_file_cache.is_imported() { - return TAG_INVALID_SPAN.encode(self); - } + // There are two possible cases here: + // 1. This span comes from a 'foreign' crate - e.g. some crate upstream of the + // crate we are writing metadata for. When the metadata for *this* crate gets + // deserialized, the deserializer will need to know which crate it originally came + // from. We use `TAG_VALID_SPAN_FOREIGN` to indicate that a `CrateNum` should + // be deserialized after the rest of the span data, which tells the deserializer + // which crate contains the source map information. + // 2. This span comes from our own crate. No special hamdling is needed - we just + // write `TAG_VALID_SPAN_LOCAL` to let the deserializer know that it should use + // our own source map information. + let (tag, lo, hi) = if self.source_file_cache.is_imported() { + // To simplify deserialization, we 'rebase' this span onto the crate it originally came from + // (the crate that 'owns' the file it references. These rebased 'lo' and 'hi' values + // are relative to the source map information for the 'foreign' crate whose CrateNum + // we write into the metadata. This allows `imported_source_files` to binary + // search through the 'foreign' crate's source map information, using the + // deserialized 'lo' and 'hi' values directly. + // + // All of this logic ensures that the final result of deserialization is a 'normal' + // Span that can be used without any additional trouble. + let external_start_pos = { + // Introduce a new scope so that we drop the 'lock()' temporary + match &*self.source_file_cache.external_src.lock() { + ExternalSource::Foreign { original_start_pos, .. } => *original_start_pos, + src => panic!("Unexpected external source {:?}", src), + } + }; + let lo = (span.lo - self.source_file_cache.start_pos) + external_start_pos; + let hi = (span.hi - self.source_file_cache.start_pos) + external_start_pos; - TAG_VALID_SPAN.encode(self)?; - span.lo.encode(self)?; + (TAG_VALID_SPAN_FOREIGN, lo, hi) + } else { + (TAG_VALID_SPAN_LOCAL, span.lo, span.hi) + }; + + tag.encode(self)?; + lo.encode(self)?; // Encode length which is usually less than span.hi and profits more // from the variable-length integer encoding that we use. - let len = span.hi - span.lo; - len.encode(self) + let len = hi - lo; + len.encode(self)?; + + if tag == TAG_VALID_SPAN_FOREIGN { + // This needs to be two lines to avoid holding the `self.source_file_cache` + // while calling `cnum.encode(self)` + let cnum = self.source_file_cache.cnum; + cnum.encode(self)?; + } + Ok(()) // Don't encode the expansion context. } @@ -331,7 +364,7 @@ fn lazy(&mut self, value: impl EncodeContentsForLazy) - fn encode_info_for_items(&mut self) { let krate = self.tcx.hir().krate(); let vis = Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public }; - self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.module, &krate.attrs, &vis); + self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module, &krate.item.attrs, &vis); krate.visit_all_item_likes(&mut self.as_deep_visitor()); for macro_def in krate.exported_macros { self.visit_macro_def(macro_def); @@ -493,7 +526,7 @@ fn encode_crate_root(&mut self) -> Lazy> { edition: tcx.sess.edition(), has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), - has_default_lib_allocator: has_default_lib_allocator, + has_default_lib_allocator, plugin_registrar_fn: tcx.plugin_registrar_fn(LOCAL_CRATE).map(|id| id.index), proc_macro_decls_static: if is_proc_macro { let id = tcx.proc_macro_decls_static(LOCAL_CRATE).unwrap(); @@ -805,12 +838,12 @@ fn encode_info_for_trait_item(&mut self, def_id: DefId) { ) } ty::AssocKind::Method => { - let fn_data = if let hir::TraitItemKind::Method(m_sig, m) = &ast_item.kind { + let fn_data = if let hir::TraitItemKind::Fn(m_sig, m) = &ast_item.kind { let param_names = match *m { - hir::TraitMethod::Required(ref names) => { + hir::TraitFn::Required(ref names) => { self.encode_fn_param_names(names) } - hir::TraitMethod::Provided(body) => { + hir::TraitFn::Provided(body) => { self.encode_fn_param_names_for_body(body) } }; @@ -822,7 +855,7 @@ fn encode_info_for_trait_item(&mut self, def_id: DefId) { } else { bug!() }; - EntryKind::Method(self.lazy(MethodData { + EntryKind::AssocFn(self.lazy(AssocFnData { fn_data, container, has_self: trait_item.method_has_self_argument, @@ -894,7 +927,7 @@ fn encode_info_for_impl_item(&mut self, def_id: DefId) { } } ty::AssocKind::Method => { - let fn_data = if let hir::ImplItemKind::Method(ref sig, body) = ast_item.kind { + let fn_data = if let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind { FnData { asyncness: sig.header.asyncness, constness: sig.header.constness, @@ -903,7 +936,7 @@ fn encode_info_for_impl_item(&mut self, def_id: DefId) { } else { bug!() }; - EntryKind::Method(self.lazy(MethodData { + EntryKind::AssocFn(self.lazy(AssocFnData { fn_data, container, has_self: impl_item.method_has_self_argument, @@ -928,7 +961,7 @@ fn encode_info_for_impl_item(&mut self, def_id: DefId) { self.encode_inferred_outlives(def_id); let mir = match ast_item.kind { hir::ImplItemKind::Const(..) => true, - hir::ImplItemKind::Method(ref sig, _) => { + hir::ImplItemKind::Fn(ref sig, _) => { let generics = self.tcx.generics_of(def_id); let needs_inline = (generics.requires_monomorphization(self.tcx) || tcx.codegen_fn_attrs(def_id).requests_inline()) @@ -1077,12 +1110,13 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) { let polarity = self.tcx.impl_polarity(def_id); let parent = if let Some(trait_ref) = trait_ref { let trait_def = self.tcx.trait_def(trait_ref.def_id); - trait_def.ancestors(self.tcx, def_id).nth(1).and_then(|node| { - match node { - specialization_graph::Node::Impl(parent) => Some(parent), - _ => None, - } - }) + trait_def.ancestors(self.tcx, def_id).ok() + .and_then(|mut an| an.nth(1).and_then(|node| { + match node { + specialization_graph::Node::Impl(parent) => Some(parent), + _ => None, + } + })) } else { None }; @@ -1114,6 +1148,7 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) { paren_sugar: trait_def.paren_sugar, has_auto_impl: self.tcx.trait_is_auto(def_id), is_marker: trait_def.is_marker, + specialization_kind: trait_def.specialization_kind, }; EntryKind::Trait(self.lazy(data)) @@ -1235,12 +1270,8 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) { /// Serialize the text of exported macros fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) { - use rustc_ast_pretty::pprust; let def_id = self.tcx.hir().local_def_id(macro_def.hir_id); - record!(self.per_def.kind[def_id] <- EntryKind::MacroDef(self.lazy(MacroDef { - body: pprust::tts_to_string(macro_def.body.clone()), - legacy: macro_def.legacy, - }))); + record!(self.per_def.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone()))); record!(self.per_def.visibility[def_id] <- ty::Visibility::Public); record!(self.per_def.span[def_id] <- macro_def.span); record!(self.per_def.attributes[def_id] <- macro_def.attrs); @@ -1319,7 +1350,7 @@ fn encode_proc_macros(&mut self) -> Option> { let is_proc_macro = self.tcx.sess.crate_types.borrow().contains(&CrateType::ProcMacro); if is_proc_macro { let tcx = self.tcx; - Some(self.lazy(tcx.hir().krate().proc_macros.iter().map(|p| p.owner))) + Some(self.lazy(tcx.hir().krate().proc_macros.iter().map(|p| p.owner.local_def_index))) } else { None } @@ -1407,8 +1438,8 @@ fn encode_impls(&mut self) -> Lazy<[TraitImpls]> { .into_iter() .map(|(trait_def_id, mut impls)| { // Bring everything into deterministic order for hashing - impls.sort_by_cached_key(|&def_index| { - tcx.hir().definitions().def_path_hash(def_index) + impls.sort_by_cached_key(|&index| { + tcx.hir().definitions().def_path_hash(LocalDefId { local_def_index: index }) }); TraitImpls { @@ -1506,8 +1537,8 @@ fn encode_info_for_foreign_item(&mut self, def_id: DefId, nitem: &hir::ForeignIt impl Visitor<'tcx> for EncodeContext<'tcx> { type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.tcx.hir()) } fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { intravisit::walk_expr(self, ex); diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 9b05fd4734c..05d834e5dee 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -7,10 +7,8 @@ use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc::middle::lang_items; use rustc::mir; -use rustc::session::config::SymbolManglingVersion; -use rustc::session::CrateDisambiguator; use rustc::ty::{self, ReprOptions, Ty}; -use rustc_ast::ast; +use rustc_ast::ast::{self, MacroDef}; use rustc_attr as attr; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; @@ -19,6 +17,8 @@ use rustc_hir::def_id::{DefId, DefIndex}; use rustc_index::vec::IndexVec; use rustc_serialize::opaque::Encoder; +use rustc_session::config::SymbolManglingVersion; +use rustc_session::CrateDisambiguator; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{self, Span}; @@ -201,7 +201,7 @@ macro_rules! Lazy { per_def: LazyPerDefTables<'tcx>, - /// The DefIndex's of any proc macros delcared by this crate. + /// The DefIndex's of any proc macros declared by this crate. proc_macro_data: Option>, compiler_builtins: bool, @@ -305,7 +305,7 @@ enum EntryKind { Generator(hir::GeneratorKind), Trait(Lazy), Impl(Lazy), - Method(Lazy), + AssocFn(Lazy), AssocType(AssocContainer), AssocOpaqueTy(AssocContainer), AssocConst(AssocContainer, mir::ConstQualifs, Lazy), @@ -322,12 +322,6 @@ struct ModData { reexports: Lazy<[Export]>, } -#[derive(RustcEncodable, RustcDecodable)] -struct MacroDef { - body: String, - legacy: bool, -} - #[derive(RustcEncodable, RustcDecodable)] struct FnData { asyncness: hir::IsAsync, @@ -349,6 +343,7 @@ struct TraitData { paren_sugar: bool, has_auto_impl: bool, is_marker: bool, + specialization_kind: ty::trait_def::TraitSpecializationKind, } #[derive(RustcEncodable, RustcDecodable)] @@ -398,7 +393,7 @@ fn defaultness(&self) -> hir::Defaultness { } #[derive(RustcEncodable, RustcDecodable)] -struct MethodData { +struct AssocFnData { fn_data: FnData, container: AssocContainer, has_self: bool, @@ -410,5 +405,6 @@ struct GeneratorData<'tcx> { } // Tags used for encoding Spans: -const TAG_VALID_SPAN: u8 = 0; -const TAG_INVALID_SPAN: u8 = 1; +const TAG_VALID_SPAN_LOCAL: u8 = 0; +const TAG_VALID_SPAN_FOREIGN: u8 = 1; +const TAG_INVALID_SPAN: u8 = 2; diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 6e606230b63..256a80076b9 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -15,7 +15,7 @@ dot = { path = "../libgraphviz", package = "graphviz" } itertools = "0.8" log = "0.4" log_settings = "0.1.1" -polonius-engine = "0.11.0" +polonius-engine = "0.12.0" rustc = { path = "../librustc" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } @@ -27,7 +27,9 @@ rustc_infer = { path = "../librustc_infer" } rustc_lexer = { path = "../librustc_lexer" } rustc_macros = { path = "../librustc_macros" } rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_session = { path = "../librustc_session" } rustc_target = { path = "../librustc_target" } +rustc_trait_selection = { path = "../librustc_trait_selection" } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } rustc_apfloat = { path = "../librustc_apfloat" } diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index ca51d16f9f2..e895eec5d52 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -10,9 +10,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; use rustc_index::vec::Idx; -use rustc_infer::traits::error_reporting::suggest_constraining_type_param; use rustc_span::source_map::DesugaringKind; use rustc_span::Span; +use rustc_trait_selection::traits::error_reporting::suggest_constraining_type_param; use crate::dataflow::drop_flag_effects; use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex}; @@ -51,7 +51,7 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized( &mut self, location: Location, desired_action: InitializationRequiringAction, - (moved_place, used_place, span): (PlaceRef<'cx, 'tcx>, PlaceRef<'cx, 'tcx>, Span), + (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span), mpi: MovePathIndex, ) { debug!( @@ -647,7 +647,7 @@ pub(in crate::borrow_check) fn describe_place_for_conflicting_borrow( // borrowed place and look for a access to a different field of the same union. let Place { local, projection } = second_borrowed_place; - let mut cursor = projection.as_ref(); + let mut cursor = &projection[..]; while let [proj_base @ .., elem] = cursor { cursor = proj_base; @@ -1231,7 +1231,7 @@ fn report_escaping_closure_capture( GeneratorKind::Async(async_kind) => match async_kind { AsyncGeneratorKind::Block => "async block", AsyncGeneratorKind::Closure => "async closure", - _ => bug!("async block/closure expected, but async funtion found."), + _ => bug!("async block/closure expected, but async function found."), }, GeneratorKind::Gen => "generator", }, @@ -1521,7 +1521,7 @@ pub(in crate::borrow_check) fn report_illegal_reassignment( err.buffer(&mut self.errors_buffer); } - fn classify_drop_access_kind(&self, place: PlaceRef<'cx, 'tcx>) -> StorageDeadOrDrop<'tcx> { + fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> { let tcx = self.infcx.tcx; match place.projection { [] => StorageDeadOrDrop::LocalStorageDead, diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs index cd6834a5a4d..7110a4a3058 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mod.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs @@ -51,7 +51,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { pub(super) fn add_moved_or_invoked_closure_note( &self, location: Location, - place: PlaceRef<'cx, 'tcx>, + place: PlaceRef<'tcx>, diag: &mut DiagnosticBuilder<'_>, ) { debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); @@ -139,7 +139,7 @@ pub(super) fn add_moved_or_invoked_closure_note( /// End-user visible description of `place` if one can be found. If the /// place is a temporary for instance, None will be returned. - pub(super) fn describe_place(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option { + pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option { self.describe_place_with_options(place_ref, IncludingDowncast(false)) } @@ -149,7 +149,7 @@ pub(super) fn describe_place(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option, + place: PlaceRef<'tcx>, including_downcast: IncludingDowncast, ) -> Option { let mut buf = String::new(); @@ -162,7 +162,7 @@ pub(super) fn describe_place_with_options( /// Appends end-user visible description of `place` to `buf`. fn append_place_to_string( &self, - place: PlaceRef<'cx, 'tcx>, + place: PlaceRef<'tcx>, buf: &mut String, mut autoderef: bool, including_downcast: &IncludingDowncast, @@ -175,7 +175,7 @@ fn append_place_to_string( if self.body.local_decls[local].is_ref_for_guard() => { self.append_place_to_string( - PlaceRef { local: local, projection: &[] }, + PlaceRef { local, projection: &[] }, buf, autoderef, &including_downcast, @@ -303,7 +303,7 @@ fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ( } /// End-user visible description of the `field`nth field of `base` - fn describe_field(&self, place: PlaceRef<'cx, 'tcx>, field: Field) -> String { + fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String { // FIXME Place2 Make this work iteratively match place { PlaceRef { local, projection: [] } => { @@ -399,7 +399,7 @@ pub(super) fn note_type_does_not_implement_copy( pub(super) fn borrowed_content_source( &self, - deref_base: PlaceRef<'cx, 'tcx>, + deref_base: PlaceRef<'tcx>, ) -> BorrowedContentSource<'tcx> { let tcx = self.infcx.tcx; @@ -694,7 +694,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// Finds the spans associated to a move or copy of move_place at location. pub(super) fn move_spans( &self, - moved_place: PlaceRef<'cx, 'tcx>, // Could also be an upvar. + moved_place: PlaceRef<'tcx>, // Could also be an upvar. location: Location, ) -> UseSpans { use self::UseSpans::*; @@ -782,7 +782,7 @@ pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpan fn closure_span( &self, def_id: DefId, - target_place: PlaceRef<'cx, 'tcx>, + target_place: PlaceRef<'tcx>, places: &Vec>, ) -> Option<(Span, Option, Span)> { debug!( diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs index d91f6edc980..c462f934148 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs @@ -23,7 +23,7 @@ pub(crate) fn report_mutability_error( &mut self, access_place: &Place<'tcx>, span: Span, - the_place_err: PlaceRef<'cx, 'tcx>, + the_place_err: PlaceRef<'tcx>, error_access: AccessKind, location: Location, ) { @@ -329,40 +329,6 @@ pub(crate) fn report_mutability_error( if self.body.local_decls[local].is_user_variable() => { let local_decl = &self.body.local_decls[local]; - let suggestion = match local_decl.local_info { - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_))) => { - Some(suggest_ampmut_self(self.infcx.tcx, local_decl)) - } - - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( - mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info, - .. - }, - ))) => Some(suggest_ampmut( - self.infcx.tcx, - self.body, - local, - local_decl, - opt_ty_info, - )), - - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( - mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByReference(_), - .. - }, - ))) => { - let pattern_span = local_decl.source_info.span; - suggest_ref_mut(self.infcx.tcx, pattern_span) - .map(|replacement| (pattern_span, replacement)) - } - - LocalInfo::User(ClearCrossCrate::Clear) => bug!("saw cleared local state"), - - _ => unreachable!(), - }; let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() { ("&", "reference") @@ -370,17 +336,53 @@ pub(crate) fn report_mutability_error( ("*const", "pointer") }; - if let Some((err_help_span, suggested_code)) = suggestion { - err.span_suggestion( - err_help_span, - &format!("consider changing this to be a mutable {}", pointer_desc), - suggested_code, - Applicability::MachineApplicable, - ); - } - match self.local_names[local] { Some(name) if !local_decl.from_compiler_desugaring() => { + let suggestion = match local_decl.local_info { + LocalInfo::User(ClearCrossCrate::Set( + mir::BindingForm::ImplicitSelf(_), + )) => Some(suggest_ampmut_self(self.infcx.tcx, local_decl)), + + LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( + mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info, + .. + }, + ))) => Some(suggest_ampmut( + self.infcx.tcx, + self.body, + local, + local_decl, + opt_ty_info, + )), + + LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( + mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByReference(_), + .. + }, + ))) => { + let pattern_span = local_decl.source_info.span; + suggest_ref_mut(self.infcx.tcx, pattern_span) + .map(|replacement| (pattern_span, replacement)) + } + + LocalInfo::User(ClearCrossCrate::Clear) => { + bug!("saw cleared local state") + } + + _ => unreachable!(), + }; + + if let Some((err_help_span, suggested_code)) = suggestion { + err.span_suggestion( + err_help_span, + &format!("consider changing this to be a mutable {}", pointer_desc), + suggested_code, + Applicability::MachineApplicable, + ); + } err.span_label( span, format!( @@ -445,7 +447,7 @@ pub(crate) fn report_mutability_error( err.buffer(&mut self.errors_buffer); } - /// Targetted error when encountering an `FnMut` closure where an `Fn` closure was expected. + /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected. fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) { err.span_label(sp, format!("cannot {}", act)); @@ -478,12 +480,12 @@ fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Spa })) | Some(hir::Node::TraitItem(hir::TraitItem { ident, - kind: hir::TraitItemKind::Method(sig, _), + kind: hir::TraitItemKind::Fn(sig, _), .. })) | Some(hir::Node::ImplItem(hir::ImplItem { ident, - kind: hir::ImplItemKind::Method(sig, _), + kind: hir::ImplItemKind::Fn(sig, _), .. })) => Some( arg_pos @@ -520,12 +522,12 @@ fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Spa hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }) | hir::Node::TraitItem(hir::TraitItem { ident, - kind: hir::TraitItemKind::Method(sig, _), + kind: hir::TraitItemKind::Fn(sig, _), .. }) | hir::Node::ImplItem(hir::ImplItem { ident, - kind: hir::ImplItemKind::Method(sig, _), + kind: hir::ImplItemKind::Fn(sig, _), .. }) => { err.span_label(ident.span, ""); diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs index 8cd75d4a2fd..494b6421fd5 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -4,7 +4,8 @@ use rustc::ty::{self, RegionVid, Ty}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_infer::infer::{ - error_reporting::nice_region_error::NiceRegionError, opaque_types, NLLRegionVariableOrigin, + error_reporting::nice_region_error::NiceRegionError, + error_reporting::unexpected_hidden_region_diagnostic, NLLRegionVariableOrigin, }; use rustc_span::symbol::kw; use rustc_span::Span; @@ -197,7 +198,7 @@ pub(in crate::borrow_check) fn report_region_errors(&mut self, nll_errors: Regio let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id); let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); - opaque_types::unexpected_hidden_region_diagnostic( + unexpected_hidden_region_diagnostic( self.infcx.tcx, Some(region_scope_tree), span, @@ -283,8 +284,7 @@ pub(in crate::borrow_check) fn report_region_error( debug!("report_region_error: category={:?} {:?}", category, span); // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - let tables = self.infcx.tcx.typeck_tables_of(self.mir_def_id); - let nice = NiceRegionError::new_from_span(self.infcx, span, o, f, Some(tables)); + let nice = NiceRegionError::new_from_span(self.infcx, span, o, f); if let Some(diag) = nice.try_report_from_nll() { diag.buffer(&mut self.errors_buffer); return; diff --git a/src/librustc_mir/borrow_check/diagnostics/region_name.rs b/src/librustc_mir/borrow_check/diagnostics/region_name.rs index 01ace742876..7103fc596c9 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_name.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_name.rs @@ -651,7 +651,7 @@ fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Opti if gen_move.is_some() { " of generator" } else { " of closure" }, ), hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Method(method_sig, _), + kind: hir::ImplItemKind::Fn(method_sig, _), .. }) => (method_sig.decl.output.span(), ""), _ => (self.body.span, ""), diff --git a/src/librustc_mir/borrow_check/facts.rs b/src/librustc_mir/borrow_check/facts.rs index 827ccb1c857..cd8139b17b4 100644 --- a/src/librustc_mir/borrow_check/facts.rs +++ b/src/librustc_mir/borrow_check/facts.rs @@ -71,16 +71,16 @@ macro_rules! write_facts_to_path { killed, outlives, invalidates, - var_used, - var_defined, - var_drop_used, - var_uses_region, - var_drops_region, - child, - path_belongs_to_var, - initialized_at, - moved_out_at, - path_accessed_at, + var_used_at, + var_defined_at, + var_dropped_at, + use_of_var_derefs_origin, + drop_of_var_derefs_origin, + child_path, + path_is_var, + path_assigned_at_base, + path_moved_at_base, + path_accessed_at_base, known_subset, ]) } diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/src/librustc_mir/borrow_check/invalidation.rs index 0bead27050c..d33639aa9d9 100644 --- a/src/librustc_mir/borrow_check/invalidation.rs +++ b/src/librustc_mir/borrow_check/invalidation.rs @@ -63,7 +63,7 @@ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { self.mutate_place(location, lhs, Shallow(None), JustWrite); } StatementKind::FakeRead(_, _) => { - // Only relavent for initialized/liveness/safety checks. + // Only relevant for initialized/liveness/safety checks. } StatementKind::SetDiscriminant { ref place, variant_index: _ } => { self.mutate_place(location, place, Shallow(None), JustWrite); diff --git a/src/librustc_mir/borrow_check/member_constraints.rs b/src/librustc_mir/borrow_check/member_constraints.rs index aeb29d2e11e..4323e2db844 100644 --- a/src/librustc_mir/borrow_check/member_constraints.rs +++ b/src/librustc_mir/borrow_check/member_constraints.rs @@ -125,7 +125,7 @@ impl MemberConstraintSet<'tcx, R1> // wind up mapped to the same key `S`, we would append the // linked list for `Ra` onto the end of the linked list for // `Rb` (or vice versa) -- this basically just requires - // rewriting the final link from one list to point at the othe + // rewriting the final link from one list to point at the other // other (see `append_list`). let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self; diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 28974dcd08b..6c1901455fd 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1,7 +1,5 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. -use rustc::lint::builtin::MUTABLE_BORROW_RESERVATION_CONFLICT; -use rustc::lint::builtin::UNUSED_MUT; use rustc::mir::{ read_only, traversal, Body, BodyAndCache, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, PlaceRef, ReadOnlyBodyAndCache, @@ -11,7 +9,7 @@ use rustc::mir::{Terminator, TerminatorKind}; use rustc::ty::query::Providers; use rustc::ty::{self, RegionVid, TyCtxt}; - +use rustc_ast::ast::Name; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder}; @@ -20,6 +18,8 @@ use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; +use rustc_span::{Span, DUMMY_SP}; use either::Either; use smallvec::SmallVec; @@ -28,9 +28,6 @@ use std::mem; use std::rc::Rc; -use rustc_ast::ast::Name; -use rustc_span::{Span, DUMMY_SP}; - use crate::dataflow; use crate::dataflow::generic::{Analysis, BorrowckFlowState as Flows, BorrowckResults}; use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex}; @@ -86,6 +83,8 @@ mutability: Mutability, } +const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref]; + pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { mir_borrowck, ..*providers }; } @@ -452,7 +451,7 @@ fn do_mir_borrowck<'a, 'tcx>( /// for the activation of the borrow. reservation_warnings: FxHashMap, Span, Location, BorrowKind, BorrowData<'tcx>)>, - /// This field keeps track of move errors that are to be reported for given move indicies. + /// This field keeps track of move errors that are to be reported for given move indices. /// /// There are situations where many errors can be reported for a single move out (see #53807) /// and we want only the best of those errors. @@ -466,10 +465,10 @@ fn do_mir_borrowck<'a, 'tcx>( /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary /// when errors in the map are being re-added to the error buffer so that errors with the /// same primary span come out in a consistent order. - move_error_reported: BTreeMap, (PlaceRef<'cx, 'tcx>, DiagnosticBuilder<'cx>)>, + move_error_reported: BTreeMap, (PlaceRef<'tcx>, DiagnosticBuilder<'cx>)>, /// This field keeps track of errors reported in the checking of uninitialized variables, /// so that we don't report seemingly duplicate errors. - uninitialized_error_reported: FxHashSet>, + uninitialized_error_reported: FxHashSet>, /// Errors to be reported buffer errors_buffer: Vec, /// This field keeps track of all the local variables that are declared mut and are mutated. @@ -841,9 +840,9 @@ enum InitializationRequiringAction { PartialAssignment, } -struct RootPlace<'d, 'tcx> { +struct RootPlace<'tcx> { place_local: Local, - place_projection: &'d [PlaceElem<'tcx>], + place_projection: &'tcx [PlaceElem<'tcx>], is_local_mutation_allowed: LocalMutationIsAllowed, } @@ -1413,7 +1412,6 @@ fn check_for_invalidation_at_exit( ) { debug!("check_for_invalidation_at_exit({:?})", borrow); let place = &borrow.borrowed_place; - let deref = [ProjectionElem::Deref]; let mut root_place = PlaceRef { local: place.local, projection: &[] }; // FIXME(nll-rfc#40): do more precise destructor tracking here. For now @@ -1427,7 +1425,7 @@ fn check_for_invalidation_at_exit( // Thread-locals might be dropped after the function exits // We have to dereference the outer reference because // borrows don't conflict behind shared references. - root_place.projection = &deref; + root_place.projection = DEREF_PROJECTION; (true, true) } else { (false, self.locals_are_invalidated_at_exit) @@ -1526,7 +1524,7 @@ fn check_if_full_path_is_moved( &mut self, location: Location, desired_action: InitializationRequiringAction, - place_span: (PlaceRef<'cx, 'tcx>, Span), + place_span: (PlaceRef<'tcx>, Span), flow_state: &Flows<'cx, 'tcx>, ) { let maybe_uninits = &flow_state.uninits; @@ -1592,7 +1590,7 @@ fn check_if_subslice_element_is_moved( &mut self, location: Location, desired_action: InitializationRequiringAction, - place_span: (PlaceRef<'cx, 'tcx>, Span), + place_span: (PlaceRef<'tcx>, Span), maybe_uninits: &BitSet, from: u32, to: u32, @@ -1631,7 +1629,7 @@ fn check_if_path_or_subpath_is_moved( &mut self, location: Location, desired_action: InitializationRequiringAction, - place_span: (PlaceRef<'cx, 'tcx>, Span), + place_span: (PlaceRef<'tcx>, Span), flow_state: &Flows<'cx, 'tcx>, ) { let maybe_uninits = &flow_state.uninits; @@ -1709,10 +1707,7 @@ fn check_if_path_or_subpath_is_moved( /// An Err result includes a tag indicated why the search failed. /// Currently this can only occur if the place is built off of a /// static variable, as we do not track those in the MoveData. - fn move_path_closest_to( - &mut self, - place: PlaceRef<'_, 'tcx>, - ) -> (PlaceRef<'cx, 'tcx>, MovePathIndex) { + fn move_path_closest_to(&mut self, place: PlaceRef<'tcx>) -> (PlaceRef<'tcx>, MovePathIndex) { match self.move_data.rev_lookup.find(place) { LookupResult::Parent(Some(mpi)) | LookupResult::Exact(mpi) => { (self.move_data.move_paths[mpi].place.as_ref(), mpi) @@ -1721,7 +1716,7 @@ fn move_path_closest_to( } } - fn move_path_for_place(&mut self, place: PlaceRef<'_, 'tcx>) -> Option { + fn move_path_for_place(&mut self, place: PlaceRef<'tcx>) -> Option { // If returns None, then there is no move path corresponding // to a direct owner of `place` (which means there is nothing // that borrowck tracks for its analysis). @@ -1816,7 +1811,7 @@ fn check_if_assigned_path_is_moved( fn check_parent_of_field<'cx, 'tcx>( this: &mut MirBorrowckCtxt<'cx, 'tcx>, location: Location, - base: PlaceRef<'cx, 'tcx>, + base: PlaceRef<'tcx>, span: Span, flow_state: &Flows<'cx, 'tcx>, ) { @@ -2029,7 +2024,7 @@ fn is_local_ever_initialized( } /// Adds the place into the used mutable variables set - fn add_used_mut<'d>(&mut self, root_place: RootPlace<'d, 'tcx>, flow_state: &Flows<'cx, 'tcx>) { + fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'cx, 'tcx>) { match root_place { RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => { // If the local may have been initialized, and it is now currently being @@ -2063,11 +2058,11 @@ fn add_used_mut<'d>(&mut self, root_place: RootPlace<'d, 'tcx>, flow_state: &Flo /// Whether this value can be written or borrowed mutably. /// Returns the root place if the place passed in is a projection. - fn is_mutable<'d>( + fn is_mutable( &self, - place: PlaceRef<'d, 'tcx>, + place: PlaceRef<'tcx>, is_local_mutation_allowed: LocalMutationIsAllowed, - ) -> Result, PlaceRef<'d, 'tcx>> { + ) -> Result, PlaceRef<'tcx>> { match place { PlaceRef { local, projection: [] } => { let local = &self.body.local_decls[local]; @@ -2218,7 +2213,7 @@ fn is_mutable<'d>( /// then returns the index of the field being projected. Note that this closure will always /// be `self` in the current MIR, because that is the only time we directly access the fields /// of a closure type. - pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option { + pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option { let mut place_projection = place_ref.projection; let mut by_ref = false; diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index 2201abe4b92..077ed49ed2c 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/src/librustc_mir/borrow_check/nll.rs @@ -86,15 +86,18 @@ fn populate_polonius_move_facts( body: &Body<'_>, ) { all_facts - .path_belongs_to_var + .path_is_var .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(v, &m)| (m, v))); for (child, move_path) in move_data.move_paths.iter_enumerated() { - all_facts - .child - .extend(move_path.parents(&move_data.move_paths).map(|(parent, _)| (child, parent))); + if let Some(parent) = move_path.parent { + all_facts.child_path.push((child, parent)); + } } + let fn_entry_start = location_table + .start_index(Location { block: BasicBlock::from_u32(0u32), statement_index: 0 }); + // initialized_at for init in move_data.inits.iter() { match init.location { @@ -115,28 +118,37 @@ fn populate_polonius_move_facts( // the successors, but not in the unwind block. let first_statement = Location { block: successor, statement_index: 0 }; all_facts - .initialized_at + .path_assigned_at_base .push((init.path, location_table.start_index(first_statement))); } } else { // In all other cases, the initialization just happens at the // midpoint, like any other effect. - all_facts.initialized_at.push((init.path, location_table.mid_index(location))); + all_facts + .path_assigned_at_base + .push((init.path, location_table.mid_index(location))); } } // Arguments are initialized on function entry InitLocation::Argument(local) => { assert!(body.local_kind(local) == LocalKind::Arg); - let fn_entry = Location { block: BasicBlock::from_u32(0u32), statement_index: 0 }; - all_facts.initialized_at.push((init.path, location_table.start_index(fn_entry))); + all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); } } } + for (local, &path) in move_data.rev_lookup.iter_locals_enumerated() { + if body.local_kind(local) != LocalKind::Arg { + // Non-arguments start out deinitialised; we simulate this with an + // initial move: + all_facts.path_moved_at_base.push((path, fn_entry_start)); + } + } + // moved_out_at // deinitialisation is assumed to always happen! all_facts - .moved_out_at + .path_moved_at_base .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); } @@ -260,7 +272,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( // Dump facts if requested. let polonius_output = all_facts.and_then(|all_facts| { if infcx.tcx.sess.opts.debugging_opts.nll_facts { - let def_path = infcx.tcx.hir().def_path(def_id); + let def_path = infcx.tcx.def_path(def_id); let dir_path = PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate()); all_facts.write_to_dir(dir_path, location_table).unwrap(); diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 984de021ca1..767ffa50fed 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -48,7 +48,7 @@ pub(super) fn borrow_conflicts_with_place<'tcx>( body: &Body<'tcx>, borrow_place: &Place<'tcx>, borrow_kind: BorrowKind, - access_place: PlaceRef<'_, 'tcx>, + access_place: PlaceRef<'tcx>, access: AccessDepth, bias: PlaceConflictBias, ) -> bool { @@ -73,7 +73,7 @@ fn place_components_conflict<'tcx>( body: &Body<'tcx>, borrow_place: &Place<'tcx>, borrow_kind: BorrowKind, - access_place: PlaceRef<'_, 'tcx>, + access_place: PlaceRef<'tcx>, access: AccessDepth, bias: PlaceConflictBias, ) -> bool { diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs index 31bee460fa0..c64e8c363af 100644 --- a/src/librustc_mir/borrow_check/prefixes.rs +++ b/src/librustc_mir/borrow_check/prefixes.rs @@ -13,12 +13,12 @@ use rustc::ty::{self, TyCtxt}; use rustc_hir as hir; -pub trait IsPrefixOf<'cx, 'tcx> { - fn is_prefix_of(&self, other: PlaceRef<'cx, 'tcx>) -> bool; +pub trait IsPrefixOf<'tcx> { + fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool; } -impl<'cx, 'tcx> IsPrefixOf<'cx, 'tcx> for PlaceRef<'cx, 'tcx> { - fn is_prefix_of(&self, other: PlaceRef<'cx, 'tcx>) -> bool { +impl<'tcx> IsPrefixOf<'tcx> for PlaceRef<'tcx> { + fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool { self.local == other.local && self.projection.len() <= other.projection.len() && self.projection == &other.projection[..self.projection.len()] @@ -29,7 +29,7 @@ pub(super) struct Prefixes<'cx, 'tcx> { body: ReadOnlyBodyAndCache<'cx, 'tcx>, tcx: TyCtxt<'tcx>, kind: PrefixSet, - next: Option>, + next: Option>, } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -50,7 +50,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// terminating the iteration early based on `kind`. pub(super) fn prefixes( &self, - place_ref: PlaceRef<'cx, 'tcx>, + place_ref: PlaceRef<'tcx>, kind: PrefixSet, ) -> Prefixes<'cx, 'tcx> { Prefixes { next: Some(place_ref), kind, body: self.body, tcx: self.infcx.tcx } @@ -58,7 +58,7 @@ pub(super) fn prefixes( } impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { - type Item = PlaceRef<'cx, 'tcx>; + type Item = PlaceRef<'tcx>; fn next(&mut self) -> Option { let mut cursor = self.next?; diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index 144f655420b..fe96b3e34a2 100644 --- a/src/librustc_mir/borrow_check/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/region_infer/mod.rs @@ -7,6 +7,7 @@ }; use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::binary_search_util; +use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::scc::Sccs; use rustc_hir::def_id::DefId; @@ -54,12 +55,12 @@ pub struct RegionInferenceContext<'tcx> { liveness_constraints: LivenessValues, /// The outlives constraints computed by the type-check. - constraints: Rc, + constraints: Frozen, /// The constraint-set, but in graph form, making it easy to traverse /// the constraints adjacent to a particular region. Used to construct /// the SCC (see `constraint_sccs`) and for error reporting. - constraint_graph: Rc, + constraint_graph: Frozen, /// The SCC computed from `constraints` and the constraint /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to @@ -112,7 +113,7 @@ pub struct RegionInferenceContext<'tcx> { /// Information about how the universally quantified regions in /// scope on this function relate to one another. - universal_region_relations: Rc>, + universal_region_relations: Frozen>, } /// Each time that `apply_member_constraint` is successful, it appends @@ -242,11 +243,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// The `outlives_constraints` and `type_tests` are an initial set /// of constraints produced by the MIR type check. - pub(crate) fn new( + pub(in crate::borrow_check) fn new( var_infos: VarInfos, universal_regions: Rc>, placeholder_indices: Rc, - universal_region_relations: Rc>, + universal_region_relations: Frozen>, outlives_constraints: OutlivesConstraintSet, member_constraints_in: MemberConstraintSet<'tcx, RegionVid>, closure_bounds_mapping: FxHashMap< @@ -263,8 +264,8 @@ pub(crate) fn new( .map(|info| RegionDefinition::new(info.universe, info.origin)) .collect(); - let constraints = Rc::new(outlives_constraints); // freeze constraints - let constraint_graph = Rc::new(constraints.graph(definitions.len())); + let constraints = Frozen::freeze(outlives_constraints); + let constraint_graph = Frozen::freeze(constraints.graph(definitions.len())); let fr_static = universal_regions.fr_static; let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static)); diff --git a/src/librustc_mir/borrow_check/region_infer/opaque_types.rs b/src/librustc_mir/borrow_check/region_infer/opaque_types.rs index 15bbc5677da..49b49437328 100644 --- a/src/librustc_mir/borrow_check/region_infer/opaque_types.rs +++ b/src/librustc_mir/borrow_check/region_infer/opaque_types.rs @@ -3,6 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; use rustc_span::Span; +use rustc_trait_selection::opaque_types::InferCtxtExt; use super::RegionInferenceContext; diff --git a/src/librustc_mir/borrow_check/region_infer/values.rs b/src/librustc_mir/borrow_check/region_infer/values.rs index 3126d44014b..675463cb1c1 100644 --- a/src/librustc_mir/borrow_check/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/region_infer/values.rs @@ -140,7 +140,7 @@ impl LivenessValues { /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. crate fn new(elements: Rc) -> Self { - Self { points: SparseBitMatrix::new(elements.num_points), elements: elements } + Self { points: SparseBitMatrix::new(elements.num_points), elements } } /// Iterate through each region that has a value in this set. diff --git a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs index 137216531a3..86951f93f0e 100644 --- a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs @@ -1,13 +1,15 @@ use rustc::mir::ConstraintCategory; +use rustc::traits::query::OutlivesBound; use rustc::ty::free_region_map::FreeRegionRelations; use rustc::ty::{self, RegionVid, Ty, TyCtxt}; +use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::outlives; use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; -use rustc_infer::traits::query::outlives_bounds::{self, OutlivesBound}; -use rustc_infer::traits::query::type_op::{self, TypeOp}; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use crate::borrow_check::{ @@ -52,7 +54,7 @@ type NormalizedInputsAndOutput<'tcx> = Vec>; crate struct CreateResult<'tcx> { - crate universal_region_relations: Rc>, + pub(in crate::borrow_check) universal_region_relations: Frozen>, crate region_bound_pairs: RegionBoundPairs<'tcx>, crate normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>, } @@ -266,7 +268,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { // Insert the facts we know from the predicates. Why? Why not. let param_env = self.param_env; - self.add_outlives_bounds(outlives_bounds::explicit_outlives_bounds(param_env)); + self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env)); // Finally: // - outlives is reflexive, so `'r: 'r` for every region `'r` @@ -297,7 +299,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { } CreateResult { - universal_region_relations: Rc::new(self.relations), + universal_region_relations: Frozen::freeze(self.relations), region_bound_pairs: self.region_bound_pairs, normalized_inputs_and_output, } diff --git a/src/librustc_mir/borrow_check/type_check/input_output.rs b/src/librustc_mir/borrow_check/type_check/input_output.rs index 37cf77b7095..f2194c77c89 100644 --- a/src/librustc_mir/borrow_check/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/type_check/input_output.rs @@ -64,13 +64,16 @@ pub(super) fn equate_inputs_and_outputs( } }; + debug!( + "equate_inputs_and_outputs: normalized_input_tys = {:?}, local_decls = {:?}", + normalized_input_tys, body.local_decls + ); + // Equate expected input tys with those in the MIR. for (&normalized_input_ty, argument_index) in normalized_input_tys.iter().zip(0..) { // In MIR, argument N is stored in local N+1. let local = Local::new(argument_index + 1); - debug!("equate_inputs_and_outputs: normalized_input_ty = {:?}", normalized_input_ty); - let mir_input_ty = body.local_decls[local].ty; let mir_input_span = body.local_decls[local].source_info.span; self.equate_normalized_input_or_output( diff --git a/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs b/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs index 055415f2872..407e0628b6e 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs @@ -12,12 +12,12 @@ type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>; struct UseFactsExtractor<'me> { - var_defined: &'me mut VarPointRelation, - var_used: &'me mut VarPointRelation, + var_defined_at: &'me mut VarPointRelation, + var_used_at: &'me mut VarPointRelation, location_table: &'me LocationTable, - var_drop_used: &'me mut Vec<(Local, Location)>, + var_dropped_at: &'me mut VarPointRelation, move_data: &'me MoveData<'me>, - path_accessed_at: &'me mut PathPointRelation, + path_accessed_at_base: &'me mut PathPointRelation, } // A Visitor to walk through the MIR and extract point-wise facts @@ -28,22 +28,22 @@ fn location_to_index(&self, location: Location) -> LocationIndex { fn insert_def(&mut self, local: Local, location: Location) { debug!("UseFactsExtractor::insert_def()"); - self.var_defined.push((local, self.location_to_index(location))); + self.var_defined_at.push((local, self.location_to_index(location))); } fn insert_use(&mut self, local: Local, location: Location) { debug!("UseFactsExtractor::insert_use()"); - self.var_used.push((local, self.location_to_index(location))); + self.var_used_at.push((local, self.location_to_index(location))); } fn insert_drop_use(&mut self, local: Local, location: Location) { debug!("UseFactsExtractor::insert_drop_use()"); - self.var_drop_used.push((local, location)); + self.var_dropped_at.push((local, self.location_to_index(location))); } fn insert_path_access(&mut self, path: MovePathIndex, location: Location) { debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location); - self.path_accessed_at.push((path, self.location_to_index(location))); + self.path_accessed_at_base.push((path, self.location_table.start_index(location))); } fn place_to_mpi(&self, place: &Place<'_>) -> Option { @@ -88,51 +88,54 @@ pub(super) fn populate_access_facts( body: ReadOnlyBodyAndCache<'_, 'tcx>, location_table: &LocationTable, move_data: &MoveData<'_>, - drop_used: &mut Vec<(Local, Location)>, + dropped_at: &mut Vec<(Local, Location)>, ) { debug!("populate_access_facts()"); if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() { let mut extractor = UseFactsExtractor { - var_defined: &mut facts.var_defined, - var_used: &mut facts.var_used, - var_drop_used: drop_used, - path_accessed_at: &mut facts.path_accessed_at, + var_defined_at: &mut facts.var_defined_at, + var_used_at: &mut facts.var_used_at, + var_dropped_at: &mut facts.var_dropped_at, + path_accessed_at_base: &mut facts.path_accessed_at_base, location_table, move_data, }; extractor.visit_body(body); - facts.var_drop_used.extend( - drop_used.iter().map(|&(local, location)| (local, location_table.mid_index(location))), + facts.var_dropped_at.extend( + dropped_at.iter().map(|&(local, location)| (local, location_table.mid_index(location))), ); for (local, local_decl) in body.local_decls.iter_enumerated() { - debug!("add var_uses_regions facts - local={:?}, type={:?}", local, local_decl.ty); + debug!( + "add use_of_var_derefs_origin facts - local={:?}, type={:?}", + local, local_decl.ty + ); let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); let universal_regions = &typeck.borrowck_context.universal_regions; typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| { let region_vid = universal_regions.to_region_vid(region); - facts.var_uses_region.push((local, region_vid)); + facts.use_of_var_derefs_origin.push((local, region_vid)); }); } } } // For every potentially drop()-touched region `region` in `local`'s type -// (`kind`), emit a Polonius `var_drops_region(local, region)` fact. -pub(super) fn add_var_drops_regions( +// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact. +pub(super) fn add_drop_of_var_derefs_origin( typeck: &mut TypeChecker<'_, 'tcx>, local: Local, kind: &GenericArg<'tcx>, ) { - debug!("add_var_drops_region(local={:?}, kind={:?}", local, kind); + debug!("add_drop_of_var_derefs_origin(local={:?}, kind={:?}", local, kind); if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() { let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); let universal_regions = &typeck.borrowck_context.universal_regions; typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| { let region_vid = universal_regions.to_region_vid(drop_live_region); - facts.var_drops_region.push((local, region_vid)); + facts.drop_of_var_derefs_origin.push((local, region_vid)); }); } } diff --git a/src/librustc_mir/borrow_check/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/type_check/liveness/trace.rs index 4c8deb0ecf8..0c49ee44f9a 100644 --- a/src/librustc_mir/borrow_check/type_check/liveness/trace.rs +++ b/src/librustc_mir/borrow_check/type_check/liveness/trace.rs @@ -3,9 +3,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::bit_set::HybridBitSet; use rustc_infer::infer::canonical::QueryRegionConstraints; -use rustc_infer::traits::query::dropck_outlives::DropckOutlivesResult; -use rustc_infer::traits::query::type_op::outlives::DropckOutlives; -use rustc_infer::traits::query::type_op::TypeOp; +use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult; +use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; +use rustc_trait_selection::traits::query::type_op::TypeOp; use std::rc::Rc; use crate::dataflow::generic::ResultsCursor; @@ -484,7 +484,7 @@ fn add_drop_live_facts_for( for &kind in &drop_data.dropck_result.kinds { Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at); - polonius::add_var_drops_regions(&mut self.typeck, dropped_local, &kind); + polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind); } } diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index f4e1bce462f..05d4fc8880e 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -18,23 +18,26 @@ self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, RegionVid, ToPolyTraitRef, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, }; +use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::canonical::QueryRegionConstraints; -use rustc_infer::infer::opaque_types::GenerateMemberConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin, }; -use rustc_infer::traits::query::type_op; -use rustc_infer::traits::query::type_op::custom::CustomTypeOp; -use rustc_infer::traits::query::{Fallible, NoSolution}; -use rustc_infer::traits::{self, ObligationCause, PredicateObligations}; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::opaque_types::{GenerateMemberConstraints, InferCtxtExt}; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::query::type_op; +use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; +use rustc_trait_selection::traits::query::{Fallible, NoSolution}; +use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations}; use crate::dataflow::generic::ResultsCursor; use crate::dataflow::move_paths::MoveData; @@ -579,7 +582,7 @@ fn sanitize_promoted( | ConstraintCategory::UseAsConst | ConstraintCategory::UseAsStatic = constraint.category { - // "Returning" from a promoted is an assigment to a + // "Returning" from a promoted is an assignment to a // temporary from the user's point of view. constraint.category = ConstraintCategory::Boring; } @@ -828,7 +831,7 @@ struct BorrowCheckContext<'a, 'tcx> { crate struct MirTypeckResults<'tcx> { crate constraints: MirTypeckRegionConstraints<'tcx>, - crate universal_region_relations: Rc>, + pub(in crate::borrow_check) universal_region_relations: Frozen>, crate opaque_type_values: FxHashMap>, } @@ -1905,7 +1908,7 @@ fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) { // expressions evaluate through `as_temp` or `into` a return // slot or local, so to find all unsized rvalues it is enough // to check all temps, return slots and locals. - if let None = self.reported_errors.replace((ty, span)) { + if self.reported_errors.replace((ty, span)).is_none() { let mut diag = struct_span_err!( self.tcx().sess, span, @@ -2005,7 +2008,9 @@ fn check_rvalue( &traits::Obligation::new( ObligationCause::new( span, - self.tcx().hir().def_index_to_hir_id(self.mir_def_id.index), + self.tcx() + .hir() + .local_def_id_to_hir_id(self.mir_def_id.expect_local()), traits::ObligationCauseCode::RepeatVec(should_suggest), ), self.param_env, diff --git a/src/librustc_mir/borrow_check/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/type_check/relate_tys.rs index 31507a184d8..ebaafd40262 100644 --- a/src/librustc_mir/borrow_check/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/type_check/relate_tys.rs @@ -3,8 +3,8 @@ use rustc::ty::{self, Ty}; use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; -use rustc_infer::traits::query::Fallible; -use rustc_infer::traits::DomainGoal; +use rustc_trait_selection::traits::query::Fallible; +use rustc_trait_selection::traits::DomainGoal; use crate::borrow_check::constraints::OutlivesConstraint; use crate::borrow_check::type_check::{BorrowCheckContext, Locations}; @@ -64,7 +64,7 @@ fn create_next_universe(&mut self) -> ty::UniverseIndex { } fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> { - if let Some(_) = &mut self.borrowck_context { + if self.borrowck_context.is_some() { let origin = NLLRegionVariableOrigin::Existential { from_forall }; self.infcx.next_nll_region_var(origin) } else { diff --git a/src/librustc_mir/borrow_check/universal_regions.rs b/src/librustc_mir/borrow_check/universal_regions.rs index af4ea759f4f..c1acd5bd9a6 100644 --- a/src/librustc_mir/borrow_check/universal_regions.rs +++ b/src/librustc_mir/borrow_check/universal_regions.rs @@ -486,7 +486,7 @@ fn build(self) -> UniversalRegions<'tcx> { defining_ty, unnormalized_output_ty, unnormalized_input_tys, - yield_ty: yield_ty, + yield_ty, } } @@ -774,9 +774,9 @@ fn for_each_late_bound_region_defined_on<'tcx>( fn_def_id: DefId, mut f: impl FnMut(ty::Region<'tcx>), ) { - if let Some(late_bounds) = tcx.is_late_bound_map(fn_def_id.index) { + if let Some(late_bounds) = tcx.is_late_bound_map(fn_def_id.expect_local()) { for late_bound in late_bounds.iter() { - let hir_id = HirId { owner: fn_def_id.index, local_id: *late_bound }; + let hir_id = HirId { owner: fn_def_id.expect_local(), local_id: *late_bound }; let name = tcx.hir().name(hir_id); let region_def_id = tcx.hir().local_def_id(hir_id); let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion { diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index 4d5464f774f..ffbff00cf37 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -89,7 +89,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>( InterpCx::new( tcx.at(span), param_env, - CompileTimeInterpreter::new(), + CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()), MemoryExtra { can_access_statics }, ) } @@ -186,7 +186,12 @@ fn validate_and_turn_into_const<'tcx>( if cid.promoted.is_none() { let mut ref_tracking = RefTracking::new(mplace); while let Some((mplace, path)) = ref_tracking.todo.pop() { - ecx.validate_operand(mplace.into(), path, Some(&mut ref_tracking))?; + ecx.const_validate_operand( + mplace.into(), + path, + &mut ref_tracking, + /*may_ref_to_static*/ is_static, + )?; } } // Now that we validated, turn this into a proper constant. @@ -226,7 +231,7 @@ pub fn const_eval_validated_provider<'tcx>( match tcx.const_eval_validated(key) { // try again with reveal all as requested Err(ErrorHandled::TooGeneric) => {} - // dedupliate calls + // deduplicate calls other => return other, } } @@ -267,7 +272,7 @@ pub fn const_eval_raw_provider<'tcx>( match tcx.const_eval_raw(key) { // try again with reveal all as requested Err(ErrorHandled::TooGeneric) => {} - // dedupliate calls + // deduplicate calls other => return other, } } @@ -297,7 +302,7 @@ pub fn const_eval_raw_provider<'tcx>( let mut ecx = InterpCx::new( tcx.at(span), key.param_env, - CompileTimeInterpreter::new(), + CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()), MemoryExtra { can_access_statics: is_static }, ); diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index 27efcd50841..470e4e7ed25 100644 --- a/src/librustc_mir/const_eval/fn_queries.rs +++ b/src/librustc_mir/const_eval/fn_queries.rs @@ -85,7 +85,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { let parent_id = tcx.hir().get_parent_did(hir_id); if !parent_id.is_top_level_module() { - is_const_impl_raw(tcx, LocalDefId::from_def_id(parent_id)) + is_const_impl_raw(tcx, parent_id.expect_local()) } else { false } @@ -171,7 +171,7 @@ fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool { pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { is_const_fn_raw, - is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, LocalDefId::from_def_id(def_id)), + is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, def_id.expect_local()), is_promotable_const_fn, const_fn_is_allowed_fn_ptr, ..*providers diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs index e40436ccf0b..28889486c38 100644 --- a/src/librustc_mir/const_eval/machine.rs +++ b/src/librustc_mir/const_eval/machine.rs @@ -1,9 +1,9 @@ use rustc::mir; use rustc::ty::layout::HasTyCtxt; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc_hir::def_id::DefId; +use rustc::ty::{self, Ty}; use std::borrow::{Borrow, Cow}; use std::collections::hash_map::Entry; +use std::convert::TryFrom; use std::hash::Hash; use rustc_data_structures::fx::FxHashMap; @@ -86,9 +86,6 @@ fn hook_panic_fn( } } -/// Number of steps until the detector even starts doing anything. -/// Also, a warning is shown to the user when this number is reached. -const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000; /// The number of steps between loop detector snapshots. /// Should be a power of two for performance reasons. const DETECTOR_SNAPSHOT_PERIOD: isize = 256; @@ -101,6 +98,8 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> { /// detector period. pub(super) steps_since_detector_enabled: isize, + pub(super) is_detector_enabled: bool, + /// Extra state to detect loops. pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>, } @@ -112,10 +111,14 @@ pub struct MemoryExtra { } impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { - pub(super) fn new() -> Self { + pub(super) fn new(const_eval_limit: usize) -> Self { + let steps_until_detector_enabled = + isize::try_from(const_eval_limit).unwrap_or(std::isize::MAX); + CompileTimeInterpreter { loop_detector: Default::default(), - steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED, + steps_since_detector_enabled: -steps_until_detector_enabled, + is_detector_enabled: const_eval_limit != 0, } } } @@ -237,7 +240,8 @@ fn find_mir_or_eval_fn( Ok(Some(match ecx.load_mir(instance.def, None) { Ok(body) => *body, Err(err) => { - if let err_unsup!(NoMirFor(ref path)) = err.kind { + if let err_unsup!(NoMirFor(did)) = err.kind { + let path = ecx.tcx.def_path_str(did); return Err(ConstEvalErrKind::NeedsRfc(format!( "calling extern function `{}`", path @@ -277,7 +281,6 @@ fn call_intrinsic( fn assert_panic( ecx: &mut InterpCx<'mir, 'tcx, Self>, - _span: Span, msg: &AssertMessage<'tcx>, _unwind: Option, ) -> InterpResult<'tcx> { @@ -320,13 +323,6 @@ fn binary_ptr_op( Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into()) } - fn find_foreign_static( - _tcx: TyCtxt<'tcx>, - _def_id: DefId, - ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> { - throw_unsup!(ReadForeignStatic) - } - #[inline(always)] fn init_allocation_extra<'b>( _memory_extra: &MemoryExtra, @@ -351,6 +347,10 @@ fn box_alloc( } fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + if !ecx.machine.is_detector_enabled { + return Ok(()); + } + { let steps = &mut ecx.machine.steps_since_detector_enabled; diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/const_eval/mod.rs index cb658397625..605091d6c7d 100644 --- a/src/librustc_mir/const_eval/mod.rs +++ b/src/librustc_mir/const_eval/mod.rs @@ -43,7 +43,7 @@ pub(crate) fn const_field<'tcx>( op_to_const(&ecx, field) } -pub(crate) fn const_caller_location<'tcx>( +pub(crate) fn const_caller_location( tcx: TyCtxt<'tcx>, (file, line, col): (Symbol, u32, u32), ) -> ConstValue<'tcx> { diff --git a/src/librustc_mir/dataflow/generic/cursor.rs b/src/librustc_mir/dataflow/generic/cursor.rs index 8c0ab150528..170157aca5d 100644 --- a/src/librustc_mir/dataflow/generic/cursor.rs +++ b/src/librustc_mir/dataflow/generic/cursor.rs @@ -2,7 +2,7 @@ use std::borrow::Borrow; -use rustc::mir::{self, BasicBlock, Location}; +use rustc::mir::{self, BasicBlock, Location, TerminatorKind}; use rustc_index::bit_set::BitSet; use super::{Analysis, Results}; @@ -29,14 +29,14 @@ pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>> pos: CursorPosition, - /// When this flag is set, the cursor is pointing at a `Call` terminator whose call return - /// effect has been applied to `state`. + /// When this flag is set, the cursor is pointing at a `Call` or `Yield` terminator whose call + /// return or resume effect has been applied to `state`. /// - /// This flag helps to ensure that multiple calls to `seek_after_assume_call_returns` with the + /// This flag helps to ensure that multiple calls to `seek_after_assume_success` with the /// same target will result in exactly one invocation of `apply_call_return_effect`. It is /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a /// terminator will always require a cursor reset. - call_return_effect_applied: bool, + success_effect_applied: bool, } impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> @@ -50,7 +50,7 @@ pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { body, pos: CursorPosition::BlockStart(mir::START_BLOCK), state: results.borrow().entry_sets[mir::START_BLOCK].clone(), - call_return_effect_applied: false, + success_effect_applied: false, results, } } @@ -76,14 +76,14 @@ pub fn contains(&self, elem: A::Idx) -> bool { pub fn seek_to_block_start(&mut self, block: BasicBlock) { self.state.overwrite(&self.results.borrow().entry_sets[block]); self.pos = CursorPosition::BlockStart(block); - self.call_return_effect_applied = false; + self.success_effect_applied = false; } /// Advances the cursor to hold all effects up to and including to the "before" effect of the /// statement (or terminator) at the given location. /// /// If you wish to observe the full effect of a statement or terminator, not just the "before" - /// effect, use `seek_after` or `seek_after_assume_call_returns`. + /// effect, use `seek_after` or `seek_after_assume_success`. pub fn seek_before(&mut self, target: Location) { assert!(target <= self.body.terminator_loc(target.block)); self.seek_(target, false); @@ -93,7 +93,7 @@ pub fn seek_before(&mut self, target: Location) { /// terminators) up to and including the `target`. /// /// If the `target` is a `Call` terminator, any call return effect for that terminator will - /// **not** be observed. Use `seek_after_assume_call_returns` if you wish to observe the call + /// **not** be observed. Use `seek_after_assume_success` if you wish to observe the call /// return effect. pub fn seek_after(&mut self, target: Location) { assert!(target <= self.body.terminator_loc(target.block)); @@ -101,7 +101,7 @@ pub fn seek_after(&mut self, target: Location) { // If we have already applied the call return effect, we are currently pointing at a `Call` // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo" // the call return effect. - if self.call_return_effect_applied { + if self.success_effect_applied { self.seek_to_block_start(target.block); } @@ -111,25 +111,25 @@ pub fn seek_after(&mut self, target: Location) { /// Advances the cursor to hold all effects up to and including of the statement (or /// terminator) at the given location. /// - /// If the `target` is a `Call` terminator, any call return effect for that terminator will - /// be observed. Use `seek_after` if you do **not** wish to observe the call return effect. - pub fn seek_after_assume_call_returns(&mut self, target: Location) { + /// If the `target` is a `Call` or `Yield` terminator, any call return or resume effect for that + /// terminator will be observed. Use `seek_after` if you do **not** wish to observe the + /// "success" effect. + pub fn seek_after_assume_success(&mut self, target: Location) { let terminator_loc = self.body.terminator_loc(target.block); assert!(target.statement_index <= terminator_loc.statement_index); self.seek_(target, true); - if target != terminator_loc { + if target != terminator_loc || self.success_effect_applied { return; } + // Apply the effect of the "success" path of the terminator. + + self.success_effect_applied = true; let terminator = self.body.basic_blocks()[target.block].terminator(); - if let mir::TerminatorKind::Call { - destination: Some((return_place, _)), func, args, .. - } = &terminator.kind - { - if !self.call_return_effect_applied { - self.call_return_effect_applied = true; + match &terminator.kind { + TerminatorKind::Call { destination: Some((return_place, _)), func, args, .. } => { self.results.borrow().analysis.apply_call_return_effect( &mut self.state, target.block, @@ -138,6 +138,14 @@ pub fn seek_after_assume_call_returns(&mut self, target: Location) { return_place, ); } + TerminatorKind::Yield { resume, resume_arg, .. } => { + self.results.borrow().analysis.apply_yield_resume_effect( + &mut self.state, + *resume, + resume_arg, + ); + } + _ => {} } } @@ -172,7 +180,7 @@ fn seek_(&mut self, target: Location, apply_after_effect_at_target: bool) { self.seek_to_block_start(target.block) } - // N.B., `call_return_effect_applied` is checked in `seek_after`, not here. + // N.B., `success_effect_applied` is checked in `seek_after`, not here. _ => (), } diff --git a/src/librustc_mir/dataflow/generic/engine.rs b/src/librustc_mir/dataflow/generic/engine.rs index 1487129f6c7..d32072125b3 100644 --- a/src/librustc_mir/dataflow/generic/engine.rs +++ b/src/librustc_mir/dataflow/generic/engine.rs @@ -218,15 +218,18 @@ fn propagate_bits_into_graph_successors_of( Goto { target } | Assert { target, cleanup: None, .. } - | Yield { resume: target, drop: None, .. } | Drop { target, location: _, unwind: None } | DropAndReplace { target, value: _, location: _, unwind: None } => { self.propagate_bits_into_entry_set_for(in_out, target, dirty_list) } - Yield { resume: target, drop: Some(drop), .. } => { + Yield { resume: target, drop, resume_arg, .. } => { + if let Some(drop) = drop { + self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list); + } + + self.analysis.apply_yield_resume_effect(in_out, target, &resume_arg); self.propagate_bits_into_entry_set_for(in_out, target, dirty_list); - self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list); } Assert { target, cleanup: Some(unwind), .. } @@ -239,23 +242,26 @@ fn propagate_bits_into_graph_successors_of( } SwitchInt { ref targets, ref values, ref discr, .. } => { - // If this is a switch on an enum discriminant, a custom effect may be applied - // along each outgoing edge. - if let Some(place) = discr.place() { - let enum_def = switch_on_enum_discriminant(self.tcx, self.body, bb_data, place); - if let Some(enum_def) = enum_def { + let Engine { tcx, body, .. } = *self; + let enum_ = discr + .place() + .and_then(|discr| switch_on_enum_discriminant(tcx, body, bb_data, discr)); + match enum_ { + // If this is a switch on an enum discriminant, a custom effect may be applied + // along each outgoing edge. + Some((enum_place, enum_def)) => { self.propagate_bits_into_enum_discriminant_switch_successors( - in_out, bb, enum_def, place, dirty_list, &*values, &*targets, + in_out, bb, enum_def, enum_place, dirty_list, &*values, &*targets, ); - - return; } - } - // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same - // exit state. - for target in targets.iter().copied() { - self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list); + // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same + // exit state. + None => { + for target in targets.iter().copied() { + self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list); + } + } } } @@ -342,22 +348,27 @@ fn propagate_bits_into_enum_discriminant_switch_successors( } } -/// Look at the last statement of a block that ends with to see if it is an assignment of an enum -/// discriminant to the local that determines the target of a `SwitchInt` like so: -/// _42 = discriminant(..) +/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is +/// an enum discriminant. +/// +/// We expect such blocks to have a call to `discriminant` as their last statement like so: +/// _42 = discriminant(_1) /// SwitchInt(_42, ..) +/// +/// If the basic block matches this pattern, this function returns the place corresponding to the +/// enum (`_1` in the example above) as well as the `AdtDef` of that enum. fn switch_on_enum_discriminant( tcx: TyCtxt<'tcx>, - body: &mir::Body<'tcx>, - block: &mir::BasicBlockData<'tcx>, + body: &'mir mir::Body<'tcx>, + block: &'mir mir::BasicBlockData<'tcx>, switch_on: &mir::Place<'tcx>, -) -> Option<&'tcx ty::AdtDef> { +) -> Option<(&'mir mir::Place<'tcx>, &'tcx ty::AdtDef)> { match block.statements.last().map(|stmt| &stmt.kind) { Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))) if lhs == switch_on => { match &discriminated.ty(body, tcx).ty.kind { - ty::Adt(def, _) => Some(def), + ty::Adt(def, _) => Some((discriminated, def)), // `Rvalue::Discriminant` is also used to get the active yield point for a // generator, but we do not need edge-specific effects in that case. This may diff --git a/src/librustc_mir/dataflow/generic/graphviz.rs b/src/librustc_mir/dataflow/generic/graphviz.rs index 157526d3c51..36decf7f5a9 100644 --- a/src/librustc_mir/dataflow/generic/graphviz.rs +++ b/src/librustc_mir/dataflow/generic/graphviz.rs @@ -241,7 +241,7 @@ fn write_node_label( )?; let state_on_unwind = this.results.get().clone(); - this.results.seek_after_assume_call_returns(terminator_loc); + this.results.seek_after_assume_success(terminator_loc); write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?; write!(w, "") @@ -604,8 +604,8 @@ fn write_diff>( Ok(()) } -const BR_LEFT: &'static str = r#"
"#; -const BR_LEFT_SPACE: &'static str = r#"
"#; +const BR_LEFT: &str = r#"
"#; +const BR_LEFT_SPACE: &str = r#"
"#; /// Line break policy that breaks at 40 characters and starts the next line with a single space. const LIMIT_30_ALIGN_1: Option = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }); diff --git a/src/librustc_mir/dataflow/generic/mod.rs b/src/librustc_mir/dataflow/generic/mod.rs index 9a102c9a3d0..fb4b7b9c5be 100644 --- a/src/librustc_mir/dataflow/generic/mod.rs +++ b/src/librustc_mir/dataflow/generic/mod.rs @@ -191,6 +191,20 @@ fn apply_call_return_effect( return_place: &mir::Place<'tcx>, ); + /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator. + /// + /// This is similar to `apply_call_return_effect` in that it only takes place after the + /// generator is resumed, not when it is dropped. + /// + /// By default, no effects happen. + fn apply_yield_resume_effect( + &self, + _state: &mut BitSet, + _resume_block: BasicBlock, + _resume_place: &mir::Place<'tcx>, + ) { + } + /// Updates the current dataflow state with the effect of taking a particular branch in a /// `SwitchInt` terminator. /// @@ -284,6 +298,15 @@ fn call_return_effect( return_place: &mir::Place<'tcx>, ); + /// See `Analysis::apply_yield_resume_effect`. + fn yield_resume_effect( + &self, + _trans: &mut BitSet, + _resume_block: BasicBlock, + _resume_place: &mir::Place<'tcx>, + ) { + } + /// See `Analysis::apply_discriminant_switch_effect`. fn discriminant_switch_effect( &self, @@ -347,6 +370,15 @@ fn apply_call_return_effect( self.call_return_effect(state, block, func, args, return_place); } + fn apply_yield_resume_effect( + &self, + state: &mut BitSet, + resume_block: BasicBlock, + resume_place: &mir::Place<'tcx>, + ) { + self.yield_resume_effect(state, resume_block, resume_place); + } + fn apply_discriminant_switch_effect( &self, state: &mut BitSet, diff --git a/src/librustc_mir/dataflow/generic/tests.rs b/src/librustc_mir/dataflow/generic/tests.rs index 50d4bdb67f7..8f07a10e1b0 100644 --- a/src/librustc_mir/dataflow/generic/tests.rs +++ b/src/librustc_mir/dataflow/generic/tests.rs @@ -294,7 +294,7 @@ fn cursor_seek() { cursor.seek_after(call_terminator_loc); assert!(!cursor.get().contains(call_return_effect)); - cursor.seek_after_assume_call_returns(call_terminator_loc); + cursor.seek_after_assume_success(call_terminator_loc); assert!(cursor.get().contains(call_return_effect)); let every_target = || { @@ -310,7 +310,7 @@ fn cursor_seek() { BlockStart(block) => cursor.seek_to_block_start(block), Before(loc) => cursor.seek_before(loc), After(loc) => cursor.seek_after(loc), - AfterAssumeCallReturns(loc) => cursor.seek_after_assume_call_returns(loc), + AfterAssumeCallReturns(loc) => cursor.seek_after_assume_success(loc), } assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ)); diff --git a/src/librustc_mir/dataflow/generic/visitor.rs b/src/librustc_mir/dataflow/generic/visitor.rs index 5bad8c61a0c..6e1513bcd1d 100644 --- a/src/librustc_mir/dataflow/generic/visitor.rs +++ b/src/librustc_mir/dataflow/generic/visitor.rs @@ -22,20 +22,20 @@ pub fn visit_results( let loc = Location { block, statement_index }; results.reconstruct_before_statement_effect(&mut state, stmt, loc); - vis.visit_statement(&mut state, stmt, loc); + vis.visit_statement(&state, stmt, loc); results.reconstruct_statement_effect(&mut state, stmt, loc); - vis.visit_statement_exit(&mut state, stmt, loc); + vis.visit_statement_exit(&state, stmt, loc); } let loc = body.terminator_loc(block); let term = block_data.terminator(); results.reconstruct_before_terminator_effect(&mut state, term, loc); - vis.visit_terminator(&mut state, term, loc); + vis.visit_terminator(&state, term, loc); results.reconstruct_terminator_effect(&mut state, term, loc); - vis.visit_terminator_exit(&mut state, term, loc); + vis.visit_terminator_exit(&state, term, loc); } } diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/dataflow/graphviz.rs index 45d2b1a71f0..a9ef7ef6c52 100644 --- a/src/librustc_mir/dataflow/graphviz.rs +++ b/src/librustc_mir/dataflow/graphviz.rs @@ -72,7 +72,7 @@ pub struct Edge { fn outgoing(body: &Body<'_>, bb: BasicBlock) -> Vec { (0..body[bb].terminator().successors().count()) - .map(|index| Edge { source: bb, index: index }) + .map(|index| Edge { source: bb, index }) .collect() } diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs index fabe562e68a..5341d661b1d 100644 --- a/src/librustc_mir/dataflow/impls/storage_liveness.rs +++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs @@ -161,11 +161,16 @@ fn before_terminator_effect( self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc); match &terminator.kind { - TerminatorKind::Call { destination: Some((place, _)), .. } - | TerminatorKind::Yield { resume_arg: place, .. } => { + TerminatorKind::Call { destination: Some((place, _)), .. } => { trans.gen(place.local); } + // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for + // that is that a `yield` will return from the function, and `resume_arg` is written + // only when the generator is later resumed. Unlike `Call`, this doesn't require the + // place to have storage *before* the yield, only after. + TerminatorKind::Yield { .. } => {} + // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. TerminatorKind::Call { destination: None, .. } @@ -230,6 +235,15 @@ fn call_return_effect( ) { trans.gen(return_place.local); } + + fn yield_resume_effect( + &self, + trans: &mut BitSet, + _resume_block: BasicBlock, + resume_place: &mir::Place<'tcx>, + ) { + trans.gen(resume_place.local); + } } impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 25463f31867..dd0f9ff75b9 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -1,22 +1,19 @@ +use rustc::mir::traversal; +use rustc::mir::{self, BasicBlock, BasicBlockData, Body, Location, Statement, Terminator}; +use rustc::ty::{self, TyCtxt}; use rustc_ast::ast::{self, MetaItem}; use rustc_ast_pretty::pprust; -use rustc_span::symbol::{sym, Symbol}; - use rustc_data_structures::work_queue::WorkQueue; +use rustc_hir::def_id::DefId; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_index::vec::Idx; - -use rustc::mir::traversal; -use rustc::mir::{self, BasicBlock, BasicBlockData, Body, Location, Statement, Terminator}; -use rustc::session::Session; -use rustc::ty::{self, TyCtxt}; -use rustc_hir::def_id::DefId; +use rustc_session::Session; +use rustc_span::symbol::{sym, Symbol}; use std::borrow::Borrow; use std::fmt; use std::io; use std::path::PathBuf; -use std::usize; pub use self::at_location::{FlowAtLocation, FlowsAtLocation}; pub(crate) use self::drop_flag_effects::*; diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 57aa5de7f7a..7c449ad1197 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -474,7 +474,7 @@ fn gather_move(&mut self, place: &Place<'tcx>) { } fn record_move(&mut self, place: &Place<'tcx>, path: MovePathIndex) { - let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc }); + let move_out = self.builder.data.moves.push(MoveOut { path, source: self.loc }); debug!( "gather_move({:?}, {:?}): adding move {:?} of {:?}", self.loc, place, move_out, path @@ -483,7 +483,7 @@ fn record_move(&mut self, place: &Place<'tcx>, path: MovePathIndex) { self.builder.data.loc_map[self.loc].push(move_out); } - fn gather_init(&mut self, place: PlaceRef<'cx, 'tcx>, kind: InitKind) { + fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) { debug!("gather_init({:?}, {:?})", self.loc, place); let mut place = place; diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 6f6ba7dc271..593952bfa7c 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -312,7 +312,7 @@ impl MovePathLookup { // alternative will *not* create a MovePath on the fly for an // unknown place, but will rather return the nearest available // parent. - pub fn find(&self, place: PlaceRef<'_, '_>) -> LookupResult { + pub fn find(&self, place: PlaceRef<'_>) -> LookupResult { let mut result = self.locals[place.local]; for elem in place.projection.iter() { diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 1a0be582ce6..3cbb0667ff3 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -202,7 +202,7 @@ fn cast_from_int( Char => { // `u8` to `char` cast - debug_assert_eq!(v as u8 as u128, v); + assert_eq!(v as u8 as u128, v); Ok(Scalar::from_uint(v, Size::from_bytes(4))) } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 7a3f09ac80b..68a893dc4be 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -21,7 +21,7 @@ use super::{ Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, OpTy, Operand, Place, PlaceTy, - ScalarMaybeUndef, StackPopInfo, + ScalarMaybeUndef, StackPopJump, }; pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { @@ -98,10 +98,10 @@ pub enum StackPopCleanup { /// Jump to the next block in the caller, or cause UB if None (that's a function /// that may never return). Also store layout of return place so /// we can validate it at that layout. - /// `ret` stores the block we jump to on a normal return, while 'unwind' - /// stores the block used for cleanup during unwinding + /// `ret` stores the block we jump to on a normal return, while `unwind` + /// stores the block used for cleanup during unwinding. Goto { ret: Option, unwind: Option }, - /// Just do nohing: Used by Main and for the box_alloc hook in miri. + /// Just do nothing: Used by Main and for the `box_alloc` hook in miri. /// `cleanup` says whether locals are deallocated. Static computation /// wants them leaked to intern what they need (and just throw away /// the entire `ecx` when it is done). @@ -138,7 +138,7 @@ pub enum LocalValue { impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { pub fn access(&self) -> InterpResult<'tcx, Operand> { match self.value { - LocalValue::Dead => throw_unsup!(DeadLocal), + LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Uninitialized => { bug!("The type checker should prevent reading from a never-written local") } @@ -152,7 +152,7 @@ pub fn access_mut( &mut self, ) -> InterpResult<'tcx, Result<&mut LocalValue, MemPlace>> { match self.value { - LocalValue::Dead => throw_unsup!(DeadLocal), + LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)), ref mut local @ LocalValue::Live(Operand::Immediate(_)) | ref mut local @ LocalValue::Uninitialized => Ok(Ok(local)), @@ -326,7 +326,7 @@ pub fn load_mir( if self.tcx.is_mir_available(did) { Ok(self.tcx.optimized_mir(did).unwrap_read_only()) } else { - throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id))) + throw_unsup!(NoMirFor(def_id)) } } _ => Ok(self.tcx.instance_mir(instance)), @@ -335,15 +335,25 @@ pub fn load_mir( /// Call this on things you got out of the MIR (so it is as generic as the current /// stack frame), to bring it into the proper environment for this interpreter. + pub(super) fn subst_from_current_frame_and_normalize_erasing_regions>( + &self, + value: T, + ) -> T { + self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value) + } + + /// Call this on things you got out of the MIR (so it is as generic as the provided + /// stack frame), to bring it into the proper environment for this interpreter. pub(super) fn subst_from_frame_and_normalize_erasing_regions>( &self, + frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, value: T, ) -> T { - self.tcx.subst_and_normalize_erasing_regions( - self.frame().instance.substs, - self.param_env, - &value, - ) + if let Some(substs) = frame.instance.substs_for_mir_body() { + self.tcx.subst_and_normalize_erasing_regions(substs, self.param_env, &value) + } else { + self.tcx.normalize_erasing_regions(self.param_env, value) + } } /// The `substs` are assumed to already be in our interpreter "universe" (param_env). @@ -371,11 +381,8 @@ pub fn layout_of_local( None => { let layout = crate::interpret::operand::from_known_layout(layout, || { let local_ty = frame.body.local_decls[local].ty; - let local_ty = self.tcx.subst_and_normalize_erasing_regions( - frame.instance.substs, - self.param_env, - &local_ty, - ); + let local_ty = + self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty); self.layout_of(local_ty) })?; if let Some(state) = frame.locals.get(local) { @@ -457,10 +464,7 @@ pub(super) fn size_and_align_of( // Check if this brought us over the size limit. if size.bytes() >= self.tcx.data_layout().obj_size_bound() { - throw_ub_format!( - "wide pointer metadata contains invalid information: \ - total size is bigger than largest supported object" - ); + throw_ub!(InvalidMeta("total size is bigger than largest supported object")); } Ok(Some((size, align))) } @@ -476,10 +480,7 @@ pub(super) fn size_and_align_of( // Make sure the slice is not too big. let size = elem.size.checked_mul(len, &*self.tcx).ok_or_else(|| { - err_ub_format!( - "invalid slice: \ - total size is bigger than largest supported object" - ) + err_ub!(InvalidMeta("slice is bigger than largest supported object")) })?; Ok(Some((size, elem.align.abi))) } @@ -629,23 +630,15 @@ pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); - let stack_pop_info = M::stack_pop(self, frame.extra, unwinding)?; - if let (false, StackPopInfo::StopUnwinding) = (unwinding, stack_pop_info) { - bug!("Attempted to stop unwinding while there is no unwinding!"); - } // Now where do we jump next? - // Determine if we leave this function normally or via unwinding. - let cur_unwinding = - if let StackPopInfo::StopUnwinding = stack_pop_info { false } else { unwinding }; - // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. // In that case, we return early. We also avoid validation in that case, // because this is CTFE and the final value will be thoroughly validated anyway. let (cleanup, next_block) = match frame.return_to_block { StackPopCleanup::Goto { ret, unwind } => { - (true, Some(if cur_unwinding { unwind } else { ret })) + (true, Some(if unwinding { unwind } else { ret })) } StackPopCleanup::None { cleanup, .. } => (cleanup, None), }; @@ -653,7 +646,8 @@ pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> if !cleanup { assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked"); assert!(next_block.is_none(), "tried to skip cleanup when we have a next block!"); - // Leak the locals, skip validation. + assert!(!unwinding, "tried to skip cleanup during unwinding"); + // Leak the locals, skip validation, skip machine hook. return Ok(()); } @@ -662,15 +656,15 @@ pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> self.deallocate_local(local.value)?; } - trace!( - "StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}", - frame.return_to_block, - stack_pop_info, - cur_unwinding - ); - if cur_unwinding { + if M::stack_pop(self, frame.extra, unwinding)? == StackPopJump::NoJump { + // The hook already did everything. + // We want to skip the `info!` below, hence early return. + return Ok(()); + } + // Normal return. + if unwinding { // Follow the unwind edge. - let unwind = next_block.expect("Encounted StackPopCleanup::None when unwinding!"); + let unwind = next_block.expect("Encountered StackPopCleanup::None when unwinding!"); self.unwind_to_block(unwind); } else { // Follow the normal return edge. @@ -685,7 +679,7 @@ pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> // invariant -- that is, unless a function somehow has a ptr to // its return place... but the way MIR is currently generated, the // return place is always a local and then this cannot happen. - self.validate_operand(self.place_to_op(return_place)?, vec![], None)?; + self.validate_operand(self.place_to_op(return_place)?)?; } } else { // Uh, that shouldn't happen... the function did not intend to return @@ -703,7 +697,7 @@ pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> "CONTINUING({}) {} (unwinding = {})", self.cur_frame(), self.frame().instance, - cur_unwinding + unwinding ); } diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 747b2dd4aa0..90b8a493299 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -187,7 +187,7 @@ fn visit_aggregate( self.walk_aggregate(mplace, fields) } - fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> { + fn visit_value(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> { // Handle Reference types, as these are the only relocations supported by const eval. // Raw pointers (and boxes) are handled by the `leftover_relocations` logic. let ty = mplace.layout.ty; @@ -263,8 +263,11 @@ fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> { None => self.ref_tracking.track((mplace, mutability, mode), || ()), } } + Ok(()) + } else { + // Not a reference -- proceed recursively. + self.walk_value(mplace) } - Ok(()) } } @@ -324,7 +327,7 @@ pub fn intern_const_alloc_recursive>( if let Err(error) = interned { // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have // to read enum discriminants in order to find references in enum variant fields. - if let err_unsup!(ValidationFailure(_)) = error.kind { + if let err_ub!(ValidationFailure(_)) = error.kind { let err = crate::const_eval::error_to_const_error(&ecx, error); match err.struct_error( ecx.tcx, @@ -387,7 +390,7 @@ pub fn intern_const_alloc_recursive>( } } else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) { // dangling pointer - throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into())) + throw_ub_format!("encountered dangling pointer in final constant") } else if ecx.tcx.alloc_map.lock().get(alloc_id).is_none() { // We have hit an `AllocId` that is neither in local or global memory and isn't marked // as dangling by local memory. diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index cd06cf01bfa..03aedad0d98 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -84,14 +84,15 @@ pub fn emulate_intrinsic( let substs = instance.substs; let intrinsic_name = self.tcx.item_name(instance.def_id()); - // We currently do not handle any intrinsics that are *allowed* to diverge, - // but `transmute` could lack a return place in case of UB. + // First handle intrinsics without return place. let (dest, ret) = match ret { - Some(p) => p, None => match intrinsic_name { - sym::transmute => throw_ub!(Unreachable), + sym::transmute => throw_ub_format!("transmuting to uninhabited type"), + sym::abort => M::abort(self)?, + // Unsupported diverging intrinsic. _ => return Ok(false), }, + Some(p) => p, }; // Keep the patterns in this match ordered the same as the list in @@ -134,7 +135,7 @@ pub fn emulate_intrinsic( let bits = self.force_bits(val, layout_of.size)?; let kind = match layout_of.abi { ty::layout::Abi::Scalar(ref scalar) => scalar.value, - _ => throw_unsup!(TypeNotPrimitive(ty)), + _ => bug!("{} called on invalid type {:?}", intrinsic_name, ty), }; let (nonzero, intrinsic_name) = match intrinsic_name { sym::cttz_nonzero => (true, sym::cttz), @@ -203,7 +204,7 @@ pub fn emulate_intrinsic( if is_add { // max unsigned Scalar::from_uint( - u128::max_value() >> (128 - num_bits), + u128::MAX >> (128 - num_bits), Size::from_bits(num_bits), ) } else { @@ -216,6 +217,11 @@ pub fn emulate_intrinsic( }; self.write_scalar(val, dest)?; } + sym::discriminant_value => { + let place = self.deref_operand(args[0])?; + let discr_val = self.read_discriminant(place.into())?.0; + self.write_scalar(Scalar::from_uint(discr_val, dest.layout.size), dest)?; + } sym::unchecked_shl | sym::unchecked_shr | sym::unchecked_add @@ -240,9 +246,9 @@ pub fn emulate_intrinsic( let layout = self.layout_of(substs.type_at(0))?; let r_val = self.force_bits(r.to_scalar()?, layout.size)?; if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name { - throw_ub_format!("Overflowing shift by {} in `{}`", r_val, intrinsic_name); + throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name); } else { - throw_ub_format!("Overflow executing `{}`", intrinsic_name); + throw_ub_format!("overflow executing `{}`", intrinsic_name); } } self.write_scalar(val, dest)?; @@ -381,11 +387,11 @@ pub fn exact_div( dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { // Performs an exact division, resulting in undefined behavior where - // `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`. + // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. // First, check x % y != 0 (or if that computation overflows). let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, a, b)?; - if overflow || res.to_bits(a.layout.size)? != 0 { - // Then, check if `b` is -1, which is the "min_value / -1" case. + if overflow || res.assert_bits(a.layout.size) != 0 { + // Then, check if `b` is -1, which is the "MIN / -1" case. let minus1 = Scalar::from_int(-1, dest.layout.size); let b_scalar = b.to_scalar().unwrap(); if b_scalar == minus1 { diff --git a/src/librustc_mir/interpret/intrinsics/type_name.rs b/src/librustc_mir/interpret/intrinsics/type_name.rs index cd8bf7085d1..677dc697735 100644 --- a/src/librustc_mir/interpret/intrinsics/type_name.rs +++ b/src/librustc_mir/interpret/intrinsics/type_name.rs @@ -157,6 +157,7 @@ fn path_generic_args( } } } + impl PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { fn region_should_not_be_omitted(&self, _region: ty::Region<'_>) -> bool { false diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 5291000d10b..c9d32f62400 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -6,8 +6,7 @@ use std::hash::Hash; use rustc::mir; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc_hir::def_id::DefId; +use rustc::ty::{self, Ty}; use rustc_span::Span; use super::{ @@ -18,16 +17,16 @@ /// Data returned by Machine::stack_pop, /// to provide further control over the popping of the stack frame #[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum StackPopInfo { +pub enum StackPopJump { /// Indicates that no special handling should be /// done - we'll either return normally or unwind /// based on the terminator for the function /// we're leaving. Normal, - /// Indicates that we should stop unwinding, - /// as we've reached a catch frame - StopUnwinding, + /// Indicates that we should *not* jump to the return/unwind address, as the callback already + /// took care of everything. + NoJump, } /// Whether this kind of memory is allowed to leak @@ -123,10 +122,6 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether to enforce the validity invariant fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; - /// Called before a basic block terminator is executed. - /// You can use this to detect endlessly running programs. - fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>; - /// Entry point to all function calls. /// /// Returns either the mir to use for the call, or `None` if execution should @@ -170,22 +165,14 @@ fn call_intrinsic( /// Called to evaluate `Assert` MIR terminators that trigger a panic. fn assert_panic( ecx: &mut InterpCx<'mir, 'tcx, Self>, - span: Span, msg: &mir::AssertMessage<'tcx>, unwind: Option, ) -> InterpResult<'tcx>; - /// Called for read access to a foreign static item. - /// - /// This will only be called once per static and machine; the result is cached in - /// the machine memory. (This relies on `AllocMap::get_or` being able to add the - /// owned allocation to the map even when the map is shared.) - /// - /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state. - fn find_foreign_static( - tcx: TyCtxt<'tcx>, - def_id: DefId, - ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>; + /// Called to evaluate `Abort` MIR terminator. + fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> { + throw_unsup_format!("aborting execution is not supported"); + } /// Called for all binary operations where the LHS has pointer type. /// @@ -204,6 +191,7 @@ fn box_alloc( ) -> InterpResult<'tcx>; /// Called to read the specified `local` from the `frame`. + #[inline] fn access_local( _ecx: &InterpCx<'mir, 'tcx, Self>, frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, @@ -212,7 +200,15 @@ fn access_local( frame.locals[local].access() } + /// Called before a basic block terminator is executed. + /// You can use this to detect endlessly running programs. + #[inline] + fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + Ok(()) + } + /// Called before a `Static` value is accessed. + #[inline] fn before_access_static( _memory_extra: &Self::MemoryExtra, _allocation: &Allocation, @@ -220,11 +216,22 @@ fn before_access_static( Ok(()) } + /// Called for *every* memory access to determine the real ID of the given allocation. + /// This provides a way for the machine to "redirect" certain allocations as it sees fit. + /// + /// This is used by Miri to redirect extern statics to real allocations. + /// + /// This function must be idempotent. + #[inline] + fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId { + id + } + /// Called to initialize the "extra" state of an allocation and make the pointers /// it contains (in relocations) tagged. The way we construct allocations is /// to always first construct it without extra and then add the extra. /// This keeps uniform code paths for handling both allocations created by CTFE - /// for statics, and allocations ceated by Miri during evaluation. + /// for statics, and allocations created by Miri during evaluation. /// /// `kind` is the kind of the allocation being tagged; it can be `None` when /// it's a static and `STATIC_KIND` is `None`. @@ -247,6 +254,8 @@ fn init_allocation_extra<'b>( /// Return the "base" tag for the given *static* allocation: the one that is used for direct /// accesses to this static/const/fn allocation. If `id` is not a static allocation, /// this will return an unusable tag (i.e., accesses will be UB)! + /// + /// Expects `id` to be already canonical, if needed. fn tag_static_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; /// Executes a retagging operation @@ -259,7 +268,7 @@ fn retag( Ok(()) } - /// Called immediately before a new stack frame got pushed + /// Called immediately before a new stack frame got pushed. fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>; /// Called immediately after a stack frame gets popped @@ -267,9 +276,9 @@ fn stack_pop( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _extra: Self::FrameExtra, _unwinding: bool, - ) -> InterpResult<'tcx, StackPopInfo> { + ) -> InterpResult<'tcx, StackPopJump> { // By default, we do not support unwinding from panics - Ok(StackPopInfo::Normal) + Ok(StackPopJump::Normal) } fn int_to_ptr( @@ -277,8 +286,10 @@ fn int_to_ptr( int: u64, ) -> InterpResult<'tcx, Pointer> { Err((if int == 0 { - err_unsup!(InvalidNullPointerUsage) + // This is UB, seriously. + err_ub!(InvalidIntPointerUsage(0)) } else { + // This is just something we cannot support during const-eval. err_unsup!(ReadBytesAsPointer) }) .into()) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 032a4bd5850..5b2cd89a122 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -150,7 +150,8 @@ pub fn new(tcx: TyCtxtAt<'tcx>, extra: M::MemoryExtra) -> Self { /// through a pointer that was created by the program. #[inline] pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer { - ptr.with_tag(M::tag_static_base_pointer(&self.extra, ptr.alloc_id)) + let id = M::canonical_alloc_id(self, ptr.alloc_id); + ptr.with_tag(M::tag_static_base_pointer(&self.extra, id)) } pub fn create_fn_alloc( @@ -214,7 +215,10 @@ pub fn reallocate( kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { if ptr.offset.bytes() != 0 { - throw_unsup!(ReallocateNonBasePtr) + throw_ub_format!( + "reallocating {:?} which does not point to the beginning of an object", + ptr + ); } // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc". @@ -250,7 +254,10 @@ pub fn deallocate( trace!("deallocating: {}", ptr.alloc_id); if ptr.offset.bytes() != 0 { - throw_unsup!(DeallocateNonBasePtr) + throw_ub_format!( + "deallocating {:?} which does not point to the beginning of an object", + ptr + ); } let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) { @@ -258,29 +265,32 @@ pub fn deallocate( None => { // Deallocating static memory -- always an error return Err(match self.tcx.alloc_map.lock().get(ptr.alloc_id) { - Some(GlobalAlloc::Function(..)) => err_unsup!(DeallocatedWrongMemoryKind( - "function".to_string(), - format!("{:?}", kind), - )), - Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => err_unsup!( - DeallocatedWrongMemoryKind("static".to_string(), format!("{:?}", kind)) - ), - None => err_unsup!(DoubleFree), + Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"), + Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => { + err_ub_format!("deallocating static memory") + } + None => err_ub!(PointerUseAfterFree(ptr.alloc_id)), } .into()); } }; if alloc_kind != kind { - throw_unsup!(DeallocatedWrongMemoryKind( - format!("{:?}", alloc_kind), - format!("{:?}", kind), - )) + throw_ub_format!( + "deallocating `{:?}` memory using `{:?}` deallocation operation", + alloc_kind, + kind + ); } if let Some((size, align)) = old_size_and_align { if size != alloc.size || align != alloc.align { - let bytes = alloc.size; - throw_unsup!(IncorrectAllocationInformation(size, bytes, align, alloc.align)) + throw_ub_format!( + "incorrect layout on deallocation: allocation has size {} and alignment {}, but gave size {} and alignment {}", + alloc.size.bytes(), + alloc.align.bytes(), + size.bytes(), + align.bytes(), + ) } } @@ -337,7 +347,7 @@ fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> { } else { // The biggest power of two through which `offset` is divisible. let offset_pow2 = 1 << offset.trailing_zeros(); - throw_unsup!(AlignmentCheckFailed { + throw_ub!(AlignmentCheckFailed { has: Align::from_bytes(offset_pow2).unwrap(), required: align, }) @@ -359,7 +369,7 @@ fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> { assert!(size.bytes() == 0); // Must be non-NULL. if bits == 0 { - throw_unsup!(InvalidNullPointerUsage) + throw_ub!(InvalidIntPointerUsage(0)) } // Must be aligned. if let Some(align) = align { @@ -374,7 +384,10 @@ fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> { // It is sufficient to check this for the end pointer. The addition // checks for overflow. let end_ptr = ptr.offset(size, self)?; - end_ptr.check_inbounds_alloc(allocation_size, msg)?; + if end_ptr.offset > allocation_size { + // equal is okay! + throw_ub!(PointerOutOfBounds { ptr: end_ptr.erase_tag(), msg, allocation_size }) + } // Test align. Check this last; if both bounds and alignment are violated // we want the error to be about the bounds. if let Some(align) = align { @@ -384,7 +397,7 @@ fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> { // got picked we might be aligned even if this check fails. // We instead have to fall back to converting to an integer and checking // the "real" alignment. - throw_unsup!(AlignmentCheckFailed { has: alloc_align, required: align }); + throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align }); } check_offset_align(ptr.offset.bytes(), align)?; } @@ -401,7 +414,9 @@ pub fn ptr_may_be_null(&self, ptr: Pointer) -> bool { let (size, _align) = self .get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead) .expect("alloc info with MaybeDead cannot fail"); - ptr.check_inbounds_alloc(size, CheckInAllocMsg::NullPointerTest).is_err() + // If the pointer is out-of-bounds, it may be null. + // Note that one-past-the-end (offset == size) is still inbounds, and never null. + ptr.offset > size } } @@ -421,6 +436,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// The `GlobalAlloc::Memory` branch here is still reachable though; when a static /// contains a reference to memory that was created during its evaluation (i.e., not to /// another static), those inner references only exist in "resolved" form. + /// + /// Assumes `id` is already canonical. fn get_static_alloc( memory_extra: &M::MemoryExtra, tcx: TyCtxtAt<'tcx>, @@ -429,36 +446,35 @@ fn get_static_alloc( let alloc = tcx.alloc_map.lock().get(id); let alloc = match alloc { Some(GlobalAlloc::Memory(mem)) => Cow::Borrowed(mem), - Some(GlobalAlloc::Function(..)) => throw_unsup!(DerefFunctionPointer), - None => throw_unsup!(DanglingPointerDeref), + Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), + None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { // We got a "lazy" static that has not been computed yet. if tcx.is_foreign_item(def_id) { - trace!("static_alloc: foreign item {:?}", def_id); - M::find_foreign_static(tcx.tcx, def_id)? - } else { - trace!("static_alloc: Need to compute {:?}", def_id); - let instance = Instance::mono(tcx.tcx, def_id); - let gid = GlobalId { instance, promoted: None }; - // use the raw query here to break validation cycles. Later uses of the static - // will call the full query anyway - let raw_const = - tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| { - // no need to report anything, the const_eval call takes care of that - // for statics - assert!(tcx.is_static(def_id)); - match err { - ErrorHandled::Reported => err_inval!(ReferencedConstant), - ErrorHandled::TooGeneric => err_inval!(TooGeneric), - } - })?; - // Make sure we use the ID of the resolved memory, not the lazy one! - let id = raw_const.alloc_id; - let allocation = tcx.alloc_map.lock().unwrap_memory(id); - - M::before_access_static(memory_extra, allocation)?; - Cow::Borrowed(allocation) + trace!("get_static_alloc: foreign item {:?}", def_id); + throw_unsup!(ReadForeignStatic(def_id)) } + trace!("get_static_alloc: Need to compute {:?}", def_id); + let instance = Instance::mono(tcx.tcx, def_id); + let gid = GlobalId { instance, promoted: None }; + // use the raw query here to break validation cycles. Later uses of the static + // will call the full query anyway + let raw_const = + tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| { + // no need to report anything, the const_eval call takes care of that + // for statics + assert!(tcx.is_static(def_id)); + match err { + ErrorHandled::Reported => err_inval!(ReferencedConstant), + ErrorHandled::TooGeneric => err_inval!(TooGeneric), + } + })?; + // Make sure we use the ID of the resolved memory, not the lazy one! + let id = raw_const.alloc_id; + let allocation = tcx.alloc_map.lock().unwrap_memory(id); + + M::before_access_static(memory_extra, allocation)?; + Cow::Borrowed(allocation) } }; // We got tcx memory. Let the machine initialize its "extra" stuff. @@ -478,6 +494,7 @@ pub fn get_raw( &self, id: AllocId, ) -> InterpResult<'tcx, &Allocation> { + let id = M::canonical_alloc_id(self, id); // The error type of the inner closure here is somewhat funny. We have two // ways of "erroring": An actual error, or because we got a reference from // `get_static_alloc` that we can actually use directly without inserting anything anywhere. @@ -513,6 +530,7 @@ pub fn get_raw_mut( &mut self, id: AllocId, ) -> InterpResult<'tcx, &mut Allocation> { + let id = M::canonical_alloc_id(self, id); let tcx = self.tcx; let memory_extra = &self.extra; let a = self.alloc_map.get_mut_or(id, || { @@ -520,7 +538,7 @@ pub fn get_raw_mut( // to give us a cheap reference. let alloc = Self::get_static_alloc(memory_extra, tcx, id)?; if alloc.mutability == Mutability::Not { - throw_unsup!(ModifiedConstantMemory) + throw_ub!(WriteToReadOnly(id)) } match M::STATIC_KIND { Some(kind) => Ok((MemoryKind::Machine(kind), alloc.into_owned())), @@ -534,7 +552,7 @@ pub fn get_raw_mut( Ok(a) => { let a = &mut a.1; if a.mutability == Mutability::Not { - throw_unsup!(ModifiedConstantMemory) + throw_ub!(WriteToReadOnly(id)) } Ok(a) } @@ -550,6 +568,7 @@ pub fn get_size_and_align( id: AllocId, liveness: AllocCheck, ) -> InterpResult<'static, (Size, Align)> { + let id = M::canonical_alloc_id(self, id); // # Regular allocations // Don't use `self.get_raw` here as that will // a) cause cycles in case `id` refers to a static @@ -560,10 +579,10 @@ pub fn get_size_and_align( // # Function pointers // (both global from `alloc_map` and local from `extra_fn_ptr_map`) - if let Some(_) = self.get_fn_alloc(id) { + if self.get_fn_alloc(id).is_some() { return if let AllocCheck::Dereferenceable = liveness { // The caller requested no function pointers. - throw_unsup!(DerefFunctionPointer) + throw_ub!(DerefFunctionPointer(id)) } else { Ok((Size::ZERO, Align::from_bytes(1).unwrap())) }; @@ -591,17 +610,18 @@ pub fn get_size_and_align( if let AllocCheck::MaybeDead = liveness { // Deallocated pointers are allowed, we should be able to find // them in the map. - Ok(*self.dead_alloc_map.get(&id).expect( - "deallocated pointers should all be recorded in \ - `dead_alloc_map`", - )) + Ok(*self + .dead_alloc_map + .get(&id) + .expect("deallocated pointers should all be recorded in `dead_alloc_map`")) } else { - throw_unsup!(DanglingPointerDeref) + throw_ub!(PointerUseAfterFree(id)) } } } } + /// Assumes `id` is already canonical. fn get_fn_alloc(&self, id: AllocId) -> Option> { trace!("reading fn ptr: {}", id); if let Some(extra) = self.extra_fn_ptr_map.get(&id) { @@ -620,9 +640,10 @@ pub fn get_fn( ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { let ptr = self.force_ptr(ptr)?; // We definitely need a pointer value. if ptr.offset.bytes() != 0 { - throw_unsup!(InvalidFunctionPointer) + throw_ub!(InvalidFunctionPointer(ptr.erase_tag())) } - self.get_fn_alloc(ptr.alloc_id).ok_or_else(|| err_unsup!(ExecuteMemory).into()) + let id = M::canonical_alloc_id(self, ptr.alloc_id); + self.get_fn_alloc(id).ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into()) } pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { @@ -791,6 +812,33 @@ pub fn read_c_str(&self, ptr: Scalar) -> InterpResult<'tcx, &[u8] self.get_raw(ptr.alloc_id)?.read_c_str(self, ptr) } + /// Reads a 0x0000-terminated u16-sequence from memory. Returns them as a Vec. + /// Terminator 0x0000 is not included in the returned Vec. + /// + /// Performs appropriate bounds checks. + pub fn read_wide_str(&self, ptr: Scalar) -> InterpResult<'tcx, Vec> { + let size_2bytes = Size::from_bytes(2); + let align_2bytes = Align::from_bytes(2).unwrap(); + // We need to read at least 2 bytes, so we *need* a ptr. + let mut ptr = self.force_ptr(ptr)?; + let allocation = self.get_raw(ptr.alloc_id)?; + let mut u16_seq = Vec::new(); + + loop { + ptr = self + .check_ptr_access(ptr.into(), size_2bytes, align_2bytes)? + .expect("cannot be a ZST"); + let single_u16 = allocation.read_scalar(self, ptr, size_2bytes)?.to_u16()?; + if single_u16 != 0x0000 { + u16_seq.push(single_u16); + ptr = ptr.offset(size_2bytes, self)?; + } else { + break; + } + } + Ok(u16_seq) + } + /// Writes the given stream of bytes into memory. /// /// Performs appropriate bounds checks. diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index a519f38e712..c3fd9682765 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -24,7 +24,7 @@ pub use self::memory::{AllocCheck, FnVal, Memory, MemoryKind}; -pub use self::machine::{AllocMap, Machine, MayLeak, StackPopInfo}; +pub use self::machine::{AllocMap, Machine, MayLeak, StackPopJump}; pub use self::operand::{ImmTy, Immediate, OpTy, Operand, ScalarMaybeUndef}; diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index df4b9b16186..b39058405f5 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -3,18 +3,20 @@ use std::convert::{TryFrom, TryInto}; -use rustc::ty::layout::{ - self, HasDataLayout, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx, -}; -use rustc::{mir, ty}; - use super::{InterpCx, MPlaceTy, Machine, MemPlace, Place, PlaceTy}; pub use rustc::mir::interpret::ScalarMaybeUndef; use rustc::mir::interpret::{ sign_extend, truncate, AllocId, ConstValue, GlobalId, InterpResult, Pointer, Scalar, }; -use rustc_ast::ast; +use rustc::ty::layout::{ + self, HasDataLayout, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx, +}; +use rustc::ty::print::{FmtPrinter, PrettyPrinter, Printer}; +use rustc::ty::Ty; +use rustc::{mir, ty}; +use rustc_hir::def::Namespace; use rustc_macros::HashStable; +use std::fmt::Write; /// An `Immediate` represents a single immediate self-contained Rust value. /// @@ -92,47 +94,44 @@ pub struct ImmTy<'tcx, Tag = ()> { pub layout: TyLayout<'tcx>, } -// `Tag: Copy` because some methods on `Scalar` consume them by value impl std::fmt::Display for ImmTy<'tcx, Tag> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.imm { - Immediate::Scalar(ScalarMaybeUndef::Scalar(s)) => match s.to_bits(self.layout.size) { - Ok(s) => { - match self.layout.ty.kind { - ty::Int(_) => { - return write!( - fmt, - "{}", - super::sign_extend(s, self.layout.size) as i128, - ); - } - ty::Uint(_) => return write!(fmt, "{}", s), - ty::Bool if s == 0 => return fmt.write_str("false"), - ty::Bool if s == 1 => return fmt.write_str("true"), - ty::Char => { - if let Some(c) = u32::try_from(s).ok().and_then(std::char::from_u32) { - return write!(fmt, "{}", c); - } - } - ty::Float(ast::FloatTy::F32) => { - if let Ok(u) = u32::try_from(s) { - return write!(fmt, "{}", f32::from_bits(u)); - } - } - ty::Float(ast::FloatTy::F64) => { - if let Ok(u) = u64::try_from(s) { - return write!(fmt, "{}", f64::from_bits(u)); - } - } - _ => {} - } - write!(fmt, "{:x}", s) + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// Helper function for printing a scalar to a FmtPrinter + fn p<'a, 'tcx, F: std::fmt::Write, Tag>( + cx: FmtPrinter<'a, 'tcx, F>, + s: ScalarMaybeUndef, + ty: Ty<'tcx>, + ) -> Result, std::fmt::Error> { + match s { + ScalarMaybeUndef::Scalar(s) => { + cx.pretty_print_const_scalar(s.erase_tag(), ty, true) } - Err(_) => fmt.write_str("{pointer}"), - }, - Immediate::Scalar(ScalarMaybeUndef::Undef) => fmt.write_str("{undef}"), - Immediate::ScalarPair(..) => fmt.write_str("{wide pointer or tuple}"), + ScalarMaybeUndef::Undef => cx.typed_value( + |mut this| { + this.write_str("{undef ")?; + Ok(this) + }, + |this| this.print_type(ty), + " ", + ), + } } + ty::tls::with(|tcx| { + match self.imm { + Immediate::Scalar(s) => { + if let Some(ty) = tcx.lift(&self.layout.ty) { + let cx = FmtPrinter::new(tcx, f, Namespace::ValueNS); + p(cx, s, ty)?; + return Ok(()); + } + write!(f, "{:?}: {}", s.erase_tag(), self.layout.ty) + } + Immediate::ScalarPair(a, b) => { + // FIXME(oli-obk): at least print tuples and slices nicely + write!(f, "({:?}, {:?}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,) + } + } + }) } } @@ -205,11 +204,6 @@ pub fn try_from_int(i: impl Into, layout: TyLayout<'tcx>) -> Option pub fn from_int(i: impl Into, layout: TyLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_int(i, layout.size), layout) } - - #[inline] - pub fn to_bits(self) -> InterpResult<'tcx, u128> { - self.to_scalar()?.to_bits(self.layout.size) - } } // Use the existing layout if given (but sanity check in debug mode), @@ -349,7 +343,7 @@ pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'t let len = mplace.len(self)?; let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?; let str = ::std::str::from_utf8(bytes) - .map_err(|err| err_unsup!(ValidationFailure(err.to_string())))?; + .map_err(|err| err_ub_format!("this string is not valid UTF-8: {}", err))?; Ok(str) } @@ -361,7 +355,7 @@ pub fn operand_field( ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let base = match op.try_as_mplace(self) { Ok(mplace) => { - // The easy case + // We can reuse the mplace field computation logic for indirect operands. let field = self.mplace_field(mplace, field)?; return Ok(field.into()); } @@ -463,7 +457,7 @@ pub fn eval_place_to_op( layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let base_op = match place.local { - mir::RETURN_PLACE => throw_unsup!(ReadFromReturnPointer), + mir::RETURN_PLACE => throw_ub!(ReadFromReturnPlace), local => { // Do not use the layout passed in as argument if the base we are looking at // here is not the entire place. @@ -496,7 +490,8 @@ pub fn eval_operand( Copy(ref place) | Move(ref place) => self.eval_place_to_op(place, layout)?, Constant(ref constant) => { - let val = self.subst_from_frame_and_normalize_erasing_regions(constant.literal); + let val = + self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal); self.eval_const_to_op(val, layout)? } }; @@ -683,7 +678,7 @@ pub fn read_discriminant( // Then computing the absolute variant idx should not overflow any more. let variant_index = variants_start .checked_add(variant_index_relative) - .expect("oveflow computing absolute variant idx"); + .expect("overflow computing absolute variant idx"); let variants_len = rval .layout .ty diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index cd7a419af95..f2ee5e047a8 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -234,7 +234,7 @@ fn binary_int_op( BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty), Add | Sub | Mul | Rem | Div => { - debug_assert!(!left_layout.abi.is_signed()); + assert!(!left_layout.abi.is_signed()); let op: fn(u128, u128) -> (u128, bool) = match bin_op { Add => u128::overflowing_add, Sub => u128::overflowing_sub, diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 4f96cb69891..5313446c253 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -640,14 +640,16 @@ pub fn eval_place( // bail out. None => Place::null(&*self), }, - layout: self.layout_of(self.subst_from_frame_and_normalize_erasing_regions( - self.frame().body.return_ty(), - ))?, + layout: self.layout_of( + self.subst_from_current_frame_and_normalize_erasing_regions( + self.frame().body.return_ty(), + ), + )?, } } local => PlaceTy { // This works even for dead/uninitialized locals; we check further when writing - place: Place::Local { frame: self.cur_frame(), local: local }, + place: Place::Local { frame: self.cur_frame(), local }, layout: self.layout_of_local(self.frame(), local, None)?, }, }; @@ -681,7 +683,7 @@ pub fn write_immediate( if M::enforce_validity(self) { // Data got changed, better make sure it matches the type! - self.validate_operand(self.place_to_op(dest)?, vec![], None)?; + self.validate_operand(self.place_to_op(dest)?)?; } Ok(()) @@ -698,7 +700,7 @@ pub fn write_immediate_to_mplace( if M::enforce_validity(self) { // Data got changed, better make sure it matches the type! - self.validate_operand(dest.into(), vec![], None)?; + self.validate_operand(dest.into())?; } Ok(()) @@ -835,7 +837,7 @@ pub fn copy_op( if M::enforce_validity(self) { // Data got changed, better make sure it matches the type! - self.validate_operand(self.place_to_op(dest)?, vec![], None)?; + self.validate_operand(self.place_to_op(dest)?)?; } Ok(()) @@ -918,7 +920,7 @@ pub fn copy_op_transmute( // most likey we *are* running `typeck` right now. Investigate whether we can bail out // on `typeck_tables().has_errors` at all const eval entry points. debug!("Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest); - throw_unsup!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); + throw_inval!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); } // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want // to avoid that here. @@ -943,7 +945,7 @@ pub fn copy_op_transmute( if M::enforce_validity(self) { // Data got changed, better make sure it matches the type! - self.validate_operand(dest.into(), vec![], None)?; + self.validate_operand(dest.into())?; } Ok(()) diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 2353b74016c..ee45179fd8b 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -292,7 +292,7 @@ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { let all_bytes = 0..self.len(); // This 'inspect' is okay since following access respects undef and relocations. This does - // influence interpreter exeuction, but only to detect the error of cycles in evalution + // influence interpreter exeuction, but only to detect the error of cycles in evaluation // dependencies. let bytes = self.inspect_with_undef_and_ptr_outside_interpreter(all_bytes); diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 7d59c0181a8..cb11df18378 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -248,7 +248,7 @@ pub fn eval_rvalue_into_place( } NullaryOp(mir::NullOp::SizeOf, ty) => { - let ty = self.subst_from_frame_and_normalize_erasing_regions(ty); + let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty); let layout = self.layout_of(ty)?; assert!( !layout.is_unsized(), @@ -287,7 +287,7 @@ fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tc self.eval_terminator(terminator)?; if !self.stack.is_empty() { // This should change *something* - debug_assert!(self.cur_frame() != old_stack || self.frame().block != old_bb); + assert!(self.cur_frame() != old_stack || self.frame().block != old_bb); if let Some(block) = self.frame().block { info!("// executing {:?}", block); } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 53e10f86a2c..22a081a9c8e 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -95,10 +95,14 @@ pub(super) fn eval_terminator( if expected == cond_val { self.go_to_block(target); } else { - M::assert_panic(self, terminator.source_info.span, msg, cleanup)?; + M::assert_panic(self, msg, cleanup)?; } } + Abort => { + M::abort(self)?; + } + // When we encounter Resume, we've finished unwinding // cleanup for the current stack frame. We pop it in order // to continue unwinding the next frame @@ -114,15 +118,13 @@ pub(super) fn eval_terminator( Unreachable => throw_ub!(Unreachable), // These should never occur for MIR we actually run. - DropAndReplace { .. } | FalseEdges { .. } | FalseUnwind { .. } => { + DropAndReplace { .. } + | FalseEdges { .. } + | FalseUnwind { .. } + | Yield { .. } + | GeneratorDrop => { bug!("{:#?} should have been eliminated by MIR pass", terminator.kind) } - - // These are not (yet) supported. It is unclear if they even can occur in - // MIR that we actually run. - Yield { .. } | GeneratorDrop | Abort => { - throw_unsup_format!("Unsupported terminator kind: {:#?}", terminator.kind) - } } Ok(()) @@ -170,13 +172,19 @@ fn pass_argument( trace!("Skipping callee ZST"); return Ok(()); } - let caller_arg = caller_arg.next().ok_or_else(|| err_unsup!(FunctionArgCountMismatch))?; + let caller_arg = caller_arg.next().ok_or_else(|| { + err_ub_format!("calling a function with fewer arguments than it requires") + })?; if rust_abi { - debug_assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out"); + assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out"); } // Now, check if !Self::check_argument_compat(rust_abi, caller_arg.layout, callee_arg.layout) { - throw_unsup!(FunctionArgMismatch(caller_arg.layout.ty, callee_arg.layout.ty)) + throw_ub_format!( + "calling a function with argument of type {:?} passing data of type {:?}", + callee_arg.layout.ty, + caller_arg.layout.ty + ) } // We allow some transmutes here self.copy_op_transmute(caller_arg, callee_arg) @@ -221,7 +229,11 @@ fn eval_fn_call( abi => abi, }; if normalize_abi(caller_abi) != normalize_abi(callee_abi) { - throw_unsup!(FunctionAbiMismatch(caller_abi, callee_abi)) + throw_ub_format!( + "calling a function with ABI {:?} using caller ABI {:?}", + callee_abi, + caller_abi + ) } } @@ -251,111 +263,108 @@ fn eval_fn_call( StackPopCleanup::Goto { ret: ret.map(|p| p.1), unwind }, )?; - // We want to pop this frame again in case there was an error, to put - // the blame in the right location. Until the 2018 edition is used in - // the compiler, we have to do this with an immediately invoked function. - let res = - (|| { - trace!( - "caller ABI: {:?}, args: {:#?}", - caller_abi, - args.iter() - .map(|arg| (arg.layout.ty, format!("{:?}", **arg))) - .collect::>() - ); - trace!( - "spread_arg: {:?}, locals: {:#?}", - body.spread_arg, - body.args_iter() - .map(|local| ( - local, - self.layout_of_local(self.frame(), local, None).unwrap().ty - )) - .collect::>() - ); - - // Figure out how to pass which arguments. - // The Rust ABI is special: ZST get skipped. - let rust_abi = match caller_abi { - Abi::Rust | Abi::RustCall => true, - _ => false, + // If an error is raised here, pop the frame again to get an accurate backtrace. + // To this end, we wrap it all in a `try` block. + let res: InterpResult<'tcx> = try { + trace!( + "caller ABI: {:?}, args: {:#?}", + caller_abi, + args.iter() + .map(|arg| (arg.layout.ty, format!("{:?}", **arg))) + .collect::>() + ); + trace!( + "spread_arg: {:?}, locals: {:#?}", + body.spread_arg, + body.args_iter() + .map(|local| ( + local, + self.layout_of_local(self.frame(), local, None).unwrap().ty + )) + .collect::>() + ); + + // Figure out how to pass which arguments. + // The Rust ABI is special: ZST get skipped. + let rust_abi = match caller_abi { + Abi::Rust | Abi::RustCall => true, + _ => false, + }; + // We have two iterators: Where the arguments come from, + // and where they go to. + + // For where they come from: If the ABI is RustCall, we untuple the + // last incoming argument. These two iterators do not have the same type, + // so to keep the code paths uniform we accept an allocation + // (for RustCall ABI only). + let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = + if caller_abi == Abi::RustCall && !args.is_empty() { + // Untuple + let (&untuple_arg, args) = args.split_last().unwrap(); + trace!("eval_fn_call: Will pass last argument by untupling"); + Cow::from( + args.iter() + .map(|&a| Ok(a)) + .chain( + (0..untuple_arg.layout.fields.count()) + .map(|i| self.operand_field(untuple_arg, i as u64)), + ) + .collect::>>>( + )?, + ) + } else { + // Plain arg passing + Cow::from(args) }; - // We have two iterators: Where the arguments come from, - // and where they go to. - - // For where they come from: If the ABI is RustCall, we untuple the - // last incoming argument. These two iterators do not have the same type, - // so to keep the code paths uniform we accept an allocation - // (for RustCall ABI only). - let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = - if caller_abi == Abi::RustCall && !args.is_empty() { - // Untuple - let (&untuple_arg, args) = args.split_last().unwrap(); - trace!("eval_fn_call: Will pass last argument by untupling"); - Cow::from(args.iter().map(|&a| Ok(a)) - .chain((0..untuple_arg.layout.fields.count()) - .map(|i| self.operand_field(untuple_arg, i as u64)) - ) - .collect::>>>()?) - } else { - // Plain arg passing - Cow::from(args) - }; - // Skip ZSTs - let mut caller_iter = caller_args - .iter() - .filter(|op| !rust_abi || !op.layout.is_zst()) - .copied(); - - // Now we have to spread them out across the callee's locals, - // taking into account the `spread_arg`. If we could write - // this is a single iterator (that handles `spread_arg`), then - // `pass_argument` would be the loop body. It takes care to - // not advance `caller_iter` for ZSTs. - let mut locals_iter = body.args_iter(); - while let Some(local) = locals_iter.next() { - let dest = self.eval_place(&mir::Place::from(local))?; - if Some(local) == body.spread_arg { - // Must be a tuple - for i in 0..dest.layout.fields.count() { - let dest = self.place_field(dest, i as u64)?; - self.pass_argument(rust_abi, &mut caller_iter, dest)?; - } - } else { - // Normal argument + // Skip ZSTs + let mut caller_iter = + caller_args.iter().filter(|op| !rust_abi || !op.layout.is_zst()).copied(); + + // Now we have to spread them out across the callee's locals, + // taking into account the `spread_arg`. If we could write + // this is a single iterator (that handles `spread_arg`), then + // `pass_argument` would be the loop body. It takes care to + // not advance `caller_iter` for ZSTs. + for local in body.args_iter() { + let dest = self.eval_place(&mir::Place::from(local))?; + if Some(local) == body.spread_arg { + // Must be a tuple + for i in 0..dest.layout.fields.count() { + let dest = self.place_field(dest, i as u64)?; self.pass_argument(rust_abi, &mut caller_iter, dest)?; } + } else { + // Normal argument + self.pass_argument(rust_abi, &mut caller_iter, dest)?; } - // Now we should have no more caller args - if caller_iter.next().is_some() { - trace!("Caller has passed too many args"); - throw_unsup!(FunctionArgCountMismatch) + } + // Now we should have no more caller args + if caller_iter.next().is_some() { + throw_ub_format!("calling a function with more arguments than it expected") + } + // Don't forget to check the return type! + if let Some((caller_ret, _)) = ret { + let callee_ret = self.eval_place(&mir::Place::return_place())?; + if !Self::check_argument_compat( + rust_abi, + caller_ret.layout, + callee_ret.layout, + ) { + throw_ub_format!( + "calling a function with return type {:?} passing \ + return place of type {:?}", + callee_ret.layout.ty, + caller_ret.layout.ty + ) } - // Don't forget to check the return type! - if let Some((caller_ret, _)) = ret { - let callee_ret = self.eval_place(&mir::Place::return_place())?; - if !Self::check_argument_compat( - rust_abi, - caller_ret.layout, - callee_ret.layout, - ) { - throw_unsup!(FunctionRetMismatch( - caller_ret.layout.ty, - callee_ret.layout.ty - )) - } - } else { - let local = mir::RETURN_PLACE; - let callee_layout = self.layout_of_local(self.frame(), local, None)?; - if !callee_layout.abi.is_uninhabited() { - throw_unsup!(FunctionRetMismatch( - self.tcx.types.never, - callee_layout.ty - )) - } + } else { + let local = mir::RETURN_PLACE; + let callee_layout = self.layout_of_local(self.frame(), local, None)?; + if !callee_layout.abi.is_uninhabited() { + throw_ub_format!("calling a returning function without a return place") } - Ok(()) - })(); + } + }; match res { Err(err) => { self.stack.pop(); diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index aa2b3040a71..d93b78a5bd5 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -29,7 +29,7 @@ macro_rules! throw_validation_failure { write_path(&mut msg, where_); } write!(&mut msg, ", but expected {}", $details).unwrap(); - throw_unsup!(ValidationFailure(msg)) + throw_ub!(ValidationFailure(msg)) }}; ($what:expr, $where:expr) => {{ let mut msg = format!("encountered {}", $what); @@ -38,7 +38,7 @@ macro_rules! throw_validation_failure { msg.push_str(" at "); write_path(&mut msg, where_); } - throw_unsup!(ValidationFailure(msg)) + throw_ub!(ValidationFailure(msg)) }}; } @@ -46,6 +46,8 @@ macro_rules! try_validation { ($e:expr, $what:expr, $where:expr, $details:expr) => {{ match $e { Ok(x) => x, + // We re-throw the error, so we are okay with allocation: + // this can only slow down builds that fail anyway. Err(_) => throw_validation_failure!($what, $where, $details), } }}; @@ -53,6 +55,8 @@ macro_rules! try_validation { ($e:expr, $what:expr, $where:expr) => {{ match $e { Ok(x) => x, + // We re-throw the error, so we are okay with allocation: + // this can only slow down builds that fail anyway. Err(_) => throw_validation_failure!($what, $where), } }}; @@ -67,11 +71,12 @@ pub enum PathElem { Field(Symbol), Variant(Symbol), GeneratorState(VariantIdx), - ClosureVar(Symbol), + CapturedVar(Symbol), ArrayElem(usize), TupleElem(usize), Deref, - Tag, + EnumTag, + GeneratorTag, DynDowncast, } @@ -109,9 +114,11 @@ fn write_path(out: &mut String, path: &Vec) { for elem in path.iter() { match elem { Field(name) => write!(out, ".{}", name), - Variant(name) => write!(out, ".", name), + EnumTag => write!(out, "."), + Variant(name) => write!(out, ".", name), + GeneratorTag => write!(out, "."), GeneratorState(idx) => write!(out, ".", idx.index()), - ClosureVar(name) => write!(out, ".", name), + CapturedVar(name) => write!(out, ".", name), TupleElem(idx) => write!(out, ".{}", idx), ArrayElem(idx) => write!(out, "[{}]", idx), // `.` does not match Rust syntax, but it is more readable for long paths -- and @@ -119,7 +126,6 @@ fn write_path(out: &mut String, path: &Vec) { // even use the usual syntax because we are just showing the projections, // not the root. Deref => write!(out, "."), - Tag => write!(out, "."), DynDowncast => write!(out, "."), } .unwrap() @@ -142,16 +148,16 @@ fn wrapping_range_contains(r: &RangeInclusive, test: u128) -> bool { // "expected something " makes sense. fn wrapping_range_format(r: &RangeInclusive, max_hi: u128) -> String { let (lo, hi) = r.clone().into_inner(); - debug_assert!(hi <= max_hi); + assert!(hi <= max_hi); if lo > hi { format!("less or equal to {}, or greater or equal to {}", hi, lo) } else if lo == hi { format!("equal to {}", lo) } else if lo == 0 { - debug_assert!(hi < max_hi, "should not be printing if the range covers everything"); + assert!(hi < max_hi, "should not be printing if the range covers everything"); format!("less or equal to {}", hi) } else if hi == max_hi { - debug_assert!(lo > 0, "should not be printing if the range covers everything"); + assert!(lo > 0, "should not be printing if the range covers everything"); format!("greater or equal to {}", lo) } else { format!("in the range {:?}", r) @@ -165,11 +171,27 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { path: Vec, ref_tracking_for_consts: Option<&'rt mut RefTracking, Vec>>, + may_ref_to_static: bool, ecx: &'rt InterpCx<'mir, 'tcx, M>, } impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> { fn aggregate_field_path_elem(&mut self, layout: TyLayout<'tcx>, field: usize) -> PathElem { + // First, check if we are projecting to a variant. + match layout.variants { + layout::Variants::Multiple { discr_index, .. } => { + if discr_index == field { + return match layout.ty.kind { + ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, + ty::Generator(..) => PathElem::GeneratorTag, + _ => bug!("non-variant type {:?}", layout.ty), + }; + } + } + layout::Variants::Single { .. } => {} + } + + // Now we know we are projecting to a field, so figure out which one. match layout.ty.kind { // generators and closures. ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { @@ -190,7 +212,7 @@ fn aggregate_field_path_elem(&mut self, layout: TyLayout<'tcx>, field: usize) -> } } - PathElem::ClosureVar(name.unwrap_or_else(|| { + PathElem::CapturedVar(name.unwrap_or_else(|| { // Fall back to showing the field index. sym::integer(field) })) @@ -201,13 +223,13 @@ fn aggregate_field_path_elem(&mut self, layout: TyLayout<'tcx>, field: usize) -> // enums ty::Adt(def, ..) if def.is_enum() => { - // we might be projecting *to* a variant, or to a field *in*a variant. + // we might be projecting *to* a variant, or to a field *in* a variant. match layout.variants { layout::Variants::Single { index } => { // Inside a variant PathElem::Field(def.variants[index].fields[field].ident.name) } - _ => bug!(), + layout::Variants::Multiple { .. } => bug!("we handled variants above"), } } @@ -288,195 +310,192 @@ fn check_wide_ptr_meta( Ok(()) } -} -impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> - for ValidityVisitor<'rt, 'mir, 'tcx, M> -{ - type V = OpTy<'tcx, M::PointerTag>; - - #[inline(always)] - fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> { - &self.ecx - } - - #[inline] - fn visit_field( + /// Check a reference or `Box`. + fn check_safe_pointer( &mut self, - old_op: OpTy<'tcx, M::PointerTag>, - field: usize, - new_op: OpTy<'tcx, M::PointerTag>, + value: OpTy<'tcx, M::PointerTag>, + kind: &str, ) -> InterpResult<'tcx> { - let elem = self.aggregate_field_path_elem(old_op.layout, field); - self.visit_elem(new_op, elem) - } - - #[inline] - fn visit_variant( - &mut self, - old_op: OpTy<'tcx, M::PointerTag>, - variant_id: VariantIdx, - new_op: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - let name = match old_op.layout.ty.kind { - ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), - // Generators also have variants - ty::Generator(..) => PathElem::GeneratorState(variant_id), - _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty), - }; - self.visit_elem(new_op, name) - } - - #[inline] - fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { - trace!("visit_value: {:?}, {:?}", *op, op.layout); - // Translate some possible errors to something nicer. - match self.walk_value(op) { - Ok(()) => Ok(()), + let value = self.ecx.read_immediate(value)?; + // Handle wide pointers. + // Check metadata early, for better diagnostics + let place = try_validation!(self.ecx.ref_to_mplace(value), "undefined pointer", self.path); + if place.layout.is_unsized() { + self.check_wide_ptr_meta(place.meta, place.layout)?; + } + // Make sure this is dereferenceable and all. + let size_and_align = match self.ecx.size_and_align_of(place.meta, place.layout) { + Ok(res) => res, Err(err) => match err.kind { - err_ub!(InvalidDiscriminant(val)) => { - throw_validation_failure!(val, self.path, "a valid enum discriminant") + err_ub!(InvalidMeta(msg)) => throw_validation_failure!( + format_args!("invalid {} metadata: {}", kind, msg), + self.path + ), + _ => bug!("Unexpected error during ptr size_and_align_of: {}", err), + }, + }; + let (size, align) = size_and_align + // for the purpose of validity, consider foreign types to have + // alignment and size determined by the layout (size will be 0, + // alignment should take attributes into account). + .unwrap_or_else(|| (place.layout.size, place.layout.align.abi)); + let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align( + place.ptr, + size, + Some(align), + CheckInAllocMsg::InboundsTest, + ) { + Ok(ptr) => ptr, + Err(err) => { + info!( + "{:?} did not pass access check for size {:?}, align {:?}", + place.ptr, size, align + ); + match err.kind { + err_ub!(InvalidIntPointerUsage(0)) => { + throw_validation_failure!(format_args!("a NULL {}", kind), self.path) + } + err_ub!(InvalidIntPointerUsage(i)) => throw_validation_failure!( + format_args!("a {} to unallocated address {}", kind, i), + self.path + ), + err_ub!(AlignmentCheckFailed { required, has }) => throw_validation_failure!( + format_args!( + "an unaligned {} (required {} byte alignment but found {})", + kind, + required.bytes(), + has.bytes() + ), + self.path + ), + err_unsup!(ReadBytesAsPointer) => throw_validation_failure!( + format_args!("a dangling {} (created from integer)", kind), + self.path + ), + err_ub!(PointerOutOfBounds { .. }) => throw_validation_failure!( + format_args!( + "a dangling {} (going beyond the bounds of its allocation)", + kind + ), + self.path + ), + // This cannot happen during const-eval (because interning already detects + // dangling pointers), but it can happen in Miri. + err_ub!(PointerUseAfterFree(_)) => throw_validation_failure!( + format_args!("a dangling {} (use-after-free)", kind), + self.path + ), + _ => bug!("Unexpected error during ptr inbounds test: {}", err), } - err_unsup!(ReadPointerAsBytes) => { - throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes") + } + }; + // Recursive checking + if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts { + if let Some(ptr) = ptr { + // not a ZST + // Skip validation entirely for some external statics + let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id); + if let Some(GlobalAlloc::Static(did)) = alloc_kind { + // `extern static` cannot be validated as they have no body. + // FIXME: Statics from other crates are also skipped. + // They might be checked at a different type, but for now we + // want to avoid recursing too deeply. This is not sound! + if !did.is_local() || self.ecx.tcx.is_foreign_item(did) { + return Ok(()); + } + if !self.may_ref_to_static && self.ecx.tcx.is_static(did) { + throw_validation_failure!( + format_args!("a {} pointing to a static variable", kind), + self.path + ); + } } - _ => Err(err), - }, + } + // Proceed recursively even for ZST, no reason to skip them! + // `!` is a ZST and we want to validate it. + // Normalize before handing `place` to tracking because that will + // check for duplicates. + let place = if size.bytes() > 0 { + self.ecx.force_mplace_ptr(place).expect("we already bounds-checked") + } else { + place + }; + let path = &self.path; + ref_tracking.track(place, || { + // We need to clone the path anyway, make sure it gets created + // with enough space for the additional `Deref`. + let mut new_path = Vec::with_capacity(path.len() + 1); + new_path.clone_from(path); + new_path.push(PathElem::Deref); + new_path + }); } + Ok(()) } - fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { - let value = self.ecx.read_immediate(value)?; + /// Check if this is a value of primitive type, and if yes check the validity of the value + /// at that type. Return `true` if the type is indeed primitive. + fn try_visit_primitive( + &mut self, + value: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, bool> { // Go over all the primitive types let ty = value.layout.ty; match ty.kind { ty::Bool => { - let value = value.to_scalar_or_undef(); + let value = self.ecx.read_scalar(value)?; try_validation!(value.to_bool(), value, self.path, "a boolean"); + Ok(true) } ty::Char => { - let value = value.to_scalar_or_undef(); + let value = self.ecx.read_scalar(value)?; try_validation!(value.to_char(), value, self.path, "a valid unicode codepoint"); + Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { + let value = self.ecx.read_scalar(value)?; // NOTE: Keep this in sync with the array optimization for int/float // types below! - let size = value.layout.size; - let value = value.to_scalar_or_undef(); if self.ref_tracking_for_consts.is_some() { // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous - try_validation!( - value.to_bits(size), - value, - self.path, - "initialized plain (non-pointer) bytes" - ); + let is_bits = value.not_undef().map_or(false, |v| v.is_bits()); + if !is_bits { + throw_validation_failure!( + value, + self.path, + "initialized plain (non-pointer) bytes" + ) + } } else { // At run-time, for now, we accept *anything* for these types, including // undef. We should fix that, but let's start low. } + Ok(true) } ty::RawPtr(..) => { // We are conservative with undef for integers, but try to // actually enforce our current rules for raw pointers. - let place = - try_validation!(self.ecx.ref_to_mplace(value), "undefined pointer", self.path); + let place = try_validation!( + self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), + "undefined pointer", + self.path + ); if place.layout.is_unsized() { self.check_wide_ptr_meta(place.meta, place.layout)?; } + Ok(true) } - _ if ty.is_box() || ty.is_region_ptr() => { - // Handle wide pointers. - // Check metadata early, for better diagnostics - let place = - try_validation!(self.ecx.ref_to_mplace(value), "undefined pointer", self.path); - if place.layout.is_unsized() { - self.check_wide_ptr_meta(place.meta, place.layout)?; - } - // Make sure this is dereferenceable and all. - let (size, align) = self - .ecx - .size_and_align_of(place.meta, place.layout)? - // for the purpose of validity, consider foreign types to have - // alignment and size determined by the layout (size will be 0, - // alignment should take attributes into account). - .unwrap_or_else(|| (place.layout.size, place.layout.align.abi)); - let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align( - place.ptr, - size, - Some(align), - CheckInAllocMsg::InboundsTest, - ) { - Ok(ptr) => ptr, - Err(err) => { - info!( - "{:?} did not pass access check for size {:?}, align {:?}", - place.ptr, size, align - ); - match err.kind { - err_unsup!(InvalidNullPointerUsage) => { - throw_validation_failure!("NULL reference", self.path) - } - err_unsup!(AlignmentCheckFailed { required, has }) => { - throw_validation_failure!( - format_args!( - "unaligned reference \ - (required {} byte alignment but found {})", - required.bytes(), - has.bytes() - ), - self.path - ) - } - err_unsup!(ReadBytesAsPointer) => throw_validation_failure!( - "dangling reference (created from integer)", - self.path - ), - _ => throw_validation_failure!( - "dangling reference (not entirely in bounds)", - self.path - ), - } - } - }; - // Recursive checking - if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts { - if let Some(ptr) = ptr { - // not a ZST - // Skip validation entirely for some external statics - let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id); - if let Some(GlobalAlloc::Static(did)) = alloc_kind { - // `extern static` cannot be validated as they have no body. - // FIXME: Statics from other crates are also skipped. - // They might be checked at a different type, but for now we - // want to avoid recursing too deeply. This is not sound! - if !did.is_local() || self.ecx.tcx.is_foreign_item(did) { - return Ok(()); - } - } - } - // Proceed recursively even for ZST, no reason to skip them! - // `!` is a ZST and we want to validate it. - // Normalize before handing `place` to tracking because that will - // check for duplicates. - let place = if size.bytes() > 0 { - self.ecx.force_mplace_ptr(place).expect("we already bounds-checked") - } else { - place - }; - let path = &self.path; - ref_tracking.track(place, || { - // We need to clone the path anyway, make sure it gets created - // with enough space for the additional `Deref`. - let mut new_path = Vec::with_capacity(path.len() + 1); - new_path.clone_from(path); - new_path.push(PathElem::Deref); - new_path - }); - } + ty::Ref(..) => { + self.check_safe_pointer(value, "reference")?; + Ok(true) + } + ty::Adt(def, ..) if def.is_box() => { + self.check_safe_pointer(value, "box")?; + Ok(true) } ty::FnPtr(_sig) => { - let value = value.to_scalar_or_undef(); + let value = self.ecx.read_scalar(value)?; let _fn = try_validation!( value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)), value, @@ -484,27 +503,49 @@ fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> InterpResult< "a function pointer" ); // FIXME: Check if the signature matches + Ok(true) + } + ty::Never => throw_validation_failure!("a value of the never type `!`", self.path), + ty::Foreign(..) | ty::FnDef(..) => { + // Nothing to check. + Ok(true) } - // This should be all the primitive types - _ => bug!("Unexpected primitive type {}", value.layout.ty), + // The above should be all the (inhabited) primitive types. The rest is compound, we + // check them by visiting their fields/variants. + // (`Str` UTF-8 check happens in `visit_aggregate`, too.) + ty::Adt(..) + | ty::Tuple(..) + | ty::Array(..) + | ty::Slice(..) + | ty::Str + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Generator(..) => Ok(false), + // Some types only occur during typechecking, they have no layout. + // We should not see them here and we could not check them anyway. + ty::Error + | ty::Infer(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Param(..) + | ty::Opaque(..) + | ty::UnnormalizedProjection(..) + | ty::Projection(..) + | ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty), } - Ok(()) - } - - fn visit_uninhabited(&mut self) -> InterpResult<'tcx> { - throw_validation_failure!("a value of an uninhabited type", self.path) } fn visit_scalar( &mut self, op: OpTy<'tcx, M::PointerTag>, - layout: &layout::Scalar, + scalar_layout: &layout::Scalar, ) -> InterpResult<'tcx> { let value = self.ecx.read_scalar(op)?; + let valid_range = &scalar_layout.valid_range; + let (lo, hi) = valid_range.clone().into_inner(); // Determine the allowed range - let (lo, hi) = layout.valid_range.clone().into_inner(); // `max_hi` is as big as the size fits - let max_hi = u128::max_value() >> (128 - op.layout.size.bits()); + let max_hi = u128::MAX >> (128 - op.layout.size.bits()); assert!(hi <= max_hi); // We could also write `(hi + 1) % (max_hi + 1) == lo` but `max_hi + 1` overflows for `u128` if (lo == 0 && hi == max_hi) || (hi + 1 == lo) { @@ -516,7 +557,7 @@ fn visit_scalar( value.not_undef(), value, self.path, - format_args!("something {}", wrapping_range_format(&layout.valid_range, max_hi),) + format_args!("something {}", wrapping_range_format(valid_range, max_hi),) ); let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) { Err(ptr) => { @@ -528,7 +569,7 @@ fn visit_scalar( self.path, format_args!( "something that cannot possibly fail to be {}", - wrapping_range_format(&layout.valid_range, max_hi) + wrapping_range_format(valid_range, max_hi) ) ) } @@ -541,7 +582,7 @@ fn visit_scalar( self.path, format_args!( "something that cannot possibly fail to be {}", - wrapping_range_format(&layout.valid_range, max_hi) + wrapping_range_format(valid_range, max_hi) ) ) } @@ -549,16 +590,122 @@ fn visit_scalar( Ok(data) => data, }; // Now compare. This is slightly subtle because this is a special "wrap-around" range. - if wrapping_range_contains(&layout.valid_range, bits) { + if wrapping_range_contains(&valid_range, bits) { Ok(()) } else { throw_validation_failure!( bits, self.path, - format_args!("something {}", wrapping_range_format(&layout.valid_range, max_hi)) + format_args!("something {}", wrapping_range_format(valid_range, max_hi)) ) } } +} + +impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> + for ValidityVisitor<'rt, 'mir, 'tcx, M> +{ + type V = OpTy<'tcx, M::PointerTag>; + + #[inline(always)] + fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> { + &self.ecx + } + + #[inline] + fn visit_field( + &mut self, + old_op: OpTy<'tcx, M::PointerTag>, + field: usize, + new_op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + let elem = self.aggregate_field_path_elem(old_op.layout, field); + self.visit_elem(new_op, elem) + } + + #[inline] + fn visit_variant( + &mut self, + old_op: OpTy<'tcx, M::PointerTag>, + variant_id: VariantIdx, + new_op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + let name = match old_op.layout.ty.kind { + ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), + // Generators also have variants + ty::Generator(..) => PathElem::GeneratorState(variant_id), + _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty), + }; + self.visit_elem(new_op, name) + } + + #[inline(always)] + fn visit_union(&mut self, op: OpTy<'tcx, M::PointerTag>, fields: usize) -> InterpResult<'tcx> { + // Empty unions are not accepted by rustc. But uninhabited enums + // claim to be unions, so allow them, too. + assert!(op.layout.abi.is_uninhabited() || fields > 0); + Ok(()) + } + + #[inline] + fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + trace!("visit_value: {:?}, {:?}", *op, op.layout); + + // Check primitive types -- the leafs of our recursive descend. + if self.try_visit_primitive(op)? { + return Ok(()); + } + // Sanity check: `builtin_deref` does not know any pointers that are not primitive. + assert!(op.layout.ty.builtin_deref(true).is_none()); + + // Recursively walk the type. Translate some possible errors to something nicer. + match self.walk_value(op) { + Ok(()) => {} + Err(err) => match err.kind { + err_ub!(InvalidDiscriminant(val)) => { + throw_validation_failure!(val, self.path, "a valid enum discriminant") + } + err_unsup!(ReadPointerAsBytes) => { + throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes") + } + // Propagate upwards (that will also check for unexpected errors). + _ => return Err(err), + }, + } + + // *After* all of this, check the ABI. We need to check the ABI to handle + // types like `NonNull` where the `Scalar` info is more restrictive than what + // the fields say (`rustc_layout_scalar_valid_range_start`). + // But in most cases, this will just propagate what the fields say, + // and then we want the error to point at the field -- so, first recurse, + // then check ABI. + // + // FIXME: We could avoid some redundant checks here. For newtypes wrapping + // scalars, we do the same check on every "level" (e.g., first we check + // MyNewtype and then the scalar in there). + match op.layout.abi { + layout::Abi::Uninhabited => { + throw_validation_failure!( + format_args!("a value of uninhabited type {:?}", op.layout.ty), + self.path + ); + } + layout::Abi::Scalar(ref scalar_layout) => { + self.visit_scalar(op, scalar_layout)?; + } + layout::Abi::ScalarPair { .. } | layout::Abi::Vector { .. } => { + // These have fields that we already visited above, so we already checked + // all their scalar-level restrictions. + // There is also no equivalent to `rustc_layout_scalar_valid_range_start` + // that would make skipping them here an issue. + } + layout::Abi::Aggregate { .. } => { + // Nothing to do. + } + } + + Ok(()) + } fn visit_aggregate( &mut self, @@ -626,11 +773,11 @@ fn visit_aggregate( Err(err) => { // For some errors we might be able to provide extra information match err.kind { - err_unsup!(ReadUndefBytes(offset)) => { + err_ub!(InvalidUndefBytes(Some(ptr))) => { // Some byte was undefined, determine which // element that byte belongs to so we can // provide an index. - let i = (offset.bytes() / layout.size.bytes()) as usize; + let i = (ptr.offset.bytes() / layout.size.bytes()) as usize; self.path.push(PathElem::ArrayElem(i)); throw_validation_failure!("undefined bytes", self.path) @@ -657,31 +804,59 @@ fn visit_aggregate( } impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// This function checks the data at `op`. `op` is assumed to cover valid memory if it - /// is an indirect operand. - /// It will error if the bits at the destination do not match the ones described by the layout. - /// - /// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references. - /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion) - /// validation (e.g., pointer values are fine in integers at runtime) and various other const - /// specific validation checks. - pub fn validate_operand( + fn validate_operand_internal( &self, op: OpTy<'tcx, M::PointerTag>, path: Vec, ref_tracking_for_consts: Option< &mut RefTracking, Vec>, >, + may_ref_to_static: bool, ) -> InterpResult<'tcx> { - trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty); + trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty); // Construct a visitor - let mut visitor = ValidityVisitor { path, ref_tracking_for_consts, ecx: self }; + let mut visitor = + ValidityVisitor { path, ref_tracking_for_consts, may_ref_to_static, ecx: self }; // Try to cast to ptr *once* instead of all the time. let op = self.force_op_ptr(op).unwrap_or(op); - // Run it - visitor.visit_value(op) + // Run it. + match visitor.visit_value(op) { + Ok(()) => Ok(()), + Err(err) if matches!(err.kind, err_ub!(ValidationFailure { .. })) => Err(err), + Err(err) if cfg!(debug_assertions) => { + bug!("Unexpected error during validation: {}", err) + } + Err(err) => Err(err), + } + } + + /// This function checks the data at `op` to be const-valid. + /// `op` is assumed to cover valid memory if it is an indirect operand. + /// It will error if the bits at the destination do not match the ones described by the layout. + /// + /// `ref_tracking` is used to record references that we encounter so that they + /// can be checked recursively by an outside driving loop. + /// + /// `may_ref_to_static` controls whether references are allowed to point to statics. + #[inline(always)] + pub fn const_validate_operand( + &self, + op: OpTy<'tcx, M::PointerTag>, + path: Vec, + ref_tracking: &mut RefTracking, Vec>, + may_ref_to_static: bool, + ) -> InterpResult<'tcx> { + self.validate_operand_internal(op, path, Some(ref_tracking), may_ref_to_static) + } + + /// This function checks the data at `op` to be runtime-valid. + /// `op` is assumed to cover valid memory if it is an indirect operand. + /// It will error if the bits at the destination do not match the ones described by the layout. + #[inline(always)] + pub fn validate_operand(&self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + self.validate_operand_internal(op, vec![], None, false) } } diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs index d2594e87071..8808fc70cf7 100644 --- a/src/librustc_mir/interpret/visitor.rs +++ b/src/librustc_mir/interpret/visitor.rs @@ -120,7 +120,7 @@ fn visit_value(&mut self, v: Self::V) -> InterpResult<'tcx> } /// Visits the given value as a union. No automatic recursion can happen here. #[inline(always)] - fn visit_union(&mut self, _v: Self::V) -> InterpResult<'tcx> + fn visit_union(&mut self, _v: Self::V, _fields: usize) -> InterpResult<'tcx> { Ok(()) } @@ -150,8 +150,9 @@ fn visit_field( ) -> InterpResult<'tcx> { self.visit_value(new_val) } - /// Called when recursing into an enum variant. + /// This gives the visitor the chance to track the stack of nested fields that + /// we are descending through. #[inline(always)] fn visit_variant( &mut self, @@ -162,33 +163,6 @@ fn visit_variant( self.visit_value(new_val) } - /// Called whenever we reach a value with uninhabited layout. - /// Recursing to fields will *always* continue after this! This is not meant to control - /// whether and how we descend recursively/ into the scalar's fields if there are any, - /// it is meant to provide the chance for additional checks when a value of uninhabited - /// layout is detected. - #[inline(always)] - fn visit_uninhabited(&mut self) -> InterpResult<'tcx> - { Ok(()) } - /// Called whenever we reach a value with scalar layout. - /// We do NOT provide a `ScalarMaybeUndef` here to avoid accessing memory if the - /// visitor is not even interested in scalars. - /// Recursing to fields will *always* continue after this! This is not meant to control - /// whether and how we descend recursively/ into the scalar's fields if there are any, - /// it is meant to provide the chance for additional checks when a value of scalar - /// layout is detected. - #[inline(always)] - fn visit_scalar(&mut self, _v: Self::V, _layout: &layout::Scalar) -> InterpResult<'tcx> - { Ok(()) } - - /// Called whenever we reach a value of primitive type. There can be no recursion - /// below such a value. This is the leaf function. - /// We do *not* provide an `ImmTy` here because some implementations might want - /// to write to the place this primitive lives in. - #[inline(always)] - fn visit_primitive(&mut self, _v: Self::V) -> InterpResult<'tcx> - { Ok(()) } - // Default recursors. Not meant to be overloaded. fn walk_aggregate( &mut self, @@ -204,23 +178,10 @@ fn walk_aggregate( fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx> { trace!("walk_value: type: {}", v.layout().ty); - // If this is a multi-variant layout, we have to find the right one and proceed with - // that. - match v.layout().variants { - layout::Variants::Multiple { .. } => { - let op = v.to_op(self.ecx())?; - let idx = self.ecx().read_discriminant(op)?.1; - let inner = v.project_downcast(self.ecx(), idx)?; - trace!("walk_value: variant layout: {:#?}", inner.layout()); - // recurse with the inner type - return self.visit_variant(v, idx, inner); - } - layout::Variants::Single { .. } => {} - } - // Even for single variants, we might be able to get a more refined type: - // If it is a trait object, switch to the actual type that was used to create it. + // Special treatment for special types, where the (static) layout is not sufficient. match v.layout().ty.kind { + // If it is a trait object, switch to the real type that was used to create it. ty::Dynamic(..) => { // immediate trait objects are not a thing let dest = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); @@ -229,56 +190,16 @@ fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx> // recurse with the inner type return self.visit_field(v, 0, Value::from_mem_place(inner)); }, - ty::Generator(..) => { - // FIXME: Generator layout is lying: it claims a whole bunch of fields exist - // when really many of them can be uninitialized. - // Just treat them as a union for now, until hopefully the layout - // computation is fixed. - return self.visit_union(v); - } + // Slices do not need special handling here: they have `Array` field + // placement with length 0, so we enter the `Array` case below which + // indirectly uses the metadata to determine the actual length. _ => {}, }; - // If this is a scalar, visit it as such. - // Things can be aggregates and have scalar layout at the same time, and that - // is very relevant for `NonNull` and similar structs: We need to visit them - // at their scalar layout *before* descending into their fields. - // FIXME: We could avoid some redundant checks here. For newtypes wrapping - // scalars, we do the same check on every "level" (e.g., first we check - // MyNewtype and then the scalar in there). - match v.layout().abi { - layout::Abi::Uninhabited => { - self.visit_uninhabited()?; - } - layout::Abi::Scalar(ref layout) => { - self.visit_scalar(v, layout)?; - } - // FIXME: Should we do something for ScalarPair? Vector? - _ => {} - } - - // Check primitive types. We do this after checking the scalar layout, - // just to have that done as well. Primitives can have varying layout, - // so we check them separately and before aggregate handling. - // It is CRITICAL that we get this check right, or we might be - // validating the wrong thing! - let primitive = match v.layout().fields { - // Primitives appear as Union with 0 fields - except for Boxes and fat pointers. - layout::FieldPlacement::Union(0) => true, - _ => v.layout().ty.builtin_deref(true).is_some(), - }; - if primitive { - return self.visit_primitive(v); - } - - // Proceed into the fields. + // Visit the fields of this value. match v.layout().fields { layout::FieldPlacement::Union(fields) => { - // Empty unions are not accepted by rustc. That's great, it means we can - // use that as an unambiguous signal for detecting primitives. Make sure - // we did not miss any primitive. - assert!(fields > 0); - self.visit_union(v) + self.visit_union(v, fields)?; }, layout::FieldPlacement::Arbitrary { ref offsets, .. } => { // FIXME: We collect in a vec because otherwise there are lifetime @@ -288,18 +209,35 @@ fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx> v.project_field(self.ecx(), i as u64) }) .collect(); - self.visit_aggregate(v, fields.into_iter()) + self.visit_aggregate(v, fields.into_iter())?; }, layout::FieldPlacement::Array { .. } => { // Let's get an mplace first. let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); // Now we can go over all the fields. + // This uses the *run-time length*, i.e., if we are a slice, + // the dynamic info from the metadata is used. let iter = self.ecx().mplace_array_fields(mplace)? .map(|f| f.and_then(|f| { Ok(Value::from_mem_place(f)) })); - self.visit_aggregate(v, iter) + self.visit_aggregate(v, iter)?; + } + } + + match v.layout().variants { + // If this is a multi-variant layout, find the right variant and proceed + // with *its* fields. + layout::Variants::Multiple { .. } => { + let op = v.to_op(self.ecx())?; + let idx = self.ecx().read_discriminant(op)?.1; + let inner = v.project_downcast(self.ecx(), idx)?; + trace!("walk_value: variant layout: {:#?}", inner.layout()); + // recurse with the inner type + self.visit_variant(v, idx, inner) } + // For single-variant layouts, we already did anything there is to do. + layout::Variants::Single { .. } => Ok(()) } } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 284dd74ce99..7d3aba3ff03 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -9,6 +9,9 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(const_if_match)] +#![feature(const_fn)] +#![feature(const_panic)] #![feature(crate_visibility_modifier)] #![feature(drain_filter)] #![feature(exhaustive_patterns)] diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index f6a93363dc1..4dd037d93ce 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -183,10 +183,9 @@ use rustc::mir::mono::{InstantiationMode, MonoItem}; use rustc::mir::visit::Visitor as MirVisitor; use rustc::mir::{self, Local, Location}; -use rustc::session::config::EntryFnType; use rustc::ty::adjustment::{CustomCoerceUnsized, PointerCast}; use rustc::ty::print::obsolete::DefPathBasedNames; -use rustc::ty::subst::{InternalSubsts, SubstsRef}; +use rustc::ty::subst::InternalSubsts; use rustc::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator}; @@ -194,6 +193,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_index::bit_set::GrowableBitSet; +use rustc_session::config::EntryFnType; use smallvec::SmallVec; use std::iter; @@ -493,7 +493,21 @@ struct MirNeighborCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, output: &'a mut Vec>, - param_substs: SubstsRef<'tcx>, + instance: Instance<'tcx>, +} + +impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { + pub fn monomorphize(&self, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + debug!("monomorphize: self.instance={:?}", self.instance); + if let Some(substs) = self.instance.substs_for_mir_body() { + self.tcx.subst_and_normalize_erasing_regions(substs, ty::ParamEnv::reveal_all(), &value) + } else { + self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), value) + } + } } impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { @@ -509,17 +523,9 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { ref operand, target_ty, ) => { - let target_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &target_ty, - ); + let target_ty = self.monomorphize(target_ty); let source_ty = operand.ty(self.body, self.tcx); - let source_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &source_ty, - ); + let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx, source_ty, target_ty); // This could also be a different Unsize instruction, like @@ -540,11 +546,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { _, ) => { let fn_ty = operand.ty(self.body, self.tcx); - let fn_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &fn_ty, - ); + let fn_ty = self.monomorphize(fn_ty); visit_fn_use(self.tcx, fn_ty, false, &mut self.output); } mir::Rvalue::Cast( @@ -553,11 +555,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { _, ) => { let source_ty = operand.ty(self.body, self.tcx); - let source_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &source_ty, - ); + let source_ty = self.monomorphize(source_ty); match source_ty.kind { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( @@ -593,7 +591,23 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { debug!("visiting const {:?} @ {:?}", *constant, location); - collect_const(self.tcx, *constant, self.param_substs, self.output); + let substituted_constant = self.monomorphize(*constant); + let param_env = ty::ParamEnv::reveal_all(); + + match substituted_constant.val { + ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output), + ty::ConstKind::Unevaluated(def_id, substs, promoted) => { + match self.tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) { + Ok(val) => collect_const_value(self.tcx, val, self.output), + Err(ErrorHandled::Reported) => {} + Err(ErrorHandled::TooGeneric) => span_bug!( + self.tcx.def_span(def_id), + "collection encountered polymorphic constant", + ), + } + } + _ => {} + } self.super_const(constant); } @@ -605,21 +619,13 @@ fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: match *kind { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.body, tcx); - let callee_ty = tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &callee_ty, - ); + let callee_ty = self.monomorphize(callee_ty); visit_fn_use(self.tcx, callee_ty, true, &mut self.output); } mir::TerminatorKind::Drop { ref location, .. } | mir::TerminatorKind::DropAndReplace { ref location, .. } => { let ty = location.ty(self.body, self.tcx).ty; - let ty = tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &ty, - ); + let ty = self.monomorphize(ty); visit_drop_use(self.tcx, ty, true, self.output); } mir::TerminatorKind::Goto { .. } @@ -824,11 +830,8 @@ fn find_vtable_types_for_unsizing<'tcx>( (&ty::Adt(source_adt_def, source_substs), &ty::Adt(target_adt_def, target_substs)) => { assert_eq!(source_adt_def, target_adt_def); - let kind = monomorphize::custom_coerce_unsize_info(tcx, source_ty, target_ty); - - let coerce_index = match kind { - CustomCoerceUnsized::Struct(i) => i, - }; + let CustomCoerceUnsized::Struct(coerce_index) = + monomorphize::custom_coerce_unsize_info(tcx, source_ty, target_ty); let source_fields = &source_adt_def.non_enum_variant().fields; let target_fields = &target_adt_def.non_enum_variant().fields; @@ -986,7 +989,7 @@ fn visit_trait_item(&mut self, _: &'v hir::TraitItem<'v>) { fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) { match ii.kind { - hir::ImplItemKind::Method(hir::FnSig { .. }, _) => { + hir::ImplItemKind::Fn(hir::FnSig { .. }, _) => { let def_id = self.tcx.hir().local_def_id(ii.hir_id); self.push_if_root(def_id); } @@ -1091,9 +1094,9 @@ fn create_mono_items_for_default_impls<'tcx>( let param_env = ty::ParamEnv::reveal_all(); let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); let overridden_methods: FxHashSet<_> = - items.iter().map(|iiref| iiref.ident.modern()).collect(); + items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); for method in tcx.provided_trait_methods(trait_ref.def_id) { - if overridden_methods.contains(&method.ident.modern()) { + if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) { continue; } @@ -1159,8 +1162,7 @@ fn collect_neighbours<'tcx>( debug!("collect_neighbours: {:?}", instance.def_id()); let body = tcx.instance_mir(instance.def); - MirNeighborCollector { tcx, body: &body, output, param_substs: instance.substs } - .visit_body(body); + MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(body); } fn def_id_to_string(tcx: TyCtxt<'_>, def_id: DefId) -> String { @@ -1170,33 +1172,6 @@ fn def_id_to_string(tcx: TyCtxt<'_>, def_id: DefId) -> String { output } -fn collect_const<'tcx>( - tcx: TyCtxt<'tcx>, - constant: &'tcx ty::Const<'tcx>, - param_substs: SubstsRef<'tcx>, - output: &mut Vec>, -) { - debug!("visiting const {:?}", constant); - - let param_env = ty::ParamEnv::reveal_all(); - let substituted_constant = - tcx.subst_and_normalize_erasing_regions(param_substs, param_env, &constant); - - match substituted_constant.val { - ty::ConstKind::Value(val) => collect_const_value(tcx, val, output), - ty::ConstKind::Unevaluated(def_id, substs, promoted) => { - match tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) { - Ok(val) => collect_const_value(tcx, val, output), - Err(ErrorHandled::Reported) => {} - Err(ErrorHandled::TooGeneric) => { - span_bug!(tcx.def_span(def_id), "collection encountered polymorphic constant",) - } - } - } - _ => {} - } -} - fn collect_const_value<'tcx>( tcx: TyCtxt<'tcx>, value: ConstValue<'tcx>, diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 8bc63bfcfcb..7177bf726d4 100644 --- a/src/librustc_mir/monomorphize/mod.rs +++ b/src/librustc_mir/monomorphize/mod.rs @@ -13,12 +13,12 @@ pub fn custom_coerce_unsize_info<'tcx>( let def_id = tcx.lang_items().coerce_unsized_trait().unwrap(); let trait_ref = ty::Binder::bind(ty::TraitRef { - def_id: def_id, + def_id, substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]), }); match tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) { - traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { + Some(traits::VtableImpl(traits::VtableImplData { impl_def_id, .. })) => { tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() } vtable => { diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 1f7db2861a2..c1d969a4b51 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -2,13 +2,13 @@ use rustc::ty::layout::VariantIdx; use rustc::ty::query::Providers; use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::vec::{Idx, IndexVec}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::spec::abi::Abi; use std::fmt; @@ -39,6 +39,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx None, ), ty::InstanceDef::FnPtrShim(def_id, ty) => { + // FIXME(eddyb) support generating shims for a "shallow type", + // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic + // `Foo` or `[String]` etc. + assert!(!ty.needs_subst()); + let trait_ = tcx.trait_of_item(def_id).unwrap(); let adjustment = match tcx.fn_trait_kind_from_lang_item(trait_) { Some(ty::ClosureKind::FnOnce) => Adjustment::Identity, @@ -81,17 +86,21 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx None, ) } - ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty), + ty::InstanceDef::DropGlue(def_id, ty) => { + // FIXME(eddyb) support generating shims for a "shallow type", + // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic + // `Foo` or `[String]` etc. + assert!(!ty.needs_subst()); + + build_drop_shim(tcx, def_id, ty) + } ty::InstanceDef::CloneShim(def_id, ty) => { - let name = tcx.item_name(def_id); - if name == sym::clone { - build_clone_shim(tcx, def_id, ty) - } else if name == sym::clone_from { - debug!("make_shim({:?}: using default trait implementation", instance); - return tcx.optimized_mir(def_id); - } else { - bug!("builtin clone shim {:?} not supported", instance) - } + // FIXME(eddyb) support generating shims for a "shallow type", + // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic + // `Foo` or `[String]` etc. + assert!(!ty.needs_subst()); + + build_clone_shim(tcx, def_id, ty) } ty::InstanceDef::Virtual(..) => { bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance) diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs index a5b467c1e10..aa9cad7ffc1 100644 --- a/src/librustc_mir/transform/add_retag.rs +++ b/src/librustc_mir/transform/add_retag.rs @@ -14,13 +14,13 @@ /// after the assignment, we can be sure to obtain the same place value. /// (Concurrent accesses by other threads are no problem as these are anyway non-atomic /// copies. Data races are UB.) -fn is_stable(place: PlaceRef<'_, '_>) -> bool { +fn is_stable(place: PlaceRef<'_>) -> bool { place.projection.iter().all(|elem| { match elem { // Which place this evaluates to can change with any memory write, // so cannot assume this to be stable. ProjectionElem::Deref => false, - // Array indices are intersting, but MIR building generates a *fresh* + // Array indices are interesting, but MIR building generates a *fresh* // temporary for every array access, so the index cannot be changed as // a side-effect. ProjectionElem::Index { .. } | @@ -34,7 +34,7 @@ fn is_stable(place: PlaceRef<'_, '_>) -> bool { } /// Determine whether this type may be a reference (or box), and thus needs retagging. -fn may_be_reference<'tcx>(ty: Ty<'tcx>) -> bool { +fn may_be_reference(ty: Ty<'tcx>) -> bool { match ty.kind { // Primitive types that are not references ty::Bool @@ -75,8 +75,8 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyAndC { let source_info = SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, - span: span, // FIXME: Consider using just the span covering the function - // argument declaration. + span, // FIXME: Consider using just the span covering the function + // argument declaration. }; // Gather all arguments, skip return value. let places = local_decls diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index 9ba44a4d18e..af7af7388bd 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -1,10 +1,9 @@ //! Concrete error types for all operations which may be invalid in a certain const context. -use rustc::session::config::nightly_options; -use rustc::session::parse::feature_err; -use rustc::ty::TyCtxt; use rustc_errors::struct_span_err; use rustc_hir::def_id::DefId; +use rustc_session::config::nightly_options; +use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -15,9 +14,9 @@ pub trait NonConstOp: std::fmt::Debug { /// Whether this operation can be evaluated by miri. const IS_SUPPORTED_IN_MIRI: bool = true; - /// Returns a boolean indicating whether the feature gate that would allow this operation is - /// enabled, or `None` if such a feature gate does not exist. - fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option { + /// Returns the `Symbol` corresponding to the feature gate that would enable this operation, + /// or `None` if such a feature gate does not exist. + fn feature_gate() -> Option { None } @@ -25,8 +24,11 @@ fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option { /// /// This check should assume that we are not in a non-const `fn`, where all operations are /// legal. + /// + /// By default, it returns `true` if and only if this operation has a corresponding feature + /// gate and that gate is enabled. fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool { - Self::feature_gate(item.tcx).unwrap_or(false) + Self::feature_gate().map_or(false, |gate| item.tcx.features().enabled(gate)) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -55,8 +57,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct Downcast; impl NonConstOp for Downcast { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_if_match) + fn feature_gate() -> Option { + Some(sym::const_if_match) } } @@ -147,8 +149,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct IfOrMatch; impl NonConstOp for IfOrMatch { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_if_match) + fn feature_gate() -> Option { + Some(sym::const_if_match) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -175,8 +177,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct Loop; impl NonConstOp for Loop { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_loop) + fn feature_gate() -> Option { + Some(sym::const_loop) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -203,8 +205,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct MutBorrow; impl NonConstOp for MutBorrow { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_mut_refs) + fn feature_gate() -> Option { + Some(sym::const_mut_refs) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -238,8 +240,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct MutAddressOf; impl NonConstOp for MutAddressOf { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_mut_refs) + fn feature_gate() -> Option { + Some(sym::const_mut_refs) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -256,16 +258,16 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct MutDeref; impl NonConstOp for MutDeref { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_mut_refs) + fn feature_gate() -> Option { + Some(sym::const_mut_refs) } } #[derive(Debug)] pub struct Panic; impl NonConstOp for Panic { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_panic) + fn feature_gate() -> Option { + Some(sym::const_panic) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -282,8 +284,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct RawPtrComparison; impl NonConstOp for RawPtrComparison { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_compare_raw_pointers) + fn feature_gate() -> Option { + Some(sym::const_compare_raw_pointers) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -300,8 +302,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct RawPtrDeref; impl NonConstOp for RawPtrDeref { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_raw_ptr_deref) + fn feature_gate() -> Option { + Some(sym::const_raw_ptr_deref) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -318,8 +320,8 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { #[derive(Debug)] pub struct RawPtrToIntCast; impl NonConstOp for RawPtrToIntCast { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_raw_ptr_to_usize_cast) + fn feature_gate() -> Option { + Some(sym::const_raw_ptr_to_usize_cast) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { @@ -386,11 +388,12 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) { impl NonConstOp for UnionAccess { fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool { // Union accesses are stable in all contexts except `const fn`. - item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap() + item.const_kind() != ConstKind::ConstFn + || item.tcx.features().enabled(Self::feature_gate().unwrap()) } - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_fn_union) + fn feature_gate() -> Option { + Some(sym::const_fn_union) } fn emit_error(&self, item: &Item<'_, '_>, span: Span) { diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index 215496e4d03..9359ec16533 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -1,7 +1,9 @@ -//! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator. +//! Structural const qualification. +//! +//! See the `Qualif` trait for more info. use rustc::mir::*; -use rustc::ty::{self, Ty}; +use rustc::ty::{self, AdtDef, Ty}; use rustc_span::DUMMY_SP; use super::Item as ConstCx; @@ -14,11 +16,16 @@ pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs } /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some -/// code for promotion or prevent it from evaluating at compile time. So `return true` means -/// "I found something bad, no reason to go on searching". `false` is only returned if we -/// definitely cannot find anything bad anywhere. +/// code for promotion or prevent it from evaluating at compile time. /// -/// The default implementations proceed structurally. +/// Normally, we would determine what qualifications apply to each type and error when an illegal +/// operation is performed on such a type. However, this was found to be too imprecise, especially +/// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we +/// needn't reject code unless it actually constructs and operates on the qualifed variant. +/// +/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a +/// type-based one). Qualifications propagate structurally across variables: If a local (or a +/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed. pub trait Qualif { /// The name of the file used to debug the dataflow analysis that computes this qualif. const ANALYSIS_NAME: &'static str; @@ -26,157 +33,27 @@ pub trait Qualif { /// Whether this `Qualif` is cleared when a local is moved from. const IS_CLEARED_ON_MOVE: bool = false; + /// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`. fn in_qualifs(qualifs: &ConstQualifs) -> bool; - /// Return the qualification that is (conservatively) correct for any value - /// of the type. - fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool; - - fn in_projection_structurally( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - place: PlaceRef<'_, 'tcx>, - ) -> bool { - if let [proj_base @ .., elem] = place.projection { - let base_qualif = Self::in_place( - cx, - per_local, - PlaceRef { local: place.local, projection: proj_base }, - ); - let qualif = base_qualif - && Self::in_any_value_of_ty( - cx, - Place::ty_from(place.local, proj_base, *cx.body, cx.tcx) - .projection_ty(cx.tcx, elem) - .ty, - ); - match elem { - ProjectionElem::Deref - | ProjectionElem::Subslice { .. } - | ProjectionElem::Field(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Downcast(..) => qualif, - - ProjectionElem::Index(local) => qualif || per_local(*local), - } - } else { - bug!("This should be called if projection is not empty"); - } - } - - fn in_projection( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - place: PlaceRef<'_, 'tcx>, - ) -> bool { - Self::in_projection_structurally(cx, per_local, place) - } - - fn in_place( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - place: PlaceRef<'_, 'tcx>, - ) -> bool { - match place { - PlaceRef { local, projection: [] } => per_local(local), - PlaceRef { local: _, projection: [.., _] } => Self::in_projection(cx, per_local, place), - } - } - - fn in_operand( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - operand: &Operand<'tcx>, - ) -> bool { - match *operand { - Operand::Copy(ref place) | Operand::Move(ref place) => { - Self::in_place(cx, per_local, place.as_ref()) - } - - Operand::Constant(ref constant) => { - // Check the qualifs of the value of `const` items. - if let ty::ConstKind::Unevaluated(def_id, _, promoted) = constant.literal.val { - assert!(promoted.is_none()); - // Don't peek inside trait associated constants. - if cx.tcx.trait_of_item(def_id).is_none() { - let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id); - if !Self::in_qualifs(&qualifs) { - return false; - } - - // Just in case the type is more specific than - // the definition, e.g., impl associated const - // with type parameters, take it into account. - } - } - // Otherwise use the qualifs of the type. - Self::in_any_value_of_ty(cx, constant.literal.ty) - } - } - } - - fn in_rvalue_structurally( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, - ) -> bool { - match *rvalue { - Rvalue::NullaryOp(..) => false, - - Rvalue::Discriminant(ref place) | Rvalue::Len(ref place) => { - Self::in_place(cx, per_local, place.as_ref()) - } - - Rvalue::Use(ref operand) - | Rvalue::Repeat(ref operand, _) - | Rvalue::UnaryOp(_, ref operand) - | Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand), - - Rvalue::BinaryOp(_, ref lhs, ref rhs) - | Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => { - Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs) - } - - Rvalue::Ref(_, _, ref place) | Rvalue::AddressOf(_, ref place) => { - // Special-case reborrows to be more like a copy of the reference. - if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, *cx.body, cx.tcx).ty; - if let ty::Ref(..) = base_ty.kind { - return Self::in_place( - cx, - per_local, - PlaceRef { local: place.local, projection: proj_base }, - ); - } - } - - Self::in_place(cx, per_local, place.as_ref()) - } - - Rvalue::Aggregate(_, ref operands) => { - operands.iter().any(|o| Self::in_operand(cx, per_local, o)) - } - } - } - - fn in_rvalue( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, - ) -> bool { - Self::in_rvalue_structurally(cx, per_local, rvalue) - } - - fn in_call( - cx: &ConstCx<'_, 'tcx>, - _per_local: &mut impl FnMut(Local) -> bool, - _callee: &Operand<'tcx>, - _args: &[Operand<'tcx>], - return_ty: Ty<'tcx>, - ) -> bool { - // Be conservative about the returned value of a const fn. - Self::in_any_value_of_ty(cx, return_ty) - } + /// Returns `true` if *any* value of the given type could possibly have this `Qualif`. + /// + /// This function determines `Qualif`s when we cannot do a value-based analysis. Since qualif + /// propagation is context-insenstive, this includes function arguments and values returned + /// from a call to another function. + /// + /// It also determines the `Qualif`s for primitive types. + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool; + + /// Returns `true` if this `Qualif` is inherent to the given struct or enum. + /// + /// By default, `Qualif`s propagate into ADTs in a structural way: An ADT only becomes + /// qualified if part of it is assigned a value with that `Qualif`. However, some ADTs *always* + /// have a certain `Qualif`, regardless of whether their fields have it. For example, a type + /// with a custom `Drop` impl is inherently `NeedsDrop`. + /// + /// Returning `true` for `in_adt_inherently` but `false` for `in_any_value_of_ty` is unsound. + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool; } /// Constant containing interior mutability (`UnsafeCell`). @@ -197,26 +74,10 @@ fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) } - fn in_rvalue( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, - ) -> bool { - match *rvalue { - Rvalue::Aggregate(ref kind, _) => { - if let AggregateKind::Adt(def, ..) = **kind { - if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() { - let ty = rvalue.ty(*cx.body, cx.tcx); - assert_eq!(Self::in_any_value_of_ty(cx, ty), true); - return true; - } - } - } - - _ => {} - } - - Self::in_rvalue_structurally(cx, per_local, rvalue) + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool { + // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently. + // It arises structurally for all other types. + Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type() } } @@ -238,19 +99,127 @@ fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { ty.needs_drop(cx.tcx, cx.param_env) } - fn in_rvalue( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, - ) -> bool { - if let Rvalue::Aggregate(ref kind, _) = *rvalue { + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool { + adt.has_dtor(cx.tcx) + } +} + +// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return. + +/// Returns `true` if this `Rvalue` contains qualif `Q`. +pub fn in_rvalue(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, rvalue: &Rvalue<'tcx>) -> bool +where + Q: Qualif, + F: FnMut(Local) -> bool, +{ + match rvalue { + Rvalue::NullaryOp(..) => Q::in_any_value_of_ty(cx, rvalue.ty(*cx.body, cx.tcx)), + + Rvalue::Discriminant(place) | Rvalue::Len(place) => { + in_place::(cx, in_local, place.as_ref()) + } + + Rvalue::Use(operand) + | Rvalue::Repeat(operand, _) + | Rvalue::UnaryOp(_, operand) + | Rvalue::Cast(_, operand, _) => in_operand::(cx, in_local, operand), + + Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + in_operand::(cx, in_local, lhs) || in_operand::(cx, in_local, rhs) + } + + Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + // Special-case reborrows to be more like a copy of the reference. + if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { + let base_ty = Place::ty_from(place.local, proj_base, *cx.body, cx.tcx).ty; + if let ty::Ref(..) = base_ty.kind { + return in_place::( + cx, + in_local, + PlaceRef { local: place.local, projection: proj_base }, + ); + } + } + + in_place::(cx, in_local, place.as_ref()) + } + + Rvalue::Aggregate(kind, operands) => { + // Return early if we know that the struct or enum being constructed is always + // qualified. if let AggregateKind::Adt(def, ..) = **kind { - if def.has_dtor(cx.tcx) { + if Q::in_adt_inherently(cx, def) { return true; } } + + // Otherwise, proceed structurally... + operands.iter().any(|o| in_operand::(cx, in_local, o)) } + } +} - Self::in_rvalue_structurally(cx, per_local, rvalue) +/// Returns `true` if this `Place` contains qualif `Q`. +pub fn in_place(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: PlaceRef<'tcx>) -> bool +where + Q: Qualif, + F: FnMut(Local) -> bool, +{ + let mut projection = place.projection; + while let [ref proj_base @ .., proj_elem] = projection { + match *proj_elem { + ProjectionElem::Index(index) if in_local(index) => return true, + + ProjectionElem::Deref + | ProjectionElem::Field(_, _) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(_, _) + | ProjectionElem::Index(_) => {} + } + + let base_ty = Place::ty_from(place.local, proj_base, *cx.body, cx.tcx); + let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty; + if !Q::in_any_value_of_ty(cx, proj_ty) { + return false; + } + + projection = proj_base; + } + + assert!(projection.is_empty()); + in_local(place.local) +} + +/// Returns `true` if this `Operand` contains qualif `Q`. +pub fn in_operand(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, operand: &Operand<'tcx>) -> bool +where + Q: Qualif, + F: FnMut(Local) -> bool, +{ + let constant = match operand { + Operand::Copy(place) | Operand::Move(place) => { + return in_place::(cx, in_local, place.as_ref()); + } + + Operand::Constant(c) => c, + }; + + // Check the qualifs of the value of `const` items. + if let ty::ConstKind::Unevaluated(def_id, _, promoted) = constant.literal.val { + assert!(promoted.is_none()); + // Don't peek inside trait associated constants. + if cx.tcx.trait_of_item(def_id).is_none() { + let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id); + if !Q::in_qualifs(&qualifs) { + return false; + } + + // Just in case the type is more specific than + // the definition, e.g., impl associated const + // with type parameters, take it into account. + } } + // Otherwise use the qualifs of the type. + Q::in_any_value_of_ty(cx, constant.literal.ty) } diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs index 3e14cc6d32a..e42f64b5c73 100644 --- a/src/librustc_mir/transform/check_consts/resolver.rs +++ b/src/librustc_mir/transform/check_consts/resolver.rs @@ -8,7 +8,7 @@ use std::marker::PhantomData; -use super::{Item, Qualif}; +use super::{qualifs, Item, Qualif}; use crate::dataflow::{self as old_dataflow, generic as dataflow}; /// A `Visitor` that propagates qualifs between locals. This defines the transfer function of @@ -66,18 +66,15 @@ fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, value: bool) { fn apply_call_return_effect( &mut self, _block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], return_place: &mir::Place<'tcx>, ) { + // We cannot reason about another function's internals, so use conservative type-based + // qualification for the result of a function call. let return_ty = return_place.ty(*self.item.body, self.item.tcx).ty; - let qualif = Q::in_call( - self.item, - &mut |l| self.qualifs_per_local.contains(l), - func, - args, - return_ty, - ); + let qualif = Q::in_any_value_of_ty(self.item, return_ty); + if !return_place.is_indirect() { self.assign_qualif_direct(return_place, qualif); } @@ -110,7 +107,11 @@ fn visit_assign( rvalue: &mir::Rvalue<'tcx>, location: Location, ) { - let qualif = Q::in_rvalue(self.item, &mut |l| self.qualifs_per_local.contains(l), rvalue); + let qualif = qualifs::in_rvalue::( + self.item, + &mut |l| self.qualifs_per_local.contains(l), + rvalue, + ); if !place.is_indirect() { self.assign_qualif_direct(place, qualif); } @@ -125,8 +126,12 @@ fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: // here; that occurs in `apply_call_return_effect`. if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind { - let qualif = - Q::in_operand(self.item, &mut |l| self.qualifs_per_local.contains(l), value); + let qualif = qualifs::in_operand::( + self.item, + &mut |l| self.qualifs_per_local.contains(l), + value, + ); + if !dest.is_indirect() { self.assign_qualif_direct(dest, qualif); } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 1553f826c7e..be461c0e03d 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -9,9 +9,10 @@ use rustc_hir::{def_id::DefId, HirId}; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::{self, TraitEngine}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::{self, TraitEngine}; use std::borrow::Cow; use std::ops::Deref; @@ -212,7 +213,7 @@ pub fn check_op_spanned(&mut self, op: O, span: Span) // If an operation is supported in miri (and is not already controlled by a feature gate) it // can be turned on with `-Zunleash-the-miri-inside-of-you`. - let is_unleashable = O::IS_SUPPORTED_IN_MIRI && O::feature_gate(self.tcx).is_none(); + let is_unleashable = O::IS_SUPPORTED_IN_MIRI && O::feature_gate().is_none(); if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { self.tcx.sess.span_warn(span, "skipping const checks"); @@ -276,7 +277,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { } }; self.visit_place_base(&place.local, ctx, location); - self.visit_projection(&place.local, reborrowed_proj, ctx, location); + self.visit_projection(place.local, reborrowed_proj, ctx, location); return; } } @@ -289,7 +290,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::AddressOf), }; self.visit_place_base(&place.local, ctx, location); - self.visit_projection(&place.local, reborrowed_proj, ctx, location); + self.visit_projection(place.local, reborrowed_proj, ctx, location); return; } } @@ -343,7 +344,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { Rvalue::Ref(_, BorrowKind::Shared, ref place) | Rvalue::Ref(_, BorrowKind::Shallow, ref place) | Rvalue::AddressOf(Mutability::Not, ref place) => { - let borrowed_place_has_mut_interior = HasMutInterior::in_place( + let borrowed_place_has_mut_interior = qualifs::in_place::( &self.item, &mut |local| self.qualifs.has_mut_interior(local, location), place.as_ref(), @@ -408,7 +409,7 @@ fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { } fn visit_projection_elem( &mut self, - place_local: &Local, + place_local: Local, proj_base: &[PlaceElem<'tcx>], elem: &PlaceElem<'tcx>, context: PlaceContext, @@ -428,11 +429,11 @@ fn visit_projection_elem( match elem { ProjectionElem::Deref => { - let base_ty = Place::ty_from(*place_local, proj_base, *self.body, self.tcx).ty; + let base_ty = Place::ty_from(place_local, proj_base, *self.body, self.tcx).ty; if let ty::RawPtr(_) = base_ty.kind { if proj_base.is_empty() { if let (local, []) = (place_local, proj_base) { - let decl = &self.body.local_decls[*local]; + let decl = &self.body.local_decls[local]; if let LocalInfo::StaticRef { def_id, .. } = decl.local_info { let span = decl.source_info.span; self.check_static(def_id, span); @@ -452,7 +453,7 @@ fn visit_projection_elem( | ProjectionElem::Subslice { .. } | ProjectionElem::Field(..) | ProjectionElem::Index(_) => { - let base_ty = Place::ty_from(*place_local, proj_base, *self.body, self.tcx).ty; + let base_ty = Place::ty_from(place_local, proj_base, *self.body, self.tcx).ty; match base_ty.ty_adt_def() { Some(def) if def.is_union() => { self.check_op(ops::UnionAccess); diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 4dcbcdcbae4..437a154a9b8 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -1,5 +1,3 @@ -use rustc::hir::map::Map; -use rustc::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE}; use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc::mir::*; use rustc::ty::cast::CastTy; @@ -11,6 +9,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit; use rustc_hir::Node; +use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE}; use rustc_span::symbol::{sym, Symbol}; use std::ops::Bound; @@ -451,9 +450,9 @@ struct UnusedUnsafeVisitor<'a> { } impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 654130c6fab..ca23c44f646 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -4,7 +4,6 @@ use std::borrow::Cow; use std::cell::Cell; -use rustc::lint; use rustc::mir::interpret::{InterpResult, Scalar}; use rustc::mir::visit::{ MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, @@ -23,11 +22,11 @@ use rustc_ast::ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_index::vec::IndexVec; -use rustc_infer::traits; +use rustc_session::lint; use rustc_span::Span; +use rustc_trait_selection::traits; use crate::const_eval::error_to_const_error; use crate::interpret::{ @@ -198,7 +197,6 @@ fn call_intrinsic( fn assert_panic( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _span: Span, _msg: &rustc::mir::AssertMessage<'tcx>, _unwind: Option, ) -> InterpResult<'tcx> { @@ -222,13 +220,6 @@ fn binary_ptr_op( )); } - fn find_foreign_static( - _tcx: TyCtxt<'tcx>, - _def_id: DefId, - ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> { - throw_unsup!(ReadForeignStatic) - } - #[inline(always)] fn init_allocation_extra<'b>( _memory_extra: &(), @@ -279,10 +270,6 @@ fn before_access_static( Ok(()) } - fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) - } - #[inline(always)] fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { Ok(()) @@ -416,32 +403,15 @@ fn use_ecx(&mut self, f: F) -> Option let r = match f(self) { Ok(val) => Some(val), Err(error) => { - use rustc::mir::interpret::{ - InterpError::*, UndefinedBehaviorInfo, UnsupportedOpInfo, - }; - match error.kind { - MachineStop(_) => bug!("ConstProp does not stop"), - - // Some error shouldn't come up because creating them causes - // an allocation, which we should avoid. When that happens, - // dedicated error variants should be introduced instead. - // Only test this in debug builds though to avoid disruptions. - Unsupported(UnsupportedOpInfo::Unsupported(_)) - | Unsupported(UnsupportedOpInfo::ValidationFailure(_)) - | UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) - | UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) - if cfg!(debug_assertions) => - { - bug!("const-prop encountered allocating error: {:?}", error.kind); - } - - Unsupported(_) - | UndefinedBehavior(_) - | InvalidProgram(_) - | ResourceExhaustion(_) => { - // Ignore these errors. - } - } + // Some errors shouldn't come up because creating them causes + // an allocation, which we should avoid. When that happens, + // dedicated error variants should be introduced instead. + // Only test this in debug builds though to avoid disruptions. + debug_assert!( + !error.kind.allocates(), + "const-prop encountered allocating error: {}", + error + ); None } }; @@ -557,7 +527,9 @@ fn check_binary_op( let left_ty = left.ty(&self.local_decls, self.tcx); let left_size_bits = self.ecx.layout_of(left_ty).ok()?.size.bits(); let right_size = r.layout.size; - let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size)); + let r_bits = r.to_scalar().ok(); + // This is basically `force_bits`. + let r_bits = r_bits.and_then(|r| r.to_bits_or_ptr(right_size, &self.tcx).ok()); if r_bits.map_or(false, |b| b >= left_size_bits as u128) { self.report_assert_as_lint( lint::builtin::ARITHMETIC_OVERFLOW, @@ -664,17 +636,18 @@ fn replace_with_const( source_info: SourceInfo, ) { trace!("attepting to replace {:?} with {:?}", rval, value); - if let Err(e) = self.ecx.validate_operand( + if let Err(e) = self.ecx.const_validate_operand( value, vec![], // FIXME: is ref tracking too expensive? - Some(&mut interpret::RefTracking::empty()), + &mut interpret::RefTracking::empty(), + /*may_ref_to_static*/ true, ) { trace!("validation error, attempt failed: {:?}", e); return; } - // FIXME> figure out what tho do when try_read_immediate fails + // FIXME> figure out what to do when try_read_immediate fails let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value)); if let Some(Ok(imm)) = imm { diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index 5dec2c6df99..795bcb57d06 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -8,8 +8,8 @@ use crate::transform::{MirPass, MirSource}; use crate::util as mir_util; use rustc::mir::{Body, BodyAndCache}; -use rustc::session::config::{OutputFilenames, OutputType}; use rustc::ty::TyCtxt; +use rustc_session::config::{OutputFilenames, OutputType}; pub struct Marker(pub &'static str); diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 3107be1b622..b2906739ff1 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -107,15 +107,15 @@ fn tcx(&self) -> TyCtxt<'tcx> { } fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { - assert_ne!(*local, self_arg()); + assert_ne!(*local, SELF_ARG); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if place.local == self_arg() { + if place.local == SELF_ARG { replace_base( place, Place { - local: self_arg(), + local: SELF_ARG, projection: self.tcx().intern_place_elems(&[ProjectionElem::Deref]), }, self.tcx, @@ -125,7 +125,7 @@ fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, locati for elem in place.projection.iter() { if let PlaceElem::Index(local) = elem { - assert_ne!(*local, self_arg()); + assert_ne!(*local, SELF_ARG); } } } @@ -143,15 +143,15 @@ fn tcx(&self) -> TyCtxt<'tcx> { } fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { - assert_ne!(*local, self_arg()); + assert_ne!(*local, SELF_ARG); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if place.local == self_arg() { + if place.local == SELF_ARG { replace_base( place, Place { - local: self_arg(), + local: SELF_ARG, projection: self.tcx().intern_place_elems(&[ProjectionElem::Field( Field::new(0), self.ref_gen_ty, @@ -164,7 +164,7 @@ fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, locati for elem in place.projection.iter() { if let PlaceElem::Index(local) = elem { - assert_ne!(*local, self_arg()); + assert_ne!(*local, SELF_ARG); } } } @@ -180,9 +180,7 @@ fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtx place.projection = tcx.intern_place_elems(&new_projection); } -fn self_arg() -> Local { - Local::new(1) -} +const SELF_ARG: Local = Local::from_u32(1); /// Generator has not been resumed yet. const UNRESUMED: usize = GeneratorSubsts::UNRESUMED; @@ -237,7 +235,7 @@ fn make_state(&self, idx: VariantIdx, val: Operand<'tcx>) -> Rvalue<'tcx> { // Create a Place referencing a generator struct field fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> { - let self_place = Place::from(self_arg()); + let self_place = Place::from(SELF_ARG); let base = self.tcx.mk_place_downcast_unnamed(self_place, variant_index); let mut projection = base.projection.to_vec(); projection.push(ProjectionElem::Field(Field::new(idx), ty)); @@ -247,7 +245,7 @@ fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Pla // Create a statement which changes the discriminant fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> { - let self_place = Place::from(self_arg()); + let self_place = Place::from(SELF_ARG); Statement { source_info, kind: StatementKind::SetDiscriminant { @@ -263,7 +261,7 @@ fn get_discr(&self, body: &mut Body<'tcx>) -> (Statement<'tcx>, Place<'tcx>) { let local_decls_len = body.local_decls.push(temp_decl); let temp = Place::from(local_decls_len); - let self_place = Place::from(self_arg()); + let self_place = Place::from(SELF_ARG); let assign = Statement { source_info: source_info(body), kind: StatementKind::Assign(box (temp, Rvalue::Discriminant(self_place))), @@ -506,7 +504,7 @@ fn locals_live_across_suspend_points( for (block, data) in body.basic_blocks().iter_enumerated() { if let TerminatorKind::Yield { .. } = data.terminator().kind { - let loc = Location { block: block, statement_index: data.statements.len() }; + let loc = Location { block, statement_index: data.statements.len() }; if !movable { // The `liveness` variable contains the liveness of MIR locals ignoring borrows. @@ -539,8 +537,8 @@ fn locals_live_across_suspend_points( let mut live_locals_here = storage_required; live_locals_here.intersect(&liveness.outs[block]); - // The generator argument is ignored - live_locals_here.remove(self_arg()); + // The generator argument is ignored. + live_locals_here.remove(SELF_ARG); debug!("loc = {:?}, live_locals_here = {:?}", loc, live_locals_here); @@ -837,7 +835,6 @@ fn elaborate_generator_drops<'tcx>( // generator's resume function. let param_env = tcx.param_env(def_id); - let gen = self_arg(); let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, param_env }; @@ -845,7 +842,7 @@ fn elaborate_generator_drops<'tcx>( let (target, unwind, source_info) = match block_data.terminator() { Terminator { source_info, kind: TerminatorKind::Drop { location, target, unwind } } => { if let Some(local) = location.as_local() { - if local == gen { + if local == SELF_ARG { (target, unwind, source_info) } else { continue; @@ -864,7 +861,7 @@ fn elaborate_generator_drops<'tcx>( elaborate_drop( &mut elaborator, *source_info, - &Place::from(gen), + &Place::from(SELF_ARG), (), *target, unwind, @@ -918,7 +915,7 @@ fn create_generator_drop_shim<'tcx>( make_generator_state_argument_indirect(tcx, def_id, &mut body); // Change the generator argument from &mut to *mut - body.local_decls[self_arg()] = LocalDecl { + body.local_decls[SELF_ARG] = LocalDecl { mutability: Mutability::Mut, ty: tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }), user_ty: UserTypeProjections::none(), @@ -933,7 +930,7 @@ fn create_generator_drop_shim<'tcx>( 0, Statement { source_info, - kind: StatementKind::Retag(RetagKind::Raw, box Place::from(self_arg())), + kind: StatementKind::Retag(RetagKind::Raw, box Place::from(SELF_ARG)), }, ) } @@ -944,7 +941,7 @@ fn create_generator_drop_shim<'tcx>( // unrelated code from the resume part of the function simplify::remove_dead_blocks(&mut body); - dump_mir(tcx, None, "generator_drop", &0, source, &mut body, |_, _| Ok(())); + dump_mir(tcx, None, "generator_drop", &0, source, &body, |_, _| Ok(())); body } @@ -991,18 +988,101 @@ fn insert_panic_block<'tcx>( assert_block } +fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { + // Returning from a function with an uninhabited return type is undefined behavior. + if body.return_ty().conservative_is_privately_uninhabited(tcx) { + return false; + } + + // If there's a return terminator the function may return. + for block in body.basic_blocks() { + if let TerminatorKind::Return = block.terminator().kind { + return true; + } + } + + // Otherwise the function can't return. + false +} + +fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { + // Nothing can unwind when landing pads are off. + if tcx.sess.no_landing_pads() { + return false; + } + + // Unwinds can only start at certain terminators. + for block in body.basic_blocks() { + match block.terminator().kind { + // These never unwind. + TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseUnwind { .. } => {} + + // Resume will *continue* unwinding, but if there's no other unwinding terminator it + // will never be reached. + TerminatorKind::Resume => {} + + TerminatorKind::Yield { .. } => { + unreachable!("`can_unwind` called before generator transform") + } + + // These may unwind. + TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::Assert { .. } => return true, + } + } + + // If we didn't find an unwinding terminator, the function cannot unwind. + false +} + fn create_generator_resume_function<'tcx>( tcx: TyCtxt<'tcx>, transform: TransformVisitor<'tcx>, def_id: DefId, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>, + can_return: bool, ) { + let can_unwind = can_unwind(tcx, body); + // Poison the generator when it unwinds - for block in body.basic_blocks_mut() { - let source_info = block.terminator().source_info; - if let &TerminatorKind::Resume = &block.terminator().kind { - block.statements.push(transform.set_discr(VariantIdx::new(POISONED), source_info)); + if can_unwind { + let poison_block = BasicBlock::new(body.basic_blocks().len()); + let source_info = source_info(body); + body.basic_blocks_mut().push(BasicBlockData { + statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)], + terminator: Some(Terminator { source_info, kind: TerminatorKind::Resume }), + is_cleanup: true, + }); + + for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { + let source_info = block.terminator().source_info; + + if let TerminatorKind::Resume = block.terminator().kind { + // An existing `Resume` terminator is redirected to jump to our dedicated + // "poisoning block" above. + if idx != poison_block { + *block.terminator_mut() = Terminator { + source_info, + kind: TerminatorKind::Goto { target: poison_block }, + }; + } + } else if !block.is_cleanup { + // Any terminators that *can* unwind but don't have an unwind target set are also + // pointed at our poisoning block (unless they're part of the cleanup path). + if let Some(unwind @ None) = block.terminator_mut().unwind_mut() { + *unwind = Some(poison_block); + } + } } } @@ -1015,8 +1095,20 @@ fn create_generator_resume_function<'tcx>( // Panic when resumed on the returned or poisoned state let generator_kind = body.generator_kind.unwrap(); - cases.insert(1, (RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(generator_kind)))); - cases.insert(2, (POISONED, insert_panic_block(tcx, body, ResumedAfterPanic(generator_kind)))); + + if can_unwind { + cases.insert( + 1, + (POISONED, insert_panic_block(tcx, body, ResumedAfterPanic(generator_kind))), + ); + } + + if can_return { + cases.insert( + 1, + (RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(generator_kind))), + ); + } insert_switch(body, cases, &transform, TerminatorKind::Unreachable); @@ -1042,7 +1134,7 @@ fn insert_clean_drop(body: &mut BodyAndCache<'_>) -> BasicBlock { // Create a block to destroy an unresumed generators. This can only destroy upvars. let drop_clean = BasicBlock::new(body.basic_blocks().len()); let term = TerminatorKind::Drop { - location: Place::from(self_arg()), + location: Place::from(SELF_ARG), target: return_block, unwind: None, }; @@ -1200,6 +1292,8 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAn let (remap, layout, storage_liveness) = compute_layout(tcx, source, &upvars, interior, movable, body); + let can_return = can_return(tcx, body); + // Run the transformation which converts Places from Local to generator struct // accesses for locals in `remap`. // It also rewrites `return x` and `yield y` as writing a new generator state and returning @@ -1243,6 +1337,6 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAn body.generator_drop = Some(box drop_shim); // Create the Generator::resume function - create_generator_resume_function(tcx, transform, def_id, source, body); + create_generator_resume_function(tcx, transform, def_id, source, body, can_return); } } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index b6802505df7..769f3fdcc01 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -1,25 +1,22 @@ //! Inlining pass for MIR functions -use rustc_hir::def_id::DefId; - -use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; - use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc::mir::visit::*; use rustc::mir::*; -use rustc::session::config::Sanitizer; use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; +use rustc_attr as attr; +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_session::config::Sanitizer; +use rustc_target::spec::abi::Abi; use super::simplify::{remove_dead_blocks, CfgSimplifier}; use crate::transform::{MirPass, MirSource}; use std::collections::VecDeque; use std::iter; -use rustc_attr as attr; -use rustc_target::spec::abi::Abi; - const DEFAULT_THRESHOLD: usize = 50; const HINT_THRESHOLD: usize = 100; diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 57e002bf3f3..3eb9d23a32a 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,5 +1,4 @@ use crate::{shim, util}; -use rustc::hir::map::Map; use rustc::mir::{BodyAndCache, ConstQualifs, MirPhase, Promoted}; use rustc::ty::query::Providers; use rustc::ty::steal::Steal; @@ -86,8 +85,8 @@ fn visit_variant_data( } intravisit::walk_struct_def(self, v) } - type Map = Map<'tcx>; - fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, Self::Map> { + type Map = intravisit::ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 286740f99dd..1336206e186 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -407,15 +407,17 @@ fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { // FIXME(eddyb) maybe cache this? fn qualif_local(&self, local: Local) -> bool { - let per_local = &mut |l| self.qualif_local::(l); - if let TempState::Defined { location: loc, .. } = self.temps[local] { let num_stmts = self.body[loc.block].statements.len(); if loc.statement_index < num_stmts { let statement = &self.body[loc.block].statements[loc.statement_index]; match &statement.kind { - StatementKind::Assign(box (_, rhs)) => Q::in_rvalue(&self.item, per_local, rhs), + StatementKind::Assign(box (_, rhs)) => qualifs::in_rvalue::( + &self.item, + &mut |l| self.qualif_local::(l), + rhs, + ), _ => { span_bug!( statement.source_info.span, @@ -427,9 +429,9 @@ fn qualif_local(&self, local: Local) -> bool { } else { let terminator = self.body[loc.block].terminator(); match &terminator.kind { - TerminatorKind::Call { func, args, .. } => { + TerminatorKind::Call { .. } => { let return_ty = self.body.local_decls[local].ty; - Q::in_call(&self.item, per_local, func, args, return_ty) + Q::in_any_value_of_ty(&self.item, return_ty) } kind => { span_bug!(terminator.source_info.span, "{:?} not promotable", kind); @@ -474,7 +476,7 @@ fn validate_local(&self, local: Local) -> Result<(), Unpromotable> { } } - fn validate_place(&self, place: PlaceRef<'_, 'tcx>) -> Result<(), Unpromotable> { + fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { match place { PlaceRef { local, projection: [] } => self.validate_local(local), PlaceRef { local: _, projection: [proj_base @ .., elem] } => { @@ -920,7 +922,7 @@ fn promote_candidate( let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); match candidate { Candidate::Ref(loc) => { - let ref mut statement = blocks[loc.block].statements[loc.statement_index]; + let statement = &mut blocks[loc.block].statements[loc.statement_index]; match statement.kind { StatementKind::Assign(box ( _, @@ -971,7 +973,7 @@ fn promote_candidate( } } Candidate::Repeat(loc) => { - let ref mut statement = blocks[loc.block].statements[loc.statement_index]; + let statement = &mut blocks[loc.block].statements[loc.statement_index]; match statement.kind { StatementKind::Assign(box (_, Rvalue::Repeat(ref mut operand, _))) => { let ty = operand.ty(local_decls, self.tcx); diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 2ae7bd4d727..22ac3410a75 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -34,7 +34,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCa let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); - let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; + let mdpe = MoveDataParamEnv { move_data, param_env }; let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) .into_engine(tcx, body, def_id) diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index ddf8d73e548..4c54a46642f 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -95,6 +95,11 @@ pub fn simplify(mut self) { let mut start = START_BLOCK; + // Vec of the blocks that should be merged. We store the indices here, instead of the + // statements itself to avoid moving the (relatively) large statements twice. + // We do not push the statements directly into the target block (`bb`) as that is slower + // due to additional reallocations + let mut merged_blocks = Vec::new(); loop { let mut changed = false; @@ -114,18 +119,28 @@ pub fn simplify(mut self) { self.collapse_goto_chain(successor, &mut changed); } - let mut new_stmts = vec![]; let mut inner_changed = true; + merged_blocks.clear(); while inner_changed { inner_changed = false; inner_changed |= self.simplify_branch(&mut terminator); - inner_changed |= self.merge_successor(&mut new_stmts, &mut terminator); + inner_changed |= self.merge_successor(&mut merged_blocks, &mut terminator); changed |= inner_changed; } - let data = &mut self.basic_blocks[bb]; - data.statements.extend(new_stmts); - data.terminator = Some(terminator); + let statements_to_merge = + merged_blocks.iter().map(|&i| self.basic_blocks[i].statements.len()).sum(); + + if statements_to_merge > 0 { + let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements); + statements.reserve(statements_to_merge); + for &from in &merged_blocks { + statements.append(&mut self.basic_blocks[from].statements); + } + self.basic_blocks[bb].statements = statements; + } + + self.basic_blocks[bb].terminator = Some(terminator); changed |= inner_changed; } @@ -199,7 +214,7 @@ fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) { // merge a block with 1 `goto` predecessor to its parent fn merge_successor( &mut self, - new_stmts: &mut Vec>, + merged_blocks: &mut Vec, terminator: &mut Terminator<'tcx>, ) -> bool { let target = match terminator.kind { @@ -216,7 +231,8 @@ fn merge_successor( return false; } }; - new_stmts.extend(self.basic_blocks[target].statements.drain(..)); + + merged_blocks.push(target); self.pred_count[target] = 0; true @@ -230,7 +246,7 @@ fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool { }; let first_succ = { - if let Some(&first_succ) = terminator.successors().nth(0) { + if let Some(&first_succ) = terminator.successors().next() { if terminator.successors().all(|s| *s == first_succ) { let count = terminator.successors().count(); self.pred_count[first_succ] -= (count - 1) as u32; diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index f7239ae55fa..ecf0a8ea83c 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -549,7 +549,7 @@ fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> Bas debug!("destructor_call_block({:?}, {:?})", self, succ); let tcx = self.tcx(); let drop_trait = tcx.lang_items().drop_trait().unwrap(); - let drop_fn = tcx.associated_items(drop_trait).in_definition_order().nth(0).unwrap(); + let drop_fn = tcx.associated_items(drop_trait).in_definition_order().next().unwrap(); let ty = self.place_ty(self.place); let substs = tcx.mk_substs_trait(ty, &[]); @@ -731,7 +731,7 @@ fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option) -> Basic self.elaborator.patch().new_block(base_block) } - /// Ceates a pair of drop-loops of `place`, which drops its contents, even + /// Creates a pair of drop-loops of `place`, which drops its contents, even /// in the case of 1 panic. If `ptr_based`, creates a pointer loop, /// otherwise create an index loop. fn drop_loop_pair( @@ -872,7 +872,7 @@ fn drop_flag_reset_block( debug!("drop_flag_reset_block({:?},{:?})", self, mode); let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); - let block_start = Location { block: block, statement_index: 0 }; + let block_start = Location { block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, mode); block } @@ -921,7 +921,7 @@ fn unelaborated_free_block( let call = TerminatorKind::Call { func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), - args: args, + args, destination: Some((unit_temp, target)), cleanup: None, from_hir_call: false, diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index b12ad1e4c15..f6c6f555495 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -293,7 +293,7 @@ fn dump_matched_mir_node<'tcx>( writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?; writeln!(file, "// source = {:?}", source)?; writeln!(file, "// pass_name = {}", pass_name)?; - writeln!(file, "")?; + writeln!(file)?; write_mir_fn(tcx, source, body, &mut file, result)?; Ok(()) }); @@ -316,7 +316,7 @@ pub fn write_mir_fn<'tcx>( write_basic_block(tcx, block, body, &mut |_, _| Ok(()), w)?; print(w, " ", &result.outs)?; if block.index() + 1 != body.basic_blocks().len() { - writeln!(w, "")?; + writeln!(w)?; } } diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 6fd8f06fe8f..f8dfddef2bb 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -134,7 +134,7 @@ fn dump_matched_mir_node<'tcx, F>( if let Some(ref layout) = body.generator_layout { writeln!(file, "// generator_layout = {:?}", layout)?; } - writeln!(file, "")?; + writeln!(file)?; extra_data(PassWhere::BeforeCFG, &mut file)?; write_user_type_annotations(body, &mut file)?; write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?; @@ -242,13 +242,13 @@ pub fn write_mir_pretty<'tcx>( first = false; } else { // Put empty lines between all items - writeln!(w, "")?; + writeln!(w)?; } write_mir_fn(tcx, MirSource::item(def_id), body, &mut |_, _| Ok(()), w)?; for (i, body) in tcx.promoted_mir(def_id).iter_enumerated() { - writeln!(w, "")?; + writeln!(w)?; let src = MirSource { instance: ty::InstanceDef::Item(def_id), promoted: Some(i) }; write_mir_fn(tcx, src, body, &mut |_, _| Ok(()), w)?; } @@ -271,7 +271,7 @@ pub fn write_mir_fn<'tcx, F>( extra_data(PassWhere::BeforeBlock(block), w)?; write_basic_block(tcx, block, body, extra_data, w)?; if block.index() + 1 != body.basic_blocks().len() { - writeln!(w, "")?; + writeln!(w)?; } } @@ -297,7 +297,7 @@ pub fn write_basic_block<'tcx, F>( writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?; // List of statements in the middle. - let mut current_location = Location { block: block, statement_index: 0 }; + let mut current_location = Location { block, statement_index: 0 }; for statement in &data.statements { extra_data(PassWhere::BeforeLocation(current_location), w)?; let indented_body = format!("{0}{0}{1:?};", INDENT, statement); @@ -490,7 +490,7 @@ fn write_scope_tree( } let children = match scope_tree.get(&parent) { - Some(childs) => childs, + Some(children) => children, None => return Ok(()), }; @@ -529,7 +529,7 @@ pub fn write_mir_intro<'tcx>( write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; // Add an empty line before the first block is printed. - writeln!(w, "")?; + writeln!(w)?; Ok(()) } @@ -545,7 +545,7 @@ fn write_mir_sig( trace!("write_mir_sig: {:?}", src.instance); let kind = tcx.def_kind(src.def_id()); let is_function = match kind { - Some(DefKind::Fn) | Some(DefKind::Method) | Some(DefKind::Ctor(..)) => true, + Some(DefKind::Fn) | Some(DefKind::AssocFn) | Some(DefKind::Ctor(..)) => true, _ => tcx.is_closure(src.def_id()), }; match (kind, src.promoted) { diff --git a/src/librustc_mir_build/Cargo.toml b/src/librustc_mir_build/Cargo.toml index d53188a39e5..96716dbd604 100644 --- a/src/librustc_mir_build/Cargo.toml +++ b/src/librustc_mir_build/Cargo.toml @@ -25,5 +25,6 @@ rustc_serialize = { path = "../libserialize", package = "serialize" } rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } rustc_target = { path = "../librustc_target" } +rustc_trait_selection = { path = "../librustc_trait_selection" } rustc_ast = { path = "../librustc_ast" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs index 56aa150dd37..aea4f5f1b3a 100644 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ b/src/librustc_mir_build/build/matches/simplify.rs @@ -113,7 +113,7 @@ fn simplify_match_pair<'pat>( // value being matched, taking the variance field into account. candidate.ascriptions.push(Ascription { span: user_ty_span, - user_ty: user_ty, + user_ty, source: match_pair.place, variance, }); @@ -209,7 +209,12 @@ fn simplify_match_pair<'pat>( i == variant_index || { self.hir.tcx().features().exhaustive_patterns && !v - .uninhabited_from(self.hir.tcx(), substs, adt_def.adt_kind()) + .uninhabited_from( + self.hir.tcx(), + substs, + adt_def.adt_kind(), + self.hir.param_env, + ) .is_empty() } }) && (adt_def.did.is_local() diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs index 9f450f8fc7b..d23a2708dc4 100644 --- a/src/librustc_mir_build/build/matches/test.rs +++ b/src/librustc_mir_build/build/matches/test.rs @@ -64,10 +64,7 @@ pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test< PatKind::Slice { ref prefix, ref slice, ref suffix } => { let len = prefix.len() + suffix.len(); let op = if slice.is_some() { BinOp::Ge } else { BinOp::Eq }; - Test { - span: match_pair.pattern.span, - kind: TestKind::Len { len: len as u64, op: op }, - } + Test { span: match_pair.pattern.span, kind: TestKind::Len { len: len as u64, op } } } PatKind::Or { .. } => bug!("or-patterns should have already been handled"), diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs index 830877f713e..821c4d68c7e 100644 --- a/src/librustc_mir_build/build/mod.rs +++ b/src/librustc_mir_build/build/mod.rs @@ -39,12 +39,12 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { .. }) | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Method(hir::FnSig { decl, .. }, body_id), + kind: hir::ImplItemKind::Fn(hir::FnSig { decl, .. }, body_id), .. }) | Node::TraitItem(hir::TraitItem { kind: - hir::TraitItemKind::Method(hir::FnSig { decl, .. }, hir::TraitMethod::Provided(body_id)), + hir::TraitItemKind::Fn(hir::FnSig { decl, .. }, hir::TraitFn::Provided(body_id)), .. }) => (*body_id, decl.output.span()), Node::Item(hir::Item { kind: hir::ItemKind::Static(ty, _, body_id), .. }) @@ -418,7 +418,7 @@ struct GuardFrameLocal { impl GuardFrameLocal { fn new(id: hir::HirId, _binding_mode: BindingMode) -> Self { - GuardFrameLocal { id: id } + GuardFrameLocal { id } } } diff --git a/src/librustc_mir_build/hair/cx/block.rs b/src/librustc_mir_build/hair/cx/block.rs index a883b84f8fe..8d7225c8c7b 100644 --- a/src/librustc_mir_build/hair/cx/block.rs +++ b/src/librustc_mir_build/hair/cx/block.rs @@ -84,7 +84,7 @@ fn mirror_stmts<'a, 'tcx>( result.push(StmtRef::Mirror(Box::new(Stmt { kind: StmtKind::Let { - remainder_scope: remainder_scope, + remainder_scope, init_scope: region::Scope { id: hir_id.local_id, data: region::ScopeData::Node, diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs index 46d49b6b493..02b596863ab 100644 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ b/src/librustc_mir_build/hair/cx/expr.rs @@ -10,7 +10,6 @@ use rustc::ty::{self, AdtKind, Ty}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::def_id::LocalDefId; use rustc_index::vec::Idx; use rustc_span::Span; @@ -600,7 +599,7 @@ fn user_substs_applied_to_res<'tcx>( // a tuple-struct or tuple-variant. This has the type of a // `Fn` but with the user-given substitutions. Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::Method, _) + | Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { @@ -703,7 +702,7 @@ fn convert_path_expr<'a, 'tcx>( match res { // A regular function, constructor function or a constant. Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::Method, _) + | Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => { let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); @@ -812,7 +811,7 @@ fn convert_var<'tcx>( let closure_def_id = cx.body_owner; let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: LocalDefId::from_def_id(closure_def_id), + closure_expr_id: closure_def_id.expect_local(), }; let var_ty = cx.tables().node_type(var_hir_id); @@ -987,7 +986,7 @@ fn capture_upvar<'tcx>( ) -> ExprRef<'tcx> { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id).to_local(), + closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id).expect_local(), }; let upvar_capture = cx.tables().upvar_capture(upvar_id); let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); diff --git a/src/librustc_mir_build/hair/cx/mod.rs b/src/librustc_mir_build/hair/cx/mod.rs index 449e2e74946..99caa6a0f95 100644 --- a/src/librustc_mir_build/hair/cx/mod.rs +++ b/src/librustc_mir_build/hair/cx/mod.rs @@ -19,6 +19,7 @@ use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_span::symbol::{sym, Symbol}; +use rustc_trait_selection::infer::InferCtxtExt; #[derive(Clone)] crate struct Cx<'a, 'tcx> { diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index 90e4f536478..82810f35675 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -235,17 +235,15 @@ use super::{compare_const_vals, PatternFoldable, PatternFolder}; use super::{FieldPat, Pat, PatKind, PatRange}; -use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; -use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef}; -use rustc_hir::def_id::DefId; -use rustc_hir::{HirId, RangeEnd}; - -use rustc::lint; use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; use rustc::mir::Field; +use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; +use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef}; use rustc::util::common::ErrorReported; - use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_hir::def_id::DefId; +use rustc_hir::{HirId, RangeEnd}; +use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; use arena::TypedArena; @@ -480,7 +478,11 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. crate fn push(&mut self, row: PatStack<'p, 'tcx>) { if let Some(rows) = row.expand_or_pat() { - self.0.extend(rows); + for row in rows { + // We recursively expand the or-patterns of the new rows. + // This is necessary as we might have `0 | (1 | 2)` or e.g., `x @ 0 | x @ (1 | 2)`. + self.push(row) + } } else { self.0.push(row); } @@ -594,7 +596,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { if self.tcx.features().exhaustive_patterns { - self.tcx.is_ty_uninhabited_from(self.module, ty) + self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) } else { false } @@ -837,7 +839,7 @@ fn subtract_ctors(&self, other_ctors: &Vec>) -> Vec( PatKind::Leaf { subpatterns } } } - ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.nth(0).unwrap() }, + ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), _ => PatKind::Wild, }, @@ -1263,7 +1265,7 @@ fn all_constructors<'a, 'tcx>( def.variants .iter() .filter(|v| { - !v.uninhabited_from(cx.tcx, substs, def.adt_kind()) + !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) .contains(cx.tcx, cx.module) }) .map(|v| Variant(v.def_id)) @@ -1396,21 +1398,19 @@ fn from_const( ) -> Option> { if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { let ty = value.ty; - let val = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, size })) = - value.val - { - // For this specific pattern we can skip a lot of effort and go - // straight to the result, after doing a bit of checking. (We - // could remove this branch and just use the next branch, which - // is more general but much slower.) - Scalar::<()>::check_raw(data, size, target_size); - data - } else if let Some(val) = value.try_eval_bits(tcx, param_env, ty) { - // This is a more general form of the previous branch. - val - } else { - return None; - }; + let val = (|| { + if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val { + // For this specific pattern we can skip a lot of effort and go + // straight to the result, after doing a bit of checking. (We + // could remove this branch and just fall through, which + // is more general but much slower.) + if let Ok(bits) = scalar.to_bits_or_ptr(target_size, &tcx) { + return Some(bits); + } + } + // This is a more general form of the previous case. + value.try_eval_bits(tcx, param_env, ty) + })()?; let val = val ^ bias; Some(IntRange { range: val..=val, ty, span }) } else { @@ -2333,7 +2333,7 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.iter().collect()), PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { - let ref variant = adt_def.variants[variant_index]; + let variant = &adt_def.variants[variant_index]; let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant); Some(Variant(variant.def_id)) .filter(|variant_constructor| variant_constructor == constructor) diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs index d0eefb2e4d1..9c86669cf9d 100644 --- a/src/librustc_mir_build/hair/pattern/check_match.rs +++ b/src/librustc_mir_build/hair/pattern/check_match.rs @@ -4,7 +4,6 @@ use super::{PatCtxt, PatKind, PatternError}; -use rustc::hir::map::Map; use rustc::ty::{self, Ty, TyCtxt}; use rustc_ast::ast::Mutability; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder}; @@ -43,9 +42,9 @@ struct MatchVisitor<'a, 'tcx> { } impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } @@ -87,6 +86,9 @@ fn report_inlining_errors(&self, pat_span: Span) { PatternError::AssocConstInPattern(span) => { self.span_e0158(span, "associated consts cannot be referenced in patterns") } + PatternError::ConstParamInPattern(span) => { + self.span_e0158(span, "const parameters cannot be referenced in patterns") + } PatternError::FloatBug => { // FIXME(#31407) this is only necessary because float parsing is buggy ::rustc::mir::interpret::struct_error( @@ -143,7 +145,7 @@ fn lower_pattern<'p>( fn check_in_cx(&self, hir_id: HirId, f: impl FnOnce(MatchCheckCtxt<'_, 'tcx>)) { let module = self.tcx.parent_module(hir_id); - MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |cx| f(cx)); + MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module.to_def_id(), |cx| f(cx)); } fn check_match( @@ -750,9 +752,9 @@ struct AtBindingPatternVisitor<'a, 'b, 'tcx> { } impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs index 27d1bce76ed..ae951e810e3 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -1,14 +1,13 @@ -use rustc::lint; use rustc::mir::Field; use rustc::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::predicate_for_trait_def; -use rustc_infer::traits::{self, ObligationCause, PredicateObligation}; - use rustc_index::vec::Idx; - +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_session::lint; use rustc_span::Span; +use rustc_trait_selection::traits::predicate_for_trait_def; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation}; use std::cell::Cell; @@ -181,7 +180,7 @@ fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> { let kind = match cv.ty.kind { ty::Float(_) => { tcx.struct_span_lint_hir( - ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, |lint| lint.build("floating-point types cannot be used in patterns").emit(), diff --git a/src/librustc_mir_build/hair/pattern/mod.rs b/src/librustc_mir_build/hair/pattern/mod.rs index f58216fbb4e..6786c356293 100644 --- a/src/librustc_mir_build/hair/pattern/mod.rs +++ b/src/librustc_mir_build/hair/pattern/mod.rs @@ -31,6 +31,7 @@ #[derive(Clone, Debug)] crate enum PatternError { AssocConstInPattern(Span), + ConstParamInPattern(Span), StaticInPattern(Span), FloatBug, NonConstPath(Span), @@ -727,7 +728,11 @@ fn lower_variant_or_leaf( | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, _ => { - self.errors.push(PatternError::NonConstPath(span)); + let pattern_error = match res { + Res::Def(DefKind::ConstParam, _) => PatternError::ConstParamInPattern(span), + _ => PatternError::NonConstPath(span), + }; + self.errors.push(pattern_error); PatKind::Wild } }; diff --git a/src/librustc_mir_build/lib.rs b/src/librustc_mir_build/lib.rs index 3c35827d15d..5a8b5a32963 100644 --- a/src/librustc_mir_build/lib.rs +++ b/src/librustc_mir_build/lib.rs @@ -4,6 +4,9 @@ #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(const_if_match)] +#![feature(const_fn)] +#![feature(const_panic)] #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] #![recursion_limit = "256"] diff --git a/src/librustc_mir_build/lints.rs b/src/librustc_mir_build/lints.rs index 0017f800de7..8b1ddf7461a 100644 --- a/src/librustc_mir_build/lints.rs +++ b/src/librustc_mir_build/lints.rs @@ -1,11 +1,11 @@ use rustc::hir::map::blocks::FnLikeNode; -use rustc::lint::builtin::UNCONDITIONAL_RECURSION; use rustc::mir::{self, Body, TerminatorKind}; use rustc::ty::subst::InternalSubsts; use rustc::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::FnKind; use rustc_index::bit_set::BitSet; +use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId) { let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs deleted file mode 100644 index 17b9e78e5df..00000000000 --- a/src/librustc_parse/config.rs +++ /dev/null @@ -1,549 +0,0 @@ -//! Process the potential `cfg` attributes on a module. -//! Also determine if the module should be included in this configuration. -//! -//! This module properly belongs in rustc_expand, but for now it's tied into -//! parsing, so we leave it here to avoid complicated out-of-line dependencies. -//! -//! A principled solution to this wrong location would be to implement [#64197]. -//! -//! [#64197]: https://github.com/rust-lang/rust/issues/64197 - -use crate::{parse_in, validate_attr}; -use rustc_ast::ast::{self, AttrItem, Attribute, MetaItem}; -use rustc_ast::attr::HasAttrs; -use rustc_ast::mut_visit::*; -use rustc_ast::ptr::P; -use rustc_ast::util::map_in_place::MapInPlace; -use rustc_attr as attr; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{error_code, struct_span_err, Applicability, Handler}; -use rustc_feature::{Feature, Features, State as FeatureState}; -use rustc_feature::{ - ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES, -}; -use rustc_session::parse::{feature_err, ParseSess}; -use rustc_span::edition::{Edition, ALL_EDITIONS}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{Span, DUMMY_SP}; - -use smallvec::SmallVec; - -/// A folder that strips out items that do not belong in the current configuration. -pub struct StripUnconfigured<'a> { - pub sess: &'a ParseSess, - pub features: Option<&'a Features>, -} - -fn get_features( - span_handler: &Handler, - krate_attrs: &[ast::Attribute], - crate_edition: Edition, - allow_features: &Option>, -) -> Features { - fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) { - let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed"); - err.span_label(span, "feature has been removed"); - if let Some(reason) = reason { - err.note(reason); - } - err.emit(); - } - - fn active_features_up_to(edition: Edition) -> impl Iterator { - ACTIVE_FEATURES.iter().filter(move |feature| { - if let Some(feature_edition) = feature.edition { - feature_edition <= edition - } else { - false - } - }) - } - - let mut features = Features::default(); - let mut edition_enabled_features = FxHashMap::default(); - - for &edition in ALL_EDITIONS { - if edition <= crate_edition { - // The `crate_edition` implies its respective umbrella feature-gate - // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX). - edition_enabled_features.insert(edition.feature_name(), edition); - } - } - - for feature in active_features_up_to(crate_edition) { - feature.set(&mut features, DUMMY_SP); - edition_enabled_features.insert(feature.name, crate_edition); - } - - // Process the edition umbrella feature-gates first, to ensure - // `edition_enabled_features` is completed before it's queried. - for attr in krate_attrs { - if !attr.check_name(sym::feature) { - continue; - } - - let list = match attr.meta_item_list() { - Some(list) => list, - None => continue, - }; - - for mi in list { - if !mi.is_word() { - continue; - } - - let name = mi.name_or_empty(); - - let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied(); - if let Some(edition) = edition { - if edition <= crate_edition { - continue; - } - - for feature in active_features_up_to(edition) { - // FIXME(Manishearth) there is currently no way to set - // lib features by edition - feature.set(&mut features, DUMMY_SP); - edition_enabled_features.insert(feature.name, edition); - } - } - } - } - - for attr in krate_attrs { - if !attr.check_name(sym::feature) { - continue; - } - - let list = match attr.meta_item_list() { - Some(list) => list, - None => continue, - }; - - let bad_input = |span| { - struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input") - }; - - for mi in list { - let name = match mi.ident() { - Some(ident) if mi.is_word() => ident.name, - Some(ident) => { - bad_input(mi.span()) - .span_suggestion( - mi.span(), - "expected just one word", - format!("{}", ident.name), - Applicability::MaybeIncorrect, - ) - .emit(); - continue; - } - None => { - bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit(); - continue; - } - }; - - if let Some(edition) = edition_enabled_features.get(&name) { - let msg = - &format!("the feature `{}` is included in the Rust {} edition", name, edition); - span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit(); - continue; - } - - if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) { - // Handled in the separate loop above. - continue; - } - - let removed = REMOVED_FEATURES.iter().find(|f| name == f.name); - let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name); - if let Some(Feature { state, .. }) = removed.or(stable_removed) { - if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } = - state - { - feature_removed(span_handler, mi.span(), *reason); - continue; - } - } - - if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) { - let since = Some(Symbol::intern(since)); - features.declared_lang_features.push((name, mi.span(), since)); - continue; - } - - if let Some(allowed) = allow_features.as_ref() { - if allowed.iter().find(|&f| name.as_str() == *f).is_none() { - struct_span_err!( - span_handler, - mi.span(), - E0725, - "the feature `{}` is not in the list of allowed features", - name - ) - .emit(); - continue; - } - } - - if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) { - f.set(&mut features, mi.span()); - features.declared_lang_features.push((name, mi.span(), None)); - continue; - } - - features.declared_lib_features.push((name, mi.span())); - } - } - - features -} - -// `cfg_attr`-process the crate's attributes and compute the crate's features. -pub fn features( - mut krate: ast::Crate, - sess: &ParseSess, - edition: Edition, - allow_features: &Option>, -) -> (ast::Crate, Features) { - let mut strip_unconfigured = StripUnconfigured { sess, features: None }; - - let unconfigured_attrs = krate.attrs.clone(); - let diag = &sess.span_diagnostic; - let err_count = diag.err_count(); - let features = match strip_unconfigured.configure(krate.attrs) { - None => { - // The entire crate is unconfigured. - krate.attrs = Vec::new(); - krate.module.items = Vec::new(); - Features::default() - } - Some(attrs) => { - krate.attrs = attrs; - let features = get_features(diag, &krate.attrs, edition, allow_features); - if err_count == diag.err_count() { - // Avoid reconfiguring malformed `cfg_attr`s. - strip_unconfigured.features = Some(&features); - strip_unconfigured.configure(unconfigured_attrs); - } - features - } - }; - (krate, features) -} - -#[macro_export] -macro_rules! configure { - ($this:ident, $node:ident) => { - match $this.configure($node) { - Some(node) => node, - None => return Default::default(), - } - }; -} - -const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; -const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ - "; - -impl<'a> StripUnconfigured<'a> { - pub fn configure(&mut self, mut node: T) -> Option { - self.process_cfg_attrs(&mut node); - self.in_cfg(node.attrs()).then_some(node) - } - - /// Parse and expand all `cfg_attr` attributes into a list of attributes - /// that are within each `cfg_attr` that has a true configuration predicate. - /// - /// Gives compiler warnigns if any `cfg_attr` does not contain any - /// attributes and is in the original source code. Gives compiler errors if - /// the syntax of any `cfg_attr` is incorrect. - pub fn process_cfg_attrs(&mut self, node: &mut T) { - node.visit_attrs(|attrs| { - attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); - }); - } - - /// Parse and expand a single `cfg_attr` attribute into a list of attributes - /// when the configuration predicate is true, or otherwise expand into an - /// empty list of attributes. - /// - /// Gives a compiler warning when the `cfg_attr` contains no attributes and - /// is in the original source file. Gives a compiler error if the syntax of - /// the attribute is incorrect. - fn process_cfg_attr(&mut self, attr: Attribute) -> Vec { - if !attr.has_name(sym::cfg_attr) { - return vec![attr]; - } - - let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { - None => return vec![], - Some(r) => r, - }; - - // Lint on zero attributes in source. - if expanded_attrs.is_empty() { - return vec![attr]; - } - - // At this point we know the attribute is considered used. - attr::mark_used(&attr); - - if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) { - return vec![]; - } - - // We call `process_cfg_attr` recursively in case there's a - // `cfg_attr` inside of another `cfg_attr`. E.g. - // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. - expanded_attrs - .into_iter() - .flat_map(|(item, span)| { - let attr = attr::mk_attr_from_item(attr.style, item, span); - self.process_cfg_attr(attr) - }) - .collect() - } - - fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { - match attr.get_normal_item().args { - ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => { - let msg = "wrong `cfg_attr` delimiters"; - validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg); - match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { - Ok(r) => return Some(r), - Err(mut e) => { - e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) - .note(CFG_ATTR_NOTE_REF) - .emit(); - } - } - } - _ => self.error_malformed_cfg_attr_missing(attr.span), - } - None - } - - fn error_malformed_cfg_attr_missing(&self, span: Span) { - self.sess - .span_diagnostic - .struct_span_err(span, "malformed `cfg_attr` attribute input") - .span_suggestion( - span, - "missing condition and attribute", - CFG_ATTR_GRAMMAR_HELP.to_string(), - Applicability::HasPlaceholders, - ) - .note(CFG_ATTR_NOTE_REF) - .emit(); - } - - /// Determines if a node with the given attributes should be included in this configuration. - pub fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| { - if !is_cfg(attr) { - return true; - } - let meta_item = match validate_attr::parse_meta(self.sess, attr) { - Ok(meta_item) => meta_item, - Err(mut err) => { - err.emit(); - return true; - } - }; - let error = |span, msg, suggestion: &str| { - let mut err = self.sess.span_diagnostic.struct_span_err(span, msg); - if !suggestion.is_empty() { - err.span_suggestion( - span, - "expected syntax is", - suggestion.into(), - Applicability::MaybeIncorrect, - ); - } - err.emit(); - true - }; - let span = meta_item.span; - match meta_item.meta_item_list() { - None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), - Some([]) => error(span, "`cfg` predicate is not specified", ""), - Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), - Some([single]) => match single.meta_item() { - Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features), - None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), - }, - } - }) - } - - /// Visit attributes on expression and statements (but not attributes on items in blocks). - fn visit_expr_attrs(&mut self, attrs: &[Attribute]) { - // flag the offending attributes - for attr in attrs.iter() { - self.maybe_emit_expr_attr_err(attr); - } - } - - /// If attributes are not allowed on expressions, emit an error for `attr` - pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { - if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) { - let mut err = feature_err( - self.sess, - sym::stmt_expr_attributes, - attr.span, - "attributes on expressions are experimental", - ); - - if attr.is_doc_comment() { - err.help("`///` is for documentation comments. For a plain comment, use `//`."); - } - - err.emit(); - } - } - - pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - let ast::ForeignMod { abi: _, items } = foreign_mod; - items.flat_map_in_place(|item| self.configure(item)); - } - - pub fn configure_generic_params(&mut self, params: &mut Vec) { - params.flat_map_in_place(|param| self.configure(param)); - } - - fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { - match vdata { - ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => { - fields.flat_map_in_place(|field| self.configure(field)) - } - ast::VariantData::Unit(_) => {} - } - } - - pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) { - match item { - ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => { - self.configure_variant_data(def) - } - ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => { - variants.flat_map_in_place(|variant| self.configure(variant)); - for variant in variants { - self.configure_variant_data(&mut variant.data); - } - } - _ => {} - } - } - - pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) { - match expr_kind { - ast::ExprKind::Match(_m, arms) => { - arms.flat_map_in_place(|arm| self.configure(arm)); - } - ast::ExprKind::Struct(_path, fields, _base) => { - fields.flat_map_in_place(|field| self.configure(field)); - } - _ => {} - } - } - - pub fn configure_expr(&mut self, expr: &mut P) { - self.visit_expr_attrs(expr.attrs()); - - // If an expr is valid to cfg away it will have been removed by the - // outer stmt or expression folder before descending in here. - // Anything else is always required, and thus has to error out - // in case of a cfg attr. - // - // N.B., this is intentionally not part of the visit_expr() function - // in order for filter_map_expr() to be able to avoid this check - if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) { - let msg = "removing an expression is not supported in this position"; - self.sess.span_diagnostic.span_err(attr.span, msg); - } - - self.process_cfg_attrs(expr) - } - - pub fn configure_pat(&mut self, pat: &mut P) { - if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind { - fields.flat_map_in_place(|field| self.configure(field)); - } - } - - pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) { - fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg)); - } -} - -impl<'a> MutVisitor for StripUnconfigured<'a> { - fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - self.configure_foreign_mod(foreign_mod); - noop_visit_foreign_mod(foreign_mod, self); - } - - fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { - self.configure_item_kind(item); - noop_visit_item_kind(item, self); - } - - fn visit_expr(&mut self, expr: &mut P) { - self.configure_expr(expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(expr, self); - } - - fn filter_map_expr(&mut self, expr: P) -> Option> { - let mut expr = configure!(self, expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(&mut expr, self); - Some(expr) - } - - fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { - noop_flat_map_stmt(configure!(self, stmt), self) - } - - fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_item(configure!(self, item), self) - } - - fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn visit_mac(&mut self, _mac: &mut ast::Mac) { - // Don't configure interpolated AST (cf. issue #34171). - // Interpolated AST will get configured once the surrounding tokens are parsed. - } - - fn visit_pat(&mut self, pat: &mut P) { - self.configure_pat(pat); - noop_visit_pat(pat, self) - } - - fn visit_fn_decl(&mut self, mut fn_decl: &mut P) { - self.configure_fn_decl(&mut fn_decl); - noop_visit_fn_decl(fn_decl, self); - } -} - -fn is_cfg(attr: &Attribute) -> bool { - attr.check_name(sym::cfg) -} - -/// Process the potential `cfg` attributes on a module. -/// Also determine if the module should be included in this configuration. -pub fn process_configure_mod(sess: &ParseSess, cfg_mods: bool, attrs: &mut Vec) -> bool { - // Don't perform gated feature checking. - let mut strip_unconfigured = StripUnconfigured { sess, features: None }; - strip_unconfigured.process_cfg_attrs(attrs); - !cfg_mods || strip_unconfigured.in_cfg(&attrs) -} diff --git a/src/librustc_parse/lexer/tokentrees.rs b/src/librustc_parse/lexer/tokentrees.rs index 6c0acd0302f..b65b8941728 100644 --- a/src/librustc_parse/lexer/tokentrees.rs +++ b/src/librustc_parse/lexer/tokentrees.rs @@ -40,6 +40,7 @@ struct TokenTreesReader<'a> { /// Used only for error recovery when arriving to EOF with mismatched braces. matching_delim_spans: Vec<(token::DelimToken, Span, Span)>, last_unclosed_found_span: Option, + /// Collect empty block spans that might have been auto-inserted by editors. last_delim_empty_block_spans: FxHashMap, } @@ -138,7 +139,11 @@ fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> { if tts.is_empty() { let empty_block_span = open_brace_span.to(close_brace_span); - self.last_delim_empty_block_spans.insert(delim, empty_block_span); + if !sm.is_multiline(empty_block_span) { + // Only track if the block is in the form of `{}`, otherwise it is + // likely that it was written on purpose. + self.last_delim_empty_block_spans.insert(delim, empty_block_span); + } } if self.open_braces.is_empty() { diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 25f9f8fd3ad..c31cc1b4c9f 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -2,9 +2,11 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(bindings_after_at)] +#![feature(try_blocks)] use rustc_ast::ast; -use rustc_ast::token::{self, Nonterminal, Token}; +use rustc_ast::token::{self, Nonterminal}; use rustc_ast::tokenstream::{self, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; @@ -12,7 +14,7 @@ use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::str; use log::info; @@ -24,24 +26,6 @@ use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser}; pub mod lexer; pub mod validate_attr; -#[macro_use] -pub mod config; - -#[derive(Clone)] -pub struct Directory { - pub path: PathBuf, - pub ownership: DirectoryOwnership, -} - -#[derive(Copy, Clone)] -pub enum DirectoryOwnership { - Owned { - // None if `mod.rs`, `Some("foo")` if we're in `foo.rs`. - relative: Option, - }, - UnownedViaBlock, - UnownedViaMod, -} // A bunch of utility functions of the form `parse__from_` // where includes crate, expr, item, stmt, tts, and one that @@ -118,10 +102,7 @@ pub fn maybe_new_parser_from_source_str( name: FileName, source: String, ) -> Result, Vec> { - let mut parser = - maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source))?; - parser.recurse_into_file_modules = false; - Ok(parser) + maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source)) } /// Creates a new parser, handling errors as appropriate if the file doesn't exist. @@ -145,12 +126,10 @@ pub fn maybe_new_parser_from_file<'a>( pub fn new_sub_parser_from_file<'a>( sess: &'a ParseSess, path: &Path, - directory_ownership: DirectoryOwnership, module_name: Option, sp: Span, ) -> Parser<'a> { let mut p = source_file_to_parser(sess, file_to_source_file(sess, path, Some(sp))); - p.directory.ownership = directory_ownership; p.root_module_name = module_name; p } @@ -171,8 +150,7 @@ fn maybe_source_file_to_parser( let mut parser = stream_to_parser(sess, stream, None); parser.unclosed_delims = unclosed_delims; if parser.token == token::Eof { - let span = Span::new(end_pos, end_pos, parser.token.span.ctxt()); - parser.set_token(Token::new(token::Eof, span)); + parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt()); } Ok(parser) @@ -257,26 +235,7 @@ pub fn stream_to_parser<'a>( stream: TokenStream, subparser_name: Option<&'static str>, ) -> Parser<'a> { - Parser::new(sess, stream, None, true, false, subparser_name) -} - -/// Given a stream, the `ParseSess` and the base directory, produces a parser. -/// -/// Use this function when you are creating a parser from the token stream -/// and also care about the current working directory of the parser (e.g., -/// you are trying to resolve modules defined inside a macro invocation). -/// -/// # Note -/// -/// The main usage of this function is outside of rustc, for those who uses -/// librustc_ast as a library. Please do not remove this function while refactoring -/// just because it is not used in rustc codebase! -pub fn stream_to_parser_with_base_dir<'a>( - sess: &'a ParseSess, - stream: TokenStream, - base_dir: Directory, -) -> Parser<'a> { - Parser::new(sess, stream, Some(base_dir), true, false, None) + Parser::new(sess, stream, false, subparser_name) } /// Runs the given subparser `f` on the tokens of the given `attr`'s item. @@ -286,7 +245,7 @@ pub fn parse_in<'a, T>( name: &'static str, mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, T> { - let mut parser = Parser::new(sess, tts, None, false, false, Some(name)); + let mut parser = Parser::new(sess, tts, false, Some(name)); let result = f(&mut parser)?; if parser.token != token::Eof { parser.unexpected()?; diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs index c5f8b2dd862..b56dd30739d 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/src/librustc_parse/parser/attr.rs @@ -1,4 +1,4 @@ -use super::{Parser, PathStyle, TokenType}; +use super::{Parser, PathStyle}; use rustc_ast::ast; use rustc_ast::attr; use rustc_ast::token::{self, Nonterminal}; @@ -10,14 +10,20 @@ use log::debug; #[derive(Debug)] -enum InnerAttributeParsePolicy<'a> { +pub(super) enum InnerAttrPolicy<'a> { Permitted, - NotPermitted { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option }, + Forbidden { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option }, } const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \ permitted in this context"; +pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden { + reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG, + saw_doc_comment: false, + prev_attr_sp: None, +}; + impl<'a> Parser<'a> { /// Parses attributes that appear before an item. pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec> { @@ -25,48 +31,44 @@ pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec { - let inner_error_reason = if just_parsed_doc_comment { - "an inner attribute is not permitted following an outer doc comment" - } else if !attrs.is_empty() { - "an inner attribute is not permitted following an outer attribute" - } else { - DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG - }; - let inner_parse_policy = InnerAttributeParsePolicy::NotPermitted { - reason: inner_error_reason, - saw_doc_comment: just_parsed_doc_comment, - prev_attr_sp: attrs.last().and_then(|a| Some(a.span)), - }; - let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; - attrs.push(attr); - just_parsed_doc_comment = false; - } - token::DocComment(s) => { - let attr = self.mk_doc_comment(s); - if attr.style != ast::AttrStyle::Outer { - let span = self.token.span; - let mut err = self.struct_span_err(span, "expected outer doc comment"); - err.note( + if self.check(&token::Pound) { + let inner_error_reason = if just_parsed_doc_comment { + "an inner attribute is not permitted following an outer doc comment" + } else if !attrs.is_empty() { + "an inner attribute is not permitted following an outer attribute" + } else { + DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG + }; + let inner_parse_policy = InnerAttrPolicy::Forbidden { + reason: inner_error_reason, + saw_doc_comment: just_parsed_doc_comment, + prev_attr_sp: attrs.last().map(|a| a.span), + }; + let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; + attrs.push(attr); + just_parsed_doc_comment = false; + } else if let token::DocComment(s) = self.token.kind { + let attr = self.mk_doc_comment(s); + if attr.style != ast::AttrStyle::Outer { + self.struct_span_err(self.token.span, "expected outer doc comment") + .note( "inner doc comments like this (starting with \ - `//!` or `/*!`) can only appear before items", - ); - return Err(err); - } - attrs.push(attr); - self.bump(); - just_parsed_doc_comment = true; + `//!` or `/*!`) can only appear before items", + ) + .emit(); } - _ => break, + attrs.push(attr); + self.bump(); + just_parsed_doc_comment = true; + } else { + break; } } Ok(attrs) } fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute { - let style = comments::doc_comment_style(&s.as_str()); - attr::mk_doc_comment(style, s, self.token.span) + attr::mk_doc_comment(comments::doc_comment_style(&s.as_str()), s, self.token.span) } /// Matches `attribute = # ! [ meta_item ]`. @@ -75,96 +77,67 @@ fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute { /// attribute. pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> { debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token); - let inner_parse_policy = if permit_inner { - InnerAttributeParsePolicy::Permitted - } else { - InnerAttributeParsePolicy::NotPermitted { - reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG, - saw_doc_comment: false, - prev_attr_sp: None, - } - }; + let inner_parse_policy = + if permit_inner { InnerAttrPolicy::Permitted } else { DEFAULT_INNER_ATTR_FORBIDDEN }; self.parse_attribute_with_inner_parse_policy(inner_parse_policy) } - /// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy` + /// The same as `parse_attribute`, except it takes in an `InnerAttrPolicy` /// that prescribes how to handle inner attributes. fn parse_attribute_with_inner_parse_policy( &mut self, - inner_parse_policy: InnerAttributeParsePolicy<'_>, + inner_parse_policy: InnerAttrPolicy<'_>, ) -> PResult<'a, ast::Attribute> { debug!( "parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}", inner_parse_policy, self.token ); - let (span, item, style) = match self.token.kind { - token::Pound => { - let lo = self.token.span; - self.bump(); - - if let InnerAttributeParsePolicy::Permitted = inner_parse_policy { - self.expected_tokens.push(TokenType::Token(token::Not)); - } - - let style = if self.token == token::Not { - self.bump(); - ast::AttrStyle::Inner - } else { - ast::AttrStyle::Outer - }; + let lo = self.token.span; + let (span, item, style) = if self.eat(&token::Pound) { + let style = + if self.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; - self.expect(&token::OpenDelim(token::Bracket))?; - let item = self.parse_attr_item()?; - self.expect(&token::CloseDelim(token::Bracket))?; - let hi = self.prev_token.span; - - let attr_sp = lo.to(hi); - - // Emit error if inner attribute is encountered and not permitted - if style == ast::AttrStyle::Inner { - if let InnerAttributeParsePolicy::NotPermitted { - reason, - saw_doc_comment, - prev_attr_sp, - } = inner_parse_policy - { - let prev_attr_note = if saw_doc_comment { - "previous doc comment" - } else { - "previous outer attribute" - }; - - let mut diagnostic = self.struct_span_err(attr_sp, reason); - - if let Some(prev_attr_sp) = prev_attr_sp { - diagnostic - .span_label(attr_sp, "not permitted following an outer attibute") - .span_label(prev_attr_sp, prev_attr_note); - } - - diagnostic - .note( - "inner attributes, like `#![no_std]`, annotate the item \ - enclosing them, and are usually found at the beginning of \ - source files. Outer attributes, like `#[test]`, annotate the \ - item following them.", - ) - .emit(); - } - } + self.expect(&token::OpenDelim(token::Bracket))?; + let item = self.parse_attr_item()?; + self.expect(&token::CloseDelim(token::Bracket))?; + let attr_sp = lo.to(self.prev_token.span); - (attr_sp, item, style) - } - _ => { - let token_str = pprust::token_to_string(&self.token); - let msg = &format!("expected `#`, found `{}`", token_str); - return Err(self.struct_span_err(self.token.span, msg)); + // Emit error if inner attribute is encountered and forbidden. + if style == ast::AttrStyle::Inner { + self.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy); } + + (attr_sp, item, style) + } else { + let token_str = pprust::token_to_string(&self.token); + let msg = &format!("expected `#`, found `{}`", token_str); + return Err(self.struct_span_err(self.token.span, msg)); }; Ok(attr::mk_attr_from_item(style, item, span)) } + pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) { + if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy { + let prev_attr_note = + if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" }; + + let mut diag = self.struct_span_err(attr_sp, reason); + + if let Some(prev_attr_sp) = prev_attr_sp { + diag.span_label(attr_sp, "not permitted following an outer attribute") + .span_label(prev_attr_sp, prev_attr_note); + } + + diag.note( + "inner attributes, like `#![no_std]`, annotate the item enclosing them, \ + and are usually found at the beginning of source files. \ + Outer attributes, like `#[test]`, annotate the item following them.", + ) + .emit(); + } + } + /// Parses an inner part of an attribute (the path and following tokens). /// The tokens must be either a delimited token stream, or empty token stream, /// or the "legacy" key-value form. @@ -200,28 +173,22 @@ pub fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> { crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec> { let mut attrs: Vec = vec![]; loop { - match self.token.kind { - token::Pound => { - // Don't even try to parse if it's not an inner attribute. - if !self.look_ahead(1, |t| t == &token::Not) { - break; - } - - let attr = self.parse_attribute(true)?; - assert_eq!(attr.style, ast::AttrStyle::Inner); + // Only try to parse if it is an inner attribute (has `!`). + if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { + let attr = self.parse_attribute(true)?; + assert_eq!(attr.style, ast::AttrStyle::Inner); + attrs.push(attr); + } else if let token::DocComment(s) = self.token.kind { + // We need to get the position of this token before we bump. + let attr = self.mk_doc_comment(s); + if attr.style == ast::AttrStyle::Inner { attrs.push(attr); + self.bump(); + } else { + break; } - token::DocComment(s) => { - // We need to get the position of this token before we bump. - let attr = self.mk_doc_comment(s); - if attr.style == ast::AttrStyle::Inner { - attrs.push(attr); - self.bump(); - } else { - break; - } - } - _ => break, + } else { + break; } } Ok(attrs) @@ -232,12 +199,10 @@ pub fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> { debug!("checking if {:?} is unusuffixed", lit); if !lit.kind.is_unsuffixed() { - let msg = "suffixed literals are not allowed in attributes"; - self.struct_span_err(lit.span, msg) + self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes") .help( - "instead of using a suffixed literal \ - (`1u8`, `1.0f32`, etc.), use an unsuffixed version \ - (`1`, `1.0`, etc.)", + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ + use an unsuffixed version (`1`, `1.0`, etc.)", ) .emit(); } diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 0759c43d452..87255386b9e 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -19,7 +19,7 @@ use log::{debug, trace}; use std::mem; -const TURBOFISH: &'static str = "use `::<...>` instead of `<...>` to specify type arguments"; +const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments"; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident) -> Param { @@ -40,55 +40,12 @@ pub(super) fn dummy_arg(ident: Ident) -> Param { } pub enum Error { - FileNotFoundForModule { - mod_name: String, - default_path: String, - secondary_path: String, - dir_path: String, - }, - DuplicatePaths { - mod_name: String, - default_path: String, - secondary_path: String, - }, UselessDocComment, } impl Error { fn span_err(self, sp: impl Into, handler: &Handler) -> DiagnosticBuilder<'_> { match self { - Error::FileNotFoundForModule { - ref mod_name, - ref default_path, - ref secondary_path, - ref dir_path, - } => { - let mut err = struct_span_err!( - handler, - sp, - E0583, - "file not found for module `{}`", - mod_name, - ); - err.help(&format!( - "name the file either {} or {} inside the directory \"{}\"", - default_path, secondary_path, dir_path, - )); - err - } - Error::DuplicatePaths { ref mod_name, ref default_path, ref secondary_path } => { - let mut err = struct_span_err!( - handler, - sp, - E0584, - "file for module `{}` found at both {} and {}", - mod_name, - default_path, - secondary_path, - ); - err.help("delete or rename one of them to remove the ambiguity"); - err - } Error::UselessDocComment => { let mut err = struct_span_err!( handler, @@ -192,17 +149,19 @@ pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> { TokenKind::CloseDelim(token::DelimToken::Brace), TokenKind::CloseDelim(token::DelimToken::Paren), ]; - if let token::Ident(name, false) = self.normalized_token.kind { - if Ident::new(name, self.normalized_token.span).is_raw_guess() - && self.look_ahead(1, |t| valid_follow.contains(&t.kind)) + match self.token.ident() { + Some((ident, false)) + if ident.is_raw_guess() + && self.look_ahead(1, |t| valid_follow.contains(&t.kind)) => { err.span_suggestion( - self.normalized_token.span, + ident.span, "you can escape reserved keywords to use them as identifiers", - format!("r#{}", name), + format!("r#{}", ident.name), Applicability::MaybeIncorrect, ); } + _ => {} } if let Some(token_descr) = super::token_descr_opt(&self.token) { err.span_label(self.token.span, format!("expected identifier, found {}", token_descr)); @@ -895,7 +854,7 @@ pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> { let msg = format!("expected `;`, found `{}`", super::token_descr(&self.token)); let appl = Applicability::MachineApplicable; if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { - // Likely inside a macro, can't provide meaninful suggestions. + // Likely inside a macro, can't provide meaningful suggestions. return self.expect(&token::Semi).map(drop); } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { // The current token is in the same line as the prior token, not recoverable. diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 18ddd23588e..c65e99842c5 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -4,8 +4,8 @@ use super::{SemiColonMode, SeqSep, TokenExpectType}; use crate::maybe_recover_from_interpolated_ty_qpath; -use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Ident, Lit, DUMMY_NODE_ID}; -use rustc_ast::ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, Mac, Param, Ty, TyKind, UnOp}; +use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Ident, Lit, UnOp, DUMMY_NODE_ID}; +use rustc_ast::ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; use rustc_ast::ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token, TokenKind}; @@ -50,7 +50,6 @@ macro_rules! maybe_whole_expr { AttrVec::new(), )); } - // N.B., `NtIdent(ident)` is normalized to `Ident` in `fn bump`. _ => {} }; } @@ -97,9 +96,9 @@ pub(super) fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { fn parse_expr_catch_underscore(&mut self) -> PResult<'a, P> { match self.parse_expr() { Ok(expr) => Ok(expr), - Err(mut err) => match self.normalized_token.kind { - token::Ident(name, false) - if name == kw::Underscore && self.look_ahead(1, |t| t == &token::Comma) => + Err(mut err) => match self.token.ident() { + Some((Ident { name: kw::Underscore, .. }, false)) + if self.look_ahead(1, |t| t == &token::Comma) => { // Special-case handling of `foo(_, _, _)` err.emit(); @@ -331,21 +330,19 @@ fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { /// /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. fn check_assoc_op(&self) -> Option> { - Some(Spanned { - node: match (AssocOp::from_token(&self.token), &self.normalized_token.kind) { - (Some(op), _) => op, - (None, token::Ident(sym::and, false)) => { - self.error_bad_logical_op("and", "&&", "conjunction"); - AssocOp::LAnd - } - (None, token::Ident(sym::or, false)) => { - self.error_bad_logical_op("or", "||", "disjunction"); - AssocOp::LOr - } - _ => return None, - }, - span: self.normalized_token.span, - }) + let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { + (Some(op), _) => (op, self.token.span), + (None, Some((Ident { name: sym::and, span }, false))) => { + self.error_bad_logical_op("and", "&&", "conjunction"); + (AssocOp::LAnd, span) + } + (None, Some((Ident { name: sym::or, span }, false))) => { + self.error_bad_logical_op("or", "||", "disjunction"); + (AssocOp::LOr, span) + } + _ => return None, + }; + Some(source_map::respan(span, op)) } /// Error on `and` and `or` suggesting `&&` and `||` respectively. @@ -436,7 +433,7 @@ fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> let attrs = self.parse_or_use_outer_attributes(attrs)?; let lo = self.token.span; // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() - let (hi, ex) = match self.normalized_token.kind { + let (hi, ex) = match self.token.uninterpolate().kind { token::Not => self.parse_unary_expr(lo, UnOp::Not), // `!expr` token::Tilde => self.recover_tilde_expr(lo), // `~expr` token::BinOp(token::Minus) => self.parse_unary_expr(lo, UnOp::Neg), // `-expr` @@ -483,7 +480,7 @@ fn parse_box_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { } fn is_mistaken_not_ident_negation(&self) -> bool { - let token_cannot_continue_expr = |t: &Token| match t.kind { + let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind { // These tokens can start an expression after `!`, but // can't continue an expression after an ident token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), @@ -544,8 +541,8 @@ fn parse_assoc_op_cast( // Save the state of the parser before parsing type normally, in case there is a // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); - match self.parse_ty_no_plus() { - Ok(rhs) => Ok(mk_expr(self, rhs)), + let cast_expr = match self.parse_ty_no_plus() { + Ok(rhs) => mk_expr(self, rhs), Err(mut type_err) => { // Rewind to before attempting to parse the type with generics, to recover // from situations like `x as usize < y` in which we first tried to parse @@ -599,17 +596,70 @@ fn parse_assoc_op_cast( ) .emit(); - Ok(expr) + expr } Err(mut path_err) => { // Couldn't parse as a path, return original error and parser state. path_err.cancel(); mem::replace(self, parser_snapshot_after_type); - Err(type_err) + return Err(type_err); } } } - } + }; + + self.parse_and_disallow_postfix_after_cast(cast_expr) + } + + /// Parses a postfix operators such as `.`, `?`, or index (`[]`) after a cast, + /// then emits an error and returns the newly parsed tree. + /// The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`. + fn parse_and_disallow_postfix_after_cast( + &mut self, + cast_expr: P, + ) -> PResult<'a, P> { + // Save the memory location of expr before parsing any following postfix operators. + // This will be compared with the memory location of the output expression. + // If they different we can assume we parsed another expression because the existing expression is not reallocated. + let addr_before = &*cast_expr as *const _ as usize; + let span = cast_expr.span; + let with_postfix = self.parse_dot_or_call_expr_with_(cast_expr, span)?; + let changed = addr_before != &*with_postfix as *const _ as usize; + + // Check if an illegal postfix operator has been added after the cast. + // If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator. + if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) || changed { + let msg = format!( + "casts cannot be followed by {}", + match with_postfix.kind { + ExprKind::Index(_, _) => "indexing", + ExprKind::Try(_) => "?", + ExprKind::Field(_, _) => "a field access", + ExprKind::MethodCall(_, _) => "a method call", + ExprKind::Call(_, _) => "a function call", + ExprKind::Await(_) => "`.await`", + _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), + } + ); + let mut err = self.struct_span_err(span, &msg); + // If type ascription is "likely an error", the user will already be getting a useful + // help message, and doesn't need a second. + if self.last_type_ascription.map_or(false, |last_ascription| last_ascription.1) { + self.maybe_annotate_with_ascription(&mut err, false); + } else { + let suggestions = vec![ + (span.shrink_to_lo(), "(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ]; + err.multipart_suggestion( + "try surrounding the expression in parentheses", + suggestions, + Applicability::MachineApplicable, + ); + } + err.emit(); + }; + Ok(with_postfix) } fn parse_assoc_op_ascribe(&mut self, lhs: P, lhs_span: Span) -> PResult<'a, P> { @@ -623,10 +673,28 @@ fn parse_assoc_op_ascribe(&mut self, lhs: P, lhs_span: Span) -> PResult<'a /// Parse `& mut? ` or `& raw [ const | mut ] `. fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { self.expect_and()?; + let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); + let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); let expr = self.parse_prefix_expr(None); - let (span, expr) = self.interpolated_or_expr_span(expr)?; - Ok((lo.to(span), ExprKind::AddrOf(borrow_kind, mutbl, expr))) + let (hi, expr) = self.interpolated_or_expr_span(expr)?; + let span = lo.to(hi); + if let Some(lt) = lifetime { + self.error_remove_borrow_lifetime(span, lt.ident.span); + } + Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr))) + } + + fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { + self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes") + .span_label(lt_span, "annotated with lifetime here") + .span_suggestion( + lt_span, + "remove the lifetime annotation", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); } /// Parse `mut?` or `raw [ const | mut ]`. @@ -665,20 +733,11 @@ pub(super) fn parse_dot_or_call_expr_with( expr.map(|mut expr| { attrs.extend::>(expr.attrs.into()); expr.attrs = attrs; - self.error_attr_on_if_expr(&expr); expr }) }) } - fn error_attr_on_if_expr(&self, expr: &Expr) { - if let (ExprKind::If(..), [a0, ..]) = (&expr.kind, &*expr.attrs) { - // Just point to the first attribute in there... - self.struct_span_err(a0.span, "attributes are not yet allowed on `if` expressions") - .emit(); - } - } - fn parse_dot_or_call_expr_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { loop { if self.eat(&token::Question) { @@ -703,7 +762,7 @@ fn parse_dot_or_call_expr_with_(&mut self, mut e: P, lo: Span) -> PResult< } fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { - match self.normalized_token.kind { + match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix)) @@ -797,7 +856,7 @@ fn parse_index_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> /// Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { - if self.normalized_token.span.rust_2018() && self.eat_keyword(kw::Await) { + if self.token.uninterpolated_span().rust_2018() && self.eat_keyword(kw::Await) { return self.mk_await_expr(self_arg, lo); } @@ -860,7 +919,7 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P> { } else if self.eat_lt() { let (qself, path) = self.parse_qpath(PathStyle::Expr)?; Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs)) - } else if self.token.is_path_start() { + } else if self.check_path() { self.parse_path_start_expr(attrs) } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { self.parse_closure_expr(attrs) @@ -911,7 +970,7 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P> { // | ^ expected expression self.bump(); Ok(self.mk_expr_err(self.token.span)) - } else if self.normalized_token.span.rust_2018() { + } else if self.token.uninterpolated_span().rust_2018() { // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. if self.check_keyword(kw::Async) { if self.is_async_block() { @@ -955,7 +1014,7 @@ fn parse_tuple_parens_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P }; let kind = if es.len() == 1 && !trailing_comma { // `(e)` is parenthesized `e`. - ExprKind::Paren(es.into_iter().nth(0).unwrap()) + ExprKind::Paren(es.into_iter().next().unwrap()) } else { // `(e,)` is a tuple with only one field, `e`. ExprKind::Tup(es) @@ -1006,12 +1065,12 @@ fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { // `!`, as an operator, is prefix, so we know this isn't that. let (hi, kind) = if self.eat(&token::Not) { // MACRO INVOCATION expression - let mac = Mac { + let mac = MacCall { path, args: self.parse_mac_args()?, prior_type_ascription: self.last_type_ascription, }; - (self.prev_token.span, ExprKind::Mac(mac)) + (self.prev_token.span, ExprKind::MacCall(mac)) } else if self.check(&token::OpenDelim(token::Brace)) { if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) { return expr; @@ -1026,26 +1085,44 @@ fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { self.maybe_recover_from_bad_qpath(expr, true) } + /// Parse `'label: $expr`. The label is already parsed. fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P> { let lo = label.ident.span; - self.expect(&token::Colon)?; - if self.eat_keyword(kw::While) { - return self.parse_while_expr(Some(label), lo, attrs); - } - if self.eat_keyword(kw::For) { - return self.parse_for_expr(Some(label), lo, attrs); - } - if self.eat_keyword(kw::Loop) { - return self.parse_loop_expr(Some(label), lo, attrs); - } - if self.token == token::OpenDelim(token::Brace) { - return self.parse_block_expr(Some(label), lo, BlockCheckMode::Default, attrs); + let label = Some(label); + let ate_colon = self.eat(&token::Colon); + let expr = if self.eat_keyword(kw::While) { + self.parse_while_expr(label, lo, attrs) + } else if self.eat_keyword(kw::For) { + self.parse_for_expr(label, lo, attrs) + } else if self.eat_keyword(kw::Loop) { + self.parse_loop_expr(label, lo, attrs) + } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() { + self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) + } else { + let msg = "expected `while`, `for`, `loop` or `{` after a label"; + self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); + // Continue as an expression in an effort to recover on `'label: non_block_expr`. + self.parse_expr() + }?; + + if !ate_colon { + self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); } - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); - // Continue as an expression in an effort to recover on `'label: non_block_expr`. - self.parse_expr() + Ok(expr) + } + + fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) { + self.struct_span_err(span, "labeled expression must be followed by `:`") + .span_label(lo, "the label") + .span_suggestion_short( + lo.shrink_to_hi(), + "add `:` after the label", + ": ".to_string(), + Applicability::MachineApplicable, + ) + .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them") + .emit(); } /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. @@ -1322,18 +1399,20 @@ pub(super) fn parse_block_expr( opt_label: Option
{ + ExplicitImpl, + PositiveImpl(A), + NegativeImpl, +} + +impl AutoTraitResult { + fn is_auto(&self) -> bool { + match *self { + AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl => true, + _ => false, + } + } +} + +pub struct AutoTraitInfo<'cx> { + pub full_user_env: ty::ParamEnv<'cx>, + pub region_data: RegionConstraintData<'cx>, + pub vid_to_region: FxHashMap>, +} + +pub struct AutoTraitFinder<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> AutoTraitFinder<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + AutoTraitFinder { tcx } + } + + /// Makes a best effort to determine whether and under which conditions an auto trait is + /// implemented for a type. For example, if you have + /// + /// ``` + /// struct Foo { data: Box } + /// ``` + /// + /// then this might return that Foo: Send if T: Send (encoded in the AutoTraitResult type). + /// The analysis attempts to account for custom impls as well as other complex cases. This + /// result is intended for use by rustdoc and other such consumers. + /// + /// (Note that due to the coinductive nature of Send, the full and correct result is actually + /// quite simple to generate. That is, when a type has no custom impl, it is Send iff its field + /// types are all Send. So, in our example, we might have that Foo: Send if Box: Send. + /// But this is often not the best way to present to the user.) + /// + /// Warning: The API should be considered highly unstable, and it may be refactored or removed + /// in the future. + pub fn find_auto_trait_generics( + &self, + ty: Ty<'tcx>, + orig_env: ty::ParamEnv<'tcx>, + trait_did: DefId, + auto_trait_callback: impl Fn(&InferCtxt<'_, 'tcx>, AutoTraitInfo<'tcx>) -> A, + ) -> AutoTraitResult { + let tcx = self.tcx; + + let trait_ref = ty::TraitRef { def_id: trait_did, substs: tcx.mk_substs_trait(ty, &[]) }; + + let trait_pred = ty::Binder::bind(trait_ref); + + let bail_out = tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::with_negative(&infcx, true); + let result = selcx.select(&Obligation::new( + ObligationCause::dummy(), + orig_env, + trait_pred.to_poly_trait_predicate(), + )); + + match result { + Ok(Some(Vtable::VtableImpl(_))) => { + debug!( + "find_auto_trait_generics({:?}): \ + manual impl found, bailing out", + trait_ref + ); + true + } + _ => false, + } + }); + + // If an explicit impl exists, it always takes priority over an auto impl + if bail_out { + return AutoTraitResult::ExplicitImpl; + } + + return tcx.infer_ctxt().enter(|infcx| { + let mut fresh_preds = FxHashSet::default(); + + // Due to the way projections are handled by SelectionContext, we need to run + // evaluate_predicates twice: once on the original param env, and once on the result of + // the first evaluate_predicates call. + // + // The problem is this: most of rustc, including SelectionContext and traits::project, + // are designed to work with a concrete usage of a type (e.g., Vec + // fn() { Vec }. This information will generally never change - given + // the 'T' in fn() { ... }, we'll never know anything else about 'T'. + // If we're unable to prove that 'T' implements a particular trait, we're done - + // there's nothing left to do but error out. + // + // However, synthesizing an auto trait impl works differently. Here, we start out with + // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing + // with - and progressively discover the conditions we need to fulfill for it to + // implement a certain auto trait. This ends up breaking two assumptions made by trait + // selection and projection: + // + // * We can always cache the result of a particular trait selection for the lifetime of + // an InfCtxt + // * Given a projection bound such as '::SomeItem = K', if 'T: + // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K' + // + // We fix the first assumption by manually clearing out all of the InferCtxt's caches + // in between calls to SelectionContext.select. This allows us to keep all of the + // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift + // them between calls. + // + // We fix the second assumption by reprocessing the result of our first call to + // evaluate_predicates. Using the example of '::SomeItem = K', our first + // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass, + // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing + // SelectionContext to return it back to us. + + let (new_env, user_env) = match self.evaluate_predicates( + &infcx, + trait_did, + ty, + orig_env, + orig_env, + &mut fresh_preds, + false, + ) { + Some(e) => e, + None => return AutoTraitResult::NegativeImpl, + }; + + let (full_env, full_user_env) = self + .evaluate_predicates( + &infcx, + trait_did, + ty, + new_env, + user_env, + &mut fresh_preds, + true, + ) + .unwrap_or_else(|| { + panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env) + }); + + debug!( + "find_auto_trait_generics({:?}): fulfilling \ + with {:?}", + trait_ref, full_env + ); + infcx.clear_caches(); + + // At this point, we already have all of the bounds we need. FulfillmentContext is used + // to store all of the necessary region/lifetime bounds in the InferContext, as well as + // an additional sanity check. + let mut fulfill = FulfillmentContext::new(); + fulfill.register_bound( + &infcx, + full_env, + ty, + trait_did, + ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID), + ); + fulfill.select_all_or_error(&infcx).unwrap_or_else(|e| { + panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, e) + }); + + let body_id_map: FxHashMap<_, _> = infcx + .inner + .borrow() + .region_obligations + .iter() + .map(|&(id, _)| (id, vec![])) + .collect(); + + infcx.process_registered_region_obligations(&body_id_map, None, full_env); + + let region_data = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .region_constraint_data() + .clone(); + + let vid_to_region = self.map_vid_to_region(®ion_data); + + let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; + + return AutoTraitResult::PositiveImpl(auto_trait_callback(&infcx, info)); + }); + } +} + +impl AutoTraitFinder<'tcx> { + /// The core logic responsible for computing the bounds for our synthesized impl. + /// + /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like + /// `FulfillmentContext`, we recursively select the nested obligations of predicates we + /// encounter. However, whenever we encounter an `UnimplementedError` involving a type + /// parameter, we add it to our `ParamEnv`. Since our goal is to determine when a particular + /// type implements an auto trait, Unimplemented errors tell us what conditions need to be met. + /// + /// This method ends up working somewhat similarly to `FulfillmentContext`, but with a few key + /// differences. `FulfillmentContext` works under the assumption that it's dealing with concrete + /// user code. According, it considers all possible ways that a `Predicate` could be met, which + /// isn't always what we want for a synthesized impl. For example, given the predicate `T: + /// Iterator`, `FulfillmentContext` can end up reporting an Unimplemented error for `T: + /// IntoIterator` -- since there's an implementation of `Iterator` where `T: IntoIterator`, + /// `FulfillmentContext` will drive `SelectionContext` to consider that impl before giving up. + /// If we were to rely on `FulfillmentContext`s decision, we might end up synthesizing an impl + /// like this: + /// + /// impl Send for Foo where T: IntoIterator + /// + /// While it might be technically true that Foo implements Send where `T: IntoIterator`, + /// the bound is overly restrictive - it's really only necessary that `T: Iterator`. + /// + /// For this reason, `evaluate_predicates` handles predicates with type variables specially. + /// When we encounter an `Unimplemented` error for a bound such as `T: Iterator`, we immediately + /// add it to our `ParamEnv`, and add it to our stack for recursive evaluation. When we later + /// select it, we'll pick up any nested bounds, without ever inferring that `T: IntoIterator` + /// needs to hold. + /// + /// One additional consideration is supertrait bounds. Normally, a `ParamEnv` is only ever + /// constructed once for a given type. As part of the construction process, the `ParamEnv` will + /// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo`, the + /// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our + /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate_predicates`, or + /// else `SelectionContext` will choke on the missing predicates. However, this should never + /// show up in the final synthesized generics: we don't want our generated docs page to contain + /// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a + /// separate `user_env`, which only holds the predicates that will actually be displayed to the + /// user. + fn evaluate_predicates( + &self, + infcx: &InferCtxt<'_, 'tcx>, + trait_did: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + user_env: ty::ParamEnv<'tcx>, + fresh_preds: &mut FxHashSet>, + only_projections: bool, + ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> { + let tcx = infcx.tcx; + + let mut select = SelectionContext::with_negative(&infcx, true); + + let mut already_visited = FxHashSet::default(); + let mut predicates = VecDeque::new(); + predicates.push_back(ty::Binder::bind(ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: trait_did, + substs: infcx.tcx.mk_substs_trait(ty, &[]), + }, + })); + + let mut computed_preds: FxHashSet<_> = param_env.caller_bounds.iter().cloned().collect(); + let mut user_computed_preds: FxHashSet<_> = + user_env.caller_bounds.iter().cloned().collect(); + + let mut new_env = param_env; + let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID); + + while let Some(pred) = predicates.pop_front() { + infcx.clear_caches(); + + if !already_visited.insert(pred) { + continue; + } + + // Call `infcx.resolve_vars_if_possible` to see if we can + // get rid of any inference variables. + let obligation = infcx.resolve_vars_if_possible(&Obligation::new( + dummy_cause.clone(), + new_env, + pred, + )); + let result = select.select(&obligation); + + match &result { + &Ok(Some(ref vtable)) => { + // If we see an explicit negative impl (e.g., `impl !Send for MyStruct`), + // we immediately bail out, since it's impossible for us to continue. + match vtable { + Vtable::VtableImpl(VtableImplData { impl_def_id, .. }) => { + // Blame 'tidy' for the weird bracket placement. + if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { + debug!( + "evaluate_nested_obligations: found explicit negative impl\ + {:?}, bailing out", + impl_def_id + ); + return None; + } + } + _ => {} + } + + let obligations = vtable.clone().nested_obligations().into_iter(); + + if !self.evaluate_nested_obligations( + ty, + obligations, + &mut user_computed_preds, + fresh_preds, + &mut predicates, + &mut select, + only_projections, + ) { + return None; + } + } + &Ok(None) => {} + &Err(SelectionError::Unimplemented) => { + if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) { + already_visited.remove(&pred); + self.add_user_pred( + &mut user_computed_preds, + ty::Predicate::Trait(pred, hir::Constness::NotConst), + ); + predicates.push_back(pred); + } else { + debug!( + "evaluate_nested_obligations: `Unimplemented` found, bailing: \ + {:?} {:?} {:?}", + ty, + pred, + pred.skip_binder().trait_ref.substs + ); + return None; + } + } + _ => panic!("Unexpected error for '{:?}': {:?}", ty, result), + }; + + computed_preds.extend(user_computed_preds.iter().cloned()); + let normalized_preds = + elaborate_predicates(tcx, computed_preds.iter().cloned().collect()); + new_env = + ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal, None); + } + + let final_user_env = ty::ParamEnv::new( + tcx.mk_predicates(user_computed_preds.into_iter()), + user_env.reveal, + None, + ); + debug!( + "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \ + '{:?}'", + ty, trait_did, new_env, final_user_env + ); + + return Some((new_env, final_user_env)); + } + + /// This method is designed to work around the following issue: + /// When we compute auto trait bounds, we repeatedly call `SelectionContext.select`, + /// progressively building a `ParamEnv` based on the results we get. + /// However, our usage of `SelectionContext` differs from its normal use within the compiler, + /// in that we capture and re-reprocess predicates from `Unimplemented` errors. + /// + /// This can lead to a corner case when dealing with region parameters. + /// During our selection loop in `evaluate_predicates`, we might end up with + /// two trait predicates that differ only in their region parameters: + /// one containing a HRTB lifetime parameter, and one containing a 'normal' + /// lifetime parameter. For example: + /// + /// T as MyTrait<'a> + /// T as MyTrait<'static> + /// + /// If we put both of these predicates in our computed `ParamEnv`, we'll + /// confuse `SelectionContext`, since it will (correctly) view both as being applicable. + /// + /// To solve this, we pick the 'more strict' lifetime bound -- i.e., the HRTB + /// Our end goal is to generate a user-visible description of the conditions + /// under which a type implements an auto trait. A trait predicate involving + /// a HRTB means that the type needs to work with any choice of lifetime, + /// not just one specific lifetime (e.g., `'static`). + fn add_user_pred<'c>( + &self, + user_computed_preds: &mut FxHashSet>, + new_pred: ty::Predicate<'c>, + ) { + let mut should_add_new = true; + user_computed_preds.retain(|&old_pred| { + match (&new_pred, old_pred) { + (&ty::Predicate::Trait(new_trait, _), ty::Predicate::Trait(old_trait, _)) => { + if new_trait.def_id() == old_trait.def_id() { + let new_substs = new_trait.skip_binder().trait_ref.substs; + let old_substs = old_trait.skip_binder().trait_ref.substs; + + if !new_substs.types().eq(old_substs.types()) { + // We can't compare lifetimes if the types are different, + // so skip checking `old_pred`. + return true; + } + + for (new_region, old_region) in + new_substs.regions().zip(old_substs.regions()) + { + match (new_region, old_region) { + // If both predicates have an `ReLateBound` (a HRTB) in the + // same spot, we do nothing. + ( + ty::RegionKind::ReLateBound(_, _), + ty::RegionKind::ReLateBound(_, _), + ) => {} + + (ty::RegionKind::ReLateBound(_, _), _) + | (_, ty::RegionKind::ReVar(_)) => { + // One of these is true: + // The new predicate has a HRTB in a spot where the old + // predicate does not (if they both had a HRTB, the previous + // match arm would have executed). A HRBT is a 'stricter' + // bound than anything else, so we want to keep the newer + // predicate (with the HRBT) in place of the old predicate. + // + // OR + // + // The old predicate has a region variable where the new + // predicate has some other kind of region. An region + // variable isn't something we can actually display to a user, + // so we choose their new predicate (which doesn't have a region + // variable). + // + // In both cases, we want to remove the old predicate, + // from `user_computed_preds`, and replace it with the new + // one. Having both the old and the new + // predicate in a `ParamEnv` would confuse `SelectionContext`. + // + // We're currently in the predicate passed to 'retain', + // so we return `false` to remove the old predicate from + // `user_computed_preds`. + return false; + } + (_, ty::RegionKind::ReLateBound(_, _)) + | (ty::RegionKind::ReVar(_), _) => { + // This is the opposite situation as the previous arm. + // One of these is true: + // + // The old predicate has a HRTB lifetime in a place where the + // new predicate does not. + // + // OR + // + // The new predicate has a region variable where the old + // predicate has some other type of region. + // + // We want to leave the old + // predicate in `user_computed_preds`, and skip adding + // new_pred to `user_computed_params`. + should_add_new = false + } + _ => {} + } + } + } + } + _ => {} + } + return true; + }); + + if should_add_new { + user_computed_preds.insert(new_pred); + } + } + + /// This is very similar to `handle_lifetimes`. However, instead of matching `ty::Region`s + /// to each other, we match `ty::RegionVid`s to `ty::Region`s. + fn map_vid_to_region<'cx>( + &self, + regions: &RegionConstraintData<'cx>, + ) -> FxHashMap> { + let mut vid_map: FxHashMap, RegionDeps<'cx>> = FxHashMap::default(); + let mut finished_map = FxHashMap::default(); + + for constraint in regions.constraints.keys() { + match constraint { + &Constraint::VarSubVar(r1, r2) => { + { + let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default(); + deps1.larger.insert(RegionTarget::RegionVid(r2)); + } + + let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default(); + deps2.smaller.insert(RegionTarget::RegionVid(r1)); + } + &Constraint::RegSubVar(region, vid) => { + { + let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default(); + deps1.larger.insert(RegionTarget::RegionVid(vid)); + } + + let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps2.smaller.insert(RegionTarget::Region(region)); + } + &Constraint::VarSubReg(vid, region) => { + finished_map.insert(vid, region); + } + &Constraint::RegSubReg(r1, r2) => { + { + let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default(); + deps1.larger.insert(RegionTarget::Region(r2)); + } + + let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default(); + deps2.smaller.insert(RegionTarget::Region(r1)); + } + } + } + + while !vid_map.is_empty() { + let target = *vid_map.keys().next().expect("Keys somehow empty"); + let deps = vid_map.remove(&target).expect("Entry somehow missing"); + + for smaller in deps.smaller.iter() { + for larger in deps.larger.iter() { + match (smaller, larger) { + (&RegionTarget::Region(_), &RegionTarget::Region(_)) => { + if let Entry::Occupied(v) = vid_map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.remove(&target); + } + + if let Entry::Occupied(v) = vid_map.entry(*larger) { + let larger_deps = v.into_mut(); + larger_deps.smaller.insert(*smaller); + larger_deps.smaller.remove(&target); + } + } + (&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => { + finished_map.insert(v1, r1); + } + (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { + // Do nothing; we don't care about regions that are smaller than vids. + } + (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { + if let Entry::Occupied(v) = vid_map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.remove(&target); + } + + if let Entry::Occupied(v) = vid_map.entry(*larger) { + let larger_deps = v.into_mut(); + larger_deps.smaller.insert(*smaller); + larger_deps.smaller.remove(&target); + } + } + } + } + } + } + finished_map + } + + fn is_param_no_infer(&self, substs: SubstsRef<'_>) -> bool { + return self.is_of_param(substs.type_at(0)) && !substs.types().any(|t| t.has_infer_types()); + } + + pub fn is_of_param(&self, ty: Ty<'_>) -> bool { + return match ty.kind { + ty::Param(_) => true, + ty::Projection(p) => self.is_of_param(p.self_ty()), + _ => false, + }; + } + + fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { + match p.ty().skip_binder().kind { + ty::Projection(proj) if proj == p.skip_binder().projection_ty => true, + _ => false, + } + } + + fn evaluate_nested_obligations( + &self, + ty: Ty<'_>, + nested: impl Iterator>>, + computed_preds: &mut FxHashSet>, + fresh_preds: &mut FxHashSet>, + predicates: &mut VecDeque>, + select: &mut SelectionContext<'_, 'tcx>, + only_projections: bool, + ) -> bool { + let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID); + + for (obligation, mut predicate) in nested.map(|o| (o.clone(), o.predicate)) { + let is_new_pred = fresh_preds.insert(self.clean_pred(select.infcx(), predicate)); + + // Resolve any inference variables that we can, to help selection succeed + predicate = select.infcx().resolve_vars_if_possible(&predicate); + + // We only add a predicate as a user-displayable bound if + // it involves a generic parameter, and doesn't contain + // any inference variables. + // + // Displaying a bound involving a concrete type (instead of a generic + // parameter) would be pointless, since it's always true + // (e.g. u8: Copy) + // Displaying an inference variable is impossible, since they're + // an internal compiler detail without a defined visual representation + // + // We check this by calling is_of_param on the relevant types + // from the various possible predicates + match &predicate { + &ty::Predicate::Trait(p, _) => { + if self.is_param_no_infer(p.skip_binder().trait_ref.substs) + && !only_projections + && is_new_pred + { + self.add_user_pred(computed_preds, predicate); + } + predicates.push_back(p); + } + &ty::Predicate::Projection(p) => { + debug!( + "evaluate_nested_obligations: examining projection predicate {:?}", + predicate + ); + + // As described above, we only want to display + // bounds which include a generic parameter but don't include + // an inference variable. + // Additionally, we check if we've seen this predicate before, + // to avoid rendering duplicate bounds to the user. + if self.is_param_no_infer(p.skip_binder().projection_ty.substs) + && !p.ty().skip_binder().has_infer_types() + && is_new_pred + { + debug!( + "evaluate_nested_obligations: adding projection predicate\ + to computed_preds: {:?}", + predicate + ); + + // Under unusual circumstances, we can end up with a self-refeential + // projection predicate. For example: + // ::Value == ::Value + // Not only is displaying this to the user pointless, + // having it in the ParamEnv will cause an issue if we try to call + // poly_project_and_unify_type on the predicate, since this kind of + // predicate will normally never end up in a ParamEnv. + // + // For these reasons, we ignore these weird predicates, + // ensuring that we're able to properly synthesize an auto trait impl + if self.is_self_referential_projection(p) { + debug!( + "evaluate_nested_obligations: encountered a projection + predicate equating a type with itself! Skipping" + ); + } else { + self.add_user_pred(computed_preds, predicate); + } + } + + // There are three possible cases when we project a predicate: + // + // 1. We encounter an error. This means that it's impossible for + // our current type to implement the auto trait - there's bound + // that we could add to our ParamEnv that would 'fix' this kind + // of error, as it's not caused by an unimplemented type. + // + // 2. We successfully project the predicate (Ok(Some(_))), generating + // some subobligations. We then process these subobligations + // like any other generated sub-obligations. + // + // 3. We receive an 'ambiguous' result (Ok(None)) + // If we were actually trying to compile a crate, + // we would need to re-process this obligation later. + // However, all we care about is finding out what bounds + // are needed for our type to implement a particular auto trait. + // We've already added this obligation to our computed ParamEnv + // above (if it was necessary). Therefore, we don't need + // to do any further processing of the obligation. + // + // Note that we *must* try to project *all* projection predicates + // we encounter, even ones without inference variable. + // This ensures that we detect any projection errors, + // which indicate that our type can *never* implement the given + // auto trait. In that case, we will generate an explicit negative + // impl (e.g. 'impl !Send for MyType'). However, we don't + // try to process any of the generated subobligations - + // they contain no new information, since we already know + // that our type implements the projected-through trait, + // and can lead to weird region issues. + // + // Normally, we'll generate a negative impl as a result of encountering + // a type with an explicit negative impl of an auto trait + // (for example, raw pointers have !Send and !Sync impls) + // However, through some **interesting** manipulations of the type + // system, it's actually possible to write a type that never + // implements an auto trait due to a projection error, not a normal + // negative impl error. To properly handle this case, we need + // to ensure that we catch any potential projection errors, + // and turn them into an explicit negative impl for our type. + debug!("Projecting and unifying projection predicate {:?}", predicate); + + match poly_project_and_unify_type(select, &obligation.with(p)) { + Err(e) => { + debug!( + "evaluate_nested_obligations: Unable to unify predicate \ + '{:?}' '{:?}', bailing out", + ty, e + ); + return false; + } + Ok(Some(v)) => { + // We only care about sub-obligations + // when we started out trying to unify + // some inference variables. See the comment above + // for more infomration + if p.ty().skip_binder().has_infer_types() { + if !self.evaluate_nested_obligations( + ty, + v.clone().iter().cloned(), + computed_preds, + fresh_preds, + predicates, + select, + only_projections, + ) { + return false; + } + } + } + Ok(None) => { + // It's ok not to make progress when hvave no inference variables - + // in that case, we were only performing unifcation to check if an + // error occurred (which would indicate that it's impossible for our + // type to implement the auto trait). + // However, we should always make progress (either by generating + // subobligations or getting an error) when we started off with + // inference variables + if p.ty().skip_binder().has_infer_types() { + panic!("Unexpected result when selecting {:?} {:?}", ty, obligation) + } + } + } + } + &ty::Predicate::RegionOutlives(ref binder) => { + if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { + return false; + } + } + &ty::Predicate::TypeOutlives(ref binder) => { + match ( + binder.no_bound_vars(), + binder.map_bound_ref(|pred| pred.0).no_bound_vars(), + ) { + (None, Some(t_a)) => { + select.infcx().register_region_obligation_with_cause( + t_a, + select.infcx().tcx.lifetimes.re_static, + &dummy_cause, + ); + } + (Some(ty::OutlivesPredicate(t_a, r_b)), _) => { + select.infcx().register_region_obligation_with_cause( + t_a, + r_b, + &dummy_cause, + ); + } + _ => {} + }; + } + _ => panic!("Unexpected predicate {:?} {:?}", ty, predicate), + }; + } + return true; + } + + pub fn clean_pred( + &self, + infcx: &InferCtxt<'_, 'tcx>, + p: ty::Predicate<'tcx>, + ) -> ty::Predicate<'tcx> { + infcx.freshen(p) + } +} + +// Replaces all ReVars in a type with ty::Region's, using the provided map +pub struct RegionReplacer<'a, 'tcx> { + vid_to_region: &'a FxHashMap>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + (match r { + &ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(), + _ => None, + }) + .unwrap_or_else(|| r.super_fold_with(self)) + } +} diff --git a/src/librustc_trait_selection/traits/codegen/mod.rs b/src/librustc_trait_selection/traits/codegen/mod.rs new file mode 100644 index 00000000000..5c2fc3f305c --- /dev/null +++ b/src/librustc_trait_selection/traits/codegen/mod.rs @@ -0,0 +1,112 @@ +// This file contains various trait resolution methods used by codegen. +// They all assume regions can be erased and monomorphic types. It +// seems likely that they should eventually be merged into more +// general routines. + +use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::traits::{ + FulfillmentContext, Obligation, ObligationCause, SelectionContext, TraitEngine, Vtable, +}; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{self, TyCtxt}; + +/// Attempts to resolve an obligation to a vtable. The result is +/// a shallow vtable resolution, meaning that we do not +/// (necessarily) resolve all nested obligations on the impl. Note +/// that type check should guarantee to us that all nested +/// obligations *could be* resolved if we wanted to. +/// Assumes that this is run after the entire crate has been successfully type-checked. +pub fn codegen_fulfill_obligation<'tcx>( + ty: TyCtxt<'tcx>, + (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), +) -> Option> { + // Remove any references to regions; this helps improve caching. + let trait_ref = ty.erase_regions(&trait_ref); + + debug!( + "codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", + (param_env, trait_ref), + trait_ref.def_id() + ); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + ty.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = ObligationCause::dummy(); + let obligation = + Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + infcx.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!( + "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ + overflow or prior type error", + trait_ref + ), + ); + return None; + } + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + Some(vtable) + }) +} + +// # Global Cache + +/// Finishes processes any obligations that remain in the +/// fulfillment context, and then returns the result with all type +/// variables removed and regions erased. Because this is intended +/// for use after type-check has completed, if any errors occur, +/// it will panic. It is used during normalization and other cases +/// where processing the obligations in `fulfill_cx` may cause +/// type inference variables that appear in `result` to be +/// unified, and hence we need to process those obligations to get +/// the complete picture of the type. +fn drain_fulfillment_cx_or_panic( + infcx: &InferCtxt<'_, 'tcx>, + fulfill_cx: &mut FulfillmentContext<'tcx>, + result: &T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + debug!("drain_fulfillment_cx_or_panic()"); + + // In principle, we only need to do this so long as `result` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + if let Err(errors) = fulfill_cx.select_all_or_error(infcx) { + bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors); + } + + let result = infcx.resolve_vars_if_possible(result); + infcx.tcx.erase_regions(&result) +} diff --git a/src/librustc_trait_selection/traits/coherence.rs b/src/librustc_trait_selection/traits/coherence.rs new file mode 100644 index 00000000000..5f542e7e13b --- /dev/null +++ b/src/librustc_trait_selection/traits/coherence.rs @@ -0,0 +1,540 @@ +//! See Rustc Dev Guide chapters on [trait-resolution] and [trait-specialization] for more info on +//! how this works. +//! +//! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html +//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html + +use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt}; +use crate::traits::select::IntercrateAmbiguityCause; +use crate::traits::SkipLeakCheck; +use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext}; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::subst::Subst; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::symbol::sym; +use rustc_span::DUMMY_SP; + +/// Whether we do the orphan check relative to this crate or +/// to some remote crate. +#[derive(Copy, Clone, Debug)] +enum InCrate { + Local, + Remote, +} + +#[derive(Debug, Copy, Clone)] +pub enum Conflict { + Upstream, + Downstream, +} + +pub struct OverlapResult<'tcx> { + pub impl_header: ty::ImplHeader<'tcx>, + pub intercrate_ambiguity_causes: Vec, + + /// `true` if the overlap might've been permitted before the shift + /// to universes. + pub involves_placeholder: bool, +} + +pub fn add_placeholder_note(err: &mut rustc_errors::DiagnosticBuilder<'_>) { + err.note( + "this behavior recently changed as a result of a bug fix; \ + see rust-lang/rust#56105 for details", + ); +} + +/// If there are types that satisfy both impls, invokes `on_overlap` +/// with a suitably-freshened `ImplHeader` with those types +/// substituted. Otherwise, invokes `no_overlap`. +pub fn overlapping_impls( + tcx: TyCtxt<'_>, + impl1_def_id: DefId, + impl2_def_id: DefId, + skip_leak_check: SkipLeakCheck, + on_overlap: F1, + no_overlap: F2, +) -> R +where + F1: FnOnce(OverlapResult<'_>) -> R, + F2: FnOnce() -> R, +{ + debug!( + "overlapping_impls(\ + impl1_def_id={:?}, \ + impl2_def_id={:?})", + impl1_def_id, impl2_def_id, + ); + + let overlaps = tcx.infer_ctxt().enter(|infcx| { + let selcx = &mut SelectionContext::intercrate(&infcx); + overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some() + }); + + if !overlaps { + return no_overlap(); + } + + // In the case where we detect an error, run the check again, but + // this time tracking intercrate ambuiguity causes for better + // diagnostics. (These take time and can lead to false errors.) + tcx.infer_ctxt().enter(|infcx| { + let selcx = &mut SelectionContext::intercrate(&infcx); + selcx.enable_tracking_intercrate_ambiguity_causes(); + on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap()) + }) +} + +fn with_fresh_ty_vars<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl_def_id: DefId, +) -> ty::ImplHeader<'tcx> { + let tcx = selcx.tcx(); + let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id); + + let header = ty::ImplHeader { + impl_def_id, + self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs), + trait_ref: tcx.impl_trait_ref(impl_def_id).subst(tcx, impl_substs), + predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates, + }; + + let Normalized { value: mut header, obligations } = + traits::normalize(selcx, param_env, ObligationCause::dummy(), &header); + + header.predicates.extend(obligations.into_iter().map(|o| o.predicate)); + header +} + +/// Can both impl `a` and impl `b` be satisfied by a common type (including +/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls. +fn overlap<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + skip_leak_check: SkipLeakCheck, + a_def_id: DefId, + b_def_id: DefId, +) -> Option> { + debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id); + + selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| { + overlap_within_probe(selcx, a_def_id, b_def_id, snapshot) + }) +} + +fn overlap_within_probe( + selcx: &mut SelectionContext<'cx, 'tcx>, + a_def_id: DefId, + b_def_id: DefId, + snapshot: &CombinedSnapshot<'_, 'tcx>, +) -> Option> { + // For the purposes of this check, we don't bring any placeholder + // types into scope; instead, we replace the generic types with + // fresh type variables, and hence we do our evaluations in an + // empty environment. + let param_env = ty::ParamEnv::empty(); + + let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id); + let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id); + + debug!("overlap: a_impl_header={:?}", a_impl_header); + debug!("overlap: b_impl_header={:?}", b_impl_header); + + // Do `a` and `b` unify? If not, no overlap. + let obligations = match selcx + .infcx() + .at(&ObligationCause::dummy(), param_env) + .eq_impl_headers(&a_impl_header, &b_impl_header) + { + Ok(InferOk { obligations, value: () }) => obligations, + Err(_) => { + return None; + } + }; + + debug!("overlap: unification check succeeded"); + + // Are any of the obligations unsatisfiable? If so, no overlap. + let infcx = selcx.infcx(); + let opt_failing_obligation = a_impl_header + .predicates + .iter() + .chain(&b_impl_header.predicates) + .map(|p| infcx.resolve_vars_if_possible(p)) + .map(|p| Obligation { + cause: ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: p, + }) + .chain(obligations) + .find(|o| !selcx.predicate_may_hold_fatal(o)); + // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported + // to the canonical trait query form, `infcx.predicate_may_hold`, once + // the new system supports intercrate mode (which coherence needs). + + if let Some(failing_obligation) = opt_failing_obligation { + debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); + return None; + } + + let impl_header = selcx.infcx().resolve_vars_if_possible(&a_impl_header); + let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); + debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); + + let involves_placeholder = match selcx.infcx().region_constraints_added_in_snapshot(snapshot) { + Some(true) => true, + _ => false, + }; + + Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) +} + +pub fn trait_ref_is_knowable<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, +) -> Option { + debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref); + if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() { + // A downstream or cousin crate is allowed to implement some + // substitution of this trait-ref. + return Some(Conflict::Downstream); + } + + if trait_ref_is_local_or_fundamental(tcx, trait_ref) { + // This is a local or fundamental trait, so future-compatibility + // is no concern. We know that downstream/cousin crates are not + // allowed to implement a substitution of this trait ref, which + // means impls could only come from dependencies of this crate, + // which we already know about. + return None; + } + + // This is a remote non-fundamental trait, so if another crate + // can be the "final owner" of a substitution of this trait-ref, + // they are allowed to implement it future-compatibly. + // + // However, if we are a final owner, then nobody else can be, + // and if we are an intermediate owner, then we don't care + // about future-compatibility, which means that we're OK if + // we are an owner. + if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() { + debug!("trait_ref_is_knowable: orphan check passed"); + return None; + } else { + debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned"); + return Some(Conflict::Upstream); + } +} + +pub fn trait_ref_is_local_or_fundamental<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, +) -> bool { + trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental) +} + +pub enum OrphanCheckErr<'tcx> { + NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>), + UncoveredTy(Ty<'tcx>, Option>), +} + +/// Checks the coherence orphan rules. `impl_def_id` should be the +/// `DefId` of a trait impl. To pass, either the trait must be local, or else +/// two conditions must be satisfied: +/// +/// 1. All type parameters in `Self` must be "covered" by some local type constructor. +/// 2. Some local type must appear in `Self`. +pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> { + debug!("orphan_check({:?})", impl_def_id); + + // We only except this routine to be invoked on implementations + // of a trait, not inherent implementations. + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + debug!("orphan_check: trait_ref={:?}", trait_ref); + + // If the *trait* is local to the crate, ok. + if trait_ref.def_id.is_local() { + debug!("trait {:?} is local to current crate", trait_ref.def_id); + return Ok(()); + } + + orphan_check_trait_ref(tcx, trait_ref, InCrate::Local) +} + +/// Checks whether a trait-ref is potentially implementable by a crate. +/// +/// The current rule is that a trait-ref orphan checks in a crate C: +/// +/// 1. Order the parameters in the trait-ref in subst order - Self first, +/// others linearly (e.g., `>` is U < V < W). +/// 2. Of these type parameters, there is at least one type parameter +/// in which, walking the type as a tree, you can reach a type local +/// to C where all types in-between are fundamental types. Call the +/// first such parameter the "local key parameter". +/// - e.g., `Box` is OK, because you can visit LocalType +/// going through `Box`, which is fundamental. +/// - similarly, `FundamentalPair, Box>` is OK for +/// the same reason. +/// - but (knowing that `Vec` is non-fundamental, and assuming it's +/// not local), `Vec` is bad, because `Vec<->` is between +/// the local type and the type parameter. +/// 3. Every type parameter before the local key parameter is fully known in C. +/// - e.g., `impl T: Trait` is bad, because `T` might be +/// an unknown type. +/// - but `impl LocalType: Trait` is OK, because `LocalType` +/// occurs before `T`. +/// 4. Every type in the local key parameter not known in C, going +/// through the parameter's type tree, must appear only as a subtree of +/// a type local to C, with only fundamental types between the type +/// local to C and the local key parameter. +/// - e.g., `Vec>>` (or equivalently `Box>>`) +/// is bad, because the only local type with `T` as a subtree is +/// `LocalType`, and `Vec<->` is between it and the type parameter. +/// - similarly, `FundamentalPair, T>` is bad, because +/// the second occurrence of `T` is not a subtree of *any* local type. +/// - however, `LocalType>` is OK, because `T` is a subtree of +/// `LocalType>`, which is local and has no types between it and +/// the type parameter. +/// +/// The orphan rules actually serve several different purposes: +/// +/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where +/// every type local to one crate is unknown in the other) can't implement +/// the same trait-ref. This follows because it can be seen that no such +/// type can orphan-check in 2 such crates. +/// +/// To check that a local impl follows the orphan rules, we check it in +/// InCrate::Local mode, using type parameters for the "generic" types. +/// +/// 2. They ground negative reasoning for coherence. If a user wants to +/// write both a conditional blanket impl and a specific impl, we need to +/// make sure they do not overlap. For example, if we write +/// ``` +/// impl IntoIterator for Vec +/// impl IntoIterator for T +/// ``` +/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. +/// We can observe that this holds in the current crate, but we need to make +/// sure this will also hold in all unknown crates (both "independent" crates, +/// which we need for link-safety, and also child crates, because we don't want +/// child crates to get error for impl conflicts in a *dependency*). +/// +/// For that, we only allow negative reasoning if, for every assignment to the +/// inference variables, every unknown crate would get an orphan error if they +/// try to implement this trait-ref. To check for this, we use InCrate::Remote +/// mode. That is sound because we already know all the impls from known crates. +/// +/// 3. For non-#[fundamental] traits, they guarantee that parent crates can +/// add "non-blanket" impls without breaking negative reasoning in dependent +/// crates. This is the "rebalancing coherence" (RFC 1023) restriction. +/// +/// For that, we only a allow crate to perform negative reasoning on +/// non-local-non-#[fundamental] only if there's a local key parameter as per (2). +/// +/// Because we never perform negative reasoning generically (coherence does +/// not involve type parameters), this can be interpreted as doing the full +/// orphan check (using InCrate::Local mode), substituting non-local known +/// types for all inference variables. +/// +/// This allows for crates to future-compatibly add impls as long as they +/// can't apply to types with a key parameter in a child crate - applying +/// the rules, this basically means that every type parameter in the impl +/// must appear behind a non-fundamental type (because this is not a +/// type-system requirement, crate owners might also go for "semantic +/// future-compatibility" involving things such as sealed traits, but +/// the above requirement is sufficient, and is necessary in "open world" +/// cases). +/// +/// Note that this function is never called for types that have both type +/// parameters and inference variables. +fn orphan_check_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + in_crate: InCrate, +) -> Result<(), OrphanCheckErr<'tcx>> { + debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})", trait_ref, in_crate); + + if trait_ref.needs_infer() && trait_ref.needs_subst() { + bug!( + "can't orphan check a trait ref with both params and inference variables {:?}", + trait_ref + ); + } + + // Given impl Trait for T0, an impl is valid only + // if at least one of the following is true: + // + // - Trait is a local trait + // (already checked in orphan_check prior to calling this function) + // - All of + // - At least one of the types T0..=Tn must be a local type. + // Let Ti be the first such type. + // - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti) + // + fn uncover_fundamental_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + in_crate: InCrate, + ) -> Vec> { + if fundamental_ty(ty) && ty_is_non_local(ty, in_crate).is_some() { + ty.walk_shallow().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).collect() + } else { + vec![ty] + } + } + + let mut non_local_spans = vec![]; + for (i, input_ty) in + trait_ref.input_types().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).enumerate() + { + debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty); + let non_local_tys = ty_is_non_local(input_ty, in_crate); + if non_local_tys.is_none() { + debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); + return Ok(()); + } else if let ty::Param(_) = input_ty.kind { + debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); + let local_type = trait_ref + .input_types() + .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) + .find(|ty| ty_is_non_local_constructor(ty, in_crate).is_none()); + + debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type); + + return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type)); + } + if let Some(non_local_tys) = non_local_tys { + for input_ty in non_local_tys { + non_local_spans.push((input_ty, i == 0)); + } + } + } + // If we exit above loop, never found a local type. + debug!("orphan_check_trait_ref: no local type"); + Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) +} + +fn ty_is_non_local<'t>(ty: Ty<'t>, in_crate: InCrate) -> Option>> { + match ty_is_non_local_constructor(ty, in_crate) { + Some(ty) => { + if !fundamental_ty(ty) { + Some(vec![ty]) + } else { + let tys: Vec<_> = ty + .walk_shallow() + .filter_map(|t| ty_is_non_local(t, in_crate)) + .flat_map(|i| i) + .collect(); + if tys.is_empty() { None } else { Some(tys) } + } + } + None => None, + } +} + +fn fundamental_ty(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Ref(..) => true, + ty::Adt(def, _) => def.is_fundamental(), + _ => false, + } +} + +fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { + match in_crate { + // The type is local to *this* crate - it will not be + // local in any other crate. + InCrate::Remote => false, + InCrate::Local => def_id.is_local(), + } +} + +fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> { + debug!("ty_is_non_local_constructor({:?})", ty); + + match ty.kind { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::Never + | ty::Tuple(..) + | ty::Param(..) + | ty::Projection(..) => Some(ty), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate { + InCrate::Local => Some(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => None, + }, + + ty::Adt(def, _) => { + if def_id_is_local(def.did, in_crate) { + None + } else { + Some(ty) + } + } + ty::Foreign(did) => { + if def_id_is_local(did, in_crate) { + None + } else { + Some(ty) + } + } + ty::Opaque(..) => { + // This merits some explanation. + // Normally, opaque types are not involed when performing + // coherence checking, since it is illegal to directly + // implement a trait on an opaque type. However, we might + // end up looking at an opaque type during coherence checking + // if an opaque type gets used within another type (e.g. as + // a type parameter). This requires us to decide whether or + // not an opaque type should be considered 'local' or not. + // + // We choose to treat all opaque types as non-local, even + // those that appear within the same crate. This seems + // somewhat surprising at first, but makes sense when + // you consider that opaque types are supposed to hide + // the underlying type *within the same crate*. When an + // opaque type is used from outside the module + // where it is declared, it should be impossible to observe + // anyything about it other than the traits that it implements. + // + // The alternative would be to look at the underlying type + // to determine whether or not the opaque type itself should + // be considered local. However, this could make it a breaking change + // to switch the underlying ('defining') type from a local type + // to a remote type. This would violate the rule that opaque + // types should be completely opaque apart from the traits + // that they implement, so we don't use this behavior. + Some(ty) + } + + ty::Dynamic(ref tt, ..) => { + if let Some(principal) = tt.principal() { + if def_id_is_local(principal.def_id(), in_crate) { None } else { Some(ty) } + } else { + Some(ty) + } + } + + ty::Error => None, + + ty::UnnormalizedProjection(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) => bug!("ty_is_local invoked on unexpected type: {:?}", ty), + } +} diff --git a/src/librustc_trait_selection/traits/engine.rs b/src/librustc_trait_selection/traits/engine.rs new file mode 100644 index 00000000000..ee4715e0c20 --- /dev/null +++ b/src/librustc_trait_selection/traits/engine.rs @@ -0,0 +1,14 @@ +use rustc::ty::TyCtxt; + +use super::FulfillmentContext; +use super::TraitEngine; + +pub trait TraitEngineExt<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Box; +} + +impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { + fn new(_tcx: TyCtxt<'tcx>) -> Box { + Box::new(FulfillmentContext::new()) + } +} diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs new file mode 100644 index 00000000000..ef62958a3f7 --- /dev/null +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -0,0 +1,1900 @@ +pub mod on_unimplemented; +pub mod suggestions; + +use super::{ + ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, + MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, +}; + +use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc::mir::interpret::ErrorHandled; +use rustc::ty::error::ExpectedFound; +use rustc::ty::fast_reject; +use rustc::ty::fold::TypeFolder; +use rustc::ty::SubtypePredicate; +use rustc::ty::{ + self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::{Node, QPath, TyKind, WhereBoundPredicate, WherePredicate}; +use rustc_session::DiagnosticMessageId; +use rustc_span::source_map::SourceMap; +use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use std::fmt; + +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::query::normalize::AtExt as _; +use on_unimplemented::InferCtxtExt as _; +use suggestions::InferCtxtExt as _; + +pub use rustc_infer::traits::error_reporting::*; + +pub trait InferCtxtExt<'tcx> { + fn report_fulfillment_errors( + &self, + errors: &[FulfillmentError<'tcx>], + body_id: Option, + fallback_has_occurred: bool, + ); + + fn report_overflow_error( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: fmt::Display + TypeFoldable<'tcx>; + + fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; + + fn report_selection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + fallback_has_occurred: bool, + points_at_arg: bool, + ); + + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> (Span, Vec); + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected_args: Vec, + found_args: Vec, + is_closure: bool, + ) -> DiagnosticBuilder<'tcx>; +} + +impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + fn report_fulfillment_errors( + &self, + errors: &[FulfillmentError<'tcx>], + body_id: Option, + fallback_has_occurred: bool, + ) { + #[derive(Debug)] + struct ErrorDescriptor<'tcx> { + predicate: ty::Predicate<'tcx>, + index: Option, // None if this is an old error + } + + let mut error_map: FxHashMap<_, Vec<_>> = self + .reported_trait_errors + .borrow() + .iter() + .map(|(&span, predicates)| { + ( + span, + predicates + .iter() + .map(|&predicate| ErrorDescriptor { predicate, index: None }) + .collect(), + ) + }) + .collect(); + + for (index, error) in errors.iter().enumerate() { + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + + error_map.entry(span).or_default().push(ErrorDescriptor { + predicate: error.obligation.predicate, + index: Some(index), + }); + + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_default() + .push(error.obligation.predicate.clone()); + } + + // We do this in 2 passes because we want to display errors in order, though + // maybe it *is* better to sort errors by span or something. + let mut is_suppressed = vec![false; errors.len()]; + for (_, error_set) in error_map.iter() { + // We want to suppress "duplicate" errors with the same span. + for error in error_set { + if let Some(index) = error.index { + // Suppress errors that are either: + // 1) strictly implied by another error. + // 2) implied by an error with a smaller index. + for error2 in error_set { + if error2.index.map_or(false, |index2| is_suppressed[index2]) { + // Avoid errors being suppressed by already-suppressed + // errors, to prevent all errors from being suppressed + // at once. + continue; + } + + if self.error_implies(&error2.predicate, &error.predicate) + && !(error2.index >= error.index + && self.error_implies(&error.predicate, &error2.predicate)) + { + info!("skipping {:?} (implied by {:?})", error, error2); + is_suppressed[index] = true; + break; + } + } + } + } + } + + for (error, suppressed) in errors.iter().zip(is_suppressed) { + if !suppressed { + self.report_fulfillment_error(error, body_id, fallback_has_occurred); + } + } + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_error( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: fmt::Display + TypeFoldable<'tcx>, + { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + let mut err = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0275, + "overflow evaluating the requirement `{}`", + predicate + ); + + if suggest_increasing_limit { + self.suggest_new_overflow_limit(&mut err); + } + + self.note_obligation_cause_code( + &mut err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); + + err.emit(); + self.tcx.sess.abort_if_errors(); + bug!(); + } + + /// Reports that a cycle was detected which led to overflow and halts + /// compilation. This is equivalent to `report_overflow_error` except + /// that we can give a more helpful error message (and, in particular, + /// we do not suggest increasing the overflow limit, which is not + /// going to help). + fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + let cycle = self.resolve_vars_if_possible(&cycle.to_owned()); + assert!(!cycle.is_empty()); + + debug!("report_overflow_error_cycle: cycle={:?}", cycle); + + self.report_overflow_error(&cycle[0], false); + } + + fn report_selection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + fallback_has_occurred: bool, + points_at_arg: bool, + ) { + let tcx = self.tcx; + let span = obligation.cause.span; + + let mut err = match *error { + SelectionError::Unimplemented => { + if let ObligationCauseCode::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } + | ObligationCauseCode::CompareImplTypeObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } = obligation.cause.code + { + self.report_extra_impl_obligation( + span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}`", obligation.predicate), + ) + .emit(); + return; + } + match obligation.predicate { + ty::Predicate::Trait(ref trait_predicate, _) => { + let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + + if self.tcx.sess.has_errors() && trait_predicate.references_error() { + return; + } + let trait_ref = trait_predicate.to_poly_trait_ref(); + let (post_message, pre_message, type_def) = self + .get_parent_trait_ref(&obligation.cause.code) + .map(|(t, s)| { + ( + format!(" in `{}`", t), + format!("within `{}`, ", t), + s.map(|s| (format!("within this `{}`", t), s)), + ) + }) + .unwrap_or_default(); + + let OnUnimplementedNote { message, label, note, enclosing_scope } = + self.on_unimplemented_note(trait_ref, obligation); + let have_alt_message = message.is_some() || label.is_some(); + let is_try = self + .tcx + .sess + .source_map() + .span_to_snippet(span) + .map(|s| &s == "?") + .unwrap_or(false); + let is_from = format!("{}", trait_ref.print_only_trait_path()) + .starts_with("std::convert::From<"); + let (message, note) = if is_try && is_from { + ( + Some(format!( + "`?` couldn't convert the error to `{}`", + trait_ref.self_ty(), + )), + Some( + "the question mark operation (`?`) implicitly performs a \ + conversion on the error value using the `From` trait" + .to_owned(), + ), + ) + } else { + (message, note) + }; + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0277, + "{}", + message.unwrap_or_else(|| format!( + "the trait bound `{}` is not satisfied{}", + trait_ref.without_const().to_predicate(), + post_message, + )) + ); + + let explanation = + if obligation.cause.code == ObligationCauseCode::MainFunctionType { + "consider using `()`, or a `Result`".to_owned() + } else { + format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_ref.print_only_trait_path(), + trait_ref.self_ty(), + ) + }; + + if self.suggest_add_reference_to_arg( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + have_alt_message, + ) { + self.note_obligation_cause(&mut err, obligation); + err.emit(); + return; + } + if let Some(ref s) = label { + // If it has a custom `#[rustc_on_unimplemented]` + // error message, let's display it as the label! + err.span_label(span, s.as_str()); + err.help(&explanation); + } else { + err.span_label(span, explanation); + } + if let Some((msg, span)) = type_def { + err.span_label(span, &msg); + } + if let Some(ref s) = note { + // If it has a custom `#[rustc_on_unimplemented]` note, let's display it + err.note(s.as_str()); + } + if let Some(ref s) = enclosing_scope { + let enclosing_scope_span = tcx.def_span( + tcx.hir() + .opt_local_def_id(obligation.cause.body_id) + .unwrap_or_else(|| { + tcx.hir().body_owner_def_id(hir::BodyId { + hir_id: obligation.cause.body_id, + }) + }), + ); + + err.span_label(enclosing_scope_span, s.as_str()); + } + + self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); + self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); + self.suggest_remove_reference(&obligation, &mut err, &trait_ref); + self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); + self.note_version_mismatch(&mut err, &trait_ref); + if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { + err.emit(); + return; + } + + // Try to report a help message + if !trait_ref.has_infer_types_or_consts() + && self.predicate_can_apply(obligation.param_env, trait_ref) + { + // If a where-clause may be useful, remind the + // user that they can add it. + // + // don't display an on-unimplemented note, as + // these notes will often be of the form + // "the type `T` can't be frobnicated" + // which is somewhat confusing. + self.suggest_restricting_param_bound( + &mut err, + &trait_ref, + obligation.cause.body_id, + ); + } else { + if !have_alt_message { + // Can't show anything else useful, try to find similar impls. + let impl_candidates = self.find_similar_impl_candidates(trait_ref); + self.report_similar_impl_candidates(impl_candidates, &mut err); + } + self.suggest_change_mut( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + ); + } + + // If this error is due to `!: Trait` not implemented but `(): Trait` is + // implemented, and fallback has occurred, then it could be due to a + // variable that used to fallback to `()` now falling back to `!`. Issue a + // note informing about the change in behaviour. + if trait_predicate.skip_binder().self_ty().is_never() + && fallback_has_occurred + { + let predicate = trait_predicate.map_bound(|mut trait_pred| { + trait_pred.trait_ref.substs = self.tcx.mk_substs_trait( + self.tcx.mk_unit(), + &trait_pred.trait_ref.substs[1..], + ); + trait_pred + }); + let unit_obligation = Obligation { + predicate: ty::Predicate::Trait( + predicate, + hir::Constness::NotConst, + ), + ..obligation.clone() + }; + if self.predicate_may_hold(&unit_obligation) { + err.note( + "the trait is implemented for `()`. \ + Possibly this error has been caused by changes to \ + Rust's type-inference algorithm (see issue #48950 \ + \ + for more information). Consider whether you meant to use \ + the type `()` here instead.", + ); + } + } + + err + } + + ty::Predicate::Subtype(ref predicate) => { + // Errors for Subtype predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) + } + + ty::Predicate::RegionOutlives(ref predicate) => { + let predicate = self.resolve_vars_if_possible(predicate); + let err = self + .region_outlives_predicate(&obligation.cause, &predicate) + .err() + .unwrap(); + struct_span_err!( + self.tcx.sess, + span, + E0279, + "the requirement `{}` is not satisfied (`{}`)", + predicate, + err, + ) + } + + ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + struct_span_err!( + self.tcx.sess, + span, + E0280, + "the requirement `{}` is not satisfied", + predicate + ) + } + + ty::Predicate::ObjectSafe(trait_def_id) => { + let violations = self.tcx.object_safety_violations(trait_def_id); + report_object_safety_error(self.tcx, span, trait_def_id, violations) + } + + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + let found_kind = self.closure_kind(closure_def_id, closure_substs).unwrap(); + let closure_span = self + .tcx + .sess + .source_map() + .def_span(self.tcx.hir().span_if_local(closure_def_id).unwrap()); + let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id).unwrap(); + let mut err = struct_span_err!( + self.tcx.sess, + closure_span, + E0525, + "expected a closure that implements the `{}` trait, \ + but this closure only implements `{}`", + kind, + found_kind + ); + + err.span_label( + closure_span, + format!("this closure implements `{}`, not `{}`", found_kind, kind), + ); + err.span_label( + obligation.cause.span, + format!("the requirement to implement `{}` derives from here", kind), + ); + + // Additional context information explaining why the closure only implements + // a particular trait. + if let Some(tables) = self.in_progress_tables { + let tables = tables.borrow(); + match (found_kind, tables.closure_kind_origins().get(hir_id)) { + (ty::ClosureKind::FnOnce, Some((span, name))) => { + err.span_label( + *span, + format!( + "closure is `FnOnce` because it moves the \ + variable `{}` out of its environment", + name + ), + ); + } + (ty::ClosureKind::FnMut, Some((span, name))) => { + err.span_label( + *span, + format!( + "closure is `FnMut` because it mutates the \ + variable `{}` here", + name + ), + ); + } + _ => {} + } + } + + err.emit(); + return; + } + + ty::Predicate::WellFormed(ty) => { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); + } + + ty::Predicate::ConstEvaluatable(..) => { + // Errors for `ConstEvaluatable` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-evaluatable requirement gave wrong error: `{:?}`", + obligation + ) + } + } + } + + OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { + let found_trait_ref = self.resolve_vars_if_possible(&*found_trait_ref); + let expected_trait_ref = self.resolve_vars_if_possible(&*expected_trait_ref); + + if expected_trait_ref.self_ty().references_error() { + return; + } + + let found_trait_ty = found_trait_ref.self_ty(); + + let found_did = match found_trait_ty.kind { + ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), + ty::Adt(def, _) => Some(def.did), + _ => None, + }; + + let found_span = found_did + .and_then(|did| self.tcx.hir().span_if_local(did)) + .map(|sp| self.tcx.sess.source_map().def_span(sp)); // the sp could be an fn def + + if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { + // We check closures twice, with obligations flowing in different directions, + // but we want to complain about them only once. + return; + } + + self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + + let found = match found_trait_ref.skip_binder().substs.type_at(1).kind { + ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], + _ => vec![ArgKind::empty()], + }; + + let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); + let expected = match expected_ty.kind { + ty::Tuple(ref tys) => tys + .iter() + .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))) + .collect(), + _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], + }; + + if found.len() == expected.len() { + self.report_closure_arg_mismatch( + span, + found_span, + found_trait_ref, + expected_trait_ref, + ) + } else { + let (closure_span, found) = found_did + .and_then(|did| self.tcx.hir().get_if_local(did)) + .map(|node| { + let (found_span, found) = self.get_fn_like_arguments(node); + (Some(found_span), found) + }) + .unwrap_or((found_span, found)); + + self.report_arg_count_mismatch( + span, + closure_span, + expected, + found, + found_trait_ty.is_closure(), + ) + } + } + + TraitNotObjectSafe(did) => { + let violations = self.tcx.object_safety_violations(did); + report_object_safety_error(self.tcx, span, did, violations) + } + + ConstEvalFailure(ErrorHandled::TooGeneric) => { + // In this instance, we have a const expression containing an unevaluated + // generic parameter. We have no idea whether this expression is valid or + // not (e.g. it might result in an error), but we don't want to just assume + // that it's okay, because that might result in post-monomorphisation time + // errors. The onus is really on the caller to provide values that it can + // prove are well-formed. + let mut err = self + .tcx + .sess + .struct_span_err(span, "constant expression depends on a generic parameter"); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + err.note("this may fail depending on what value the parameter takes"); + err + } + + // Already reported in the query. + ConstEvalFailure(ErrorHandled::Reported) => { + self.tcx.sess.delay_span_bug(span, "constant in type had an ignored error"); + return; + } + + Overflow => { + bug!("overflow should be handled before the `report_selection_error` path"); + } + }; + + self.note_obligation_cause(&mut err, obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + + err.emit(); + } + + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> (Span, Vec) { + match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(_, ref _decl, id, span, _), + .. + }) => ( + self.tcx.sess.source_map().def_span(span), + self.tcx + .hir() + .body(id) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = + *arg.pat + { + ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + let snippet = self + .tcx + .sess + .source_map() + .span_to_snippet(pat.span) + .unwrap(); + (snippet, "_".to_owned()) + }) + .collect::>(), + ) + } else { + let name = + self.tcx.sess.source_map().span_to_snippet(arg.pat.span).unwrap(); + ArgKind::Arg(name, "_".to_owned()) + } + }) + .collect::>(), + ), + Node::Item(&hir::Item { span, kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { + span, + kind: hir::ImplItemKind::Fn(ref sig, _), + .. + }) + | Node::TraitItem(&hir::TraitItem { + span, + kind: hir::TraitItemKind::Fn(ref sig, _), + .. + }) => ( + self.tcx.sess.source_map().def_span(span), + sig.decl + .inputs + .iter() + .map(|arg| match arg.clone().kind { + hir::TyKind::Tup(ref tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::>(), + ), + Node::Ctor(ref variant_data) => { + let span = variant_data + .ctor_hir_id() + .map(|hir_id| self.tcx.hir().span(hir_id)) + .unwrap_or(DUMMY_SP); + let span = self.tcx.sess.source_map().def_span(span); + + (span, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {:?}", node), + } + } + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected_args: Vec, + found_args: Vec, + is_closure: bool, + ) -> DiagnosticBuilder<'tcx> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = match &other[..] { + &[ArgKind::Tuple(..)] => true, + _ => false, + }; + match (arg_length, arguments.get(0)) { + (1, Some(&ArgKind::Tuple(_, ref fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {}", found_str)); + + // move |_| { ... } + // ^^^^^^^^-- def_span + // + // move |_| { ... } + // ^^^^^-- prefix + let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); + // move |_| { ... } + // ^^^-- pipe_span + let pipe_span = + if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion( + pipe_span, + &format!( + "consider changing the closure to take and ignore the expected argument{}", + if expected_args.len() < 2 { "" } else { "s" } + ), + format!("|{}|", underscores), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::>() + .join(", "); + err.span_suggestion( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{}|", sugg), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] { + if fields.len() == found_args.len() && is_closure { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + } + + err + } +} + +trait InferCtxtPrivExt<'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) -> bool; + + fn report_fulfillment_error( + &self, + error: &FulfillmentError<'tcx>, + body_id: Option, + fallback_has_occurred: bool, + ); + + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ); + + fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool; + + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str>; + + fn find_similar_impl_candidates( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Vec>; + + fn report_similar_impl_candidates( + &self, + impl_candidates: Vec>, + err: &mut DiagnosticBuilder<'_>, + ); + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(String, Option)>; + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ); + + fn mk_obligation_for_def_id( + &self, + def_id: DefId, + output_ty: Ty<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> PredicateObligation<'tcx>; + + fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + body_id: Option, + ); + + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitRef<'tcx>, + ) -> bool; + + fn note_obligation_cause( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ); + + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ); + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<&ty::TyS<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool; +} + +impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) -> bool { + if cond == error { + return true; + } + + let (cond, error) = match (cond, error) { + (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error, _)) => (cond, error), + _ => { + // FIXME: make this work in other cases too. + return false; + } + }; + + for implication in super::elaborate_predicates(self.tcx, vec![*cond]) { + if let ty::Predicate::Trait(implication, _) = implication { + let error = error.to_poly_trait_ref(); + let implication = implication.to_poly_trait_ref(); + // FIXME: I'm just not taking associated types at all here. + // Eventually I'll need to implement param-env-aware + // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. + let param_env = ty::ParamEnv::empty(); + if self.can_sub(param_env, error, implication).is_ok() { + debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); + return true; + } + } + } + + false + } + + fn report_fulfillment_error( + &self, + error: &FulfillmentError<'tcx>, + body_id: Option, + fallback_has_occurred: bool, + ) { + debug!("report_fulfillment_error({:?})", error); + match error.code { + FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { + self.report_selection_error( + &error.obligation, + selection_error, + fallback_has_occurred, + error.points_at_arg_span, + ); + } + FulfillmentErrorCode::CodeProjectionError(ref e) => { + self.report_projection_error(&error.obligation, e); + } + FulfillmentErrorCode::CodeAmbiguity => { + self.maybe_report_ambiguity(&error.obligation, body_id); + } + FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { + self.report_mismatched_types( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + err.clone(), + ) + .emit(); + } + } + } + + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ) { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + + if predicate.references_error() { + return; + } + + self.probe(|_| { + let err_buf; + let mut err = &error.err; + let mut values = None; + + // try to find the mismatched types to report the error with. + // + // this can fail if the problem was higher-ranked, in which + // cause I have no idea for a good error message. + if let ty::Predicate::Projection(ref data) = predicate { + let mut selcx = SelectionContext::new(self); + let (data, _) = self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + infer::LateBoundRegionConversionTime::HigherRankedType, + data, + ); + let mut obligations = vec![]; + let normalized_ty = super::normalize_projection_type( + &mut selcx, + obligation.param_env, + data.projection_ty, + obligation.cause.clone(), + 0, + &mut obligations, + ); + + debug!( + "report_projection_error obligation.cause={:?} obligation.param_env={:?}", + obligation.cause, obligation.param_env + ); + + debug!( + "report_projection_error normalized_ty={:?} data.ty={:?}", + normalized_ty, data.ty + ); + + let is_normalized_ty_expected = match &obligation.cause.code { + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ObjectCastObligation(_) => false, + _ => true, + }; + + if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( + is_normalized_ty_expected, + normalized_ty, + data.ty, + ) { + values = Some(infer::ValuePairs::Types(ExpectedFound::new( + is_normalized_ty_expected, + normalized_ty, + data.ty, + ))); + + err_buf = error; + err = &err_buf; + } + } + + let msg = format!("type mismatch resolving `{}`", predicate); + let error_id = (DiagnosticMessageId::ErrorId(271), Some(obligation.cause.span), msg); + let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + let mut diag = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0271, + "type mismatch resolving `{}`", + predicate + ); + self.note_type_err(&mut diag, &obligation.cause, None, values, err); + self.note_obligation_cause(&mut diag, obligation); + diag.emit(); + } + }); + } + + fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + /// returns the fuzzy category of a given type, or None + /// if the type can be equated to any type. + fn type_category(t: Ty<'_>) -> Option { + match t.kind { + ty::Bool => Some(0), + ty::Char => Some(1), + ty::Str => Some(2), + ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3), + ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4), + ty::Ref(..) | ty::RawPtr(..) => Some(5), + ty::Array(..) | ty::Slice(..) => Some(6), + ty::FnDef(..) | ty::FnPtr(..) => Some(7), + ty::Dynamic(..) => Some(8), + ty::Closure(..) => Some(9), + ty::Tuple(..) => Some(10), + ty::Projection(..) => Some(11), + ty::Param(..) => Some(12), + ty::Opaque(..) => Some(13), + ty::Never => Some(14), + ty::Adt(adt, ..) => match adt.adt_kind() { + AdtKind::Struct => Some(15), + AdtKind::Union => Some(16), + AdtKind::Enum => Some(17), + }, + ty::Generator(..) => Some(18), + ty::Foreign(..) => Some(19), + ty::GeneratorWitness(..) => Some(20), + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => None, + ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), + } + } + + match (type_category(a), type_category(b)) { + (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) { + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, + _ => cat_a == cat_b, + }, + // infer and error can be equated to all types + _ => true, + } + } + + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { + self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { + hir::GeneratorKind::Gen => "a generator", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", + }) + } + + fn find_similar_impl_candidates( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Vec> { + let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true); + let all_impls = self.tcx.all_impls(trait_ref.def_id()); + + match simp { + Some(simp) => all_impls + .iter() + .filter_map(|&def_id| { + let imp = self.tcx.impl_trait_ref(def_id).unwrap(); + let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); + if let Some(imp_simp) = imp_simp { + if simp != imp_simp { + return None; + } + } + + Some(imp) + }) + .collect(), + None => { + all_impls.iter().map(|&def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect() + } + } + } + + fn report_similar_impl_candidates( + &self, + impl_candidates: Vec>, + err: &mut DiagnosticBuilder<'_>, + ) { + if impl_candidates.is_empty() { + return; + } + + let len = impl_candidates.len(); + let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 }; + + let normalize = |candidate| { + self.tcx.infer_ctxt().enter(|ref infcx| { + let normalized = infcx + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .normalize(candidate) + .ok(); + match normalized { + Some(normalized) => format!("\n {:?}", normalized.value), + None => format!("\n {:?}", candidate), + } + }) + }; + + // Sort impl candidates so that ordering is consistent for UI tests. + let mut normalized_impl_candidates = + impl_candidates.iter().map(normalize).collect::>(); + + // Sort before taking the `..end` range, + // because the ordering of `impl_candidates` may not be deterministic: + // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 + normalized_impl_candidates.sort(); + + err.help(&format!( + "the following implementations were found:{}{}", + normalized_impl_candidates[..end].join(""), + if len > 5 { format!("\nand {} others", len - 4) } else { String::new() } + )); + } + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(String, Option)> { + match code { + &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + match self.get_parent_trait_ref(&data.parent_code) { + Some(t) => Some(t), + None => { + let ty = parent_trait_ref.skip_binder().self_ty(); + let span = + TyCategory::from_ty(ty).map(|(_, def_id)| self.tcx.def_span(def_id)); + Some((ty.to_string(), span)) + } + } + } + _ => None, + } + } + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let get_trait_impl = |trait_def_id| { + let mut trait_impl = None; + self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| { + if trait_impl.is_none() { + trait_impl = Some(impl_def_id); + } + }); + trait_impl + }; + let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let all_traits = self.tcx.all_traits(LOCAL_CRATE); + let traits_with_same_path: std::collections::BTreeSet<_> = all_traits + .iter() + .filter(|trait_def_id| **trait_def_id != trait_ref.def_id()) + .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path) + .collect(); + for trait_with_same_path in traits_with_same_path { + if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) { + let impl_span = self.tcx.def_span(impl_def_id); + err.span_help(impl_span, "trait impl with same name found"); + let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); + let crate_msg = format!( + "perhaps two different versions of crate `{}` are being used?", + trait_crate + ); + err.note(&crate_msg); + } + } + } + + fn mk_obligation_for_def_id( + &self, + def_id: DefId, + output_ty: Ty<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> PredicateObligation<'tcx> { + let new_trait_ref = + ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) }; + Obligation::new(cause, param_env, new_trait_ref.without_const().to_predicate()) + } + + fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + body_id: Option, + ) { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + let span = obligation.cause.span; + + debug!( + "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})", + predicate, obligation, body_id, obligation.cause.code, + ); + + // Ambiguity errors are often caused as fallout from earlier + // errors. So just ignore them if this infcx is tainted. + if self.is_tainted_by_errors() { + return; + } + + let mut err = match predicate { + ty::Predicate::Trait(ref data, _) => { + let trait_ref = data.to_poly_trait_ref(); + let self_ty = trait_ref.self_ty(); + debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); + + if predicate.references_error() { + return; + } + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. + + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and don't add the obligations to + // begin with in those cases. + if self + .tcx + .lang_items() + .sized_trait() + .map_or(false, |sized_id| sized_id == trait_ref.def_id()) + { + self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit(); + return; + } + let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283); + err.note(&format!("cannot resolve `{}`", predicate)); + if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + } else if let ( + Ok(ref snippet), + ObligationCauseCode::BindingObligation(ref def_id, _), + ) = + (self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code) + { + let generics = self.tcx.generics_of(*def_id); + if !generics.params.is_empty() && !snippet.ends_with('>') { + // FIXME: To avoid spurious suggestions in functions where type arguments + // where already supplied, we check the snippet to make sure it doesn't + // end with a turbofish. Ideally we would have access to a `PathSegment` + // instead. Otherwise we would produce the following output: + // + // error[E0283]: type annotations needed + // --> $DIR/issue-54954.rs:3:24 + // | + // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); + // | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + // | | + // | cannot infer type + // | help: consider specifying the type argument + // | in the function call: + // | `Tt::const_val::<[i8; 123]>::` + // ... + // LL | const fn const_val() -> usize { + // | --------- - required by this bound in `Tt::const_val` + // | + // = note: cannot resolve `_: Tt` + + err.span_suggestion( + span, + &format!( + "consider specifying the type argument{} in the function call", + if generics.params.len() > 1 { "s" } else { "" }, + ), + format!( + "{}::<{}>", + snippet, + generics + .params + .iter() + .map(|p| p.name.to_string()) + .collect::>() + .join(", ") + ), + Applicability::HasPlaceholders, + ); + } + } + err + } + + ty::Predicate::WellFormed(ty) => { + // Same hacky approach as above to avoid deluging user + // with error messages. + if ty.references_error() || self.tcx.sess.has_errors() { + return; + } + self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) + } + + ty::Predicate::Subtype(ref data) => { + if data.references_error() || self.tcx.sess.has_errors() { + // no need to overload user in such cases + return; + } + let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); + self.need_type_info_err(body_id, span, a, ErrorCode::E0282) + } + ty::Predicate::Projection(ref data) => { + let trait_ref = data.to_poly_trait_ref(self.tcx); + let self_ty = trait_ref.self_ty(); + if predicate.references_error() { + return; + } + let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); + err.note(&format!("cannot resolve `{}`", predicate)); + err + } + + _ => { + if self.tcx.sess.has_errors() { + return; + } + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot resolve `{}`", + predicate, + ); + err.span_label(span, &format!("cannot resolve `{}`", predicate)); + err + } + }; + self.note_obligation_cause(&mut err, obligation); + err.emit(); + } + + /// Returns `true` if the trait predicate may apply for *some* assignment + /// to the type parameters. + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitRef<'tcx>, + ) -> bool { + struct ParamToVarFolder<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + var_map: FxHashMap, Ty<'tcx>>, + } + + impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Param(ty::ParamTy { name, .. }) = ty.kind { + let infcx = self.infcx; + self.var_map.entry(ty).or_insert_with(|| { + infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), + span: DUMMY_SP, + }) + }) + } else { + ty.super_fold_with(self) + } + } + } + + self.probe(|_| { + let mut selcx = SelectionContext::new(self); + + let cleaned_pred = + pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); + + let cleaned_pred = super::project::normalize( + &mut selcx, + param_env, + ObligationCause::dummy(), + &cleaned_pred, + ) + .value; + + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + cleaned_pred.without_const().to_predicate(), + ); + + self.predicate_may_hold(&obligation) + }) + } + + fn note_obligation_cause( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ) { + // First, attempt to add note to this error with an async-await-specific + // message, and fall back to regular note otherwise. + if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { + self.note_obligation_cause_code( + err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); + self.suggest_unsized_bound_if_applicable(err, obligation); + } + } + + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ) { + if let ( + ty::Predicate::Trait(pred, _), + ObligationCauseCode::BindingObligation(item_def_id, span), + ) = (&obligation.predicate, &obligation.cause.code) + { + if let (Some(generics), true) = ( + self.tcx.hir().get_if_local(*item_def_id).as_ref().and_then(|n| n.generics()), + Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), + ) { + for param in generics.params { + if param.span == *span + && !param.bounds.iter().any(|bound| { + bound.trait_def_id() == self.tcx.lang_items().sized_trait() + }) + { + let (span, separator) = match param.bounds { + [] => (span.shrink_to_hi(), ":"), + [.., bound] => (bound.span().shrink_to_hi(), " + "), + }; + err.span_suggestion( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{} ?Sized", separator), + Applicability::MachineApplicable, + ); + return; + } + } + } + } + } + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<&ty::TyS<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool { + if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + + if obligated_types.iter().any(|ot| ot == &parent_trait_ref.skip_binder().self_ty()) { + return true; + } + } + false + } +} + +pub fn recursive_type_with_infinite_size_error( + tcx: TyCtxt<'tcx>, + type_def_id: DefId, +) -> DiagnosticBuilder<'tcx> { + assert!(type_def_id.is_local()); + let span = tcx.hir().span_if_local(type_def_id).unwrap(); + let span = tcx.sess.source_map().def_span(span); + let mut err = struct_span_err!( + tcx.sess, + span, + E0072, + "recursive type `{}` has infinite size", + tcx.def_path_str(type_def_id) + ); + err.span_label(span, "recursive type has infinite size"); + err.help(&format!( + "insert indirection (e.g., a `Box`, `Rc`, or `&`) \ + at some point to make `{}` representable", + tcx.def_path_str(type_def_id) + )); + err +} + +/// Summarizes information +#[derive(Clone)] +pub enum ArgKind { + /// An argument of non-tuple type. Parameters are (name, ty) + Arg(String, String), + + /// An argument of tuple type. For a "found" argument, the span is + /// the locationo in the source of the pattern. For a "expected" + /// argument, it will be None. The vector is a list of (name, ty) + /// strings for the components of the tuple. + Tuple(Option, Vec<(String, String)>), +} + +impl ArgKind { + fn empty() -> ArgKind { + ArgKind::Arg("_".to_owned(), "_".to_owned()) + } + + /// Creates an `ArgKind` from the expected type of an + /// argument. It has no name (`_`) and an optional source span. + pub fn from_expected_ty(t: Ty<'_>, span: Option) -> ArgKind { + match t.kind { + ty::Tuple(ref tys) => ArgKind::Tuple( + span, + tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::>(), + ), + _ => ArgKind::Arg("_".to_owned(), t.to_string()), + } + } +} + +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_param( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut DiagnosticBuilder<'_>, + param_name: &str, + constraint: &str, + source_map: &SourceMap, + span: Span, + def_id: Option, +) -> bool { + const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound with"; + const MSG_RESTRICT_TYPE: &str = "consider restricting this type parameter with"; + const MSG_RESTRICT_TYPE_FURTHER: &str = "consider further restricting this type parameter with"; + + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + + let param = if let Some(param) = param { + param + } else { + return false; + }; + + if def_id == tcx.lang_items().sized_trait() { + // Type parameters are already `Sized` by default. + err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); + return true; + } + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + err.span_help(param.span, &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint)); + + err.tool_only_span_suggestion( + param.span, + MSG_RESTRICT_BOUND_FURTHER, + format!("{} + {}", param_name, constraint), + Applicability::MachineApplicable, + ); + + return true; + } + + if generics.where_clause.predicates.is_empty() { + if let Some(bounds_span) = param.bounds_span() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + + err.span_help( + bounds_span, + &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint), + ); + + let span_hi = param.span.with_hi(span.hi()); + let span_with_colon = source_map.span_through_char(span_hi, ':'); + + if span_hi != param.span && span_with_colon != span_hi { + err.tool_only_span_suggestion( + span_with_colon, + MSG_RESTRICT_BOUND_FURTHER, + format!("{}: {} + ", param_name, constraint), + Applicability::MachineApplicable, + ); + } + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + + err.span_help( + param.span, + &format!("{} `{}: {}`", MSG_RESTRICT_TYPE, param_name, constraint), + ); + + err.tool_only_span_suggestion( + param.span, + MSG_RESTRICT_TYPE, + format!("{}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } + + true + } else { + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, bounded_ty, .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } + } + } + } + } + + let where_clause_span = + generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(); + + match ¶m_spans[..] { + &[] => { + err.span_help( + param.span, + &format!("{} `where {}: {}`", MSG_RESTRICT_TYPE, param_name, constraint), + ); + + err.tool_only_span_suggestion( + where_clause_span, + MSG_RESTRICT_TYPE, + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } + + &[¶m_span] => { + err.span_help( + param_span, + &format!("{} `+ {}`", MSG_RESTRICT_BOUND_FURTHER, constraint), + ); + + let span_hi = param_span.with_hi(span.hi()); + let span_with_colon = source_map.span_through_char(span_hi, ':'); + + if span_hi != param_span && span_with_colon != span_hi { + err.tool_only_span_suggestion( + span_with_colon, + MSG_RESTRICT_BOUND_FURTHER, + format!("{}: {} +", param_name, constraint), + Applicability::MachineApplicable, + ); + } + } + + _ => { + err.span_help( + param.span, + &format!( + "{} `where {}: {}`", + MSG_RESTRICT_TYPE_FURTHER, param_name, constraint, + ), + ); + + err.tool_only_span_suggestion( + where_clause_span, + MSG_RESTRICT_BOUND_FURTHER, + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } + } + + true + } +} diff --git a/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs b/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs new file mode 100644 index 00000000000..3d0dd73f03c --- /dev/null +++ b/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs @@ -0,0 +1,243 @@ +use super::{ + ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, +}; +use crate::infer::InferCtxt; +use rustc::ty::subst::Subst; +use rustc::ty::{self, GenericParamDefKind}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::sym; + +use super::InferCtxtPrivExt; + +crate trait InferCtxtExt<'tcx> { + /*private*/ + fn impl_similar_to( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> Option; + + /*private*/ + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>; + + fn on_unimplemented_note( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> OnUnimplementedNote; +} + +impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + fn impl_similar_to( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> Option { + let tcx = self.tcx; + let param_env = obligation.param_env; + let trait_ref = tcx.erase_late_bound_regions(&trait_ref); + let trait_self_ty = trait_ref.self_ty(); + + let mut self_match_impls = vec![]; + let mut fuzzy_match_impls = vec![]; + + self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { + let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); + let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); + + let impl_self_ty = impl_trait_ref.self_ty(); + + if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { + self_match_impls.push(def_id); + + if trait_ref + .substs + .types() + .skip(1) + .zip(impl_trait_ref.substs.types().skip(1)) + .all(|(u, v)| self.fuzzy_match_tys(u, v)) + { + fuzzy_match_impls.push(def_id); + } + } + }); + + let impl_def_id = if self_match_impls.len() == 1 { + self_match_impls[0] + } else if fuzzy_match_impls.len() == 1 { + fuzzy_match_impls[0] + } else { + return None; + }; + + tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) + } + + /// Used to set on_unimplemented's `ItemContext` + /// to be the enclosing (async) block/function/closure + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { + let hir = &self.tcx.hir(); + let node = hir.find(hir_id)?; + match &node { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { + self.describe_generator(*body_id).or_else(|| { + Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { + "an async function" + } else { + "a function" + }) + }) + } + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)), + .. + }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")), + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, body_id), + .. + }) => self.describe_generator(*body_id).or_else(|| { + Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { + "an async method" + } else { + "a method" + }) + }), + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), + .. + }) => self.describe_generator(*body_id).or_else(|| { + Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) + }), + hir::Node::Expr(hir::Expr { .. }) => { + let parent_hid = hir.get_parent_node(hir_id); + if parent_hid != hir_id { + return self.describe_enclosure(parent_hid); + } else { + None + } + } + _ => None, + } + } + + fn on_unimplemented_note( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> OnUnimplementedNote { + let def_id = + self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); + let trait_ref = *trait_ref.skip_binder(); + + let mut flags = vec![]; + flags.push(( + sym::item_context, + self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), + )); + + match obligation.cause.code { + ObligationCauseCode::BuiltinDerivedObligation(..) + | ObligationCauseCode::ImplDerivedObligation(..) => {} + _ => { + // this is a "direct", user-specified, rather than derived, + // obligation. + flags.push((sym::direct, None)); + } + } + + if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code { + // FIXME: maybe also have some way of handling methods + // from other traits? That would require name resolution, + // which we might want to be some sort of hygienic. + // + // Currently I'm leaving it for what I need for `try`. + if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { + let method = self.tcx.item_name(item); + flags.push((sym::from_method, None)); + flags.push((sym::from_method, Some(method.to_string()))); + } + } + if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { + flags.push((sym::parent_trait, Some(t))); + } + + if let Some(k) = obligation.cause.span.desugaring_kind() { + flags.push((sym::from_desugaring, None)); + flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); + } + let generics = self.tcx.generics_of(def_id); + let self_ty = trait_ref.self_ty(); + // This is also included through the generics list as `Self`, + // but the parser won't allow you to use it + flags.push((sym::_Self, Some(self_ty.to_string()))); + if let Some(def) = self_ty.ty_adt_def() { + // We also want to be able to select self's original + // signature with no type arguments resolved + flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); + } + + for param in generics.params.iter() { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => continue, + }; + let name = param.name; + flags.push((name, Some(value))); + } + + if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { + flags.push((sym::crate_local, None)); + } + + // Allow targeting all integers using `{integral}`, even if the exact type was resolved + if self_ty.is_integral() { + flags.push((sym::_Self, Some("{integral}".to_owned()))); + } + + if let ty::Array(aty, len) = self_ty.kind { + flags.push((sym::_Self, Some("[]".to_owned()))); + flags.push((sym::_Self, Some(format!("[{}]", aty)))); + if let Some(def) = aty.ty_adt_def() { + // We also want to be able to select the array's type's original + // signature with no type arguments resolved + flags.push(( + sym::_Self, + Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), + )); + let tcx = self.tcx; + if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { + flags.push(( + sym::_Self, + Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), + )); + } else { + flags.push(( + sym::_Self, + Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), + )); + } + } + } + if let ty::Dynamic(traits, _) = self_ty.kind { + for t in *traits.skip_binder() { + match t { + ty::ExistentialPredicate::Trait(trait_ref) => { + flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) + } + _ => {} + } + } + } + + if let Ok(Some(command)) = + OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) + { + command.evaluate(self.tcx, trait_ref, &flags[..]) + } else { + OnUnimplementedNote::default() + } + } +} diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs new file mode 100644 index 00000000000..0523a201986 --- /dev/null +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -0,0 +1,1617 @@ +use super::{ + EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, +}; + +use crate::infer::InferCtxt; +use crate::traits::error_reporting::suggest_constraining_type_param; + +use rustc::ty::TypeckTables; +use rustc::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; +use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style}; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_hir::Node; +use rustc_span::symbol::{kw, sym}; +use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use std::fmt; + +use super::InferCtxtPrivExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; + +crate trait InferCtxtExt<'tcx> { + fn suggest_restricting_param_bound( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'_>, + body_id: hir::HirId, + ); + + fn suggest_borrow_on_unsized_slice( + &self, + code: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + ); + + fn get_closure_name( + &self, + def_id: DefId, + err: &mut DiagnosticBuilder<'_>, + msg: &str, + ) -> Option; + + fn suggest_fn_call( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::Binder>, + points_at_arg: bool, + ); + + fn suggest_add_reference_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder>, + points_at_arg: bool, + has_custom_message: bool, + ) -> bool; + + fn suggest_remove_reference( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder>, + ); + + fn suggest_change_mut( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder>, + points_at_arg: bool, + ); + + fn suggest_semicolon_removal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + span: Span, + trait_ref: &ty::Binder>, + ); + + fn suggest_impl_trait( + &self, + err: &mut DiagnosticBuilder<'tcx>, + span: Span, + obligation: &PredicateObligation<'tcx>, + trait_ref: &ty::Binder>, + ) -> bool; + + fn point_at_returns_when_relevant( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ); + + fn report_closure_arg_mismatch( + &self, + span: Span, + found_span: Option, + expected_ref: ty::PolyTraitRef<'tcx>, + found: ty::PolyTraitRef<'tcx>, + ) -> DiagnosticBuilder<'tcx>; + + fn suggest_fully_qualified_path( + &self, + err: &mut DiagnosticBuilder<'_>, + def_id: DefId, + span: Span, + trait_ref: DefId, + ); + + fn maybe_note_obligation_cause_for_async_await( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ) -> bool; + + fn note_obligation_cause_for_async_await( + &self, + err: &mut DiagnosticBuilder<'_>, + target_span: Span, + scope_span: &Option, + expr: Option, + snippet: String, + first_generator: DefId, + last_generator: Option, + trait_ref: ty::TraitRef<'_>, + target_ty: Ty<'tcx>, + tables: &ty::TypeckTables<'_>, + obligation: &PredicateObligation<'tcx>, + next_code: Option<&ObligationCauseCode<'tcx>>, + ); + + fn note_obligation_cause_code( + &self, + err: &mut DiagnosticBuilder<'_>, + predicate: &T, + cause_code: &ObligationCauseCode<'tcx>, + obligated_types: &mut Vec<&ty::TyS<'tcx>>, + ) where + T: fmt::Display; + + fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>); +} + +impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + fn suggest_restricting_param_bound( + &self, + mut err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'_>, + body_id: hir::HirId, + ) { + let self_ty = trait_ref.self_ty(); + let (param_ty, projection) = match &self_ty.kind { + ty::Param(_) => (true, None), + ty::Projection(projection) => (false, Some(projection)), + _ => return, + }; + + let suggest_restriction = + |generics: &hir::Generics<'_>, msg, err: &mut DiagnosticBuilder<'_>| { + let span = generics.where_clause.span_for_predicates_or_empty_place(); + if !span.from_expansion() && span.desugaring_kind().is_none() { + err.span_suggestion( + generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(), + &format!("consider further restricting {}", msg), + format!( + "{} {} ", + if !generics.where_clause.predicates.is_empty() { + "," + } else { + " where" + }, + trait_ref.without_const().to_predicate(), + ), + Applicability::MachineApplicable, + ); + } + }; + + // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we + // don't suggest `T: Sized + ?Sized`. + let mut hir_id = body_id; + while let Some(node) = self.tcx.hir().find(hir_id) { + match node { + hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(..), + .. + }) if param_ty && self_ty == self.tcx.types.self_param => { + // Restricting `Self` for a single method. + suggest_restriction(&generics, "`Self`", err); + return; + } + + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) + | hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(..), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + generics, + kind: hir::ImplItemKind::Fn(..), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, generics, _, _), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { generics, .. }, .. + }) if projection.is_some() => { + // Missing associated type bound. + suggest_restriction(&generics, "the associated type", err); + return; + } + + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, generics), + span, + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Enum(_, generics), span, .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Union(_, generics), + span, + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, generics, ..), + span, + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { generics, .. }, + span, + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(_, generics, _), + span, + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::TyAlias(_, generics), + span, + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::TraitAlias(generics, _), + span, + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), + span, + .. + }) + | hir::Node::TraitItem(hir::TraitItem { generics, span, .. }) + | hir::Node::ImplItem(hir::ImplItem { generics, span, .. }) + if param_ty => + { + // Missing generic type parameter bound. + let param_name = self_ty.to_string(); + let constraint = trait_ref.print_only_trait_path().to_string(); + if suggest_constraining_type_param( + self.tcx, + generics, + &mut err, + ¶m_name, + &constraint, + self.tcx.sess.source_map(), + *span, + Some(trait_ref.def_id()), + ) { + return; + } + } + + hir::Node::Crate(..) => return, + + _ => {} + } + + hir_id = self.tcx.hir().get_parent_item(hir_id); + } + } + + /// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a + /// suggestion to borrow the initializer in order to use have a slice instead. + fn suggest_borrow_on_unsized_slice( + &self, + code: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + ) { + if let &ObligationCauseCode::VariableType(hir_id) = code { + let parent_node = self.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(ref local)) = self.tcx.hir().find(parent_node) { + if let Some(ref expr) = local.init { + if let hir::ExprKind::Index(_, _) = expr.kind { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(expr.span) { + err.span_suggestion( + expr.span, + "consider borrowing here", + format!("&{}", snippet), + Applicability::MachineApplicable, + ); + } + } + } + } + } + } + + /// Given a closure's `DefId`, return the given name of the closure. + /// + /// This doesn't account for reassignments, but it's only used for suggestions. + fn get_closure_name( + &self, + def_id: DefId, + err: &mut DiagnosticBuilder<'_>, + msg: &str, + ) -> Option { + let get_name = + |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind<'_>| -> Option { + // Get the local name of this closure. This can be inaccurate because + // of the possibility of reassignment, but this should be good enough. + match &kind { + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { + Some(format!("{}", name)) + } + _ => { + err.note(&msg); + None + } + } + }; + + let hir = self.tcx.hir(); + let hir_id = hir.as_local_hir_id(def_id)?; + let parent_node = hir.get_parent_node(hir_id); + match hir.find(parent_node) { + Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { + get_name(err, &local.pat.kind) + } + // Different to previous arm because one is `&hir::Local` and the other + // is `P`. + Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind), + _ => return None, + } + } + + /// We tried to apply the bound to an `fn` or closure. Check whether calling it would + /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling + /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. + fn suggest_fn_call( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::Binder>, + points_at_arg: bool, + ) { + let self_ty = trait_ref.self_ty(); + let (def_id, output_ty, callable) = match self_ty.kind { + ty::Closure(def_id, substs) => { + (def_id, self.closure_sig(def_id, substs).output(), "closure") + } + ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), + _ => return, + }; + let msg = format!("use parentheses to call the {}", callable); + + let obligation = self.mk_obligation_for_def_id( + trait_ref.def_id(), + output_ty.skip_binder(), + obligation.cause.clone(), + obligation.param_env, + ); + + match self.evaluate_obligation(&obligation) { + Ok(EvaluationResult::EvaluatedToOk) + | Ok(EvaluationResult::EvaluatedToOkModuloRegions) + | Ok(EvaluationResult::EvaluatedToAmbig) => {} + _ => return, + } + let hir = self.tcx.hir(); + // Get the name of the callable and the arguments to be used in the suggestion. + let snippet = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_, decl, _, span, ..), + .. + })) => { + err.span_label(*span, "consider calling this closure"); + let name = match self.get_closure_name(def_id, err, &msg) { + Some(name) => name, + None => return, + }; + let args = decl.inputs.iter().map(|_| "_").collect::>().join(", "); + format!("{}({})", name, args) + } + Some(hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Fn(.., body_id), + .. + })) => { + err.span_label(ident.span, "consider calling this function"); + let body = hir.body(*body_id); + let args = body + .params + .iter() + .map(|arg| match &arg.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + // FIXME: provide a better suggestion when encountering `SelfLower`, it + // should suggest a method call. + if ident.name != kw::SelfLower => ident.to_string(), + _ => "_".to_string(), + }) + .collect::>() + .join(", "); + format!("{}({})", ident, args) + } + _ => return, + }; + if points_at_arg { + // When the obligation error has been ensured to have been caused by + // an argument, the `obligation.cause.span` points at the expression + // of the argument, so we can provide a suggestion. This is signaled + // by `points_at_arg`. Otherwise, we give a more general note. + err.span_suggestion( + obligation.cause.span, + &msg, + snippet, + Applicability::HasPlaceholders, + ); + } else { + err.help(&format!("{}: `{}`", msg, snippet)); + } + } + + fn suggest_add_reference_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder>, + points_at_arg: bool, + has_custom_message: bool, + ) -> bool { + if !points_at_arg { + return false; + } + + let span = obligation.cause.span; + let param_env = obligation.param_env; + let trait_ref = trait_ref.skip_binder(); + + if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code { + // Try to apply the original trait binding obligation by borrowing. + let self_ty = trait_ref.self_ty(); + let found = self_ty.to_string(); + let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty); + let substs = self.tcx.mk_substs_trait(new_self_ty, &[]); + let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs); + let new_obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + new_trait_ref.without_const().to_predicate(), + ); + if self.predicate_must_hold_modulo_regions(&new_obligation) { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + // We have a very specific type of error, where just borrowing this argument + // might solve the problem. In cases like this, the important part is the + // original type obligation, not the last one that failed, which is arbitrary. + // Because of this, we modify the error to refer to the original obligation and + // return early in the caller. + let msg = format!( + "the trait bound `{}: {}` is not satisfied", + found, + obligation.parent_trait_ref.skip_binder().print_only_trait_path(), + ); + if has_custom_message { + err.note(&msg); + } else { + err.message = vec![(msg, Style::NoStyle)]; + } + if snippet.starts_with('&') { + // This is already a literal borrow and the obligation is failing + // somewhere else in the obligation chain. Do not suggest non-sense. + return false; + } + err.span_label( + span, + &format!( + "expected an implementor of trait `{}`", + obligation.parent_trait_ref.skip_binder().print_only_trait_path(), + ), + ); + err.span_suggestion( + span, + "consider borrowing here", + format!("&{}", snippet), + Applicability::MaybeIncorrect, + ); + return true; + } + } + } + false + } + + /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, + /// suggest removing these references until we reach a type that implements the trait. + fn suggest_remove_reference( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder>, + ) { + let trait_ref = trait_ref.skip_binder(); + let span = obligation.cause.span; + + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let refs_number = + snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count(); + if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) { + // Do not suggest removal of borrow from type arguments. + return; + } + + let mut trait_type = trait_ref.self_ty(); + + for refs_remaining in 0..refs_number { + if let ty::Ref(_, t_type, _) = trait_type.kind { + trait_type = t_type; + + let new_obligation = self.mk_obligation_for_def_id( + trait_ref.def_id, + trait_type, + ObligationCause::dummy(), + obligation.param_env, + ); + + if self.predicate_may_hold(&new_obligation) { + let sp = self + .tcx + .sess + .source_map() + .span_take_while(span, |c| c.is_whitespace() || *c == '&'); + + let remove_refs = refs_remaining + 1; + + let msg = if remove_refs == 1 { + "consider removing the leading `&`-reference".to_string() + } else { + format!("consider removing {} leading `&`-references", remove_refs) + }; + + err.span_suggestion_short( + sp, + &msg, + String::new(), + Applicability::MachineApplicable, + ); + break; + } + } else { + break; + } + } + } + } + + /// Check if the trait bound is implemented for a different mutability and note it in the + /// final error. + fn suggest_change_mut( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder>, + points_at_arg: bool, + ) { + let span = obligation.cause.span; + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let refs_number = + snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count(); + if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) { + // Do not suggest removal of borrow from type arguments. + return; + } + let trait_ref = self.resolve_vars_if_possible(trait_ref); + if trait_ref.has_infer_types_or_consts() { + // Do not ICE while trying to find if a reborrow would succeed on a trait with + // unresolved bindings. + return; + } + + if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { + let trait_type = match mutability { + hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type), + hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type), + }; + + let new_obligation = self.mk_obligation_for_def_id( + trait_ref.skip_binder().def_id, + trait_type, + ObligationCause::dummy(), + obligation.param_env, + ); + + if self.evaluate_obligation_no_overflow(&new_obligation).must_apply_modulo_regions() + { + let sp = self + .tcx + .sess + .source_map() + .span_take_while(span, |c| c.is_whitespace() || *c == '&'); + if points_at_arg && mutability == hir::Mutability::Not && refs_number > 0 { + err.span_suggestion( + sp, + "consider changing this borrow's mutability", + "&mut ".to_string(), + Applicability::MachineApplicable, + ); + } else { + err.note(&format!( + "`{}` is implemented for `{:?}`, but not for `{:?}`", + trait_ref.print_only_trait_path(), + trait_type, + trait_ref.skip_binder().self_ty(), + )); + } + } + } + } + } + + fn suggest_semicolon_removal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + span: Span, + trait_ref: &ty::Binder>, + ) { + let hir = self.tcx.hir(); + let parent_node = hir.get_parent_node(obligation.cause.body_id); + let node = hir.find(parent_node); + if let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(sig, _, body_id), .. + })) = node + { + let body = hir.body(*body_id); + if let hir::ExprKind::Block(blk, _) = &body.value.kind { + if sig.decl.output.span().overlaps(span) + && blk.expr.is_none() + && "()" == &trait_ref.self_ty().to_string() + { + // FIXME(estebank): When encountering a method with a trait + // bound not satisfied in the return type with a body that has + // no return, suggest removal of semicolon on last statement. + // Once that is added, close #54771. + if let Some(ref stmt) = blk.stmts.last() { + let sp = self.tcx.sess.source_map().end_point(stmt.span); + err.span_label(sp, "consider removing this semicolon"); + } + } + } + } + } + + /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if + /// applicable and signal that the error has been expanded appropriately and needs to be + /// emitted. + fn suggest_impl_trait( + &self, + err: &mut DiagnosticBuilder<'tcx>, + span: Span, + obligation: &PredicateObligation<'tcx>, + trait_ref: &ty::Binder>, + ) -> bool { + match obligation.cause.code.peel_derives() { + // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`. + ObligationCauseCode::SizedReturnType => {} + _ => return false, + } + + let hir = self.tcx.hir(); + let parent_node = hir.get_parent_node(obligation.cause.body_id); + let node = hir.find(parent_node); + let (sig, body_id) = if let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(sig, _, body_id), + .. + })) = node + { + (sig, body_id) + } else { + return false; + }; + let body = hir.body(*body_id); + let trait_ref = self.resolve_vars_if_possible(trait_ref); + let ty = trait_ref.skip_binder().self_ty(); + let is_object_safe = match ty.kind { + ty::Dynamic(predicates, _) => { + // If the `dyn Trait` is not object safe, do not suggest `Box`. + predicates + .principal_def_id() + .map_or(true, |def_id| self.tcx.object_safety_violations(def_id).is_empty()) + } + // We only want to suggest `impl Trait` to `dyn Trait`s. + // For example, `fn foo() -> str` needs to be filtered out. + _ => return false, + }; + + let ret_ty = if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { + ret_ty + } else { + return false; + }; + + // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for + // cases like `fn foo() -> (dyn Trait, i32) {}`. + // Recursively look for `TraitObject` types and if there's only one, use that span to + // suggest `impl Trait`. + + // Visit to make sure there's a single `return` type to suggest `impl Trait`, + // otherwise suggest using `Box` or an enum. + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(&body); + + let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); + + let mut ret_types = visitor + .returns + .iter() + .filter_map(|expr| tables.node_type_opt(expr.hir_id)) + .map(|ty| self.resolve_vars_if_possible(&ty)); + let (last_ty, all_returns_have_same_type) = ret_types.clone().fold( + (None, true), + |(last_ty, mut same): (std::option::Option>, bool), ty| { + let ty = self.resolve_vars_if_possible(&ty); + same &= last_ty.map_or(true, |last_ty| last_ty == ty) && ty.kind != ty::Error; + (Some(ty), same) + }, + ); + let all_returns_conform_to_trait = + if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { + match ty_ret_ty.kind { + ty::Dynamic(predicates, _) => { + let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); + let param_env = ty::ParamEnv::empty(); + ret_types.all(|returned_ty| { + predicates.iter().all(|predicate| { + let pred = predicate.with_self_ty(self.tcx, returned_ty); + let obl = Obligation::new(cause.clone(), param_env, pred); + self.predicate_may_hold(&obl) + }) + }) + } + _ => false, + } + } else { + true + }; + + let (snippet, last_ty) = + if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( + // Verify that we're dealing with a return `dyn Trait` + ret_ty.span.overlaps(span), + &ret_ty.kind, + self.tcx.sess.source_map().span_to_snippet(ret_ty.span), + // If any of the return types does not conform to the trait, then we can't + // suggest `impl Trait` nor trait objects, it is a type mismatch error. + all_returns_conform_to_trait, + last_ty, + ) { + (snippet, last_ty) + } else { + return false; + }; + err.code(error_code!(E0746)); + err.set_primary_message("return type cannot have an unboxed trait object"); + err.children.clear(); + let impl_trait_msg = "for information on `impl Trait`, see \ + "; + let trait_obj_msg = "for information on trait objects, see \ + "; + let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); + let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; + if all_returns_have_same_type { + // Suggest `-> impl Trait`. + err.span_suggestion( + ret_ty.span, + &format!( + "return `impl {1}` instead, as all return paths are of type `{}`, \ + which implements `{1}`", + last_ty, trait_obj, + ), + format!("impl {}", trait_obj), + Applicability::MachineApplicable, + ); + err.note(impl_trait_msg); + } else { + if is_object_safe { + // Suggest `-> Box` and `Box::new(returned_value)`. + // Get all the return values and collect their span and suggestion. + let mut suggestions = visitor + .returns + .iter() + .map(|expr| { + ( + expr.span, + format!( + "Box::new({})", + self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap() + ), + ) + }) + .collect::>(); + // Add the suggestion for the return type. + suggestions.push((ret_ty.span, format!("Box", trait_obj))); + err.multipart_suggestion( + "return a boxed trait object instead", + suggestions, + Applicability::MaybeIncorrect, + ); + } else { + // This is currently not possible to trigger because E0038 takes precedence, but + // leave it in for completeness in case anything changes in an earlier stage. + err.note(&format!( + "if trait `{}` was object safe, you could return a trait object", + trait_obj, + )); + } + err.note(trait_obj_msg); + err.note(&format!( + "if all the returned values were of the same type you could use \ + `impl {}` as the return type", + trait_obj, + )); + err.note(impl_trait_msg); + err.note("you can create a new `enum` with a variant for each returned type"); + } + true + } + + fn point_at_returns_when_relevant( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) { + match obligation.cause.code.peel_derives() { + ObligationCauseCode::SizedReturnType => {} + _ => return, + } + + let hir = self.tcx.hir(); + let parent_node = hir.get_parent_node(obligation.cause.body_id); + let node = hir.find(parent_node); + if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) = + node + { + let body = hir.body(*body_id); + // Point at all the `return`s in the function as they have failed trait bounds. + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(&body); + let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); + for expr in &visitor.returns { + if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { + let ty = self.resolve_vars_if_possible(&returned_ty); + err.span_label(expr.span, &format!("this returned value is of type `{}`", ty)); + } + } + } + } + + fn report_closure_arg_mismatch( + &self, + span: Span, + found_span: Option, + expected_ref: ty::PolyTraitRef<'tcx>, + found: ty::PolyTraitRef<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + crate fn build_fn_sig_string<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: &ty::TraitRef<'tcx>, + ) -> String { + let inputs = trait_ref.substs.type_at(1); + let sig = if let ty::Tuple(inputs) = inputs.kind { + tcx.mk_fn_sig( + inputs.iter().map(|k| k.expect_ty()), + tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), + false, + hir::Unsafety::Normal, + ::rustc_target::spec::abi::Abi::Rust, + ) + } else { + tcx.mk_fn_sig( + ::std::iter::once(inputs), + tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), + false, + hir::Unsafety::Normal, + ::rustc_target::spec::abi::Abi::Rust, + ) + }; + ty::Binder::bind(sig).to_string() + } + + let argument_is_closure = expected_ref.skip_binder().substs.type_at(0).is_closure(); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0631, + "type mismatch in {} arguments", + if argument_is_closure { "closure" } else { "function" } + ); + + let found_str = format!( + "expected signature of `{}`", + build_fn_sig_string(self.tcx, found.skip_binder()) + ); + err.span_label(span, found_str); + + let found_span = found_span.unwrap_or(span); + let expected_str = format!( + "found signature of `{}`", + build_fn_sig_string(self.tcx, expected_ref.skip_binder()) + ); + err.span_label(found_span, expected_str); + + err + } + + fn suggest_fully_qualified_path( + &self, + err: &mut DiagnosticBuilder<'_>, + def_id: DefId, + span: Span, + trait_ref: DefId, + ) { + if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) { + if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind { + err.note(&format!( + "{}s cannot be accessed directly on a `trait`, they can only be \ + accessed through a specific `impl`", + assoc_item.kind.suggestion_descr(), + )); + err.span_suggestion( + span, + "use the fully qualified path to an implementation", + format!("::{}", self.tcx.def_path_str(trait_ref), assoc_item.ident), + Applicability::HasPlaceholders, + ); + } + } + } + + /// Adds an async-await specific note to the diagnostic when the future does not implement + /// an auto trait because of a captured type. + /// + /// ```ignore (diagnostic) + /// note: future does not implement `Qux` as this value is used across an await + /// --> $DIR/issue-64130-3-other.rs:17:5 + /// | + /// LL | let x = Foo; + /// | - has type `Foo` + /// LL | baz().await; + /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later + /// LL | } + /// | - `x` is later dropped here + /// ``` + /// + /// When the diagnostic does not implement `Send` or `Sync` specifically, then the diagnostic + /// is "replaced" with a different message and a more specific error. + /// + /// ```ignore (diagnostic) + /// error: future cannot be sent between threads safely + /// --> $DIR/issue-64130-2-send.rs:21:5 + /// | + /// LL | fn is_send(t: T) { } + /// | ------- ---- required by this bound in `is_send` + /// ... + /// LL | is_send(bar()); + /// | ^^^^^^^ future returned by `bar` is not send + /// | + /// = help: within `impl std::future::Future`, the trait `std::marker::Send` is not + /// implemented for `Foo` + /// note: future is not send as this value is used across an await + /// --> $DIR/issue-64130-2-send.rs:15:5 + /// | + /// LL | let x = Foo; + /// | - has type `Foo` + /// LL | baz().await; + /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later + /// LL | } + /// | - `x` is later dropped here + /// ``` + /// + /// Returns `true` if an async-await specific note was added to the diagnostic. + fn maybe_note_obligation_cause_for_async_await( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ) -> bool { + debug!( + "maybe_note_obligation_cause_for_async_await: obligation.predicate={:?} \ + obligation.cause.span={:?}", + obligation.predicate, obligation.cause.span + ); + let source_map = self.tcx.sess.source_map(); + + // Attempt to detect an async-await error by looking at the obligation causes, looking + // for a generator to be present. + // + // When a future does not implement a trait because of a captured type in one of the + // generators somewhere in the call stack, then the result is a chain of obligations. + // + // Given a `async fn` A that calls a `async fn` B which captures a non-send type and that + // future is passed as an argument to a function C which requires a `Send` type, then the + // chain looks something like this: + // + // - `BuiltinDerivedObligation` with a generator witness (B) + // - `BuiltinDerivedObligation` with a generator (B) + // - `BuiltinDerivedObligation` with `std::future::GenFuture` (B) + // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) + // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) + // - `BuiltinDerivedObligation` with a generator witness (A) + // - `BuiltinDerivedObligation` with a generator (A) + // - `BuiltinDerivedObligation` with `std::future::GenFuture` (A) + // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) + // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) + // - `BindingObligation` with `impl_send (Send requirement) + // + // The first obligation in the chain is the most useful and has the generator that captured + // the type. The last generator has information about where the bound was introduced. At + // least one generator should be present for this diagnostic to be modified. + let (mut trait_ref, mut target_ty) = match obligation.predicate { + ty::Predicate::Trait(p, _) => { + (Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty())) + } + _ => (None, None), + }; + let mut generator = None; + let mut last_generator = None; + let mut next_code = Some(&obligation.cause.code); + while let Some(code) = next_code { + debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code); + match code { + ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) + | ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { + let ty = derived_obligation.parent_trait_ref.self_ty(); + debug!( + "maybe_note_obligation_cause_for_async_await: \ + parent_trait_ref={:?} self_ty.kind={:?}", + derived_obligation.parent_trait_ref, ty.kind + ); + + match ty.kind { + ty::Generator(did, ..) => { + generator = generator.or(Some(did)); + last_generator = Some(did); + } + ty::GeneratorWitness(..) => {} + _ if generator.is_none() => { + trait_ref = Some(*derived_obligation.parent_trait_ref.skip_binder()); + target_ty = Some(ty); + } + _ => {} + } + + next_code = Some(derived_obligation.parent_code.as_ref()); + } + _ => break, + } + } + + // Only continue if a generator was found. + debug!( + "maybe_note_obligation_cause_for_async_await: generator={:?} trait_ref={:?} \ + target_ty={:?}", + generator, trait_ref, target_ty + ); + let (generator_did, trait_ref, target_ty) = match (generator, trait_ref, target_ty) { + (Some(generator_did), Some(trait_ref), Some(target_ty)) => { + (generator_did, trait_ref, target_ty) + } + _ => return false, + }; + + let span = self.tcx.def_span(generator_did); + + // Do not ICE on closure typeck (#66868). + if self.tcx.hir().as_local_hir_id(generator_did).is_none() { + return false; + } + + // Get the tables from the infcx if the generator is the function we are + // currently type-checking; otherwise, get them by performing a query. + // This is needed to avoid cycles. + let in_progress_tables = self.in_progress_tables.map(|t| t.borrow()); + let generator_did_root = self.tcx.closure_base_def_id(generator_did); + debug!( + "maybe_note_obligation_cause_for_async_await: generator_did={:?} \ + generator_did_root={:?} in_progress_tables.local_id_root={:?} span={:?}", + generator_did, + generator_did_root, + in_progress_tables.as_ref().map(|t| t.local_id_root), + span + ); + let query_tables; + let tables: &TypeckTables<'tcx> = match &in_progress_tables { + Some(t) if t.local_id_root == Some(generator_did_root) => t, + _ => { + query_tables = self.tcx.typeck_tables_of(generator_did); + &query_tables + } + }; + + // Look for a type inside the generator interior that matches the target type to get + // a span. + let target_ty_erased = self.tcx.erase_regions(&target_ty); + let target_span = tables + .generator_interior_types + .iter() + .find(|ty::GeneratorInteriorTypeCause { ty, .. }| { + // Careful: the regions for types that appear in the + // generator interior are not generally known, so we + // want to erase them when comparing (and anyway, + // `Send` and other bounds are generally unaffected by + // the choice of region). When erasing regions, we + // also have to erase late-bound regions. This is + // because the types that appear in the generator + // interior generally contain "bound regions" to + // represent regions that are part of the suspended + // generator frame. Bound regions are preserved by + // `erase_regions` and so we must also call + // `erase_late_bound_regions`. + let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(*ty)); + let ty_erased = self.tcx.erase_regions(&ty_erased); + let eq = ty::TyS::same_type(ty_erased, target_ty_erased); + debug!( + "maybe_note_obligation_cause_for_async_await: ty_erased={:?} \ + target_ty_erased={:?} eq={:?}", + ty_erased, target_ty_erased, eq + ); + eq + }) + .map(|ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. }| { + (span, source_map.span_to_snippet(*span), scope_span, expr) + }); + + debug!( + "maybe_note_obligation_cause_for_async_await: target_ty={:?} \ + generator_interior_types={:?} target_span={:?}", + target_ty, tables.generator_interior_types, target_span + ); + if let Some((target_span, Ok(snippet), scope_span, expr)) = target_span { + self.note_obligation_cause_for_async_await( + err, + *target_span, + scope_span, + *expr, + snippet, + generator_did, + last_generator, + trait_ref, + target_ty, + tables, + obligation, + next_code, + ); + true + } else { + false + } + } + + /// Unconditionally adds the diagnostic note described in + /// `maybe_note_obligation_cause_for_async_await`'s documentation comment. + fn note_obligation_cause_for_async_await( + &self, + err: &mut DiagnosticBuilder<'_>, + target_span: Span, + scope_span: &Option, + expr: Option, + snippet: String, + first_generator: DefId, + last_generator: Option, + trait_ref: ty::TraitRef<'_>, + target_ty: Ty<'tcx>, + tables: &ty::TypeckTables<'_>, + obligation: &PredicateObligation<'tcx>, + next_code: Option<&ObligationCauseCode<'tcx>>, + ) { + let source_map = self.tcx.sess.source_map(); + + let is_async_fn = self + .tcx + .parent(first_generator) + .map(|parent_did| self.tcx.asyncness(parent_did)) + .map(|parent_asyncness| parent_asyncness == hir::IsAsync::Async) + .unwrap_or(false); + let is_async_move = self + .tcx + .hir() + .as_local_hir_id(first_generator) + .and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id)) + .map(|body_id| self.tcx.hir().body(body_id)) + .and_then(|body| body.generator_kind()) + .map(|generator_kind| match generator_kind { + hir::GeneratorKind::Async(..) => true, + _ => false, + }) + .unwrap_or(false); + let await_or_yield = if is_async_fn || is_async_move { "await" } else { "yield" }; + + // Special case the primary error message when send or sync is the trait that was + // not implemented. + let is_send = self.tcx.is_diagnostic_item(sym::send_trait, trait_ref.def_id); + let is_sync = self.tcx.is_diagnostic_item(sym::sync_trait, trait_ref.def_id); + let hir = self.tcx.hir(); + let trait_explanation = if is_send || is_sync { + let (trait_name, trait_verb) = + if is_send { ("`Send`", "sent") } else { ("`Sync`", "shared") }; + + err.clear_code(); + err.set_primary_message(format!( + "future cannot be {} between threads safely", + trait_verb + )); + + let original_span = err.span.primary_span().unwrap(); + let mut span = MultiSpan::from_span(original_span); + + let message = if let Some(name) = last_generator + .and_then(|generator_did| self.tcx.parent(generator_did)) + .and_then(|parent_did| hir.as_local_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + { + format!("future returned by `{}` is not {}", name, trait_name) + } else { + format!("future is not {}", trait_name) + }; + + span.push_span_label(original_span, message); + err.set_span(span); + + format!("is not {}", trait_name) + } else { + format!("does not implement `{}`", trait_ref.print_only_trait_path()) + }; + + // Look at the last interior type to get a span for the `.await`. + let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap(); + let mut span = MultiSpan::from_span(await_span); + span.push_span_label( + await_span, + format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet), + ); + + span.push_span_label(target_span, format!("has type `{}`", target_ty)); + + // If available, use the scope span to annotate the drop location. + if let Some(scope_span) = scope_span { + span.push_span_label( + source_map.end_point(*scope_span), + format!("`{}` is later dropped here", snippet), + ); + } + + err.span_note( + span, + &format!( + "future {} as this value is used across an {}", + trait_explanation, await_or_yield, + ), + ); + + if let Some(expr_id) = expr { + let expr = hir.expect_expr(expr_id); + debug!("target_ty evaluated from {:?}", expr); + + let parent = hir.get_parent_node(expr_id); + if let Some(hir::Node::Expr(e)) = hir.find(parent) { + let parent_span = hir.span(parent); + let parent_did = parent.owner.to_def_id(); + // ```rust + // impl T { + // fn foo(&self) -> i32 {} + // } + // T.foo(); + // ^^^^^^^ a temporary `&T` created inside this method call due to `&self` + // ``` + // + let is_region_borrow = + tables.expr_adjustments(expr).iter().any(|adj| adj.is_region_borrow()); + + // ```rust + // struct Foo(*const u8); + // bar(Foo(std::ptr::null())).await; + // ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor. + // ``` + debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did)); + let is_raw_borrow_inside_fn_like_call = match self.tcx.def_kind(parent_did) { + Some(DefKind::Fn) | Some(DefKind::Ctor(..)) => target_ty.is_unsafe_ptr(), + _ => false, + }; + + if (tables.is_method_call(e) && is_region_borrow) + || is_raw_borrow_inside_fn_like_call + { + err.span_help( + parent_span, + "consider moving this into a `let` \ + binding to create a shorter lived borrow", + ); + } + } + } + + // Add a note for the item obligation that remains - normally a note pointing to the + // bound that introduced the obligation (e.g. `T: Send`). + debug!("note_obligation_cause_for_async_await: next_code={:?}", next_code); + self.note_obligation_cause_code( + err, + &obligation.predicate, + next_code.unwrap(), + &mut Vec::new(), + ); + } + + fn note_obligation_cause_code( + &self, + err: &mut DiagnosticBuilder<'_>, + predicate: &T, + cause_code: &ObligationCauseCode<'tcx>, + obligated_types: &mut Vec<&ty::TyS<'tcx>>, + ) where + T: fmt::Display, + { + let tcx = self.tcx; + match *cause_code { + ObligationCauseCode::ExprAssignable + | ObligationCauseCode::MatchExpressionArm { .. } + | ObligationCauseCode::Pattern { .. } + | ObligationCauseCode::IfExpression { .. } + | ObligationCauseCode::IfExpressionWithNoElse + | ObligationCauseCode::MainFunctionType + | ObligationCauseCode::StartFunctionType + | ObligationCauseCode::IntrinsicType + | ObligationCauseCode::MethodReceiver + | ObligationCauseCode::ReturnNoExpression + | ObligationCauseCode::MiscObligation => {} + ObligationCauseCode::SliceOrArrayElem => { + err.note("slice and array elements must have `Sized` type"); + } + ObligationCauseCode::TupleElem => { + err.note("only the last element of a tuple may have a dynamically sized type"); + } + ObligationCauseCode::ProjectionWf(data) => { + err.note(&format!("required so that the projection `{}` is well-formed", data,)); + } + ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => { + err.note(&format!( + "required so that reference `{}` does not outlive its referent", + ref_ty, + )); + } + ObligationCauseCode::ObjectTypeBound(object_ty, region) => { + err.note(&format!( + "required so that the lifetime bound of `{}` for `{}` is satisfied", + region, object_ty, + )); + } + ObligationCauseCode::ItemObligation(item_def_id) => { + let item_name = tcx.def_path_str(item_def_id); + let msg = format!("required by `{}`", item_name); + + if let Some(sp) = tcx.hir().span_if_local(item_def_id) { + let sp = tcx.sess.source_map().def_span(sp); + err.span_label(sp, &msg); + } else { + err.note(&msg); + } + } + ObligationCauseCode::BindingObligation(item_def_id, span) => { + let item_name = tcx.def_path_str(item_def_id); + let msg = format!("required by this bound in `{}`", item_name); + if let Some(ident) = tcx.opt_item_name(item_def_id) { + err.span_label(ident.span, ""); + } + if span != DUMMY_SP { + err.span_label(span, &msg); + } else { + err.note(&msg); + } + } + ObligationCauseCode::ObjectCastObligation(object_ty) => { + err.note(&format!( + "required for the cast to the object type `{}`", + self.ty_to_string(object_ty) + )); + } + ObligationCauseCode::Coercion { source: _, target } => { + err.note(&format!("required by cast to type `{}`", self.ty_to_string(target))); + } + ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => { + err.note( + "the `Copy` trait is required because the repeated element will be copied", + ); + if suggest_const_in_array_repeat_expressions { + err.note( + "this array initializer can be evaluated at compile-time, see issue \ + #48147 \ + for more information", + ); + if tcx.sess.opts.unstable_features.is_nightly_build() { + err.help( + "add `#![feature(const_in_array_repeat_expressions)]` to the \ + crate attributes to enable", + ); + } + } + } + ObligationCauseCode::VariableType(_) => { + err.note("all local variables must have a statically known size"); + if !self.tcx.features().unsized_locals { + err.help("unsized locals are gated as an unstable feature"); + } + } + ObligationCauseCode::SizedArgumentType => { + err.note("all function arguments must have a statically known size"); + if !self.tcx.features().unsized_locals { + err.help("unsized locals are gated as an unstable feature"); + } + } + ObligationCauseCode::SizedReturnType => { + err.note("the return type of a function must have a statically known size"); + } + ObligationCauseCode::SizedYieldType => { + err.note("the yield type of a generator must have a statically known size"); + } + ObligationCauseCode::AssignmentLhsSized => { + err.note("the left-hand-side of an assignment must have a statically known size"); + } + ObligationCauseCode::TupleInitializerSized => { + err.note("tuples must have a statically known size to be initialized"); + } + ObligationCauseCode::StructInitializerSized => { + err.note("structs must have a statically known size to be initialized"); + } + ObligationCauseCode::FieldSized { adt_kind: ref item, last } => match *item { + AdtKind::Struct => { + if last { + err.note( + "the last field of a packed struct may only have a \ + dynamically sized type if it does not need drop to be run", + ); + } else { + err.note( + "only the last field of a struct may have a dynamically sized type", + ); + } + } + AdtKind::Union => { + err.note("no field of a union may have a dynamically sized type"); + } + AdtKind::Enum => { + err.note("no field of an enum variant may have a dynamically sized type"); + } + }, + ObligationCauseCode::ConstSized => { + err.note("constant expressions must have a statically known size"); + } + ObligationCauseCode::ConstPatternStructural => { + err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`"); + } + ObligationCauseCode::SharedStatic => { + err.note("shared static variables must have a type that implements `Sync`"); + } + ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + let ty = parent_trait_ref.skip_binder().self_ty(); + err.note(&format!("required because it appears within the type `{}`", ty)); + obligated_types.push(ty); + + let parent_predicate = parent_trait_ref.without_const().to_predicate(); + if !self.is_recursive_obligation(obligated_types, &data.parent_code) { + self.note_obligation_cause_code( + err, + &parent_predicate, + &data.parent_code, + obligated_types, + ); + } + } + ObligationCauseCode::ImplDerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + err.note(&format!( + "required because of the requirements on the impl of `{}` for `{}`", + parent_trait_ref.print_only_trait_path(), + parent_trait_ref.skip_binder().self_ty() + )); + let parent_predicate = parent_trait_ref.without_const().to_predicate(); + self.note_obligation_cause_code( + err, + &parent_predicate, + &data.parent_code, + obligated_types, + ); + } + ObligationCauseCode::CompareImplMethodObligation { .. } => { + err.note(&format!( + "the requirement `{}` appears on the impl method \ + but not on the corresponding trait method", + predicate + )); + } + ObligationCauseCode::CompareImplTypeObligation { .. } => { + err.note(&format!( + "the requirement `{}` appears on the associated impl type \ + but not on the corresponding associated trait type", + predicate + )); + } + ObligationCauseCode::ReturnType + | ObligationCauseCode::ReturnValue(_) + | ObligationCauseCode::BlockTailExpression(_) => (), + ObligationCauseCode::TrivialBound => { + err.help("see issue #48214"); + if tcx.sess.opts.unstable_features.is_nightly_build() { + err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable"); + } + } + ObligationCauseCode::AssocTypeBound(ref data) => { + err.span_label(data.original, "associated type defined here"); + if let Some(sp) = data.impl_span { + err.span_label(sp, "in this `impl` item"); + } + for sp in &data.bounds { + err.span_label(*sp, "restricted in this bound"); + } + } + } + } + + fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) { + let current_limit = self.tcx.sess.recursion_limit.get(); + let suggested_limit = current_limit * 2; + err.help(&format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", + suggested_limit, self.tcx.crate_name, + )); + } +} + +/// Collect all the returned expressions within the input expression. +/// Used to point at the return spans when we want to suggest some change to them. +#[derive(Default)] +struct ReturnsVisitor<'v> { + returns: Vec<&'v hir::Expr<'v>>, + in_block_tail: bool, +} + +impl<'v> Visitor<'v> for ReturnsVisitor<'v> { + type Map = hir::intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + // Visit every expression to detect `return` paths, either through the function's tail + // expression or `return` statements. We walk all nodes to find `return` statements, but + // we only care about tail expressions when `in_block_tail` is `true`, which means that + // they're in the return path of the function body. + match ex.kind { + hir::ExprKind::Ret(Some(ex)) => { + self.returns.push(ex); + } + hir::ExprKind::Block(block, _) if self.in_block_tail => { + self.in_block_tail = false; + for stmt in block.stmts { + hir::intravisit::walk_stmt(self, stmt); + } + self.in_block_tail = true; + if let Some(expr) = block.expr { + self.visit_expr(expr); + } + } + hir::ExprKind::Match(_, arms, _) if self.in_block_tail => { + for arm in arms { + self.visit_expr(arm.body); + } + } + // We need to walk to find `return`s in the entire body. + _ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex), + _ => self.returns.push(ex), + } + } + + fn visit_body(&mut self, body: &'v hir::Body<'v>) { + assert!(!self.in_block_tail); + if body.generator_kind().is_none() { + if let hir::ExprKind::Block(block, None) = body.value.kind { + if block.expr.is_some() { + self.in_block_tail = true; + } + } + } + hir::intravisit::walk_body(self, body); + } +} diff --git a/src/librustc_trait_selection/traits/fulfill.rs b/src/librustc_trait_selection/traits/fulfill.rs new file mode 100644 index 00000000000..5def77ce732 --- /dev/null +++ b/src/librustc_trait_selection/traits/fulfill.rs @@ -0,0 +1,559 @@ +use crate::infer::{InferCtxt, ShallowResolver}; +use rustc::ty::error::ExpectedFound; +use rustc::ty::{self, ToPolyTraitRef, Ty, TypeFoldable}; +use rustc_data_structures::obligation_forest::ProcessResult; +use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; +use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_infer::traits::{TraitEngine, TraitEngineExt as _}; +use std::marker::PhantomData; + +use super::project; +use super::select::SelectionContext; +use super::wf; +use super::CodeAmbiguity; +use super::CodeProjectionError; +use super::CodeSelectionError; +use super::{ConstEvalFailure, Unimplemented}; +use super::{FulfillmentError, FulfillmentErrorCode}; +use super::{ObligationCause, PredicateObligation}; + +use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; + +impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { + /// Note that we include both the `ParamEnv` and the `Predicate`, + /// as the `ParamEnv` can influence whether fulfillment succeeds + /// or fails. + type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>; + + fn as_cache_key(&self) -> Self::CacheKey { + self.obligation.param_env.and(self.obligation.predicate) + } +} + +/// The fulfillment context is used to drive trait resolution. It +/// consists of a list of obligations that must be (eventually) +/// satisfied. The job is to track which are satisfied, which yielded +/// errors, and which are still pending. At any point, users can call +/// `select_where_possible`, and the fulfillment context will try to do +/// selection, retaining only those obligations that remain +/// ambiguous. This may be helpful in pushing type inference +/// along. Once all type inference constraints have been generated, the +/// method `select_all_or_error` can be used to report any remaining +/// ambiguous cases as errors. +pub struct FulfillmentContext<'tcx> { + // A list of all obligations that have been registered with this + // fulfillment context. + predicates: ObligationForest>, + // Should this fulfillment context register type-lives-for-region + // obligations on its parent infcx? In some cases, region + // obligations are either already known to hold (normalization) or + // hopefully verifed elsewhere (type-impls-bound), and therefore + // should not be checked. + // + // Note that if we are normalizing a type that we already + // know is well-formed, there should be no harm setting this + // to true - all the region variables should be determinable + // using the RFC 447 rules, which don't depend on + // type-lives-for-region constraints, and because the type + // is well-formed, the constraints should hold. + register_region_obligations: bool, + // Is it OK to register obligations into this infcx inside + // an infcx snapshot? + // + // The "primary fulfillment" in many cases in typeck lives + // outside of any snapshot, so any use of it inside a snapshot + // will lead to trouble and therefore is checked against, but + // other fulfillment contexts sometimes do live inside of + // a snapshot (they don't *straddle* a snapshot, so there + // is no trouble there). + usable_in_snapshot: bool, +} + +#[derive(Clone, Debug)] +pub struct PendingPredicateObligation<'tcx> { + pub obligation: PredicateObligation<'tcx>, + pub stalled_on: Vec, +} + +// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(PendingPredicateObligation<'_>, 136); + +impl<'a, 'tcx> FulfillmentContext<'tcx> { + /// Creates a new fulfillment context. + pub fn new() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: true, + usable_in_snapshot: false, + } + } + + pub fn new_in_snapshot() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: true, + usable_in_snapshot: true, + } + } + + pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: false, + usable_in_snapshot: false, + } + } + + /// Attempts to select obligations using `selcx`. + fn select( + &mut self, + selcx: &mut SelectionContext<'a, 'tcx>, + ) -> Result<(), Vec>> { + debug!("select(obligation-forest-size={})", self.predicates.len()); + + let mut errors = Vec::new(); + + loop { + debug!("select: starting another iteration"); + + // Process pending obligations. + let outcome = self.predicates.process_obligations( + &mut FulfillProcessor { + selcx, + register_region_obligations: self.register_region_obligations, + }, + DoCompleted::No, + ); + debug!("select: outcome={:#?}", outcome); + + // FIXME: if we kept the original cache key, we could mark projection + // obligations as complete for the projection cache here. + + errors.extend(outcome.errors.into_iter().map(|e| to_fulfillment_error(e))); + + // If nothing new was added, no need to keep looping. + if outcome.stalled { + break; + } + } + + debug!( + "select({} predicates remaining, {} errors) done", + self.predicates.len(), + errors.len() + ); + + if errors.is_empty() { Ok(()) } else { Err(errors) } + } +} + +impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { + /// "Normalize" a projection type `::X` by + /// creating a fresh type variable `$0` as well as a projection + /// predicate `::X == $0`. When the + /// inference engine runs, it will attempt to find an impl of + /// `SomeTrait` or a where-clause that lets us unify `$0` with + /// something concrete. If this fails, we'll unify `$0` with + /// `projection_ty` again. + fn normalize_projection_type( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + ) -> Ty<'tcx> { + debug!("normalize_projection_type(projection_ty={:?})", projection_ty); + + debug_assert!(!projection_ty.has_escaping_bound_vars()); + + // FIXME(#20304) -- cache + + let mut selcx = SelectionContext::new(infcx); + let mut obligations = vec![]; + let normalized_ty = project::normalize_projection_type( + &mut selcx, + param_env, + projection_ty, + cause, + 0, + &mut obligations, + ); + self.register_predicate_obligations(infcx, obligations); + + debug!("normalize_projection_type: result={:?}", normalized_ty); + + normalized_ty + } + + fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + obligation: PredicateObligation<'tcx>, + ) { + // this helps to reduce duplicate errors, as well as making + // debug output much nicer to read and so on. + let obligation = infcx.resolve_vars_if_possible(&obligation); + + debug!("register_predicate_obligation(obligation={:?})", obligation); + + assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot); + + self.predicates + .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); + } + + fn select_all_or_error( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec>> { + self.select_where_possible(infcx)?; + + let errors: Vec<_> = self + .predicates + .to_errors(CodeAmbiguity) + .into_iter() + .map(|e| to_fulfillment_error(e)) + .collect(); + if errors.is_empty() { Ok(()) } else { Err(errors) } + } + + fn select_where_possible( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec>> { + let mut selcx = SelectionContext::new(infcx); + self.select(&mut selcx) + } + + fn pending_obligations(&self) -> Vec> { + self.predicates.map_pending_obligations(|o| o.obligation.clone()) + } +} + +struct FulfillProcessor<'a, 'b, 'tcx> { + selcx: &'a mut SelectionContext<'b, 'tcx>, + register_region_obligations: bool, +} + +fn mk_pending(os: Vec>) -> Vec> { + os.into_iter() + .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) + .collect() +} + +impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { + type Obligation = PendingPredicateObligation<'tcx>; + type Error = FulfillmentErrorCode<'tcx>; + + /// Processes a predicate obligation and returns either: + /// - `Changed(v)` if the predicate is true, presuming that `v` are also true + /// - `Unchanged` if we don't have enough info to be sure + /// - `Error(e)` if the predicate does not hold + /// + /// This is always inlined, despite its size, because it has a single + /// callsite and it is called *very* frequently. + #[inline(always)] + fn process_obligation( + &mut self, + pending_obligation: &mut Self::Obligation, + ) -> ProcessResult { + // If we were stalled on some unresolved variables, first check whether + // any of them have been resolved; if not, don't bother doing more work + // yet. + let change = match pending_obligation.stalled_on.len() { + // Match arms are in order of frequency, which matters because this + // code is so hot. 1 and 0 dominate; 2+ is fairly rare. + 1 => { + let infer = pending_obligation.stalled_on[0]; + ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(infer) + } + 0 => { + // In this case we haven't changed, but wish to make a change. + true + } + _ => { + // This `for` loop was once a call to `all()`, but this lower-level + // form was a perf win. See #64545 for details. + (|| { + for &infer in &pending_obligation.stalled_on { + if ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(infer) { + return true; + } + } + false + })() + } + }; + + if !change { + debug!( + "process_predicate: pending obligation {:?} still stalled on {:?}", + self.selcx.infcx().resolve_vars_if_possible(&pending_obligation.obligation), + pending_obligation.stalled_on + ); + return ProcessResult::Unchanged; + } + + // This part of the code is much colder. + + pending_obligation.stalled_on.truncate(0); + + let obligation = &mut pending_obligation.obligation; + + if obligation.predicate.has_infer_types_or_consts() { + obligation.predicate = + self.selcx.infcx().resolve_vars_if_possible(&obligation.predicate); + } + + debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); + + fn infer_ty(ty: Ty<'tcx>) -> ty::InferTy { + match ty.kind { + ty::Infer(infer) => infer, + _ => panic!(), + } + } + + match obligation.predicate { + ty::Predicate::Trait(ref data, _) => { + let trait_obligation = obligation.with(data.clone()); + + if data.is_global() { + // no type variables present, can use evaluation for better caching. + // FIXME: consider caching errors too. + if self.selcx.infcx().predicate_must_hold_considering_regions(&obligation) { + debug!( + "selecting trait `{:?}` at depth {} evaluated to holds", + data, obligation.recursion_depth + ); + return ProcessResult::Changed(vec![]); + } + } + + match self.selcx.select(&trait_obligation) { + Ok(Some(vtable)) => { + debug!( + "selecting trait `{:?}` at depth {} yielded Ok(Some)", + data, obligation.recursion_depth + ); + ProcessResult::Changed(mk_pending(vtable.nested_obligations())) + } + Ok(None) => { + debug!( + "selecting trait `{:?}` at depth {} yielded Ok(None)", + data, obligation.recursion_depth + ); + + // This is a bit subtle: for the most part, the + // only reason we can fail to make progress on + // trait selection is because we don't have enough + // information about the types in the trait. + pending_obligation.stalled_on = + trait_ref_type_vars(self.selcx, data.to_poly_trait_ref()); + + debug!( + "process_predicate: pending obligation {:?} now stalled on {:?}", + self.selcx.infcx().resolve_vars_if_possible(obligation), + pending_obligation.stalled_on + ); + + ProcessResult::Unchanged + } + Err(selection_err) => { + info!( + "selecting trait `{:?}` at depth {} yielded Err", + data, obligation.recursion_depth + ); + + ProcessResult::Error(CodeSelectionError(selection_err)) + } + } + } + + ty::Predicate::RegionOutlives(ref binder) => { + match self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder) { + Ok(()) => ProcessResult::Changed(vec![]), + Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), + } + } + + ty::Predicate::TypeOutlives(ref binder) => { + // Check if there are higher-ranked vars. + match binder.no_bound_vars() { + // If there are, inspect the underlying type further. + None => { + // Convert from `Binder>` to `Binder`. + let binder = binder.map_bound_ref(|pred| pred.0); + + // Check if the type has any bound vars. + match binder.no_bound_vars() { + // If so, this obligation is an error (for now). Eventually we should be + // able to support additional cases here, like `for<'a> &'a str: 'a`. + // NOTE: this is duplicate-implemented between here and fulfillment. + None => ProcessResult::Error(CodeSelectionError(Unimplemented)), + // Otherwise, we have something of the form + // `for<'a> T: 'a where 'a not in T`, which we can treat as + // `T: 'static`. + Some(t_a) => { + let r_static = self.selcx.tcx().lifetimes.re_static; + if self.register_region_obligations { + self.selcx.infcx().register_region_obligation_with_cause( + t_a, + r_static, + &obligation.cause, + ); + } + ProcessResult::Changed(vec![]) + } + } + } + // If there aren't, register the obligation. + Some(ty::OutlivesPredicate(t_a, r_b)) => { + if self.register_region_obligations { + self.selcx.infcx().register_region_obligation_with_cause( + t_a, + r_b, + &obligation.cause, + ); + } + ProcessResult::Changed(vec![]) + } + } + } + + ty::Predicate::Projection(ref data) => { + let project_obligation = obligation.with(data.clone()); + match project::poly_project_and_unify_type(self.selcx, &project_obligation) { + Ok(None) => { + let tcx = self.selcx.tcx(); + pending_obligation.stalled_on = + trait_ref_type_vars(self.selcx, data.to_poly_trait_ref(tcx)); + ProcessResult::Unchanged + } + Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)), + Err(e) => ProcessResult::Error(CodeProjectionError(e)), + } + } + + ty::Predicate::ObjectSafe(trait_def_id) => { + if !self.selcx.tcx().is_object_safe(trait_def_id) { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } else { + ProcessResult::Changed(vec![]) + } + } + + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + match self.selcx.infcx().closure_kind(closure_def_id, closure_substs) { + Some(closure_kind) => { + if closure_kind.extends(kind) { + ProcessResult::Changed(vec![]) + } else { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } + } + None => ProcessResult::Unchanged, + } + } + + ty::Predicate::WellFormed(ty) => { + match wf::obligations( + self.selcx.infcx(), + obligation.param_env, + obligation.cause.body_id, + ty, + obligation.cause.span, + ) { + None => { + pending_obligation.stalled_on = vec![infer_ty(ty)]; + ProcessResult::Unchanged + } + Some(os) => ProcessResult::Changed(mk_pending(os)), + } + } + + ty::Predicate::Subtype(ref subtype) => { + match self.selcx.infcx().subtype_predicate( + &obligation.cause, + obligation.param_env, + subtype, + ) { + None => { + // None means that both are unresolved. + pending_obligation.stalled_on = vec![ + infer_ty(subtype.skip_binder().a), + infer_ty(subtype.skip_binder().b), + ]; + ProcessResult::Unchanged + } + Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Some(Err(err)) => { + let expected_found = ExpectedFound::new( + subtype.skip_binder().a_is_expected, + subtype.skip_binder().a, + subtype.skip_binder().b, + ); + ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( + expected_found, + err, + )) + } + } + } + + ty::Predicate::ConstEvaluatable(def_id, substs) => { + match self.selcx.infcx().const_eval_resolve( + obligation.param_env, + def_id, + substs, + None, + Some(obligation.cause.span), + ) { + Ok(_) => ProcessResult::Changed(vec![]), + Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), + } + } + } + } + + fn process_backedge<'c, I>( + &mut self, + cycle: I, + _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, + ) where + I: Clone + Iterator>, + { + if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { + debug!("process_child_obligations: coinductive match"); + } else { + let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); + self.selcx.infcx().report_overflow_error_cycle(&cycle); + } + } +} + +/// Returns the set of type variables contained in a trait ref +fn trait_ref_type_vars<'a, 'tcx>( + selcx: &mut SelectionContext<'a, 'tcx>, + t: ty::PolyTraitRef<'tcx>, +) -> Vec { + t.skip_binder() // ok b/c this check doesn't care about regions + .input_types() + .map(|t| selcx.infcx().resolve_vars_if_possible(&t)) + .filter(|t| t.has_infer_types()) + .flat_map(|t| t.walk()) + .filter_map(|t| match t.kind { + ty::Infer(infer) => Some(infer), + _ => None, + }) + .collect() +} + +fn to_fulfillment_error<'tcx>( + error: Error, FulfillmentErrorCode<'tcx>>, +) -> FulfillmentError<'tcx> { + let obligation = error.backtrace.into_iter().next().unwrap().obligation; + FulfillmentError::new(obligation, error.error) +} diff --git a/src/librustc_trait_selection/traits/misc.rs b/src/librustc_trait_selection/traits/misc.rs new file mode 100644 index 00000000000..d500cff67c6 --- /dev/null +++ b/src/librustc_trait_selection/traits/misc.rs @@ -0,0 +1,74 @@ +//! Miscellaneous type-system utilities that are too small to deserve their own modules. + +use crate::infer::InferCtxtExt as _; +use crate::traits::{self, ObligationCause}; + +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_hir as hir; +use rustc_infer::infer::TyCtxtInferExt; + +use crate::traits::error_reporting::InferCtxtExt; + +#[derive(Clone)] +pub enum CopyImplementationError<'tcx> { + InfrigingFields(Vec<&'tcx ty::FieldDef>), + NotAnAdt, + HasDestructor, +} + +pub fn can_type_implement_copy( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, +) -> Result<(), CopyImplementationError<'tcx>> { + // FIXME: (@jroesch) float this code up + tcx.infer_ctxt().enter(|infcx| { + let (adt, substs) = match self_type.kind { + // These types used to have a builtin impl. + // Now libcore provides that impl. + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()), + + ty::Adt(adt, substs) => (adt, substs), + + _ => return Err(CopyImplementationError::NotAnAdt), + }; + + let mut infringing = Vec::new(); + for variant in &adt.variants { + for field in &variant.fields { + let ty = field.ty(tcx, substs); + if ty.references_error() { + continue; + } + let span = tcx.def_span(field.did); + let cause = ObligationCause { span, ..ObligationCause::dummy() }; + let ctx = traits::FulfillmentContext::new(); + match traits::fully_normalize(&infcx, ctx, cause, param_env, &ty) { + Ok(ty) => { + if !infcx.type_is_copy_modulo_regions(param_env, ty, span) { + infringing.push(field); + } + } + Err(errors) => { + infcx.report_fulfillment_errors(&errors, None, false); + } + }; + } + } + if !infringing.is_empty() { + return Err(CopyImplementationError::InfrigingFields(infringing)); + } + if adt.has_dtor(tcx) { + return Err(CopyImplementationError::HasDestructor); + } + + Ok(()) + }) +} diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs new file mode 100644 index 00000000000..43a90c4a6c1 --- /dev/null +++ b/src/librustc_trait_selection/traits/mod.rs @@ -0,0 +1,533 @@ +//! Trait Resolution. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +#[allow(dead_code)] +pub mod auto_trait; +pub mod codegen; +mod coherence; +mod engine; +pub mod error_reporting; +mod fulfill; +pub mod misc; +mod object_safety; +mod on_unimplemented; +mod project; +pub mod query; +mod select; +mod specialize; +mod structural_match; +mod util; +pub mod wf; + +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::{InferCtxt, RegionckMode, TyCtxtInferExt}; +use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc::middle::region; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::subst::{InternalSubsts, SubstsRef}; +use rustc::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc::util::common::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::{Span, DUMMY_SP}; + +use std::fmt::Debug; + +pub use self::FulfillmentErrorCode::*; +pub use self::ObligationCauseCode::*; +pub use self::SelectionError::*; +pub use self::Vtable::*; + +pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; +pub use self::coherence::{OrphanCheckErr, OverlapResult}; +pub use self::engine::TraitEngineExt; +pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; +pub use self::object_safety::astconv_object_safety_violations; +pub use self::object_safety::is_vtable_safe_method; +pub use self::object_safety::MethodViolationCode; +pub use self::object_safety::ObjectSafetyViolation; +pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote}; +pub use self::project::{ + normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type, +}; +pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; +pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; +pub use self::specialize::find_associated_item; +pub use self::specialize::specialization_graph::FutureCompatOverlapError; +pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; +pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; +pub use self::structural_match::search_for_structural_match_violation; +pub use self::structural_match::type_marked_structural; +pub use self::structural_match::NonStructuralMatchTy; +pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; +pub use self::util::{expand_trait_aliases, TraitAliasExpander}; +pub use self::util::{ + get_vtable_index_of_object_method, impl_is_default, impl_item_is_final, + predicate_for_trait_def, upcast_choices, +}; +pub use self::util::{ + supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, +}; + +pub use rustc_infer::traits::*; + +/// Whether to skip the leak check, as part of a future compatibility warning step. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum SkipLeakCheck { + Yes, + No, +} + +impl SkipLeakCheck { + fn is_yes(self) -> bool { + self == SkipLeakCheck::Yes + } +} + +/// The "default" for skip-leak-check corresponds to the current +/// behavior (do not skip the leak check) -- not the behavior we are +/// transitioning into. +impl Default for SkipLeakCheck { + fn default() -> Self { + SkipLeakCheck::No + } +} + +/// The mode that trait queries run in. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum TraitQueryMode { + // Standard/un-canonicalized queries get accurate + // spans etc. passed in and hence can do reasonable + // error reporting on their own. + Standard, + // Canonicalized queries get dummy spans and hence + // must generally propagate errors to + // pre-canonicalization callsites. + Canonical, +} + +/// Creates predicate obligations from the generic bounds. +pub fn predicates_for_generics<'tcx>( + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + generic_bounds: &ty::InstantiatedPredicates<'tcx>, +) -> PredicateObligations<'tcx> { + util::predicates_for_generics(cause, 0, param_env, generic_bounds) +} + +/// Determines whether the type `ty` is known to meet `bound` and +/// returns true if so. Returns false if `ty` either does not meet +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). +pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + def_id: DefId, + span: Span, +) -> bool { + debug!( + "type_known_to_meet_bound_modulo_regions(ty={:?}, bound={:?})", + ty, + infcx.tcx.def_path_str(def_id) + ); + + let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) }; + let obligation = Obligation { + param_env, + cause: ObligationCause::misc(span, hir::DUMMY_HIR_ID), + recursion_depth: 0, + predicate: trait_ref.without_const().to_predicate(), + }; + + let result = infcx.predicate_must_hold_modulo_regions(&obligation); + debug!( + "type_known_to_meet_ty={:?} bound={} => {:?}", + ty, + infcx.tcx.def_path_str(def_id), + result + ); + + if result && ty.has_infer_types_or_consts() { + // Because of inference "guessing", selection can sometimes claim + // to succeed while the success requires a guess. To ensure + // this function's result remains infallible, we must confirm + // that guess. While imperfect, I believe this is sound. + + // The handling of regions in this area of the code is terrible, + // see issue #29149. We should be able to improve on this with + // NLL. + let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + + // We can use a dummy node-id here because we won't pay any mind + // to region obligations that arise (there shouldn't really be any + // anyhow). + let cause = ObligationCause::misc(span, hir::DUMMY_HIR_ID); + + fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause); + + // Note: we only assume something is `Copy` if we can + // *definitively* show that it implements `Copy`. Otherwise, + // assume it is move; linear is always ok. + match fulfill_cx.select_all_or_error(infcx) { + Ok(()) => { + debug!( + "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success", + ty, + infcx.tcx.def_path_str(def_id) + ); + true + } + Err(e) => { + debug!( + "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} errors={:?}", + ty, + infcx.tcx.def_path_str(def_id), + e + ); + false + } + } + } else { + result + } +} + +fn do_normalize_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + region_context: DefId, + cause: ObligationCause<'tcx>, + elaborated_env: ty::ParamEnv<'tcx>, + predicates: Vec>, +) -> Result>, ErrorReported> { + debug!( + "do_normalize_predicates(predicates={:?}, region_context={:?}, cause={:?})", + predicates, region_context, cause, + ); + let span = cause.span; + tcx.infer_ctxt().enter(|infcx| { + // FIXME. We should really... do something with these region + // obligations. But this call just continues the older + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis + // + // @arielby: In any case, these obligations are checked + // by wfcheck anyway, so I'm not sure we have to check + // them here too, and we will remove this function when + // we move over to lazy normalization *anyway*. + let fulfill_cx = FulfillmentContext::new_ignoring_regions(); + let predicates = + match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, &predicates) { + Ok(predicates) => predicates, + Err(errors) => { + infcx.report_fulfillment_errors(&errors, None, false); + return Err(ErrorReported); + } + }; + + debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); + + let region_scope_tree = region::ScopeTree::default(); + + // We can use the `elaborated_env` here; the region code only + // cares about declarations like `'a: 'b`. + let outlives_env = OutlivesEnvironment::new(elaborated_env); + + infcx.resolve_regions_and_report_errors( + region_context, + ®ion_scope_tree, + &outlives_env, + RegionckMode::default(), + ); + + let predicates = match infcx.fully_resolve(&predicates) { + Ok(predicates) => predicates, + Err(fixup_err) => { + // If we encounter a fixup error, it means that some type + // variable wound up unconstrained. I actually don't know + // if this can happen, and I certainly don't expect it to + // happen often, but if it did happen it probably + // represents a legitimate failure due to some kind of + // unconstrained variable, and it seems better not to ICE, + // all things considered. + tcx.sess.span_err(span, &fixup_err.to_string()); + return Err(ErrorReported); + } + }; + if predicates.has_local_value() { + // FIXME: shouldn't we, you know, actually report an error here? or an ICE? + Err(ErrorReported) + } else { + Ok(predicates) + } + }) +} + +// FIXME: this is gonna need to be removed ... +/// Normalizes the parameter environment, reporting errors if they occur. +pub fn normalize_param_env_or_error<'tcx>( + tcx: TyCtxt<'tcx>, + region_context: DefId, + unnormalized_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, +) -> ty::ParamEnv<'tcx> { + // I'm not wild about reporting errors here; I'd prefer to + // have the errors get reported at a defined place (e.g., + // during typeck). Instead I have all parameter + // environments, in effect, going through this function + // and hence potentially reporting errors. This ensures of + // course that we never forget to normalize (the + // alternative seemed like it would involve a lot of + // manual invocations of this fn -- and then we'd have to + // deal with the errors at each of those sites). + // + // In any case, in practice, typeck constructs all the + // parameter environments once for every fn as it goes, + // and errors will get reported then; so after typeck we + // can be sure that no errors should occur. + + debug!( + "normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})", + region_context, unnormalized_env, cause + ); + + let mut predicates: Vec<_> = + util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec()).collect(); + + debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); + + let elaborated_env = ty::ParamEnv::new( + tcx.intern_predicates(&predicates), + unnormalized_env.reveal, + unnormalized_env.def_id, + ); + + // HACK: we are trying to normalize the param-env inside *itself*. The problem is that + // normalization expects its param-env to be already normalized, which means we have + // a circularity. + // + // The way we handle this is by normalizing the param-env inside an unnormalized version + // of the param-env, which means that if the param-env contains unnormalized projections, + // we'll have some normalization failures. This is unfortunate. + // + // Lazy normalization would basically handle this by treating just the + // normalizing-a-trait-ref-requires-itself cycles as evaluation failures. + // + // Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated + // types, so to make the situation less bad, we normalize all the predicates *but* + // the `TypeOutlives` predicates first inside the unnormalized parameter environment, and + // then we normalize the `TypeOutlives` bounds inside the normalized parameter environment. + // + // This works fairly well because trait matching does not actually care about param-env + // TypeOutlives predicates - these are normally used by regionck. + let outlives_predicates: Vec<_> = predicates + .drain_filter(|predicate| match predicate { + ty::Predicate::TypeOutlives(..) => true, + _ => false, + }) + .collect(); + + debug!( + "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})", + predicates, outlives_predicates + ); + let non_outlives_predicates = match do_normalize_predicates( + tcx, + region_context, + cause.clone(), + elaborated_env, + predicates, + ) { + Ok(predicates) => predicates, + // An unnormalized env is better than nothing. + Err(ErrorReported) => { + debug!("normalize_param_env_or_error: errored resolving non-outlives predicates"); + return elaborated_env; + } + }; + + debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates); + + // Not sure whether it is better to include the unnormalized TypeOutlives predicates + // here. I believe they should not matter, because we are ignoring TypeOutlives param-env + // predicates here anyway. Keeping them here anyway because it seems safer. + let outlives_env: Vec<_> = + non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect(); + let outlives_env = + ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal, None); + let outlives_predicates = match do_normalize_predicates( + tcx, + region_context, + cause, + outlives_env, + outlives_predicates, + ) { + Ok(predicates) => predicates, + // An unnormalized env is better than nothing. + Err(ErrorReported) => { + debug!("normalize_param_env_or_error: errored resolving outlives predicates"); + return elaborated_env; + } + }; + debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates); + + let mut predicates = non_outlives_predicates; + predicates.extend(outlives_predicates); + debug!("normalize_param_env_or_error: final predicates={:?}", predicates); + ty::ParamEnv::new( + tcx.intern_predicates(&predicates), + unnormalized_env.reveal, + unnormalized_env.def_id, + ) +} + +pub fn fully_normalize<'a, 'tcx, T>( + infcx: &InferCtxt<'a, 'tcx>, + mut fulfill_cx: FulfillmentContext<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &T, +) -> Result>> +where + T: TypeFoldable<'tcx>, +{ + debug!("fully_normalize_with_fulfillcx(value={:?})", value); + let selcx = &mut SelectionContext::new(infcx); + let Normalized { value: normalized_value, obligations } = + project::normalize(selcx, param_env, cause, value); + debug!( + "fully_normalize: normalized_value={:?} obligations={:?}", + normalized_value, obligations + ); + for obligation in obligations { + fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation); + } + + debug!("fully_normalize: select_all_or_error start"); + fulfill_cx.select_all_or_error(infcx)?; + debug!("fully_normalize: select_all_or_error complete"); + let resolved_value = infcx.resolve_vars_if_possible(&normalized_value); + debug!("fully_normalize: resolved_value={:?}", resolved_value); + Ok(resolved_value) +} + +/// Normalizes the predicates and checks whether they hold in an empty +/// environment. If this returns false, then either normalize +/// encountered an error or one of the predicates did not hold. Used +/// when creating vtables to check for unsatisfiable methods. +pub fn normalize_and_test_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: Vec>, +) -> bool { + debug!("normalize_and_test_predicates(predicates={:?})", predicates); + + let result = tcx.infer_ctxt().enter(|infcx| { + let param_env = ty::ParamEnv::reveal_all(); + let mut selcx = SelectionContext::new(&infcx); + let mut fulfill_cx = FulfillmentContext::new(); + let cause = ObligationCause::dummy(); + let Normalized { value: predicates, obligations } = + normalize(&mut selcx, param_env, cause.clone(), &predicates); + for obligation in obligations { + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + for predicate in predicates { + let obligation = Obligation::new(cause.clone(), param_env, predicate); + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + + fulfill_cx.select_all_or_error(&infcx).is_ok() + }); + debug!("normalize_and_test_predicates(predicates={:?}) = {:?}", predicates, result); + result +} + +fn substitute_normalize_and_test_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + key: (DefId, SubstsRef<'tcx>), +) -> bool { + debug!("substitute_normalize_and_test_predicates(key={:?})", key); + + let predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; + let result = normalize_and_test_predicates(tcx, predicates); + + debug!("substitute_normalize_and_test_predicates(key={:?}) = {:?}", key, result); + result +} + +/// Given a trait `trait_ref`, iterates the vtable entries +/// that come from `trait_ref`, including its supertraits. +#[inline] // FIXME(#35870): avoid closures being unexported due to `impl Trait`. +fn vtable_methods<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { + debug!("vtable_methods({:?})", trait_ref); + + tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| { + let trait_methods = tcx + .associated_items(trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Method); + + // Now list each method's DefId and InternalSubsts (for within its trait). + // If the method can never be called from this object, produce None. + trait_methods.map(move |trait_method| { + debug!("vtable_methods: trait_method={:?}", trait_method); + let def_id = trait_method.def_id; + + // Some methods cannot be called on an object; skip those. + if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { + debug!("vtable_methods: not vtable safe"); + return None; + } + + // The method may have some early-bound lifetimes; add regions for those. + let substs = trait_ref.map_bound(|trait_ref| { + InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize] + } + }) + }); + + // The trait type may have higher-ranked lifetimes in it; + // erase them if they appear, so that we get the type + // at some particular call site. + let substs = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); + + // It's possible that the method relies on where-clauses that + // do not hold for this particular set of type parameters. + // Note that this method could then never be called, so we + // do not want to try and codegen it, in that case (see #23435). + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); + if !normalize_and_test_predicates(tcx, predicates.predicates) { + debug!("vtable_methods: predicates do not hold"); + return None; + } + + Some((def_id, substs)) + }) + })) +} + +pub fn provide(providers: &mut ty::query::Providers<'_>) { + object_safety::provide(providers); + *providers = ty::query::Providers { + specialization_graph_of: specialize::specialization_graph_provider, + specializes: specialize::specializes, + codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, + vtable_methods, + substitute_normalize_and_test_predicates, + ..*providers + }; +} diff --git a/src/librustc_trait_selection/traits/object_safety.rs b/src/librustc_trait_selection/traits/object_safety.rs new file mode 100644 index 00000000000..452f965014b --- /dev/null +++ b/src/librustc_trait_selection/traits/object_safety.rs @@ -0,0 +1,778 @@ +//! "Object safety" refers to the ability for a trait to be converted +//! to an object. In general, traits may only be converted to an +//! object if all of their methods meet certain criteria. In particular, +//! they must: +//! +//! - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version +//! that doesn't contain the vtable; +//! - not reference the erased type `Self` except for in this receiver; +//! - not have generic type parameters. + +use super::elaborate_predicates; + +use crate::infer::TyCtxtInferExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{self, Obligation, ObligationCause}; +use rustc::ty::subst::{InternalSubsts, Subst}; +use rustc::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; +use rustc_span::symbol::Symbol; +use rustc_span::Span; +use smallvec::SmallVec; + +use std::iter; + +pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; + +/// Returns the object safety violations that affect +/// astconv -- currently, `Self` in supertraits. This is needed +/// because `object_safety_violations` can't be used during +/// type collection. +pub fn astconv_object_safety_violations( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> Vec { + debug_assert!(tcx.generics_of(trait_def_id).has_self); + let violations = traits::supertrait_def_ids(tcx, trait_def_id) + .map(|def_id| predicates_reference_self(tcx, def_id, true)) + .filter(|spans| !spans.is_empty()) + .map(|spans| ObjectSafetyViolation::SupertraitSelf(spans)) + .collect(); + + debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations); + + violations +} + +fn object_safety_violations(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Vec { + debug_assert!(tcx.generics_of(trait_def_id).has_self); + debug!("object_safety_violations: {:?}", trait_def_id); + + traits::supertrait_def_ids(tcx, trait_def_id) + .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)) + .collect() +} + +/// We say a method is *vtable safe* if it can be invoked on a trait +/// object. Note that object-safe traits can have some +/// non-vtable-safe methods, so long as they require `Self: Sized` or +/// otherwise ensure that they cannot be used when `Self = Trait`. +pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: &ty::AssocItem) -> bool { + debug_assert!(tcx.generics_of(trait_def_id).has_self); + debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method); + // Any method that has a `Self: Sized` bound cannot be called. + if generics_require_sized_self(tcx, method.def_id) { + return false; + } + + match virtual_call_violation_for_method(tcx, trait_def_id, method) { + None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true, + Some(_) => false, + } +} + +fn object_safety_violations_for_trait( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> Vec { + // Check methods for violations. + let mut violations: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Method) + .filter_map(|item| { + object_safety_violation_for_method(tcx, trait_def_id, &item) + .map(|(code, span)| ObjectSafetyViolation::Method(item.ident.name, code, span)) + }) + .filter(|violation| { + if let ObjectSafetyViolation::Method( + _, + MethodViolationCode::WhereClauseReferencesSelf, + span, + ) = violation + { + // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. + // It's also hard to get a use site span, so we use the method definition span. + tcx.struct_span_lint_hir( + WHERE_CLAUSES_OBJECT_SAFETY, + hir::CRATE_HIR_ID, + *span, + |lint| { + let mut err = lint.build(&format!( + "the trait `{}` cannot be made into an object", + tcx.def_path_str(trait_def_id) + )); + let node = tcx.hir().get_if_local(trait_def_id); + let msg = if let Some(hir::Node::Item(item)) = node { + err.span_label( + item.ident.span, + "this trait cannot be made into an object...", + ); + format!("...because {}", violation.error_msg()) + } else { + format!( + "the trait cannot be made into an object because {}", + violation.error_msg() + ) + }; + err.span_label(*span, &msg); + match (node, violation.solution()) { + (Some(_), Some((note, None))) => { + err.help(¬e); + } + (Some(_), Some((note, Some((sugg, span))))) => { + err.span_suggestion( + span, + ¬e, + sugg, + Applicability::MachineApplicable, + ); + } + // Only provide the help if its a local trait, otherwise it's not actionable. + _ => {} + } + err.emit(); + }, + ); + false + } else { + true + } + }) + .collect(); + + // Check the trait itself. + if trait_has_sized_self(tcx, trait_def_id) { + // We don't want to include the requirement from `Sized` itself to be `Sized` in the list. + let spans = get_sized_bounds(tcx, trait_def_id); + violations.push(ObjectSafetyViolation::SizedSelf(spans)); + } + let spans = predicates_reference_self(tcx, trait_def_id, false); + if !spans.is_empty() { + violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); + } + + violations.extend( + tcx.associated_items(trait_def_id) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Const) + .map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)), + ); + + debug!( + "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", + trait_def_id, violations + ); + + violations +} + +fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { + tcx.hir() + .get_if_local(trait_def_id) + .and_then(|node| match node { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(.., generics, bounds, _), + .. + }) => Some( + generics + .where_clause + .predicates + .iter() + .filter_map(|pred| { + match pred { + hir::WherePredicate::BoundPredicate(pred) + if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id => + { + // Fetch spans for trait bounds that are Sized: + // `trait T where Self: Pred` + Some(pred.bounds.iter().filter_map(|b| match b { + hir::GenericBound::Trait( + trait_ref, + hir::TraitBoundModifier::None, + ) if trait_has_sized_self( + tcx, + trait_ref.trait_ref.trait_def_id(), + ) => + { + Some(trait_ref.span) + } + _ => None, + })) + } + _ => None, + } + }) + .flatten() + .chain(bounds.iter().filter_map(|b| match b { + hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None) + if trait_has_sized_self(tcx, trait_ref.trait_ref.trait_def_id()) => + { + // Fetch spans for supertraits that are `Sized`: `trait T: Super` + Some(trait_ref.span) + } + _ => None, + })) + .collect::>(), + ), + _ => None, + }) + .unwrap_or_else(SmallVec::new) +} + +fn predicates_reference_self( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + supertraits_only: bool, +) -> SmallVec<[Span; 1]> { + let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); + let predicates = if supertraits_only { + tcx.super_predicates_of(trait_def_id) + } else { + tcx.predicates_of(trait_def_id) + }; + let self_ty = tcx.types.self_param; + let has_self_ty = |t: Ty<'_>| t.walk().any(|t| t == self_ty); + predicates + .predicates + .iter() + .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) + .filter_map(|(predicate, &sp)| { + match predicate { + ty::Predicate::Trait(ref data, _) => { + // In the case of a trait predicate, we can skip the "self" type. + if data.skip_binder().input_types().skip(1).any(has_self_ty) { + Some(sp) + } else { + None + } + } + ty::Predicate::Projection(ref data) => { + // And similarly for projections. This should be redundant with + // the previous check because any projection should have a + // matching `Trait` predicate with the same inputs, but we do + // the check to be safe. + // + // Note that we *do* allow projection *outputs* to contain + // `self` (i.e., `trait Foo: Bar { type Result; }`), + // we just require the user to specify *both* outputs + // in the object type (i.e., `dyn Foo`). + // + // This is ALT2 in issue #56288, see that for discussion of the + // possible alternatives. + if data + .skip_binder() + .projection_ty + .trait_ref(tcx) + .input_types() + .skip(1) + .any(has_self_ty) + { + Some(sp) + } else { + None + } + } + ty::Predicate::WellFormed(..) + | ty::Predicate::ObjectSafe(..) + | ty::Predicate::TypeOutlives(..) + | ty::Predicate::RegionOutlives(..) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::Subtype(..) + | ty::Predicate::ConstEvaluatable(..) => None, + } + }) + .collect() +} + +fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { + generics_require_sized_self(tcx, trait_def_id) +} + +fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let sized_def_id = match tcx.lang_items().sized_trait() { + Some(def_id) => def_id, + None => { + return false; /* No Sized trait, can't require it! */ + } + }; + + // Search for a predicate like `Self : Sized` amongst the trait bounds. + let predicates = tcx.predicates_of(def_id); + let predicates = predicates.instantiate_identity(tcx).predicates; + elaborate_predicates(tcx, predicates).any(|predicate| match predicate { + ty::Predicate::Trait(ref trait_pred, _) => { + trait_pred.def_id() == sized_def_id && trait_pred.skip_binder().self_ty().is_param(0) + } + ty::Predicate::Projection(..) + | ty::Predicate::Subtype(..) + | ty::Predicate::RegionOutlives(..) + | ty::Predicate::WellFormed(..) + | ty::Predicate::ObjectSafe(..) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::TypeOutlives(..) + | ty::Predicate::ConstEvaluatable(..) => false, + }) +} + +/// Returns `Some(_)` if this method makes the containing trait not object safe. +fn object_safety_violation_for_method( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + method: &ty::AssocItem, +) -> Option<(MethodViolationCode, Span)> { + debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method); + // Any method that has a `Self : Sized` requisite is otherwise + // exempt from the regulations. + if generics_require_sized_self(tcx, method.def_id) { + return None; + } + + let violation = virtual_call_violation_for_method(tcx, trait_def_id, method); + // Get an accurate span depending on the violation. + violation.map(|v| { + let node = tcx.hir().get_if_local(method.def_id); + let span = match (v, node) { + (MethodViolationCode::ReferencesSelfInput(arg), Some(node)) => node + .fn_decl() + .and_then(|decl| decl.inputs.get(arg + 1)) + .map_or(method.ident.span, |arg| arg.span), + (MethodViolationCode::UndispatchableReceiver, Some(node)) => node + .fn_decl() + .and_then(|decl| decl.inputs.get(0)) + .map_or(method.ident.span, |arg| arg.span), + (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { + node.fn_decl().map_or(method.ident.span, |decl| decl.output.span()) + } + _ => method.ident.span, + }; + (v, span) + }) +} + +/// Returns `Some(_)` if this method cannot be called on a trait +/// object; this does not necessarily imply that the enclosing trait +/// is not object safe, because the method might have a where clause +/// `Self:Sized`. +fn virtual_call_violation_for_method<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + method: &ty::AssocItem, +) -> Option { + // The method's first parameter must be named `self` + if !method.method_has_self_argument { + // We'll attempt to provide a structured suggestion for `Self: Sized`. + let sugg = + tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map( + |generics| match generics.where_clause.predicates { + [] => (" where Self: Sized", generics.where_clause.span), + [.., pred] => (", Self: Sized", pred.span().shrink_to_hi()), + }, + ); + return Some(MethodViolationCode::StaticMethod(sugg)); + } + + let sig = tcx.fn_sig(method.def_id); + + for (i, input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { + if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) { + return Some(MethodViolationCode::ReferencesSelfInput(i)); + } + } + if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output().skip_binder()) { + return Some(MethodViolationCode::ReferencesSelfOutput); + } + + // We can't monomorphize things like `fn foo(...)`. + let own_counts = tcx.generics_of(method.def_id).own_counts(); + if own_counts.types + own_counts.consts != 0 { + return Some(MethodViolationCode::Generic); + } + + if tcx + .predicates_of(method.def_id) + .predicates + .iter() + // A trait object can't claim to live more than the concrete type, + // so outlives predicates will always hold. + .cloned() + .filter(|(p, _)| p.to_opt_type_outlives().is_none()) + .collect::>() + // Do a shallow visit so that `contains_illegal_self_type_reference` + // may apply it's custom visiting. + .visit_tys_shallow(|t| contains_illegal_self_type_reference(tcx, trait_def_id, t)) + { + return Some(MethodViolationCode::WhereClauseReferencesSelf); + } + + let receiver_ty = + tcx.liberate_late_bound_regions(method.def_id, &sig.map_bound(|sig| sig.inputs()[0])); + + // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. + // However, this is already considered object-safe. We allow it as a special case here. + // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows + // `Receiver: Unsize dyn Trait]>`. + if receiver_ty != tcx.types.self_param { + if !receiver_is_dispatchable(tcx, method, receiver_ty) { + return Some(MethodViolationCode::UndispatchableReceiver); + } else { + // Do sanity check to make sure the receiver actually has the layout of a pointer. + + use rustc::ty::layout::Abi; + + let param_env = tcx.param_env(method.def_id); + + let abi_of_ty = |ty: Ty<'tcx>| -> &Abi { + match tcx.layout_of(param_env.and(ty)) { + Ok(layout) => &layout.abi, + Err(err) => bug!("error: {}\n while computing layout for type {:?}", err, ty), + } + }; + + // e.g., `Rc<()>` + let unit_receiver_ty = + receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id); + + match abi_of_ty(unit_receiver_ty) { + &Abi::Scalar(..) => (), + abi => { + tcx.sess.delay_span_bug( + tcx.def_span(method.def_id), + &format!( + "receiver when `Self = ()` should have a Scalar ABI; found {:?}", + abi + ), + ); + } + } + + let trait_object_ty = + object_ty_for_trait(tcx, trait_def_id, tcx.mk_region(ty::ReStatic)); + + // e.g., `Rc` + let trait_object_receiver = + receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id); + + match abi_of_ty(trait_object_receiver) { + &Abi::ScalarPair(..) => (), + abi => { + tcx.sess.delay_span_bug( + tcx.def_span(method.def_id), + &format!( + "receiver when `Self = {}` should have a ScalarPair ABI; \ + found {:?}", + trait_object_ty, abi + ), + ); + } + } + } + } + + None +} + +/// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`. +/// For example, for `receiver_ty = Rc` and `self_ty = Foo`, returns `Rc`. +fn receiver_for_self_ty<'tcx>( + tcx: TyCtxt<'tcx>, + receiver_ty: Ty<'tcx>, + self_ty: Ty<'tcx>, + method_def_id: DefId, +) -> Ty<'tcx> { + debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id); + let substs = InternalSubsts::for_item(tcx, method_def_id, |param, _| { + if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) } + }); + + let result = receiver_ty.subst(tcx, substs); + debug!( + "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}", + receiver_ty, self_ty, method_def_id, result + ); + result +} + +/// Creates the object type for the current trait. For example, +/// if the current trait is `Deref`, then this will be +/// `dyn Deref + 'static`. +fn object_ty_for_trait<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + lifetime: ty::Region<'tcx>, +) -> Ty<'tcx> { + debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id); + + let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); + + let trait_predicate = + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + + let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref)) + .flat_map(|super_trait_ref| { + tcx.associated_items(super_trait_ref.def_id()) + .in_definition_order() + .map(move |item| (super_trait_ref, item)) + }) + .filter(|(_, item)| item.kind == ty::AssocKind::Type) + .collect::>(); + + // existential predicates need to be in a specific order + associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id)); + + let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| { + // We *can* get bound lifetimes here in cases like + // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. + // + // binder moved to (*)... + let super_trait_ref = super_trait_ref.skip_binder(); + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + ty: tcx.mk_projection(item.def_id, super_trait_ref.substs), + item_def_id: item.def_id, + substs: super_trait_ref.substs, + }) + }); + + let existential_predicates = + tcx.mk_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); + + let object_ty = tcx.mk_dynamic( + // (*) ... binder re-introduced here + ty::Binder::bind(existential_predicates), + lifetime, + ); + + debug!("object_ty_for_trait: object_ty=`{}`", object_ty); + + object_ty +} + +/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a +/// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type +/// in the following way: +/// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc`, +/// - require the following bound: +/// +/// ``` +/// Receiver[Self => T]: DispatchFromDyn dyn Trait]> +/// ``` +/// +/// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`" +/// (substitution notation). +/// +/// Some examples of receiver types and their required obligation: +/// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`, +/// - `self: Rc` requires `Rc: DispatchFromDyn>`, +/// - `self: Pin>` requires `Pin>: DispatchFromDyn>>`. +/// +/// The only case where the receiver is not dispatchable, but is still a valid receiver +/// type (just not object-safe), is when there is more than one level of pointer indirection. +/// E.g., `self: &&Self`, `self: &Rc`, `self: Box>`. In these cases, there +/// is no way, or at least no inexpensive way, to coerce the receiver from the version where +/// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type +/// contained by the trait object, because the object that needs to be coerced is behind +/// a pointer. +/// +/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result +/// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch +/// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561). +/// Instead, we fudge a little by introducing a new type parameter `U` such that +/// `Self: Unsize` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. +/// Written as a chalk-style query: +/// +/// forall (U: Trait + ?Sized) { +/// if (Self: Unsize) { +/// Receiver: DispatchFromDyn U]> +/// } +/// } +/// +/// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>` +/// for `self: Rc`, this means `Rc: DispatchFromDyn>` +/// for `self: Pin>`, this means `Pin>: DispatchFromDyn>>` +// +// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this +// fallback query: `Receiver: Unsize U]>` to support receivers like +// `self: Wrapper`. +#[allow(dead_code)] +fn receiver_is_dispatchable<'tcx>( + tcx: TyCtxt<'tcx>, + method: &ty::AssocItem, + receiver_ty: Ty<'tcx>, +) -> bool { + debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty); + + let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait()); + let (unsize_did, dispatch_from_dyn_did) = if let (Some(u), Some(cu)) = traits { + (u, cu) + } else { + debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits"); + return false; + }; + + // the type `U` in the query + // use a bogus type parameter to mimic a forall(U) query using u32::MAX for now. + // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can + // replace this with `dyn Trait` + let unsized_self_ty: Ty<'tcx> = + tcx.mk_ty_param(::std::u32::MAX, Symbol::intern("RustaceansAreAwesome")); + + // `Receiver[Self => U]` + let unsized_receiver_ty = + receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id); + + // create a modified param env, with `Self: Unsize` and `U: Trait` added to caller bounds + // `U: ?Sized` is already implied here + let param_env = { + let mut param_env = tcx.param_env(method.def_id); + + // Self: Unsize + let unsize_predicate = ty::TraitRef { + def_id: unsize_did, + substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]), + } + .without_const() + .to_predicate(); + + // U: Trait + let trait_predicate = { + let substs = + InternalSubsts::for_item(tcx, method.container.assert_trait(), |param, _| { + if param.index == 0 { + unsized_self_ty.into() + } else { + tcx.mk_param_from_def(param) + } + }); + + ty::TraitRef { def_id: unsize_did, substs }.without_const().to_predicate() + }; + + let caller_bounds: Vec> = param_env + .caller_bounds + .iter() + .cloned() + .chain(iter::once(unsize_predicate)) + .chain(iter::once(trait_predicate)) + .collect(); + + param_env.caller_bounds = tcx.intern_predicates(&caller_bounds); + + param_env + }; + + // Receiver: DispatchFromDyn U]> + let obligation = { + let predicate = ty::TraitRef { + def_id: dispatch_from_dyn_did, + substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]), + } + .without_const() + .to_predicate(); + + Obligation::new(ObligationCause::dummy(), param_env, predicate) + }; + + tcx.infer_ctxt().enter(|ref infcx| { + // the receiver is dispatchable iff the obligation holds + infcx.predicate_must_hold_modulo_regions(&obligation) + }) +} + +fn contains_illegal_self_type_reference<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + ty: Ty<'tcx>, +) -> bool { + // This is somewhat subtle. In general, we want to forbid + // references to `Self` in the argument and return types, + // since the value of `Self` is erased. However, there is one + // exception: it is ok to reference `Self` in order to access + // an associated type of the current trait, since we retain + // the value of those associated types in the object type + // itself. + // + // ```rust + // trait SuperTrait { + // type X; + // } + // + // trait Trait : SuperTrait { + // type Y; + // fn foo(&self, x: Self) // bad + // fn foo(&self) -> Self // bad + // fn foo(&self) -> Option // bad + // fn foo(&self) -> Self::Y // OK, desugars to next example + // fn foo(&self) -> ::Y // OK + // fn foo(&self) -> Self::X // OK, desugars to next example + // fn foo(&self) -> ::X // OK + // } + // ``` + // + // However, it is not as simple as allowing `Self` in a projected + // type, because there are illegal ways to use `Self` as well: + // + // ```rust + // trait Trait : SuperTrait { + // ... + // fn foo(&self) -> ::X; + // } + // ``` + // + // Here we will not have the type of `X` recorded in the + // object type, and we cannot resolve `Self as SomeOtherTrait` + // without knowing what `Self` is. + + let mut supertraits: Option>> = None; + let mut error = false; + let self_ty = tcx.types.self_param; + ty.maybe_walk(|ty| { + match ty.kind { + ty::Param(_) => { + if ty == self_ty { + error = true; + } + + false // no contained types to walk + } + + ty::Projection(ref data) => { + // This is a projected type `::X`. + + // Compute supertraits of current trait lazily. + if supertraits.is_none() { + let trait_ref = ty::Binder::bind(ty::TraitRef::identity(tcx, trait_def_id)); + supertraits = Some(traits::supertraits(tcx, trait_ref).collect()); + } + + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let projection_trait_ref = ty::Binder::bind(data.trait_ref(tcx)); + let is_supertrait_of_current_trait = + supertraits.as_ref().unwrap().contains(&projection_trait_ref); + + if is_supertrait_of_current_trait { + false // do not walk contained types, do not report error, do collect $200 + } else { + true // DO walk contained types, POSSIBLY reporting an error + } + } + + _ => true, // walk contained types, if any + } + }); + + error +} + +pub fn provide(providers: &mut ty::query::Providers<'_>) { + *providers = ty::query::Providers { object_safety_violations, ..*providers }; +} diff --git a/src/librustc_trait_selection/traits/on_unimplemented.rs b/src/librustc_trait_selection/traits/on_unimplemented.rs new file mode 100644 index 00000000000..19260293ee6 --- /dev/null +++ b/src/librustc_trait_selection/traits/on_unimplemented.rs @@ -0,0 +1,383 @@ +use fmt_macros::{Parser, Piece, Position}; + +use rustc::ty::{self, GenericParamDefKind, TyCtxt}; +use rustc::util::common::ErrorReported; + +use rustc_ast::ast::{MetaItem, NestedMetaItem}; +use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::struct_span_err; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::Span; + +#[derive(Clone, Debug)] +pub struct OnUnimplementedFormatString(Symbol); + +#[derive(Debug)] +pub struct OnUnimplementedDirective { + pub condition: Option, + pub subcommands: Vec, + pub message: Option, + pub label: Option, + pub note: Option, + pub enclosing_scope: Option, +} + +#[derive(Default)] +pub struct OnUnimplementedNote { + pub message: Option, + pub label: Option, + pub note: Option, + pub enclosing_scope: Option, +} + +fn parse_error( + tcx: TyCtxt<'_>, + span: Span, + message: &str, + label: &str, + note: Option<&str>, +) -> ErrorReported { + let mut diag = struct_span_err!(tcx.sess, span, E0232, "{}", message); + diag.span_label(span, label); + if let Some(note) = note { + diag.note(note); + } + diag.emit(); + ErrorReported +} + +impl<'tcx> OnUnimplementedDirective { + fn parse( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + items: &[NestedMetaItem], + span: Span, + is_root: bool, + ) -> Result { + let mut errored = false; + let mut item_iter = items.iter(); + + let condition = if is_root { + None + } else { + let cond = item_iter + .next() + .ok_or_else(|| { + parse_error( + tcx, + span, + "empty `on`-clause in `#[rustc_on_unimplemented]`", + "empty on-clause here", + None, + ) + })? + .meta_item() + .ok_or_else(|| { + parse_error( + tcx, + span, + "invalid `on`-clause in `#[rustc_on_unimplemented]`", + "invalid on-clause here", + None, + ) + })?; + attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true); + Some(cond.clone()) + }; + + let mut message = None; + let mut label = None; + let mut note = None; + let mut enclosing_scope = None; + let mut subcommands = vec![]; + + let parse_value = |value_str| { + OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some) + }; + + for item in item_iter { + if item.check_name(sym::message) && message.is_none() { + if let Some(message_) = item.value_str() { + message = parse_value(message_)?; + continue; + } + } else if item.check_name(sym::label) && label.is_none() { + if let Some(label_) = item.value_str() { + label = parse_value(label_)?; + continue; + } + } else if item.check_name(sym::note) && note.is_none() { + if let Some(note_) = item.value_str() { + note = parse_value(note_)?; + continue; + } + } else if item.check_name(sym::enclosing_scope) && enclosing_scope.is_none() { + if let Some(enclosing_scope_) = item.value_str() { + enclosing_scope = parse_value(enclosing_scope_)?; + continue; + } + } else if item.check_name(sym::on) + && is_root + && message.is_none() + && label.is_none() + && note.is_none() + { + if let Some(items) = item.meta_item_list() { + if let Ok(subcommand) = + Self::parse(tcx, trait_def_id, &items, item.span(), false) + { + subcommands.push(subcommand); + } else { + errored = true; + } + continue; + } + } + + // nothing found + parse_error( + tcx, + item.span(), + "this attribute must have a valid value", + "expected value here", + Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#), + ); + } + + if errored { + Err(ErrorReported) + } else { + Ok(OnUnimplementedDirective { + condition, + subcommands, + message, + label, + note, + enclosing_scope, + }) + } + } + + pub fn of_item( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + impl_def_id: DefId, + ) -> Result, ErrorReported> { + let attrs = tcx.get_attrs(impl_def_id); + + let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) { + item + } else { + return Ok(None); + }; + + let result = if let Some(items) = attr.meta_item_list() { + Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some) + } else if let Some(value) = attr.value_str() { + Ok(Some(OnUnimplementedDirective { + condition: None, + message: None, + subcommands: vec![], + label: Some(OnUnimplementedFormatString::try_parse( + tcx, + trait_def_id, + value, + attr.span, + )?), + note: None, + enclosing_scope: None, + })) + } else { + return Err(ErrorReported); + }; + debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result); + result + } + + pub fn evaluate( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &[(Symbol, Option)], + ) -> OnUnimplementedNote { + let mut message = None; + let mut label = None; + let mut note = None; + let mut enclosing_scope = None; + info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); + + for command in self.subcommands.iter().chain(Some(self)).rev() { + if let Some(ref condition) = command.condition { + if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| { + c.ident().map_or(false, |ident| { + options.contains(&(ident.name, c.value_str().map(|s| s.to_string()))) + }) + }) { + debug!("evaluate: skipping {:?} due to condition", command); + continue; + } + } + debug!("evaluate: {:?} succeeded", command); + if let Some(ref message_) = command.message { + message = Some(message_.clone()); + } + + if let Some(ref label_) = command.label { + label = Some(label_.clone()); + } + + if let Some(ref note_) = command.note { + note = Some(note_.clone()); + } + + if let Some(ref enclosing_scope_) = command.enclosing_scope { + enclosing_scope = Some(enclosing_scope_.clone()); + } + } + + let options: FxHashMap = + options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect(); + OnUnimplementedNote { + label: label.map(|l| l.format(tcx, trait_ref, &options)), + message: message.map(|m| m.format(tcx, trait_ref, &options)), + note: note.map(|n| n.format(tcx, trait_ref, &options)), + enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)), + } + } +} + +impl<'tcx> OnUnimplementedFormatString { + fn try_parse( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + from: Symbol, + err_sp: Span, + ) -> Result { + let result = OnUnimplementedFormatString(from); + result.verify(tcx, trait_def_id, err_sp)?; + Ok(result) + } + + fn verify( + &self, + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + span: Span, + ) -> Result<(), ErrorReported> { + let name = tcx.item_name(trait_def_id); + let generics = tcx.generics_of(trait_def_id); + let s = self.0.as_str(); + let parser = Parser::new(&s, None, vec![], false); + let mut result = Ok(()); + for token in parser { + match token { + Piece::String(_) => (), // Normal string, no need to check it + Piece::NextArgument(a) => match a.position { + // `{Self}` is allowed + Position::ArgumentNamed(s) if s == kw::SelfUpper => (), + // `{ThisTraitsName}` is allowed + Position::ArgumentNamed(s) if s == name => (), + // `{from_method}` is allowed + Position::ArgumentNamed(s) if s == sym::from_method => (), + // `{from_desugaring}` is allowed + Position::ArgumentNamed(s) if s == sym::from_desugaring => (), + // `{ItemContext}` is allowed + Position::ArgumentNamed(s) if s == sym::item_context => (), + // So is `{A}` if A is a type parameter + Position::ArgumentNamed(s) => { + match generics.params.iter().find(|param| param.name == s) { + Some(_) => (), + None => { + struct_span_err!( + tcx.sess, + span, + E0230, + "there is no parameter `{}` on trait `{}`", + s, + name + ) + .emit(); + result = Err(ErrorReported); + } + } + } + // `{:1}` and `{}` are not to be used + Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => { + struct_span_err!( + tcx.sess, + span, + E0231, + "only named substitution parameters are allowed" + ) + .emit(); + result = Err(ErrorReported); + } + }, + } + } + + result + } + + pub fn format( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &FxHashMap, + ) -> String { + let name = tcx.item_name(trait_ref.def_id); + let trait_str = tcx.def_path_str(trait_ref.def_id); + let generics = tcx.generics_of(trait_ref.def_id); + let generic_map = generics + .params + .iter() + .filter_map(|param| { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => return None, + }; + let name = param.name; + Some((name, value)) + }) + .collect::>(); + let empty_string = String::new(); + + let s = self.0.as_str(); + let parser = Parser::new(&s, None, vec![], false); + let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string); + parser + .map(|p| match p { + Piece::String(s) => s, + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(s) => match generic_map.get(&s) { + Some(val) => val, + None if s == name => &trait_str, + None => { + if let Some(val) = options.get(&s) { + val + } else if s == sym::from_desugaring || s == sym::from_method { + // don't break messages using these two arguments incorrectly + &empty_string + } else if s == sym::item_context { + &item_context + } else { + bug!( + "broken on_unimplemented {:?} for {:?}: \ + no argument matching {:?}", + self.0, + trait_ref, + s + ) + } + } + }, + _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), + }, + }) + .collect() + } +} diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs new file mode 100644 index 00000000000..1ad91574212 --- /dev/null +++ b/src/librustc_trait_selection/traits/project.rs @@ -0,0 +1,1515 @@ +//! Code for projecting associated types out of trait references. + +use super::elaborate_predicates; +use super::specialization_graph; +use super::translate_substs; +use super::util; +use super::MismatchedProjectionTypes; +use super::Obligation; +use super::ObligationCause; +use super::PredicateObligation; +use super::Selection; +use super::SelectionContext; +use super::SelectionError; +use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use super::{VtableClosureData, VtableFnPointerData, VtableGeneratorData, VtableImplData}; + +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; +use crate::traits::error_reporting::InferCtxtExt; +use rustc::ty::fold::{TypeFoldable, TypeFolder}; +use rustc::ty::subst::{InternalSubsts, Subst}; +use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_ast::ast::Ident; +use rustc_errors::ErrorReported; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::sym; +use rustc_span::DUMMY_SP; + +pub use rustc::traits::Reveal; + +pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; + +pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<'tcx>>; + +pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>>; + +/// When attempting to resolve `::Name` ... +#[derive(Debug)] +pub enum ProjectionTyError<'tcx> { + /// ...we found multiple sources of information and couldn't resolve the ambiguity. + TooManyCandidates, + + /// ...an error occurred matching `T : TraitRef` + TraitSelectionError(SelectionError<'tcx>), +} + +#[derive(PartialEq, Eq, Debug)] +enum ProjectionTyCandidate<'tcx> { + // from a where-clause in the env or object type + ParamEnv(ty::PolyProjectionPredicate<'tcx>), + + // from the definition of `Trait` when you have something like <::B as Trait2>::C + TraitDef(ty::PolyProjectionPredicate<'tcx>), + + // from a "impl" (or a "pseudo-impl" returned by select) + Select(Selection<'tcx>), +} + +enum ProjectionTyCandidateSet<'tcx> { + None, + Single(ProjectionTyCandidate<'tcx>), + Ambiguous, + Error(SelectionError<'tcx>), +} + +impl<'tcx> ProjectionTyCandidateSet<'tcx> { + fn mark_ambiguous(&mut self) { + *self = ProjectionTyCandidateSet::Ambiguous; + } + + fn mark_error(&mut self, err: SelectionError<'tcx>) { + *self = ProjectionTyCandidateSet::Error(err); + } + + // Returns true if the push was successful, or false if the candidate + // was discarded -- this could be because of ambiguity, or because + // a higher-priority candidate is already there. + fn push_candidate(&mut self, candidate: ProjectionTyCandidate<'tcx>) -> bool { + use self::ProjectionTyCandidate::*; + use self::ProjectionTyCandidateSet::*; + + // This wacky variable is just used to try and + // make code readable and avoid confusing paths. + // It is assigned a "value" of `()` only on those + // paths in which we wish to convert `*self` to + // ambiguous (and return false, because the candidate + // was not used). On other paths, it is not assigned, + // and hence if those paths *could* reach the code that + // comes after the match, this fn would not compile. + let convert_to_ambiguous; + + match self { + None => { + *self = Single(candidate); + return true; + } + + Single(current) => { + // Duplicates can happen inside ParamEnv. In the case, we + // perform a lazy deduplication. + if current == &candidate { + return false; + } + + // Prefer where-clauses. As in select, if there are multiple + // candidates, we prefer where-clause candidates over impls. This + // may seem a bit surprising, since impls are the source of + // "truth" in some sense, but in fact some of the impls that SEEM + // applicable are not, because of nested obligations. Where + // clauses are the safer choice. See the comment on + // `select::SelectionCandidate` and #21974 for more details. + match (current, candidate) { + (ParamEnv(..), ParamEnv(..)) => convert_to_ambiguous = (), + (ParamEnv(..), _) => return false, + (_, ParamEnv(..)) => unreachable!(), + (_, _) => convert_to_ambiguous = (), + } + } + + Ambiguous | Error(..) => { + return false; + } + } + + // We only ever get here when we moved from a single candidate + // to ambiguous. + let () = convert_to_ambiguous; + *self = Ambiguous; + false + } +} + +/// Evaluates constraints of the form: +/// +/// for<...> ::U == V +/// +/// If successful, this may result in additional obligations. Also returns +/// the projection cache key used to track these additional obligations. +pub fn poly_project_and_unify_type<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &PolyProjectionObligation<'tcx>, +) -> Result>>, MismatchedProjectionTypes<'tcx>> { + debug!("poly_project_and_unify_type(obligation={:?})", obligation); + + let infcx = selcx.infcx(); + infcx.commit_if_ok(|snapshot| { + let (placeholder_predicate, placeholder_map) = + infcx.replace_bound_vars_with_placeholders(&obligation.predicate); + + let placeholder_obligation = obligation.with(placeholder_predicate); + let result = project_and_unify_type(selcx, &placeholder_obligation)?; + infcx + .leak_check(false, &placeholder_map, snapshot) + .map_err(|err| MismatchedProjectionTypes { err })?; + Ok(result) + }) +} + +/// Evaluates constraints of the form: +/// +/// ::U == V +/// +/// If successful, this may result in additional obligations. +fn project_and_unify_type<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionObligation<'tcx>, +) -> Result>>, MismatchedProjectionTypes<'tcx>> { + debug!("project_and_unify_type(obligation={:?})", obligation); + + let mut obligations = vec![]; + let normalized_ty = match opt_normalize_projection_type( + selcx, + obligation.param_env, + obligation.predicate.projection_ty, + obligation.cause.clone(), + obligation.recursion_depth, + &mut obligations, + ) { + Some(n) => n, + None => return Ok(None), + }; + + debug!( + "project_and_unify_type: normalized_ty={:?} obligations={:?}", + normalized_ty, obligations + ); + + let infcx = selcx.infcx(); + match infcx + .at(&obligation.cause, obligation.param_env) + .eq(normalized_ty, obligation.predicate.ty) + { + Ok(InferOk { obligations: inferred_obligations, value: () }) => { + obligations.extend(inferred_obligations); + Ok(Some(obligations)) + } + Err(err) => { + debug!("project_and_unify_type: equating types encountered error {:?}", err); + Err(MismatchedProjectionTypes { err }) + } + } +} + +/// Normalizes any associated type projections in `value`, replacing +/// them with a fully resolved type where possible. The return value +/// combines the normalized result and any additional obligations that +/// were incurred as result. +pub fn normalize<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + value: &T, +) -> Normalized<'tcx, T> +where + T: TypeFoldable<'tcx>, +{ + let mut obligations = Vec::new(); + let value = normalize_to(selcx, param_env, cause, value, &mut obligations); + Normalized { value, obligations } +} + +pub fn normalize_to<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + value: &T, + obligations: &mut Vec>, +) -> T +where + T: TypeFoldable<'tcx>, +{ + normalize_with_depth_to(selcx, param_env, cause, 0, value, obligations) +} + +/// As `normalize`, but with a custom depth. +pub fn normalize_with_depth<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: &T, +) -> Normalized<'tcx, T> +where + T: TypeFoldable<'tcx>, +{ + let mut obligations = Vec::new(); + let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); + Normalized { value, obligations } +} + +pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: &T, + obligations: &mut Vec>, +) -> T +where + T: TypeFoldable<'tcx>, +{ + debug!("normalize_with_depth(depth={}, value={:?})", depth, value); + let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); + let result = normalizer.fold(value); + debug!( + "normalize_with_depth: depth={} result={:?} with {} obligations", + depth, + result, + normalizer.obligations.len() + ); + debug!("normalize_with_depth: depth={} obligations={:?}", depth, normalizer.obligations); + result +} + +struct AssocTypeNormalizer<'a, 'b, 'tcx> { + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + obligations: &'a mut Vec>, + depth: usize, +} + +impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { + fn new( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &'a mut Vec>, + ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { + AssocTypeNormalizer { selcx, param_env, cause, obligations, depth } + } + + fn fold>(&mut self, value: &T) -> T { + let value = self.selcx.infcx().resolve_vars_if_possible(value); + + if !value.has_projections() { value } else { value.fold_with(self) } + } +} + +impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { + fn tcx<'c>(&'c self) -> TyCtxt<'tcx> { + self.selcx.tcx() + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_projections() { + return ty; + } + // We don't want to normalize associated types that occur inside of region + // binders, because they may contain bound regions, and we can't cope with that. + // + // Example: + // + // for<'a> fn(>::A) + // + // Instead of normalizing `>::A` here, we'll + // normalize it when we instantiate those bound regions (which + // should occur eventually). + + let ty = ty.super_fold_with(self); + match ty.kind { + ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { + // (*) + // Only normalize `impl Trait` after type-checking, usually in codegen. + match self.param_env.reveal { + Reveal::UserFacing => ty, + + Reveal::All => { + let recursion_limit = *self.tcx().sess.recursion_limit.get(); + if self.depth >= recursion_limit { + let obligation = Obligation::with_depth( + self.cause.clone(), + recursion_limit, + self.param_env, + ty, + ); + self.selcx.infcx().report_overflow_error(&obligation, true); + } + + let generic_ty = self.tcx().type_of(def_id); + let concrete_ty = generic_ty.subst(self.tcx(), substs); + self.depth += 1; + let folded_ty = self.fold_ty(concrete_ty); + self.depth -= 1; + folded_ty + } + } + } + + ty::Projection(ref data) if !data.has_escaping_bound_vars() => { + // (*) + + // (*) This is kind of hacky -- we need to be able to + // handle normalization within binders because + // otherwise we wind up a need to normalize when doing + // trait matching (since you can have a trait + // obligation like `for<'a> T::B : Fn(&'a int)`), but + // we can't normalize with bound regions in scope. So + // far now we just ignore binders but only normalize + // if all bound regions are gone (and then we still + // have to renormalize whenever we instantiate a + // binder). It would be better to normalize in a + // binding-aware fashion. + + let normalized_ty = normalize_projection_type( + self.selcx, + self.param_env, + *data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ); + debug!( + "AssocTypeNormalizer: depth={} normalized {:?} to {:?}, \ + now with {} obligations", + self.depth, + ty, + normalized_ty, + self.obligations.len() + ); + normalized_ty + } + + _ => ty, + } + } + + fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + constant.eval(self.selcx.tcx(), self.param_env) + } +} + +/// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly +/// additional obligations). If ambiguity arises, which implies that +/// there are unresolved type variables in the projection, we will +/// substitute a fresh type variable `$X` and generate a new +/// obligation `::Item == $X` for later. +pub fn normalize_projection_type<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec>, +) -> Ty<'tcx> { + opt_normalize_projection_type( + selcx, + param_env, + projection_ty, + cause.clone(), + depth, + obligations, + ) + .unwrap_or_else(move || { + // if we bottom out in ambiguity, create a type variable + // and a deferred predicate to resolve this when more type + // information is available. + + let tcx = selcx.infcx().tcx; + let def_id = projection_ty.item_def_id; + let ty_var = selcx.infcx().next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: tcx.def_span(def_id), + }); + let projection = ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, ty: ty_var }); + let obligation = + Obligation::with_depth(cause, depth + 1, param_env, projection.to_predicate()); + obligations.push(obligation); + ty_var + }) +} + +/// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly +/// additional obligations). Returns `None` in the case of ambiguity, +/// which indicates that there are unbound type variables. +/// +/// This function used to return `Option>`, which contains a +/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very +/// often immediately appended to another obligations vector. So now this +/// function takes an obligations vector and appends to it directly, which is +/// slightly uglier but avoids the need for an extra short-lived allocation. +fn opt_normalize_projection_type<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec>, +) -> Option> { + let infcx = selcx.infcx(); + + let projection_ty = infcx.resolve_vars_if_possible(&projection_ty); + let cache_key = ProjectionCacheKey::new(projection_ty); + + debug!( + "opt_normalize_projection_type(\ + projection_ty={:?}, \ + depth={})", + projection_ty, depth + ); + + // FIXME(#20304) For now, I am caching here, which is good, but it + // means we don't capture the type variables that are created in + // the case of ambiguity. Which means we may create a large stream + // of such variables. OTOH, if we move the caching up a level, we + // would not benefit from caching when proving `T: Trait` + // bounds. It might be the case that we want two distinct caches, + // or else another kind of cache entry. + + let cache_result = infcx.inner.borrow_mut().projection_cache.try_start(cache_key); + match cache_result { + Ok(()) => {} + Err(ProjectionCacheEntry::Ambiguous) => { + // If we found ambiguity the last time, that means we will continue + // to do so until some type in the key changes (and we know it + // hasn't, because we just fully resolved it). + debug!( + "opt_normalize_projection_type: \ + found cache entry: ambiguous" + ); + return None; + } + Err(ProjectionCacheEntry::InProgress) => { + // If while normalized A::B, we are asked to normalize + // A::B, just return A::B itself. This is a conservative + // answer, in the sense that A::B *is* clearly equivalent + // to A::B, though there may be a better value we can + // find. + + // Under lazy normalization, this can arise when + // bootstrapping. That is, imagine an environment with a + // where-clause like `A::B == u32`. Now, if we are asked + // to normalize `A::B`, we will want to check the + // where-clauses in scope. So we will try to unify `A::B` + // with `A::B`, which can trigger a recursive + // normalization. In that case, I think we will want this code: + // + // ``` + // let ty = selcx.tcx().mk_projection(projection_ty.item_def_id, + // projection_ty.substs; + // return Some(NormalizedTy { value: v, obligations: vec![] }); + // ``` + + debug!( + "opt_normalize_projection_type: \ + found cache entry: in-progress" + ); + + // But for now, let's classify this as an overflow: + let recursion_limit = *selcx.tcx().sess.recursion_limit.get(); + let obligation = + Obligation::with_depth(cause, recursion_limit, param_env, projection_ty); + selcx.infcx().report_overflow_error(&obligation, false); + } + Err(ProjectionCacheEntry::NormalizedTy(ty)) => { + // This is the hottest path in this function. + // + // If we find the value in the cache, then return it along + // with the obligations that went along with it. Note + // that, when using a fulfillment context, these + // obligations could in principle be ignored: they have + // already been registered when the cache entry was + // created (and hence the new ones will quickly be + // discarded as duplicated). But when doing trait + // evaluation this is not the case, and dropping the trait + // evaluations can causes ICEs (e.g., #43132). + debug!( + "opt_normalize_projection_type: \ + found normalized ty `{:?}`", + ty + ); + + // Once we have inferred everything we need to know, we + // can ignore the `obligations` from that point on. + if infcx.unresolved_type_vars(&ty.value).is_none() { + infcx.inner.borrow_mut().projection_cache.complete_normalized(cache_key, &ty); + // No need to extend `obligations`. + } else { + obligations.extend(ty.obligations); + } + + obligations.push(get_paranoid_cache_value_obligation( + infcx, + param_env, + projection_ty, + cause, + depth, + )); + return Some(ty.value); + } + Err(ProjectionCacheEntry::Error) => { + debug!( + "opt_normalize_projection_type: \ + found error" + ); + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + return Some(result.value); + } + } + + let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty); + match project_type(selcx, &obligation) { + Ok(ProjectedTy::Progress(Progress { + ty: projected_ty, + obligations: mut projected_obligations, + })) => { + // if projection succeeded, then what we get out of this + // is also non-normalized (consider: it was derived from + // an impl, where-clause etc) and hence we must + // re-normalize it + + debug!( + "opt_normalize_projection_type: \ + projected_ty={:?} \ + depth={} \ + projected_obligations={:?}", + projected_ty, depth, projected_obligations + ); + + let result = if projected_ty.has_projections() { + let mut normalizer = AssocTypeNormalizer::new( + selcx, + param_env, + cause, + depth + 1, + &mut projected_obligations, + ); + let normalized_ty = normalizer.fold(&projected_ty); + + debug!( + "opt_normalize_projection_type: \ + normalized_ty={:?} depth={}", + normalized_ty, depth + ); + + Normalized { value: normalized_ty, obligations: projected_obligations } + } else { + Normalized { value: projected_ty, obligations: projected_obligations } + }; + + let cache_value = prune_cache_value_obligations(infcx, &result); + infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, cache_value); + obligations.extend(result.obligations); + Some(result.value) + } + Ok(ProjectedTy::NoProgress(projected_ty)) => { + debug!( + "opt_normalize_projection_type: \ + projected_ty={:?} no progress", + projected_ty + ); + let result = Normalized { value: projected_ty, obligations: vec![] }; + infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, result.clone()); + // No need to extend `obligations`. + Some(result.value) + } + Err(ProjectionTyError::TooManyCandidates) => { + debug!( + "opt_normalize_projection_type: \ + too many candidates" + ); + infcx.inner.borrow_mut().projection_cache.ambiguous(cache_key); + None + } + Err(ProjectionTyError::TraitSelectionError(_)) => { + debug!("opt_normalize_projection_type: ERROR"); + // if we got an error processing the `T as Trait` part, + // just return `ty::err` but add the obligation `T : + // Trait`, which when processed will cause the error to be + // reported later + + infcx.inner.borrow_mut().projection_cache.error(cache_key); + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + Some(result.value) + } + } +} + +/// If there are unresolved type variables, then we need to include +/// any subobligations that bind them, at least until those type +/// variables are fully resolved. +fn prune_cache_value_obligations<'a, 'tcx>( + infcx: &'a InferCtxt<'a, 'tcx>, + result: &NormalizedTy<'tcx>, +) -> NormalizedTy<'tcx> { + if infcx.unresolved_type_vars(&result.value).is_none() { + return NormalizedTy { value: result.value, obligations: vec![] }; + } + + let mut obligations: Vec<_> = result + .obligations + .iter() + .filter(|obligation| match obligation.predicate { + // We found a `T: Foo` predicate, let's check + // if `U` references any unresolved type + // variables. In principle, we only care if this + // projection can help resolve any of the type + // variables found in `result.value` -- but we just + // check for any type variables here, for fear of + // indirect obligations (e.g., we project to `?0`, + // but we have `T: Foo` and `?1: Bar`). + ty::Predicate::Projection(ref data) => infcx.unresolved_type_vars(&data.ty()).is_some(), + + // We are only interested in `T: Foo` predicates, whre + // `U` references one of `unresolved_type_vars`. =) + _ => false, + }) + .cloned() + .collect(); + + obligations.shrink_to_fit(); + + NormalizedTy { value: result.value, obligations } +} + +/// Whenever we give back a cache result for a projection like `::Item ==> X`, we *always* include the obligation to prove +/// that `T: Trait` (we may also include some other obligations). This +/// may or may not be necessary -- in principle, all the obligations +/// that must be proven to show that `T: Trait` were also returned +/// when the cache was first populated. But there are some vague concerns, +/// and so we take the precautionary measure of including `T: Trait` in +/// the result: +/// +/// Concern #1. The current setup is fragile. Perhaps someone could +/// have failed to prove the concerns from when the cache was +/// populated, but also not have used a snapshot, in which case the +/// cache could remain populated even though `T: Trait` has not been +/// shown. In this case, the "other code" is at fault -- when you +/// project something, you are supposed to either have a snapshot or +/// else prove all the resulting obligations -- but it's still easy to +/// get wrong. +/// +/// Concern #2. Even within the snapshot, if those original +/// obligations are not yet proven, then we are able to do projections +/// that may yet turn out to be wrong. This *may* lead to some sort +/// of trouble, though we don't have a concrete example of how that +/// can occur yet. But it seems risky at best. +fn get_paranoid_cache_value_obligation<'a, 'tcx>( + infcx: &'a InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, +) -> PredicateObligation<'tcx> { + let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); + Obligation { + cause, + recursion_depth: depth, + param_env, + predicate: trait_ref.without_const().to_predicate(), + } +} + +/// If we are projecting `::Item`, but `T: Trait` does not +/// hold. In various error cases, we cannot generate a valid +/// normalized projection. Therefore, we create an inference variable +/// return an associated obligation that, when fulfilled, will lead to +/// an error. +/// +/// Note that we used to return `Error` here, but that was quite +/// dubious -- the premise was that an error would *eventually* be +/// reported, when the obligation was processed. But in general once +/// you see a `Error` you are supposed to be able to assume that an +/// error *has been* reported, so that you can take whatever heuristic +/// paths you want to take. To make things worse, it was possible for +/// cycles to arise, where you basically had a setup like ` +/// as Trait>::Foo == $0`. Here, normalizing ` as +/// Trait>::Foo> to `[type error]` would lead to an obligation of +/// ` as Trait>::Foo`. We are supposed to report +/// an error for this obligation, but we legitimately should not, +/// because it contains `[type error]`. Yuck! (See issue #29857 for +/// one case where this arose.) +fn normalize_to_error<'a, 'tcx>( + selcx: &mut SelectionContext<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, +) -> NormalizedTy<'tcx> { + let trait_ref = projection_ty.trait_ref(selcx.tcx()).to_poly_trait_ref(); + let trait_obligation = Obligation { + cause, + recursion_depth: depth, + param_env, + predicate: trait_ref.without_const().to_predicate(), + }; + let tcx = selcx.infcx().tcx; + let def_id = projection_ty.item_def_id; + let new_value = selcx.infcx().next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: tcx.def_span(def_id), + }); + Normalized { value: new_value, obligations: vec![trait_obligation] } +} + +enum ProjectedTy<'tcx> { + Progress(Progress<'tcx>), + NoProgress(Ty<'tcx>), +} + +struct Progress<'tcx> { + ty: Ty<'tcx>, + obligations: Vec>, +} + +impl<'tcx> Progress<'tcx> { + fn error(tcx: TyCtxt<'tcx>) -> Self { + Progress { ty: tcx.types.err, obligations: vec![] } + } + + fn with_addl_obligations(mut self, mut obligations: Vec>) -> Self { + debug!( + "with_addl_obligations: self.obligations.len={} obligations.len={}", + self.obligations.len(), + obligations.len() + ); + + debug!( + "with_addl_obligations: self.obligations={:?} obligations={:?}", + self.obligations, obligations + ); + + self.obligations.append(&mut obligations); + self + } +} + +/// Computes the result of a projection type (if we can). +/// +/// IMPORTANT: +/// - `obligation` must be fully normalized +fn project_type<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, +) -> Result, ProjectionTyError<'tcx>> { + debug!("project(obligation={:?})", obligation); + + let recursion_limit = *selcx.tcx().sess.recursion_limit.get(); + if obligation.recursion_depth >= recursion_limit { + debug!("project: overflow!"); + return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow)); + } + + let obligation_trait_ref = &obligation.predicate.trait_ref(selcx.tcx()); + + debug!("project: obligation_trait_ref={:?}", obligation_trait_ref); + + if obligation_trait_ref.references_error() { + return Ok(ProjectedTy::Progress(Progress::error(selcx.tcx()))); + } + + let mut candidates = ProjectionTyCandidateSet::None; + + // Make sure that the following procedures are kept in order. ParamEnv + // needs to be first because it has highest priority, and Select checks + // the return value of push_candidate which assumes it's ran at last. + assemble_candidates_from_param_env(selcx, obligation, &obligation_trait_ref, &mut candidates); + + assemble_candidates_from_trait_def(selcx, obligation, &obligation_trait_ref, &mut candidates); + + assemble_candidates_from_impls(selcx, obligation, &obligation_trait_ref, &mut candidates); + + match candidates { + ProjectionTyCandidateSet::Single(candidate) => Ok(ProjectedTy::Progress( + confirm_candidate(selcx, obligation, &obligation_trait_ref, candidate), + )), + ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress( + selcx + .tcx() + .mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs), + )), + // Error occurred while trying to processing impls. + ProjectionTyCandidateSet::Error(e) => Err(ProjectionTyError::TraitSelectionError(e)), + // Inherent ambiguity that prevents us from even enumerating the + // candidates. + ProjectionTyCandidateSet::Ambiguous => Err(ProjectionTyError::TooManyCandidates), + } +} + +/// The first thing we have to do is scan through the parameter +/// environment to see whether there are any projection predicates +/// there that can answer this question. +fn assemble_candidates_from_param_env<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + candidate_set: &mut ProjectionTyCandidateSet<'tcx>, +) { + debug!("assemble_candidates_from_param_env(..)"); + assemble_candidates_from_predicates( + selcx, + obligation, + obligation_trait_ref, + candidate_set, + ProjectionTyCandidate::ParamEnv, + obligation.param_env.caller_bounds.iter().cloned(), + ); +} + +/// In the case of a nested projection like <::FooT as Bar>::BarT, we may find +/// that the definition of `Foo` has some clues: +/// +/// ``` +/// trait Foo { +/// type FooT : Bar +/// } +/// ``` +/// +/// Here, for example, we could conclude that the result is `i32`. +fn assemble_candidates_from_trait_def<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + candidate_set: &mut ProjectionTyCandidateSet<'tcx>, +) { + debug!("assemble_candidates_from_trait_def(..)"); + + let tcx = selcx.tcx(); + // Check whether the self-type is itself a projection. + let (def_id, substs) = match obligation_trait_ref.self_ty().kind { + ty::Projection(ref data) => (data.trait_ref(tcx).def_id, data.substs), + ty::Opaque(def_id, substs) => (def_id, substs), + ty::Infer(ty::TyVar(_)) => { + // If the self-type is an inference variable, then it MAY wind up + // being a projected type, so induce an ambiguity. + candidate_set.mark_ambiguous(); + return; + } + _ => return, + }; + + // If so, extract what we know from the trait and try to come up with a good answer. + let trait_predicates = tcx.predicates_of(def_id); + let bounds = trait_predicates.instantiate(tcx, substs); + let bounds = elaborate_predicates(tcx, bounds.predicates); + assemble_candidates_from_predicates( + selcx, + obligation, + obligation_trait_ref, + candidate_set, + ProjectionTyCandidate::TraitDef, + bounds, + ) +} + +fn assemble_candidates_from_predicates<'cx, 'tcx, I>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + candidate_set: &mut ProjectionTyCandidateSet<'tcx>, + ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>, + env_predicates: I, +) where + I: IntoIterator>, +{ + debug!("assemble_candidates_from_predicates(obligation={:?})", obligation); + let infcx = selcx.infcx(); + for predicate in env_predicates { + debug!("assemble_candidates_from_predicates: predicate={:?}", predicate); + if let ty::Predicate::Projection(data) = predicate { + let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id; + + let is_match = same_def_id + && infcx.probe(|_| { + let data_poly_trait_ref = data.to_poly_trait_ref(infcx.tcx); + let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation_poly_trait_ref, data_poly_trait_ref) + .map(|InferOk { obligations: _, value: () }| { + // FIXME(#32730) -- do we need to take obligations + // into account in any way? At the moment, no. + }) + .is_ok() + }); + + debug!( + "assemble_candidates_from_predicates: candidate={:?} \ + is_match={} same_def_id={}", + data, is_match, same_def_id + ); + + if is_match { + candidate_set.push_candidate(ctor(data)); + } + } + } +} + +fn assemble_candidates_from_impls<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + candidate_set: &mut ProjectionTyCandidateSet<'tcx>, +) { + // If we are resolving `>::Item == Type`, + // start out by selecting the predicate `T as TraitRef<...>`: + let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); + let _ = selcx.infcx().commit_if_ok(|_| { + let vtable = match selcx.select(&trait_obligation) { + Ok(Some(vtable)) => vtable, + Ok(None) => { + candidate_set.mark_ambiguous(); + return Err(()); + } + Err(e) => { + debug!("assemble_candidates_from_impls: selection error {:?}", e); + candidate_set.mark_error(e); + return Err(()); + } + }; + + let eligible = match &vtable { + super::VtableClosure(_) + | super::VtableGenerator(_) + | super::VtableFnPointer(_) + | super::VtableObject(_) + | super::VtableTraitAlias(_) => { + debug!("assemble_candidates_from_impls: vtable={:?}", vtable); + true + } + super::VtableImpl(impl_data) => { + // We have to be careful when projecting out of an + // impl because of specialization. If we are not in + // codegen (i.e., projection mode is not "any"), and the + // impl's type is declared as default, then we disable + // projection (even if the trait ref is fully + // monomorphic). In the case where trait ref is not + // fully monomorphic (i.e., includes type parameters), + // this is because those type parameters may + // ultimately be bound to types from other crates that + // may have specialized impls we can't see. In the + // case where the trait ref IS fully monomorphic, this + // is a policy decision that we made in the RFC in + // order to preserve flexibility for the crate that + // defined the specializable impl to specialize later + // for existing types. + // + // In either case, we handle this by not adding a + // candidate for an impl if it contains a `default` + // type. + // + // NOTE: This should be kept in sync with the similar code in + // `rustc::ty::instance::resolve_associated_item()`. + let node_item = + assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id) + .map_err(|ErrorReported| ())?; + + let is_default = if node_item.node.is_from_trait() { + // If true, the impl inherited a `type Foo = Bar` + // given in the trait, which is implicitly default. + // Otherwise, the impl did not specify `type` and + // neither did the trait: + // + // ```rust + // trait Foo { type T; } + // impl Foo for Bar { } + // ``` + // + // This is an error, but it will be + // reported in `check_impl_items_against_trait`. + // We accept it here but will flag it as + // an error when we confirm the candidate + // (which will ultimately lead to `normalize_to_error` + // being invoked). + false + } else { + // If we're looking at a trait *impl*, the item is + // specializable if the impl or the item are marked + // `default`. + node_item.item.defaultness.is_default() + || super::util::impl_is_default(selcx.tcx(), node_item.node.def_id()) + }; + + match is_default { + // Non-specializable items are always projectable + false => true, + + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + true if obligation.param_env.reveal == Reveal::All => { + // NOTE(eddyb) inference variables can resolve to parameters, so + // assume `poly_trait_ref` isn't monomorphic, if it contains any. + let poly_trait_ref = + selcx.infcx().resolve_vars_if_possible(&poly_trait_ref); + !poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst() + } + + true => { + debug!( + "assemble_candidates_from_impls: not eligible due to default: \ + assoc_ty={} predicate={}", + selcx.tcx().def_path_str(node_item.item.def_id), + obligation.predicate, + ); + false + } + } + } + super::VtableParam(..) => { + // This case tell us nothing about the value of an + // associated type. Consider: + // + // ``` + // trait SomeTrait { type Foo; } + // fn foo(...) { } + // ``` + // + // If the user writes `::Foo`, then the `T + // : SomeTrait` binding does not help us decide what the + // type `Foo` is (at least, not more specifically than + // what we already knew). + // + // But wait, you say! What about an example like this: + // + // ``` + // fn bar>(...) { ... } + // ``` + // + // Doesn't the `T : Sometrait` predicate help + // resolve `T::Foo`? And of course it does, but in fact + // that single predicate is desugared into two predicates + // in the compiler: a trait predicate (`T : SomeTrait`) and a + // projection. And the projection where clause is handled + // in `assemble_candidates_from_param_env`. + false + } + super::VtableAutoImpl(..) | super::VtableBuiltin(..) => { + // These traits have no associated types. + span_bug!( + obligation.cause.span, + "Cannot project an associated type from `{:?}`", + vtable + ); + } + }; + + if eligible { + if candidate_set.push_candidate(ProjectionTyCandidate::Select(vtable)) { + Ok(()) + } else { + Err(()) + } + } else { + Err(()) + } + }); +} + +fn confirm_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + candidate: ProjectionTyCandidate<'tcx>, +) -> Progress<'tcx> { + debug!("confirm_candidate(candidate={:?}, obligation={:?})", candidate, obligation); + + match candidate { + ProjectionTyCandidate::ParamEnv(poly_projection) + | ProjectionTyCandidate::TraitDef(poly_projection) => { + confirm_param_env_candidate(selcx, obligation, poly_projection) + } + + ProjectionTyCandidate::Select(vtable) => { + confirm_select_candidate(selcx, obligation, obligation_trait_ref, vtable) + } + } +} + +fn confirm_select_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + vtable: Selection<'tcx>, +) -> Progress<'tcx> { + match vtable { + super::VtableImpl(data) => confirm_impl_candidate(selcx, obligation, data), + super::VtableGenerator(data) => confirm_generator_candidate(selcx, obligation, data), + super::VtableClosure(data) => confirm_closure_candidate(selcx, obligation, data), + super::VtableFnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data), + super::VtableObject(_) => confirm_object_candidate(selcx, obligation, obligation_trait_ref), + super::VtableAutoImpl(..) + | super::VtableParam(..) + | super::VtableBuiltin(..) + | super::VtableTraitAlias(..) => + // we don't create Select candidates with this kind of resolution + { + span_bug!( + obligation.cause.span, + "Cannot project an associated type from `{:?}`", + vtable + ) + } + } +} + +fn confirm_object_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, +) -> Progress<'tcx> { + let self_ty = obligation_trait_ref.self_ty(); + let object_ty = selcx.infcx().shallow_resolve(self_ty); + debug!("confirm_object_candidate(object_ty={:?})", object_ty); + let data = match object_ty.kind { + ty::Dynamic(ref data, ..) => data, + _ => span_bug!( + obligation.cause.span, + "confirm_object_candidate called with non-object: {:?}", + object_ty + ), + }; + let env_predicates = data + .projection_bounds() + .map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate()) + .collect(); + let env_predicate = { + let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates); + + // select only those projections that are actually projecting an + // item with the correct name + let env_predicates = env_predicates.filter_map(|p| match p { + ty::Predicate::Projection(data) => { + if data.projection_def_id() == obligation.predicate.item_def_id { + Some(data) + } else { + None + } + } + _ => None, + }); + + // select those with a relevant trait-ref + let mut env_predicates = env_predicates.filter(|data| { + let data_poly_trait_ref = data.to_poly_trait_ref(selcx.tcx()); + let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + selcx.infcx().probe(|_| { + selcx + .infcx() + .at(&obligation.cause, obligation.param_env) + .sup(obligation_poly_trait_ref, data_poly_trait_ref) + .is_ok() + }) + }); + + // select the first matching one; there really ought to be one or + // else the object type is not WF, since an object type should + // include all of its projections explicitly + match env_predicates.next() { + Some(env_predicate) => env_predicate, + None => { + debug!( + "confirm_object_candidate: no env-predicate \ + found in object type `{:?}`; ill-formed", + object_ty + ); + return Progress::error(selcx.tcx()); + } + } + }; + + confirm_param_env_candidate(selcx, obligation, env_predicate) +} + +fn confirm_generator_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let gen_sig = vtable.substs.as_generator().poly_sig(vtable.generator_def_id, selcx.tcx()); + let Normalized { value: gen_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &gen_sig, + ); + + debug!( + "confirm_generator_candidate: obligation={:?},gen_sig={:?},obligations={:?}", + obligation, gen_sig, obligations + ); + + let tcx = selcx.tcx(); + + let gen_def_id = tcx.lang_items().gen_trait().unwrap(); + + let predicate = super::util::generator_trait_ref_and_outputs( + tcx, + gen_def_id, + obligation.predicate.self_ty(), + gen_sig, + ) + .map_bound(|(trait_ref, yield_ty, return_ty)| { + let name = tcx.associated_item(obligation.predicate.item_def_id).ident.name; + let ty = if name == sym::Return { + return_ty + } else if name == sym::Yield { + yield_ty + } else { + bug!() + }; + + ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + substs: trait_ref.substs, + item_def_id: obligation.predicate.item_def_id, + }, + ty, + } + }); + + confirm_param_env_candidate(selcx, obligation, predicate) + .with_addl_obligations(vtable.nested) + .with_addl_obligations(obligations) +} + +fn confirm_fn_pointer_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + fn_pointer_vtable: VtableFnPointerData<'tcx, PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty); + let sig = fn_type.fn_sig(selcx.tcx()); + let Normalized { value: sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &sig, + ); + + confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) + .with_addl_obligations(fn_pointer_vtable.nested) + .with_addl_obligations(obligations) +} + +fn confirm_closure_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + let infcx = selcx.infcx(); + let closure_sig_ty = vtable.substs.as_closure().sig_ty(vtable.closure_def_id, tcx); + let closure_sig = infcx.shallow_resolve(closure_sig_ty).fn_sig(tcx); + let Normalized { value: closure_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &closure_sig, + ); + + debug!( + "confirm_closure_candidate: obligation={:?},closure_sig={:?},obligations={:?}", + obligation, closure_sig, obligations + ); + + confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) + .with_addl_obligations(vtable.nested) + .with_addl_obligations(obligations) +} + +fn confirm_callable_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, + flag: util::TupleArgumentsFlag, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + + debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig); + + // the `Output` associated type is declared on `FnOnce` + let fn_once_def_id = tcx.lang_items().fn_once_trait().unwrap(); + + let predicate = super::util::closure_trait_ref_and_return_type( + tcx, + fn_once_def_id, + obligation.predicate.self_ty(), + fn_sig, + flag, + ) + .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + Ident::with_dummy_span(rustc_hir::FN_OUTPUT_NAME), + ), + ty: ret_type, + }); + + confirm_param_env_candidate(selcx, obligation, predicate) +} + +fn confirm_param_env_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, +) -> Progress<'tcx> { + let infcx = selcx.infcx(); + let cause = &obligation.cause; + let param_env = obligation.param_env; + + let (cache_entry, _) = infcx.replace_bound_vars_with_fresh_vars( + cause.span, + LateBoundRegionConversionTime::HigherRankedType, + &poly_cache_entry, + ); + + let cache_trait_ref = cache_entry.projection_ty.trait_ref(infcx.tcx); + let obligation_trait_ref = obligation.predicate.trait_ref(infcx.tcx); + match infcx.at(cause, param_env).eq(cache_trait_ref, obligation_trait_ref) { + Ok(InferOk { value: _, obligations }) => Progress { ty: cache_entry.ty, obligations }, + Err(e) => { + let msg = format!( + "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}", + obligation, poly_cache_entry, e, + ); + debug!("confirm_param_env_candidate: {}", msg); + infcx.tcx.sess.delay_span_bug(obligation.cause.span, &msg); + Progress { ty: infcx.tcx.types.err, obligations: vec![] } + } + } +} + +fn confirm_impl_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + + let VtableImplData { impl_def_id, substs, nested } = impl_vtable; + let assoc_item_id = obligation.predicate.item_def_id; + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + + let param_env = obligation.param_env; + let assoc_ty = match assoc_ty_def(selcx, impl_def_id, assoc_item_id) { + Ok(assoc_ty) => assoc_ty, + Err(ErrorReported) => return Progress { ty: tcx.types.err, obligations: nested }, + }; + + if !assoc_ty.item.defaultness.has_value() { + // This means that the impl is missing a definition for the + // associated type. This error will be reported by the type + // checker method `check_impl_items_against_trait`, so here we + // just return Error. + debug!( + "confirm_impl_candidate: no associated type {:?} for {:?}", + assoc_ty.item.ident, obligation.predicate + ); + return Progress { ty: tcx.types.err, obligations: nested }; + } + let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs); + let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node); + let ty = if let ty::AssocKind::OpaqueTy = assoc_ty.item.kind { + let item_substs = InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id); + tcx.mk_opaque(assoc_ty.item.def_id, item_substs) + } else { + tcx.type_of(assoc_ty.item.def_id) + }; + if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { + tcx.sess + .delay_span_bug(DUMMY_SP, "impl item and trait item have different parameter counts"); + Progress { ty: tcx.types.err, obligations: nested } + } else { + Progress { ty: ty.subst(tcx, substs), obligations: nested } + } +} + +/// Locate the definition of an associated type in the specialization hierarchy, +/// starting from the given impl. +/// +/// Based on the "projection mode", this lookup may in fact only examine the +/// topmost impl. See the comments for `Reveal` for more details. +fn assoc_ty_def( + selcx: &SelectionContext<'_, '_>, + impl_def_id: DefId, + assoc_ty_def_id: DefId, +) -> Result, ErrorReported> { + let tcx = selcx.tcx(); + let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident; + let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; + let trait_def = tcx.trait_def(trait_def_id); + + // This function may be called while we are still building the + // specialization graph that is queried below (via TraitDef::ancestors()), + // so, in order to avoid unnecessary infinite recursion, we manually look + // for the associated item at the given impl. + // If there is no such item in that impl, this function will fail with a + // cycle error if the specialization graph is currently being built. + let impl_node = specialization_graph::Node::Impl(impl_def_id); + for item in impl_node.items(tcx) { + if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy) + && tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id) + { + return Ok(specialization_graph::NodeItem { + node: specialization_graph::Node::Impl(impl_def_id), + item: *item, + }); + } + } + + let ancestors = trait_def.ancestors(tcx, impl_def_id)?; + if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) { + Ok(assoc_item) + } else { + // This is saying that neither the trait nor + // the impl contain a definition for this + // associated type. Normally this situation + // could only arise through a compiler bug -- + // if the user wrote a bad item name, it + // should have failed in astconv. + bug!("No associated type `{}` for {}", assoc_ty_name, tcx.def_path_str(impl_def_id)) + } +} + +crate trait ProjectionCacheKeyExt<'tcx>: Sized { + fn from_poly_projection_predicate( + selcx: &mut SelectionContext<'cx, 'tcx>, + predicate: &ty::PolyProjectionPredicate<'tcx>, + ) -> Option; +} + +impl<'tcx> ProjectionCacheKeyExt<'tcx> for ProjectionCacheKey<'tcx> { + fn from_poly_projection_predicate( + selcx: &mut SelectionContext<'cx, 'tcx>, + predicate: &ty::PolyProjectionPredicate<'tcx>, + ) -> Option { + let infcx = selcx.infcx(); + // We don't do cross-snapshot caching of obligations with escaping regions, + // so there's no cache key to use + predicate.no_bound_vars().map(|predicate| { + ProjectionCacheKey::new( + // We don't attempt to match up with a specific type-variable state + // from a specific call to `opt_normalize_projection_type` - if + // there's no precise match, the original cache entry is "stranded" + // anyway. + infcx.resolve_vars_if_possible(&predicate.projection_ty), + ) + }) + } +} diff --git a/src/librustc_trait_selection/traits/query/dropck_outlives.rs b/src/librustc_trait_selection/traits/query/dropck_outlives.rs new file mode 100644 index 00000000000..40a21b5a6ed --- /dev/null +++ b/src/librustc_trait_selection/traits/query/dropck_outlives.rs @@ -0,0 +1,141 @@ +use crate::infer::at::At; +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::InferOk; + +use rustc::ty::subst::GenericArg; +use rustc::ty::{self, Ty, TyCtxt}; + +pub use rustc::traits::query::{DropckOutlivesResult, DtorckConstraint}; + +pub trait AtExt<'tcx> { + fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>>; +} + +impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { + /// Given a type `ty` of some value being dropped, computes a set + /// of "kinds" (types, regions) that must be outlive the execution + /// of the destructor. These basically correspond to data that the + /// destructor might access. This is used during regionck to + /// impose "outlives" constraints on any lifetimes referenced + /// within. + /// + /// The rules here are given by the "dropck" RFCs, notably [#1238] + /// and [#1327]. This is a fixed-point computation, where we + /// explore all the data that will be dropped (transitively) when + /// a value of type `ty` is dropped. For each type T that will be + /// dropped and which has a destructor, we must assume that all + /// the types/regions of T are live during the destructor, unless + /// they are marked with a special attribute (`#[may_dangle]`). + /// + /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md + /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md + fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { + debug!("dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env,); + + // Quick check: there are a number of cases that we know do not require + // any destructor. + let tcx = self.infcx.tcx; + if trivial_dropck_outlives(tcx, ty) { + return InferOk { value: vec![], obligations: vec![] }; + } + + let mut orig_values = OriginalQueryValues::default(); + let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values); + let span = self.cause.span; + debug!("c_ty = {:?}", c_ty); + if let Ok(result) = &tcx.dropck_outlives(c_ty) { + if result.is_proven() { + if let Ok(InferOk { value, obligations }) = + self.infcx.instantiate_query_response_and_region_obligations( + self.cause, + self.param_env, + &orig_values, + result, + ) + { + let ty = self.infcx.resolve_vars_if_possible(&ty); + let kinds = value.into_kinds_reporting_overflows(tcx, span, ty); + return InferOk { value: kinds, obligations }; + } + } + } + + // Errors and ambiuity in dropck occur in two cases: + // - unresolved inference variables at the end of typeck + // - non well-formed types where projections cannot be resolved + // Either of these should have created an error before. + tcx.sess.delay_span_bug(span, "dtorck encountered internal error"); + + InferOk { value: vec![], obligations: vec![] } + } +} + +/// This returns true if the type `ty` is "trivial" for +/// dropck-outlives -- that is, if it doesn't require any types to +/// outlive. This is similar but not *quite* the same as the +/// `needs_drop` test in the compiler already -- that is, for every +/// type T for which this function return true, needs-drop would +/// return `false`. But the reverse does not hold: in particular, +/// `needs_drop` returns false for `PhantomData`, but it is not +/// trivial for dropck-outlives. +/// +/// Note also that `needs_drop` requires a "global" type (i.e., one +/// with erased regions), but this function does not. +pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind { + // None of these types have a destructor and hence they do not + // require anything in particular to outlive the dtor's + // execution. + ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + | ty::Bool + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Never + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Char + | ty::GeneratorWitness(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Str + | ty::Foreign(..) + | ty::Error => true, + + // [T; N] and [T] have same properties as T. + ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), + + // (T1..Tn) and closures have same properties as T1..Tn -- + // check if *any* of those are trivial. + ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), + ty::Closure(def_id, ref substs) => { + substs.as_closure().upvar_tys(def_id, tcx).all(|t| trivial_dropck_outlives(tcx, t)) + } + + ty::Adt(def, _) => { + if Some(def.did) == tcx.lang_items().manually_drop() { + // `ManuallyDrop` never has a dtor. + true + } else { + // Other types might. Moreover, PhantomData doesn't + // have a dtor, but it is considered to own its + // content, so it is non-trivial. Unions can have `impl Drop`, + // and hence are non-trivial as well. + false + } + } + + // The following *might* require a destructor: needs deeper inspection. + ty::Dynamic(..) + | ty::Projection(..) + | ty::Param(_) + | ty::Opaque(..) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Bound(..) + | ty::Generator(..) => false, + + ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), + } +} diff --git a/src/librustc_trait_selection/traits/query/evaluate_obligation.rs b/src/librustc_trait_selection/traits/query/evaluate_obligation.rs new file mode 100644 index 00000000000..0569f6217da --- /dev/null +++ b/src/librustc_trait_selection/traits/query/evaluate_obligation.rs @@ -0,0 +1,97 @@ +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::InferCtxt; +use crate::traits::{ + EvaluationResult, OverflowError, PredicateObligation, SelectionContext, TraitQueryMode, +}; + +pub trait InferCtxtExt<'tcx> { + fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool; + + fn predicate_must_hold_considering_regions( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> bool; + + fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool; + + fn evaluate_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> Result; + + // Helper function that canonicalizes and runs the query. If an + // overflow results, we re-run it in the local context so we can + // report a nice error. + /*crate*/ + fn evaluate_obligation_no_overflow( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> EvaluationResult; +} + +impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { + /// Evaluates whether the predicate can be satisfied (by any means) + /// in the given `ParamEnv`. + fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool { + self.evaluate_obligation_no_overflow(obligation).may_apply() + } + + /// Evaluates whether the predicate can be satisfied in the given + /// `ParamEnv`, and returns `false` if not certain. However, this is + /// not entirely accurate if inference variables are involved. + /// + /// This version may conservatively fail when outlives obligations + /// are required. + fn predicate_must_hold_considering_regions( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> bool { + self.evaluate_obligation_no_overflow(obligation).must_apply_considering_regions() + } + + /// Evaluates whether the predicate can be satisfied in the given + /// `ParamEnv`, and returns `false` if not certain. However, this is + /// not entirely accurate if inference variables are involved. + /// + /// This version ignores all outlives constraints. + fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool { + self.evaluate_obligation_no_overflow(obligation).must_apply_modulo_regions() + } + + /// Evaluate a given predicate, capturing overflow and propagating it back. + fn evaluate_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> Result { + let mut _orig_values = OriginalQueryValues::default(); + let c_pred = self + .canonicalize_query(&obligation.param_env.and(obligation.predicate), &mut _orig_values); + // Run canonical query. If overflow occurs, rerun from scratch but this time + // in standard trait query mode so that overflow is handled appropriately + // within `SelectionContext`. + self.tcx.evaluate_obligation(c_pred) + } + + // Helper function that canonicalizes and runs the query. If an + // overflow results, we re-run it in the local context so we can + // report a nice error. + fn evaluate_obligation_no_overflow( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> EvaluationResult { + match self.evaluate_obligation(obligation) { + Ok(result) => result, + Err(OverflowError) => { + let mut selcx = SelectionContext::with_query_mode(&self, TraitQueryMode::Standard); + selcx.evaluate_root_obligation(obligation).unwrap_or_else(|r| { + span_bug!( + obligation.cause.span, + "Overflow should be caught earlier in standard query mode: {:?}, {:?}", + obligation, + r, + ) + }) + } + } + } +} diff --git a/src/librustc_trait_selection/traits/query/method_autoderef.rs b/src/librustc_trait_selection/traits/query/method_autoderef.rs new file mode 100644 index 00000000000..80748c5ef38 --- /dev/null +++ b/src/librustc_trait_selection/traits/query/method_autoderef.rs @@ -0,0 +1 @@ +pub use rustc::traits::query::{CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult}; diff --git a/src/librustc_trait_selection/traits/query/mod.rs b/src/librustc_trait_selection/traits/query/mod.rs new file mode 100644 index 00000000000..77b5ec669a0 --- /dev/null +++ b/src/librustc_trait_selection/traits/query/mod.rs @@ -0,0 +1,15 @@ +//! Experimental types for the trait query interface. The methods +//! defined in this module are all based on **canonicalization**, +//! which makes a canonical query by replacing unbound inference +//! variables and regions, so that results can be reused more broadly. +//! The providers for the queries defined here can be found in +//! `librustc_traits`. + +pub mod dropck_outlives; +pub mod evaluate_obligation; +pub mod method_autoderef; +pub mod normalize; +pub mod outlives_bounds; +pub mod type_op; + +pub use rustc::traits::query::*; diff --git a/src/librustc_trait_selection/traits/query/normalize.rs b/src/librustc_trait_selection/traits/query/normalize.rs new file mode 100644 index 00000000000..adec2ddb253 --- /dev/null +++ b/src/librustc_trait_selection/traits/query/normalize.rs @@ -0,0 +1,196 @@ +//! Code for the 'normalization' query. This consists of a wrapper +//! which folds deeply, invoking the underlying +//! `normalize_projection_ty` query when it encounters projections. + +use crate::infer::at::At; +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; +use rustc::ty::fold::{TypeFoldable, TypeFolder}; +use rustc::ty::subst::Subst; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc_infer::traits::Normalized; + +use super::NoSolution; + +pub use rustc::traits::query::NormalizationResult; + +pub trait AtExt<'tcx> { + fn normalize(&self, value: &T) -> Result, NoSolution> + where + T: TypeFoldable<'tcx>; +} + +impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { + /// Normalize `value` in the context of the inference context, + /// yielding a resulting type, or an error if `value` cannot be + /// normalized. If you don't care about regions, you should prefer + /// `normalize_erasing_regions`, which is more efficient. + /// + /// If the normalization succeeds and is unambiguous, returns back + /// the normalized value along with various outlives relations (in + /// the form of obligations that must be discharged). + /// + /// N.B., this will *eventually* be the main means of + /// normalizing, but for now should be used only when we actually + /// know that normalization will succeed, since error reporting + /// and other details are still "under development". + fn normalize(&self, value: &T) -> Result, NoSolution> + where + T: TypeFoldable<'tcx>, + { + debug!( + "normalize::<{}>(value={:?}, param_env={:?})", + ::std::any::type_name::(), + value, + self.param_env, + ); + if !value.has_projections() { + return Ok(Normalized { value: value.clone(), obligations: vec![] }); + } + + let mut normalizer = QueryNormalizer { + infcx: self.infcx, + cause: self.cause, + param_env: self.param_env, + obligations: vec![], + error: false, + anon_depth: 0, + }; + + let value1 = value.fold_with(&mut normalizer); + if normalizer.error { + Err(NoSolution) + } else { + Ok(Normalized { value: value1, obligations: normalizer.obligations }) + } + } +} + +struct QueryNormalizer<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + cause: &'cx ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + obligations: Vec>, + error: bool, + anon_depth: usize, +} + +impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { + fn tcx<'c>(&'c self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_projections() { + return ty; + } + + let ty = ty.super_fold_with(self); + match ty.kind { + ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { + // (*) + // Only normalize `impl Trait` after type-checking, usually in codegen. + match self.param_env.reveal { + Reveal::UserFacing => ty, + + Reveal::All => { + let recursion_limit = *self.tcx().sess.recursion_limit.get(); + if self.anon_depth >= recursion_limit { + let obligation = Obligation::with_depth( + self.cause.clone(), + recursion_limit, + self.param_env, + ty, + ); + self.infcx.report_overflow_error(&obligation, true); + } + + let generic_ty = self.tcx().type_of(def_id); + let concrete_ty = generic_ty.subst(self.tcx(), substs); + self.anon_depth += 1; + if concrete_ty == ty { + bug!( + "infinite recursion generic_ty: {:#?}, substs: {:#?}, \ + concrete_ty: {:#?}, ty: {:#?}", + generic_ty, + substs, + concrete_ty, + ty + ); + } + let folded_ty = self.fold_ty(concrete_ty); + self.anon_depth -= 1; + folded_ty + } + } + } + + ty::Projection(ref data) if !data.has_escaping_bound_vars() => { + // (*) + // (*) This is kind of hacky -- we need to be able to + // handle normalization within binders because + // otherwise we wind up a need to normalize when doing + // trait matching (since you can have a trait + // obligation like `for<'a> T::B : Fn(&'a int)`), but + // we can't normalize with bound regions in scope. So + // far now we just ignore binders but only normalize + // if all bound regions are gone (and then we still + // have to renormalize whenever we instantiate a + // binder). It would be better to normalize in a + // binding-aware fashion. + + let tcx = self.infcx.tcx; + + let mut orig_values = OriginalQueryValues::default(); + // HACK(matthewjasper) `'static` is special-cased in selection, + // so we cannot canonicalize it. + let c_data = self + .infcx + .canonicalize_hr_query_hack(&self.param_env.and(*data), &mut orig_values); + debug!("QueryNormalizer: c_data = {:#?}", c_data); + debug!("QueryNormalizer: orig_values = {:#?}", orig_values); + match tcx.normalize_projection_ty(c_data) { + Ok(result) => { + // We don't expect ambiguity. + if result.is_ambiguous() { + self.error = true; + return ty; + } + + match self.infcx.instantiate_query_response_and_region_obligations( + self.cause, + self.param_env, + &orig_values, + &result, + ) { + Ok(InferOk { value: result, obligations }) => { + debug!("QueryNormalizer: result = {:#?}", result); + debug!("QueryNormalizer: obligations = {:#?}", obligations); + self.obligations.extend(obligations); + return result.normalized_ty; + } + + Err(_) => { + self.error = true; + return ty; + } + } + } + + Err(NoSolution) => { + self.error = true; + ty + } + } + } + + _ => ty, + } + } + + fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + constant.eval(self.infcx.tcx, self.param_env) + } +} diff --git a/src/librustc_trait_selection/traits/query/outlives_bounds.rs b/src/librustc_trait_selection/traits/query/outlives_bounds.rs new file mode 100644 index 00000000000..05c96dd520a --- /dev/null +++ b/src/librustc_trait_selection/traits/query/outlives_bounds.rs @@ -0,0 +1,95 @@ +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::InferCtxt; +use crate::traits::query::NoSolution; +use crate::traits::{FulfillmentContext, ObligationCause, TraitEngine}; +use rustc::ty::{self, Ty}; +use rustc_hir as hir; +use rustc_infer::traits::TraitEngineExt as _; +use rustc_span::source_map::Span; + +pub use rustc::traits::query::OutlivesBound; + +pub trait InferCtxtExt<'tcx> { + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ty: Ty<'tcx>, + span: Span, + ) -> Vec>; +} + +impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { + /// Implied bounds are region relationships that we deduce + /// automatically. The idea is that (e.g.) a caller must check that a + /// function's argument types are well-formed immediately before + /// calling that fn, and hence the *callee* can assume that its + /// argument types are well-formed. This may imply certain relationships + /// between generic parameters. For example: + /// + /// fn foo<'a,T>(x: &'a T) + /// + /// can only be called with a `'a` and `T` such that `&'a T` is WF. + /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. + /// + /// # Parameters + /// + /// - `param_env`, the where-clauses in scope + /// - `body_id`, the body-id to use when normalizing assoc types. + /// Note that this may cause outlives obligations to be injected + /// into the inference context with this body-id. + /// - `ty`, the type that we are supposed to assume is WF. + /// - `span`, a span to use when normalizing, hopefully not important, + /// might be useful if a `bug!` occurs. + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ty: Ty<'tcx>, + span: Span, + ) -> Vec> { + debug!("implied_outlives_bounds(ty = {:?})", ty); + + let mut orig_values = OriginalQueryValues::default(); + let key = self.canonicalize_query(¶m_env.and(ty), &mut orig_values); + let result = match self.tcx.implied_outlives_bounds(key) { + Ok(r) => r, + Err(NoSolution) => { + self.tcx.sess.delay_span_bug( + span, + "implied_outlives_bounds failed to solve all obligations", + ); + return vec![]; + } + }; + assert!(result.value.is_proven()); + + let result = self.instantiate_query_response_and_region_obligations( + &ObligationCause::misc(span, body_id), + param_env, + &orig_values, + &result, + ); + debug!("implied_outlives_bounds for {:?}: {:#?}", ty, result); + let result = match result { + Ok(v) => v, + Err(_) => { + self.tcx.sess.delay_span_bug(span, "implied_outlives_bounds failed to instantiate"); + return vec![]; + } + }; + + // Instantiation may have produced new inference variables and constraints on those + // variables. Process these constraints. + let mut fulfill_cx = FulfillmentContext::new(); + fulfill_cx.register_predicate_obligations(self, result.obligations); + if fulfill_cx.select_all_or_error(self).is_err() { + self.tcx.sess.delay_span_bug( + span, + "implied_outlives_bounds failed to solve obligations from instantiation", + ); + } + + result.value + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs b/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs new file mode 100644 index 00000000000..b14b79f0907 --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs @@ -0,0 +1,23 @@ +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::traits::query::Fallible; +use rustc::ty::{ParamEnvAnd, TyCtxt}; + +pub use rustc::traits::query::type_op::AscribeUserType; + +impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { + type QueryResponse = (); + + fn try_fast_path( + _tcx: TyCtxt<'tcx>, + _key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { + None + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + tcx.type_op_ascribe_user_type(canonicalized) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/custom.rs b/src/librustc_trait_selection/traits/query/type_op/custom.rs new file mode 100644 index 00000000000..915e8ae4a7a --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/custom.rs @@ -0,0 +1,108 @@ +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::query::Fallible; +use std::fmt; + +use crate::infer::canonical::query_response; +use crate::infer::canonical::QueryRegionConstraints; +use crate::traits::engine::TraitEngineExt as _; +use crate::traits::{ObligationCause, TraitEngine}; +use rustc_infer::traits::TraitEngineExt as _; +use rustc_span::source_map::DUMMY_SP; +use std::rc::Rc; + +pub struct CustomTypeOp { + closure: F, + description: G, +} + +impl CustomTypeOp { + pub fn new<'tcx, R>(closure: F, description: G) -> Self + where + F: FnOnce(&InferCtxt<'_, 'tcx>) -> Fallible>, + G: Fn() -> String, + { + CustomTypeOp { closure, description } + } +} + +impl<'tcx, F, R, G> super::TypeOp<'tcx> for CustomTypeOp +where + F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'tcx>) -> Fallible>, + G: Fn() -> String, +{ + type Output = R; + + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + fn fully_perform( + self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Fallible<(Self::Output, Option>>)> { + if cfg!(debug_assertions) { + info!("fully_perform({:?})", self); + } + + scrape_region_constraints(infcx, || Ok((self.closure)(infcx)?)) + } +} + +impl fmt::Debug for CustomTypeOp +where + G: Fn() -> String, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", (self.description)()) + } +} + +/// Executes `op` and then scrapes out all the "old style" region +/// constraints that result, creating query-region-constraints. +fn scrape_region_constraints<'tcx, R>( + infcx: &InferCtxt<'_, 'tcx>, + op: impl FnOnce() -> Fallible>, +) -> Fallible<(R, Option>>)> { + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let dummy_body_id = ObligationCause::dummy().body_id; + + // During NLL, we expect that nobody will register region + // obligations **except** as part of a custom type op (and, at the + // end of each custom type op, we scrape out the region + // obligations that resulted). So this vector should be empty on + // entry. + let pre_obligations = infcx.take_registered_region_obligations(); + assert!( + pre_obligations.is_empty(), + "scrape_region_constraints: incoming region obligations = {:#?}", + pre_obligations, + ); + + let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; + debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); + fulfill_cx.register_predicate_obligations(infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(infcx) { + infcx.tcx.sess.diagnostic().delay_span_bug( + DUMMY_SP, + &format!("errors selecting obligation during MIR typeck: {:?}", e), + ); + } + + let region_obligations = infcx.take_registered_region_obligations(); + + let region_constraint_data = infcx.take_and_reset_region_constraints(); + + let region_constraints = query_response::make_query_region_constraints( + infcx.tcx, + region_obligations + .iter() + .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)) + .map(|(ty, r)| (infcx.resolve_vars_if_possible(&ty), r)), + ®ion_constraint_data, + ); + + if region_constraints.is_empty() { + Ok((value, None)) + } else { + Ok((value, Some(Rc::new(region_constraints)))) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/eq.rs b/src/librustc_trait_selection/traits/query/type_op/eq.rs new file mode 100644 index 00000000000..3b6fbc7d8dd --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/eq.rs @@ -0,0 +1,23 @@ +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::traits::query::Fallible; +use rustc::ty::{ParamEnvAnd, TyCtxt}; + +pub use rustc::traits::query::type_op::Eq; + +impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { + type QueryResponse = (); + + fn try_fast_path( + _tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Eq<'tcx>>, + ) -> Option { + if key.value.a == key.value.b { Some(()) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + tcx.type_op_eq(canonicalized) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs new file mode 100644 index 00000000000..3dad546872e --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs @@ -0,0 +1,41 @@ +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::traits::query::outlives_bounds::OutlivesBound; +use crate::traits::query::Fallible; +use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; + +#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] +pub struct ImpliedOutlivesBounds<'tcx> { + pub ty: Ty<'tcx>, +} + +impl<'tcx> ImpliedOutlivesBounds<'tcx> { + pub fn new(ty: Ty<'tcx>) -> Self { + ImpliedOutlivesBounds { ty } + } +} + +impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { + type QueryResponse = Vec>; + + fn try_fast_path( + _tcx: TyCtxt<'tcx>, + _key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { + None + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + // FIXME this `unchecked_map` is only necessary because the + // query is defined as taking a `ParamEnvAnd`; it should + // take a `ImpliedOutlivesBounds` instead + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let ImpliedOutlivesBounds { ty } = value; + param_env.and(ty) + }); + + tcx.implied_outlives_bounds(canonicalized) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/mod.rs b/src/librustc_trait_selection/traits/query/type_op/mod.rs new file mode 100644 index 00000000000..1644746c16e --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/mod.rs @@ -0,0 +1,136 @@ +use crate::infer::canonical::{ + Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, QueryRegionConstraints, +}; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::query::Fallible; +use crate::traits::ObligationCause; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{ParamEnvAnd, TyCtxt}; +use std::fmt; +use std::rc::Rc; + +pub mod ascribe_user_type; +pub mod custom; +pub mod eq; +pub mod implied_outlives_bounds; +pub mod normalize; +pub mod outlives; +pub mod prove_predicate; +use self::prove_predicate::ProvePredicate; +pub mod subtype; + +pub use rustc::traits::query::type_op::*; + +/// "Type ops" are used in NLL to perform some particular action and +/// extract out the resulting region constraints (or an error if it +/// cannot be completed). +pub trait TypeOp<'tcx>: Sized + fmt::Debug { + type Output; + + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + fn fully_perform( + self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Fallible<(Self::Output, Option>>)>; +} + +/// "Query type ops" are type ops that are implemented using a +/// [canonical query][c]. The `Self` type here contains the kernel of +/// information needed to do the operation -- `TypeOp` is actually +/// implemented for `ParamEnvAnd`, since we always need to bring +/// along a parameter environment as well. For query type-ops, we will +/// first canonicalize the key and then invoke the query on the tcx, +/// which produces the resulting query region constraints. +/// +/// [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html +pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx { + type QueryResponse: TypeFoldable<'tcx>; + + /// Give query the option for a simple fast path that never + /// actually hits the tcx cache lookup etc. Return `Some(r)` with + /// a final result or `None` to do the full path. + fn try_fast_path( + tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option; + + /// Performs the actual query with the canonicalized key -- the + /// real work happens here. This method is not given an `infcx` + /// because it shouldn't need one -- and if it had access to one, + /// it might do things like invoke `sub_regions`, which would be + /// bad, because it would create subregion relationships that are + /// not captured in the return value. + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible>; + + fn fully_perform_into( + query_key: ParamEnvAnd<'tcx, Self>, + infcx: &InferCtxt<'_, 'tcx>, + output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, + ) -> Fallible { + if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { + return Ok(result); + } + + // FIXME(#33684) -- We need to use + // `canonicalize_hr_query_hack` here because of things + // like the subtype query, which go awry around + // `'static` otherwise. + let mut canonical_var_values = OriginalQueryValues::default(); + let canonical_self = + infcx.canonicalize_hr_query_hack(&query_key, &mut canonical_var_values); + let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; + + let param_env = query_key.param_env; + + let InferOk { value, obligations } = infcx + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + canonical_result, + output_query_region_constraints, + )?; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + for obligation in obligations { + let () = ProvePredicate::fully_perform_into( + obligation.param_env.and(ProvePredicate::new(obligation.predicate)), + infcx, + output_query_region_constraints, + )?; + } + + Ok(value) + } +} + +impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q> +where + Q: QueryTypeOp<'tcx>, +{ + type Output = Q::QueryResponse; + + fn fully_perform( + self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Fallible<(Self::Output, Option>>)> { + let mut region_constraints = QueryRegionConstraints::default(); + let r = Q::fully_perform_into(self, infcx, &mut region_constraints)?; + + // Promote the final query-region-constraints into a + // (optional) ref-counted vector: + let opt_qrc = + if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) }; + + Ok((r, opt_qrc)) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/normalize.rs b/src/librustc_trait_selection/traits/query/type_op/normalize.rs new file mode 100644 index 00000000000..d2eec53bf80 --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/normalize.rs @@ -0,0 +1,68 @@ +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::traits::query::Fallible; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt}; +use std::fmt; + +pub use rustc::traits::query::type_op::Normalize; + +impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize +where + T: Normalizable<'tcx> + 'tcx, +{ + type QueryResponse = T; + + fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { + if !key.value.value.has_projections() { Some(key.value.value) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + T::type_op_method(tcx, canonicalized) + } +} + +pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'tcx> + Copy { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, + ) -> Fallible>; +} + +impl Normalizable<'tcx> for Ty<'tcx> { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, + ) -> Fallible> { + tcx.type_op_normalize_ty(canonicalized) + } +} + +impl Normalizable<'tcx> for ty::Predicate<'tcx> { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, + ) -> Fallible> { + tcx.type_op_normalize_predicate(canonicalized) + } +} + +impl Normalizable<'tcx> for ty::PolyFnSig<'tcx> { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, + ) -> Fallible> { + tcx.type_op_normalize_poly_fn_sig(canonicalized) + } +} + +impl Normalizable<'tcx> for ty::FnSig<'tcx> { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize>>, + ) -> Fallible> { + tcx.type_op_normalize_fn_sig(canonicalized) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/outlives.rs b/src/librustc_trait_selection/traits/query/type_op/outlives.rs new file mode 100644 index 00000000000..b94948cffd6 --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/outlives.rs @@ -0,0 +1,55 @@ +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutlivesResult}; +use crate::traits::query::Fallible; +use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; + +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, Lift)] +pub struct DropckOutlives<'tcx> { + dropped_ty: Ty<'tcx>, +} + +impl<'tcx> DropckOutlives<'tcx> { + pub fn new(dropped_ty: Ty<'tcx>) -> Self { + DropckOutlives { dropped_ty } + } +} + +impl super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { + type QueryResponse = DropckOutlivesResult<'tcx>; + + fn try_fast_path( + tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { + if trivial_dropck_outlives(tcx, key.value.dropped_ty) { + Some(DropckOutlivesResult::default()) + } else { + None + } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + // Subtle: note that we are not invoking + // `infcx.at(...).dropck_outlives(...)` here, but rather the + // underlying `dropck_outlives` query. This same underlying + // query is also used by the + // `infcx.at(...).dropck_outlives(...)` fn. Avoiding the + // wrapper means we don't need an infcx in this code, which is + // good because the interface doesn't give us one (so that we + // know we are not registering any subregion relations or + // other things). + + // FIXME convert to the type expected by the `dropck_outlives` + // query. This should eventually be fixed by changing the + // *underlying query*. + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let DropckOutlives { dropped_ty } = value; + param_env.and(dropped_ty) + }); + + tcx.dropck_outlives(canonicalized) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs b/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs new file mode 100644 index 00000000000..8c68f7db9e5 --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs @@ -0,0 +1,37 @@ +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::traits::query::Fallible; +use rustc::ty::{ParamEnvAnd, Predicate, TyCtxt}; + +pub use rustc::traits::query::type_op::ProvePredicate; + +impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { + type QueryResponse = (); + + fn try_fast_path( + tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { + // Proving Sized, very often on "obviously sized" types like + // `&T`, accounts for about 60% percentage of the predicates + // we have to prove. No need to canonicalize and all that for + // such cases. + if let Predicate::Trait(trait_ref, _) = key.value.predicate { + if let Some(sized_def_id) = tcx.lang_items().sized_trait() { + if trait_ref.def_id() == sized_def_id { + if trait_ref.skip_binder().self_ty().is_trivially_sized(tcx) { + return Some(()); + } + } + } + } + + None + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + tcx.type_op_prove_predicate(canonicalized) + } +} diff --git a/src/librustc_trait_selection/traits/query/type_op/subtype.rs b/src/librustc_trait_selection/traits/query/type_op/subtype.rs new file mode 100644 index 00000000000..053411b0cac --- /dev/null +++ b/src/librustc_trait_selection/traits/query/type_op/subtype.rs @@ -0,0 +1,20 @@ +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::traits::query::Fallible; +use rustc::ty::{ParamEnvAnd, TyCtxt}; + +pub use rustc::traits::query::type_op::Subtype; + +impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { + type QueryResponse = (); + + fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> { + if key.value.sub == key.value.sup { Some(()) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + tcx.type_op_subtype(canonicalized) + } +} diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs new file mode 100644 index 00000000000..ab3214d8d2d --- /dev/null +++ b/src/librustc_trait_selection/traits/select.rs @@ -0,0 +1,3790 @@ +// ignore-tidy-filelength + +//! Candidate selection. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection + +use self::EvaluationResult::*; +use self::SelectionCandidate::*; + +use super::coherence::{self, Conflict}; +use super::project; +use super::project::{normalize_with_depth, normalize_with_depth_to}; +use super::util; +use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; +use super::wf; +use super::DerivedObligationCause; +use super::Selection; +use super::SelectionResult; +use super::TraitNotObjectSafe; +use super::TraitQueryMode; +use super::{BuiltinDerivedObligation, ImplDerivedObligation, ObligationCauseCode}; +use super::{Normalized, ProjectionCacheKey}; +use super::{ObjectCastObligation, Obligation}; +use super::{ObligationCause, PredicateObligation, TraitObligation}; +use super::{OutputTypeParameterMismatch, Overflow, SelectionError, Unimplemented}; +use super::{ + VtableAutoImpl, VtableBuiltin, VtableClosure, VtableFnPointer, VtableGenerator, VtableImpl, + VtableObject, VtableParam, VtableTraitAlias, +}; +use super::{ + VtableAutoImplData, VtableBuiltinData, VtableClosureData, VtableFnPointerData, + VtableGeneratorData, VtableImplData, VtableObjectData, VtableTraitAliasData, +}; + +use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, PlaceholderMap, TypeFreshener}; +use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::project::ProjectionCacheKeyExt; +use rustc::dep_graph::{DepKind, DepNodeIndex}; +use rustc::middle::lang_items; +use rustc::ty::fast_reject; +use rustc::ty::relate::TypeRelation; +use rustc::ty::subst::{Subst, SubstsRef}; +use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; +use rustc_ast::attr; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::GrowableBitSet; +use rustc_span::symbol::sym; +use rustc_target::spec::abi::Abi; + +use std::cell::{Cell, RefCell}; +use std::cmp; +use std::fmt::{self, Display}; +use std::iter; +use std::rc::Rc; + +pub use rustc::traits::select::*; + +pub struct SelectionContext<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + + /// Freshener used specifically for entries on the obligation + /// stack. This ensures that all entries on the stack at one time + /// will have the same set of placeholder entries, which is + /// important for checking for trait bounds that recursively + /// require themselves. + freshener: TypeFreshener<'cx, 'tcx>, + + /// If `true`, indicates that the evaluation should be conservative + /// and consider the possibility of types outside this crate. + /// This comes up primarily when resolving ambiguity. Imagine + /// there is some trait reference `$0: Bar` where `$0` is an + /// inference variable. If `intercrate` is true, then we can never + /// say for sure that this reference is not implemented, even if + /// there are *no impls at all for `Bar`*, because `$0` could be + /// bound to some type that in a downstream crate that implements + /// `Bar`. This is the suitable mode for coherence. Elsewhere, + /// though, we set this to false, because we are only interested + /// in types that the user could actually have written --- in + /// other words, we consider `$0: Bar` to be unimplemented if + /// there is no type that the user could *actually name* that + /// would satisfy it. This avoids crippling inference, basically. + intercrate: bool, + + intercrate_ambiguity_causes: Option>, + + /// Controls whether or not to filter out negative impls when selecting. + /// This is used in librustdoc to distinguish between the lack of an impl + /// and a negative impl + allow_negative_impls: bool, + + /// The mode that trait queries run in, which informs our error handling + /// policy. In essence, canonicalized queries need their errors propagated + /// rather than immediately reported because we do not have accurate spans. + query_mode: TraitQueryMode, +} + +// A stack that walks back up the stack frame. +struct TraitObligationStack<'prev, 'tcx> { + obligation: &'prev TraitObligation<'tcx>, + + /// The trait ref from `obligation` but "freshened" with the + /// selection-context's freshener. Used to check for recursion. + fresh_trait_ref: ty::PolyTraitRef<'tcx>, + + /// Starts out equal to `depth` -- if, during evaluation, we + /// encounter a cycle, then we will set this flag to the minimum + /// depth of that cycle for all participants in the cycle. These + /// participants will then forego caching their results. This is + /// not the most efficient solution, but it addresses #60010. The + /// problem we are trying to prevent: + /// + /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait` + /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok) + /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok) + /// + /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` + /// is `EvaluatedToOk`; this is because they were only considered + /// ok on the premise that if `A: AutoTrait` held, but we indeed + /// encountered a problem (later on) with `A: AutoTrait. So we + /// currently set a flag on the stack node for `B: AutoTrait` (as + /// well as the second instance of `A: AutoTrait`) to suppress + /// caching. + /// + /// This is a simple, targeted fix. A more-performant fix requires + /// deeper changes, but would permit more caching: we could + /// basically defer caching until we have fully evaluated the + /// tree, and then cache the entire tree at once. In any case, the + /// performance impact here shouldn't be so horrible: every time + /// this is hit, we do cache at least one trait, so we only + /// evaluate each member of a cycle up to N times, where N is the + /// length of the cycle. This means the performance impact is + /// bounded and we shouldn't have any terrible worst-cases. + reached_depth: Cell, + + previous: TraitObligationStackList<'prev, 'tcx>, + + /// The number of parent frames plus one (thus, the topmost frame has depth 1). + depth: usize, + + /// The depth-first number of this node in the search graph -- a + /// pre-order index. Basically, a freshly incremented counter. + dfn: usize, +} + +struct SelectionCandidateSet<'tcx> { + // A list of candidates that definitely apply to the current + // obligation (meaning: types unify). + vec: Vec>, + + // If `true`, then there were candidates that might or might + // not have applied, but we couldn't tell. This occurs when some + // of the input types are type variables, in which case there are + // various "builtin" rules that might or might not trigger. + ambiguous: bool, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +struct EvaluatedCandidate<'tcx> { + candidate: SelectionCandidate<'tcx>, + evaluation: EvaluationResult, +} + +/// When does the builtin impl for `T: Trait` apply? +enum BuiltinImplConditions<'tcx> { + /// The impl is conditional on `T1, T2, ...: Trait`. + Where(ty::Binder>>), + /// There is no built-in impl. There may be some other + /// candidate (a where-clause or user-defined impl). + None, + /// It is unknown whether there is an impl. + Ambiguous, +} + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: true, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn with_negative( + infcx: &'cx InferCtxt<'cx, 'tcx>, + allow_negative_impls: bool, + ) -> SelectionContext<'cx, 'tcx> { + debug!("with_negative({:?})", allow_negative_impls); + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn with_query_mode( + infcx: &'cx InferCtxt<'cx, 'tcx>, + query_mode: TraitQueryMode, + ) -> SelectionContext<'cx, 'tcx> { + debug!("with_query_mode({:?})", query_mode); + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode, + } + } + + /// Enables tracking of intercrate ambiguity causes. These are + /// used in coherence to give improved diagnostics. We don't do + /// this until we detect a coherence error because it can lead to + /// false overflow results (#47139) and because it costs + /// computation time. + pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { + assert!(self.intercrate); + assert!(self.intercrate_ambiguity_causes.is_none()); + self.intercrate_ambiguity_causes = Some(vec![]); + debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); + } + + /// Gets the intercrate ambiguity causes collected since tracking + /// was enabled and disables tracking at the same time. If + /// tracking is not enabled, just returns an empty vector. + pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec { + assert!(self.intercrate); + self.intercrate_ambiguity_causes.take().unwrap_or(vec![]) + } + + pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { + self.infcx + } + + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> { + self.infcx + } + + /////////////////////////////////////////////////////////////////////////// + // Selection + // + // The selection phase tries to identify *how* an obligation will + // be resolved. For example, it will identify which impl or + // parameter bound is to be used. The process can be inconclusive + // if the self type in the obligation is not fully inferred. Selection + // can result in an error in one of two ways: + // + // 1. If no applicable impl or parameter bound can be found. + // 2. If the output type parameters in the obligation do not match + // those specified by the impl/bound. For example, if the obligation + // is `Vec: Iterable`, but the impl specifies + // `impl Iterable for Vec`, than an error would result. + + /// Attempts to satisfy the obligation. If successful, this will affect the surrounding + /// type environment by performing unification. + pub fn select( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>> { + debug!("select({:?})", obligation); + debug_assert!(!obligation.predicate.has_escaping_bound_vars()); + + let pec = &ProvisionalEvaluationCache::default(); + let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); + + let candidate = match self.candidate_from_obligation(&stack) { + Err(SelectionError::Overflow) => { + // In standard mode, overflow must have been caught and reported + // earlier. + assert!(self.query_mode == TraitQueryMode::Canonical); + return Err(SelectionError::Overflow); + } + Err(e) => { + return Err(e); + } + Ok(None) => { + return Ok(None); + } + Ok(Some(candidate)) => candidate, + }; + + match self.confirm_candidate(obligation, candidate) { + Err(SelectionError::Overflow) => { + assert!(self.query_mode == TraitQueryMode::Canonical); + Err(SelectionError::Overflow) + } + Err(e) => Err(e), + Ok(candidate) => Ok(Some(candidate)), + } + } + + /////////////////////////////////////////////////////////////////////////// + // EVALUATION + // + // Tests whether an obligation can be selected or whether an impl + // can be applied to particular types. It skips the "confirmation" + // step and hence completely ignores output type parameters. + // + // The result is "true" if the obligation *may* hold and "false" if + // we can be sure it does not. + + /// Evaluates whether the obligation `obligation` can be satisfied (by any means). + pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { + debug!("predicate_may_hold_fatal({:?})", obligation); + + // This fatal query is a stopgap that should only be used in standard mode, + // where we do not expect overflow to be propagated. + assert!(self.query_mode == TraitQueryMode::Standard); + + self.evaluate_root_obligation(obligation) + .expect("Overflow should be caught earlier in standard query mode") + .may_apply() + } + + /// Evaluates whether the obligation `obligation` can be satisfied + /// and returns an `EvaluationResult`. This is meant for the + /// *initial* call. + pub fn evaluate_root_obligation( + &mut self, + obligation: &PredicateObligation<'tcx>, + ) -> Result { + self.evaluation_probe(|this| { + this.evaluate_predicate_recursively( + TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), + obligation.clone(), + ) + }) + } + + fn evaluation_probe( + &mut self, + op: impl FnOnce(&mut Self) -> Result, + ) -> Result { + self.infcx.probe(|snapshot| -> Result { + let result = op(self)?; + match self.infcx.region_constraints_added_in_snapshot(snapshot) { + None => Ok(result), + Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), + } + }) + } + + /// Evaluates the predicates in `predicates` recursively. Note that + /// this applies projections in the predicates, and therefore + /// is run within an inference probe. + fn evaluate_predicates_recursively<'o, I>( + &mut self, + stack: TraitObligationStackList<'o, 'tcx>, + predicates: I, + ) -> Result + where + I: IntoIterator>, + { + let mut result = EvaluatedToOk; + for obligation in predicates { + let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; + debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); + if let EvaluatedToErr = eval { + // fast-path - EvaluatedToErr is the top of the lattice, + // so we don't need to look on the other predicates. + return Ok(EvaluatedToErr); + } else { + result = cmp::max(result, eval); + } + } + Ok(result) + } + + fn evaluate_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: PredicateObligation<'tcx>, + ) -> Result { + debug!( + "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", + previous_stack.head(), + obligation + ); + + // `previous_stack` stores a `TraitObligatiom`, while `obligation` is + // a `PredicateObligation`. These are distinct types, so we can't + // use any `Option` combinator method that would force them to be + // the same. + match previous_stack.head() { + Some(h) => self.check_recursion_limit(&obligation, h.obligation)?, + None => self.check_recursion_limit(&obligation, &obligation)?, + } + + match obligation.predicate { + ty::Predicate::Trait(ref t, _) => { + debug_assert!(!t.has_escaping_bound_vars()); + let obligation = obligation.with(t.clone()); + self.evaluate_trait_predicate_recursively(previous_stack, obligation) + } + + ty::Predicate::Subtype(ref p) => { + // Does this code ever run? + match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { + Some(Ok(InferOk { mut obligations, .. })) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ) + } + Some(Err(_)) => Ok(EvaluatedToErr), + None => Ok(EvaluatedToAmbig), + } + } + + ty::Predicate::WellFormed(ty) => match wf::obligations( + self.infcx, + obligation.param_env, + obligation.cause.body_id, + ty, + obligation.cause.span, + ) { + Some(mut obligations) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively(previous_stack, obligations.into_iter()) + } + None => Ok(EvaluatedToAmbig), + }, + + ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => { + // We do not consider region relationships when evaluating trait matches. + Ok(EvaluatedToOkModuloRegions) + } + + ty::Predicate::ObjectSafe(trait_def_id) => { + if self.tcx().is_object_safe(trait_def_id) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } + } + + ty::Predicate::Projection(ref data) => { + let project_obligation = obligation.with(data.clone()); + match project::poly_project_and_unify_type(self, &project_obligation) { + Ok(Some(mut subobligations)) => { + self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); + let result = self.evaluate_predicates_recursively( + previous_stack, + subobligations.into_iter(), + ); + if let Some(key) = + ProjectionCacheKey::from_poly_projection_predicate(self, data) + { + self.infcx.inner.borrow_mut().projection_cache.complete(key); + } + result + } + Ok(None) => Ok(EvaluatedToAmbig), + Err(_) => Ok(EvaluatedToErr), + } + } + + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + match self.infcx.closure_kind(closure_def_id, closure_substs) { + Some(closure_kind) => { + if closure_kind.extends(kind) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } + } + None => Ok(EvaluatedToAmbig), + } + } + + ty::Predicate::ConstEvaluatable(def_id, substs) => { + match self.tcx().const_eval_resolve( + obligation.param_env, + def_id, + substs, + None, + None, + ) { + Ok(_) => Ok(EvaluatedToOk), + Err(_) => Ok(EvaluatedToErr), + } + } + } + } + + fn evaluate_trait_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + mut obligation: TraitObligation<'tcx>, + ) -> Result { + debug!("evaluate_trait_predicate_recursively({:?})", obligation); + + if !self.intercrate + && obligation.is_global() + && obligation.param_env.caller_bounds.iter().all(|bound| bound.needs_subst()) + { + // If a param env has no global bounds, global obligations do not + // depend on its particular value in order to work, so we can clear + // out the param env and get better caching. + debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); + obligation.param_env = obligation.param_env.without_caller_bounds(); + } + + let stack = self.push_stack(previous_stack, &obligation); + let fresh_trait_ref = stack.fresh_trait_ref; + if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { + debug!("CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + return Ok(result); + } + + if let Some(result) = stack.cache().get_provisional(fresh_trait_ref) { + debug!("PROVISIONAL CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + stack.update_reached_depth(stack.cache().current_reached_depth()); + return Ok(result); + } + + // Check if this is a match for something already on the + // stack. If so, we don't want to insert the result into the + // main cache (it is cycle dependent) nor the provisional + // cache (which is meant for things that have completed but + // for a "backedge" -- this result *is* the backedge). + if let Some(cycle_result) = self.check_evaluation_cycle(&stack) { + return Ok(cycle_result); + } + + let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack)); + let result = result?; + + if !result.must_apply_modulo_regions() { + stack.cache().on_failure(stack.dfn); + } + + let reached_depth = stack.reached_depth.get(); + if reached_depth >= stack.depth { + debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result); + self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); + + stack.cache().on_completion(stack.depth, |fresh_trait_ref, provisional_result| { + self.insert_evaluation_cache( + obligation.param_env, + fresh_trait_ref, + dep_node, + provisional_result.max(result), + ); + }); + } else { + debug!("PROVISIONAL: {:?}={:?}", fresh_trait_ref, result); + debug!( + "evaluate_trait_predicate_recursively: caching provisionally because {:?} \ + is a cycle participant (at depth {}, reached depth {})", + fresh_trait_ref, stack.depth, reached_depth, + ); + + stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_ref, result); + } + + Ok(result) + } + + /// If there is any previous entry on the stack that precisely + /// matches this obligation, then we can assume that the + /// obligation is satisfied for now (still all other conditions + /// must be met of course). One obvious case this comes up is + /// marker traits like `Send`. Think of a linked list: + /// + /// struct List { data: T, next: Option>> } + /// + /// `Box>` will be `Send` if `T` is `Send` and + /// `Option>>` is `Send`, and in turn + /// `Option>>` is `Send` if `Box>` is + /// `Send`. + /// + /// Note that we do this comparison using the `fresh_trait_ref` + /// fields. Because these have all been freshened using + /// `self.freshener`, we can be sure that (a) this will not + /// affect the inferencer state and (b) that if we see two + /// fresh regions with the same index, they refer to the same + /// unbound type variable. + fn check_evaluation_cycle( + &mut self, + stack: &TraitObligationStack<'_, 'tcx>, + ) -> Option { + if let Some(cycle_depth) = stack + .iter() + .skip(1) // Skip top-most frame. + .find(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && stack.fresh_trait_ref == prev.fresh_trait_ref + }) + .map(|stack| stack.depth) + { + debug!( + "evaluate_stack({:?}) --> recursive at depth {}", + stack.fresh_trait_ref, cycle_depth, + ); + + // If we have a stack like `A B C D E A`, where the top of + // the stack is the final `A`, then this will iterate over + // `A, E, D, C, B` -- i.e., all the participants apart + // from the cycle head. We mark them as participating in a + // cycle. This suppresses caching for those nodes. See + // `in_cycle` field for more details. + stack.update_reached_depth(cycle_depth); + + // Subtle: when checking for a coinductive cycle, we do + // not compare using the "freshened trait refs" (which + // have erased regions) but rather the fully explicit + // trait refs. This is important because it's only a cycle + // if the regions match exactly. + let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth); + let cycle = cycle.map(|stack| { + ty::Predicate::Trait(stack.obligation.predicate, hir::Constness::NotConst) + }); + if self.coinductive_match(cycle) { + debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref); + Some(EvaluatedToOk) + } else { + debug!("evaluate_stack({:?}) --> recursive, inductive", stack.fresh_trait_ref); + Some(EvaluatedToRecur) + } + } else { + None + } + } + + fn evaluate_stack<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> Result { + // In intercrate mode, whenever any of the types are unbound, + // there can always be an impl. Even if there are no impls in + // this crate, perhaps the type would be unified with + // something from another crate that does provide an impl. + // + // In intra mode, we must still be conservative. The reason is + // that we want to avoid cycles. Imagine an impl like: + // + // impl Eq for Vec + // + // and a trait reference like `$0 : Eq` where `$0` is an + // unbound variable. When we evaluate this trait-reference, we + // will unify `$0` with `Vec<$1>` (for some fresh variable + // `$1`), on the condition that `$1 : Eq`. We will then wind + // up with many candidates (since that are other `Eq` impls + // that apply) and try to winnow things down. This results in + // a recursive evaluation that `$1 : Eq` -- as you can + // imagine, this is just where we started. To avoid that, we + // check for unbound variables and return an ambiguous (hence possible) + // match if we've seen this trait before. + // + // This suffices to allow chains like `FnMut` implemented in + // terms of `Fn` etc, but we could probably make this more + // precise still. + let unbound_input_types = + stack.fresh_trait_ref.skip_binder().input_types().any(|ty| ty.is_fresh()); + // This check was an imperfect workaround for a bug in the old + // intercrate mode; it should be removed when that goes away. + if unbound_input_types && self.intercrate { + debug!( + "evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous", + stack.fresh_trait_ref + ); + // Heuristics: show the diagnostics when there are no candidates in crate. + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + if let Ok(candidate_set) = self.assemble_candidates(stack) { + if !candidate_set.ambiguous && candidate_set.vec.is_empty() { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let cause = IntercrateAmbiguityCause::DownstreamCrate { + trait_desc: trait_ref.print_only_trait_path().to_string(), + self_desc: if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }, + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(EvaluatedToAmbig); + } + if unbound_input_types + && stack.iter().skip(1).any(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && self.match_fresh_trait_refs( + &stack.fresh_trait_ref, + &prev.fresh_trait_ref, + prev.obligation.param_env, + ) + }) + { + debug!( + "evaluate_stack({:?}) --> unbound argument, recursive --> giving up", + stack.fresh_trait_ref + ); + return Ok(EvaluatedToUnknown); + } + + match self.candidate_from_obligation(stack) { + Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Ok(None) => Ok(EvaluatedToAmbig), + Err(Overflow) => Err(OverflowError), + Err(..) => Ok(EvaluatedToErr), + } + } + + /// For defaulted traits, we use a co-inductive strategy to solve, so + /// that recursion is ok. This routine returns `true` if the top of the + /// stack (`cycle[0]`): + /// + /// - is a defaulted trait, + /// - it also appears in the backtrace at some position `X`, + /// - all the predicates at positions `X..` between `X` and the top are + /// also defaulted traits. + pub fn coinductive_match(&mut self, cycle: I) -> bool + where + I: Iterator>, + { + let mut cycle = cycle; + cycle.all(|predicate| self.coinductive_predicate(predicate)) + } + + fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { + let result = match predicate { + ty::Predicate::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), + _ => false, + }; + debug!("coinductive_predicate({:?}) = {:?}", predicate, result); + result + } + + /// Further evaluates `candidate` to decide whether all type parameters match and whether nested + /// obligations are met. Returns whether `candidate` remains viable after this further + /// scrutiny. + fn evaluate_candidate<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + candidate: &SelectionCandidate<'tcx>, + ) -> Result { + debug!( + "evaluate_candidate: depth={} candidate={:?}", + stack.obligation.recursion_depth, candidate + ); + let result = self.evaluation_probe(|this| { + let candidate = (*candidate).clone(); + match this.confirm_candidate(stack.obligation, candidate) { + Ok(selection) => this.evaluate_predicates_recursively( + stack.list(), + selection.nested_obligations().into_iter(), + ), + Err(..) => Ok(EvaluatedToErr), + } + })?; + debug!( + "evaluate_candidate: depth={} result={:?}", + stack.obligation.recursion_depth, result + ); + Ok(result) + } + + fn check_evaluation_cache( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option { + let tcx = self.tcx(); + if self.can_use_global_caches(param_env) { + let cache = tcx.evaluation_cache.hashmap.borrow(); + if let Some(cached) = cache.get(¶m_env.and(trait_ref)) { + return Some(cached.get(tcx)); + } + } + self.infcx + .evaluation_cache + .hashmap + .borrow() + .get(¶m_env.and(trait_ref)) + .map(|v| v.get(tcx)) + } + + fn insert_evaluation_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + dep_node: DepNodeIndex, + result: EvaluationResult, + ) { + // Avoid caching results that depend on more than just the trait-ref + // - the stack can create recursion. + if result.is_stack_dependent() { + return; + } + + if self.can_use_global_caches(param_env) { + if !trait_ref.has_local_value() { + debug!( + "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", + trait_ref, result, + ); + // This may overwrite the cache with the same value + // FIXME: Due to #50507 this overwrites the different values + // This should be changed to use HashMapExt::insert_same + // when that is fixed + self.tcx() + .evaluation_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); + return; + } + } + + debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); + self.infcx + .evaluation_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); + } + + /// For various reasons, it's possible for a subobligation + /// to have a *lower* recursion_depth than the obligation used to create it. + /// Projection sub-obligations may be returned from the projection cache, + /// which results in obligations with an 'old' `recursion_depth`. + /// Additionally, methods like `wf::obligations` and + /// `InferCtxt.subtype_predicate` produce subobligations without + /// taking in a 'parent' depth, causing the generated subobligations + /// to have a `recursion_depth` of `0`. + /// + /// To ensure that obligation_depth never decreasees, we force all subobligations + /// to have at least the depth of the original obligation. + fn add_depth>>( + &self, + it: I, + min_depth: usize, + ) { + it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); + } + + /// Checks that the recursion limit has not been exceeded. + /// + /// The weird return type of this function allows it to be used with the `try` (`?`) + /// operator within certain functions. + fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( + &self, + obligation: &Obligation<'tcx, T>, + error_obligation: &Obligation<'tcx, V>, + ) -> Result<(), OverflowError> { + let recursion_limit = *self.infcx.tcx.sess.recursion_limit.get(); + if obligation.recursion_depth >= recursion_limit { + match self.query_mode { + TraitQueryMode::Standard => { + self.infcx().report_overflow_error(error_obligation, true); + } + TraitQueryMode::Canonical => { + return Err(OverflowError); + } + } + } + Ok(()) + } + + /////////////////////////////////////////////////////////////////////////// + // CANDIDATE ASSEMBLY + // + // The selection process begins by examining all in-scope impls, + // caller obligations, and so forth and assembling a list of + // candidates. See the [rustc dev guide] for more details. + // + // [rustc dev guide]: + // https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly + + fn candidate_from_obligation<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + // Watch out for overflow. This intentionally bypasses (and does + // not update) the cache. + self.check_recursion_limit(&stack.obligation, &stack.obligation)?; + + // Check the cache. Note that we freshen the trait-ref + // separately rather than using `stack.fresh_trait_ref` -- + // this is because we want the unbound variables to be + // replaced with fresh types starting from index 0. + let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate.clone()); + debug!( + "candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})", + cache_fresh_trait_pred, stack + ); + debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); + + if let Some(c) = + self.check_candidate_cache(stack.obligation.param_env, &cache_fresh_trait_pred) + { + debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c); + return c; + } + + // If no match, compute result and insert into cache. + // + // FIXME(nikomatsakis) -- this cache is not taking into + // account cycles that may have occurred in forming the + // candidate. I don't know of any specific problems that + // result but it seems awfully suspicious. + let (candidate, dep_node) = + self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); + + debug!("CACHE MISS: SELECT({:?})={:?}", cache_fresh_trait_pred, candidate); + self.insert_candidate_cache( + stack.obligation.param_env, + cache_fresh_trait_pred, + dep_node, + candidate.clone(), + ); + candidate + } + + fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) + where + OP: FnOnce(&mut Self) -> R, + { + let (result, dep_node) = + self.tcx().dep_graph.with_anon_task(DepKind::TraitSelect, || op(self)); + self.tcx().dep_graph.read_index(dep_node); + (result, dep_node) + } + + // Treat negative impls as unimplemented, and reservation impls as ambiguity. + fn filter_negative_and_reservation_impls( + &mut self, + candidate: SelectionCandidate<'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let ImplCandidate(def_id) = candidate { + let tcx = self.tcx(); + match tcx.impl_polarity(def_id) { + ty::ImplPolarity::Negative if !self.allow_negative_impls => { + return Err(Unimplemented); + } + ty::ImplPolarity::Reservation => { + if let Some(intercrate_ambiguity_clauses) = + &mut self.intercrate_ambiguity_causes + { + let attrs = tcx.get_attrs(def_id); + let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl); + let value = attr.and_then(|a| a.value_str()); + if let Some(value) = value { + debug!( + "filter_negative_and_reservation_impls: \ + reservation impl ambiguity on {:?}", + def_id + ); + intercrate_ambiguity_clauses.push( + IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }, + ); + } + } + return Ok(None); + } + _ => {} + }; + } + Ok(Some(candidate)) + } + + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if stack.obligation.predicate.references_error() { + // If we encounter a `Error`, we generally prefer the + // most "optimistic" result in response -- that is, the + // one least likely to report downstream errors. But + // because this routine is shared by coherence and by + // trait selection, there isn't an obvious "right" choice + // here in that respect, so we opt to just return + // ambiguity and let the upstream clients sort it out. + return Ok(None); + } + + if let Some(conflict) = self.is_knowable(stack) { + debug!("coherence stage: not knowable"); + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + { + let evaluated_candidates = + candidate_set.vec.iter().map(|c| self.evaluate_candidate(stack, &c)); + + for ec in evaluated_candidates { + match ec { + Ok(c) => { + if c.may_apply() { + no_candidates_apply = false; + break; + } + } + Err(e) => return Err(e.into()), + } + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let mut candidates = candidate_set.vec; + + debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl Vec { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError) => Err(Overflow), + }) + .flat_map(Result::transpose) + .collect::, _>>()?; + + debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + let needs_infer = stack.obligation.predicate.needs_infer(); + + // If there are STILL multiple candidates, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + needs_infer, + ) + }); + if is_dup { + debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + candidates.swap_remove(i); + } else { + debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) + } + + fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { + debug!("is_knowable(intercrate={:?})", self.intercrate); + + if !self.intercrate { + return None; + } + + let obligation = &stack.obligation; + let predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); + + // Okay to skip binder because of the nature of the + // trait-ref-is-knowable check, which does not care about + // bound regions. + let trait_ref = predicate.skip_binder().trait_ref; + + coherence::trait_ref_is_knowable(self.tcx(), trait_ref) + } + + /// Returns `true` if the global caches can be used. + /// Do note that if the type itself is not in the + /// global tcx, the local caches will be used. + fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { + // If there are any e.g. inference variables in the `ParamEnv`, then we + // always use a cache local to this particular scope. Otherwise, we + // switch to a global cache. + if param_env.has_local_value() { + return false; + } + + // Avoid using the master cache during coherence and just rely + // on the local cache. This effectively disables caching + // during coherence. It is really just a simplification to + // avoid us having to fear that coherence results "pollute" + // the master cache. Since coherence executes pretty quickly, + // it's not worth going to more trouble to increase the + // hit-rate, I don't think. + if self.intercrate { + return false; + } + + // Otherwise, we can use the global cache. + true + } + + fn check_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>, + ) -> Option>> { + let tcx = self.tcx(); + let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref; + if self.can_use_global_caches(param_env) { + let cache = tcx.selection_cache.hashmap.borrow(); + if let Some(cached) = cache.get(¶m_env.and(*trait_ref)) { + return Some(cached.get(tcx)); + } + } + self.infcx + .selection_cache + .hashmap + .borrow() + .get(¶m_env.and(*trait_ref)) + .map(|v| v.get(tcx)) + } + + /// Determines whether can we safely cache the result + /// of selecting an obligation. This is almost always `true`, + /// except when dealing with certain `ParamCandidate`s. + /// + /// Ordinarily, a `ParamCandidate` will contain no inference variables, + /// since it was usually produced directly from a `DefId`. However, + /// certain cases (currently only librustdoc's blanket impl finder), + /// a `ParamEnv` may be explicitly constructed with inference types. + /// When this is the case, we do *not* want to cache the resulting selection + /// candidate. This is due to the fact that it might not always be possible + /// to equate the obligation's trait ref and the candidate's trait ref, + /// if more constraints end up getting added to an inference variable. + /// + /// Because of this, we always want to re-run the full selection + /// process for our obligation the next time we see it, since + /// we might end up picking a different `SelectionCandidate` (or none at all). + fn can_cache_candidate( + &self, + result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) -> bool { + match result { + Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => { + !trait_ref.skip_binder().input_types().any(|t| t.walk().any(|t_| t_.is_ty_infer())) + } + _ => true, + } + } + + fn insert_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + dep_node: DepNodeIndex, + candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) { + let tcx = self.tcx(); + let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; + + if !self.can_cache_candidate(&candidate) { + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ + candidate is not cacheable", + trait_ref, candidate + ); + return; + } + + if self.can_use_global_caches(param_env) { + if let Err(Overflow) = candidate { + // Don't cache overflow globally; we only produce this in certain modes. + } else if !trait_ref.has_local_value() { + if !candidate.has_local_value() { + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", + trait_ref, candidate, + ); + // This may overwrite the cache with the same value. + tcx.selection_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); + return; + } + } + } + + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", + trait_ref, candidate, + ); + self.infcx + .selection_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); + } + + fn assemble_candidates<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> Result, SelectionError<'tcx>> { + let TraitObligationStack { obligation, .. } = *stack; + let obligation = &Obligation { + param_env: obligation.param_env, + cause: obligation.cause.clone(), + recursion_depth: obligation.recursion_depth, + predicate: self.infcx().resolve_vars_if_possible(&obligation.predicate), + }; + + if obligation.predicate.skip_binder().self_ty().is_ty_var() { + // Self is a type variable (e.g., `_: AsRef`). + // + // This is somewhat problematic, as the current scheme can't really + // handle it turning to be a projection. This does end up as truly + // ambiguous in most cases anyway. + // + // Take the fast path out - this also improves + // performance by preventing assemble_candidates_from_impls from + // matching every impl for this trait. + return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true }); + } + + let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; + + self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?; + + // Other bounds. Consider both in-scope bounds from fn decl + // and applicable impls. There is a certain set of precedence rules here. + let def_id = obligation.predicate.def_id(); + let lang_items = self.tcx().lang_items(); + + if lang_items.copy_trait() == Some(def_id) { + debug!("obligation self ty is {:?}", obligation.predicate.skip_binder().self_ty()); + + // User-defined copy impls are permitted, but only for + // structs and enums. + self.assemble_candidates_from_impls(obligation, &mut candidates)?; + + // For other types, we'll use the builtin rules. + let copy_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?; + } else if lang_items.sized_trait() == Some(def_id) { + // Sized is never implementable by end-users, it is + // always automatically computed. + let sized_conditions = self.sized_conditions(obligation); + self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; + } else if lang_items.unsize_trait() == Some(def_id) { + self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else { + if lang_items.clone_trait() == Some(def_id) { + // Same builtin conditions as `Copy`, i.e., every type which has builtin support + // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` + // types have builtin support for `Clone`. + let clone_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; + } + + self.assemble_generator_candidates(obligation, &mut candidates)?; + self.assemble_closure_candidates(obligation, &mut candidates)?; + self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; + self.assemble_candidates_from_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_object_ty(obligation, &mut candidates); + } + + self.assemble_candidates_from_projected_tys(obligation, &mut candidates); + self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; + // Auto implementations have lower priority, so we only + // consider triggering a default if there is no other impl that can apply. + if candidates.vec.is_empty() { + self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?; + } + debug!("candidate list size: {}", candidates.vec.len()); + Ok(candidates) + } + + fn assemble_candidates_from_projected_tys( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + debug!("assemble_candidates_for_projected_tys({:?})", obligation); + + // Before we go into the whole placeholder thing, just + // quickly check if the self-type is a projection at all. + match obligation.predicate.skip_binder().trait_ref.self_ty().kind { + ty::Projection(_) | ty::Opaque(..) => {} + ty::Infer(ty::TyVar(_)) => { + span_bug!( + obligation.cause.span, + "Self=_ should have been handled by assemble_candidates" + ); + } + _ => return, + } + + let result = self.infcx.probe(|snapshot| { + self.match_projection_obligation_against_definition_bounds(obligation, snapshot) + }); + + if result { + candidates.vec.push(ProjectionCandidate); + } + } + + fn match_projection_obligation_against_definition_bounds( + &mut self, + obligation: &TraitObligation<'tcx>, + snapshot: &CombinedSnapshot<'_, 'tcx>, + ) -> bool { + let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); + let (placeholder_trait_predicate, placeholder_map) = + self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); + debug!( + "match_projection_obligation_against_definition_bounds: \ + placeholder_trait_predicate={:?}", + placeholder_trait_predicate, + ); + + let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().kind { + ty::Projection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs), + ty::Opaque(def_id, substs) => (def_id, substs), + _ => { + span_bug!( + obligation.cause.span, + "match_projection_obligation_against_definition_bounds() called \ + but self-ty is not a projection: {:?}", + placeholder_trait_predicate.trait_ref.self_ty() + ); + } + }; + debug!( + "match_projection_obligation_against_definition_bounds: \ + def_id={:?}, substs={:?}", + def_id, substs + ); + + let predicates_of = self.tcx().predicates_of(def_id); + let bounds = predicates_of.instantiate(self.tcx(), substs); + debug!( + "match_projection_obligation_against_definition_bounds: \ + bounds={:?}", + bounds + ); + + let elaborated_predicates = util::elaborate_predicates(self.tcx(), bounds.predicates); + let matching_bound = elaborated_predicates.filter_to_traits().find(|bound| { + self.infcx.probe(|_| { + self.match_projection( + obligation, + bound.clone(), + placeholder_trait_predicate.trait_ref.clone(), + &placeholder_map, + snapshot, + ) + }) + }); + + debug!( + "match_projection_obligation_against_definition_bounds: \ + matching_bound={:?}", + matching_bound + ); + match matching_bound { + None => false, + Some(bound) => { + // Repeat the successful match, if any, this time outside of a probe. + let result = self.match_projection( + obligation, + bound, + placeholder_trait_predicate.trait_ref.clone(), + &placeholder_map, + snapshot, + ); + + assert!(result); + true + } + } + } + + fn match_projection( + &mut self, + obligation: &TraitObligation<'tcx>, + trait_bound: ty::PolyTraitRef<'tcx>, + placeholder_trait_ref: ty::TraitRef<'tcx>, + placeholder_map: &PlaceholderMap<'tcx>, + snapshot: &CombinedSnapshot<'_, 'tcx>, + ) -> bool { + debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) + .is_ok() + && self.infcx.leak_check(false, placeholder_map, snapshot).is_ok() + } + + /// Given an obligation like ``, searches the obligations that the caller + /// supplied to find out whether it is listed among them. + /// + /// Never affects the inference environment. + fn assemble_candidates_from_caller_bounds<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + debug!("assemble_candidates_from_caller_bounds({:?})", stack.obligation); + + let all_bounds = stack + .obligation + .param_env + .caller_bounds + .iter() + .filter_map(|o| o.to_opt_poly_trait_ref()); + + // Micro-optimization: filter out predicates relating to different traits. + let matching_bounds = + all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id()); + + // Keep only those bounds which may apply, and propagate overflow if it occurs. + let mut param_candidates = vec![]; + for bound in matching_bounds { + let wc = self.evaluate_where_clause(stack, bound.clone())?; + if wc.may_apply() { + param_candidates.push(ParamCandidate(bound)); + } + } + + candidates.vec.extend(param_candidates); + + Ok(()) + } + + fn evaluate_where_clause<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result { + self.evaluation_probe(|this| { + match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { + Ok(obligations) => { + this.evaluate_predicates_recursively(stack.list(), obligations.into_iter()) + } + Err(()) => Ok(EvaluatedToErr), + } + }) + } + + fn assemble_generator_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) { + return Ok(()); + } + + // Okay to skip binder because the substs on generator types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = *obligation.self_ty().skip_binder(); + match self_ty.kind { + ty::Generator(..) => { + debug!( + "assemble_generator_candidates: self_ty={:?} obligation={:?}", + self_ty, obligation + ); + + candidates.vec.push(GeneratorCandidate); + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_generator_candidates: ambiguous self-type"); + candidates.ambiguous = true; + } + _ => {} + } + + Ok(()) + } + + /// Checks for the artificial impl that the compiler will create for an obligation like `X : + /// FnMut<..>` where `X` is a closure type. + /// + /// Note: the type parameters on a closure candidate are modeled as *output* type + /// parameters and hence do not affect whether this trait is a match or not. They will be + /// unified during the confirmation step. + fn assemble_closure_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) { + Some(k) => k, + None => { + return Ok(()); + } + }; + + // Okay to skip binder because the substs on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters + match obligation.self_ty().skip_binder().kind { + ty::Closure(closure_def_id, closure_substs) => { + debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); + match self.infcx.closure_kind(closure_def_id, closure_substs) { + Some(closure_kind) => { + debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); + if closure_kind.extends(kind) { + candidates.vec.push(ClosureCandidate); + } + } + None => { + debug!("assemble_unboxed_candidates: closure_kind not yet known"); + candidates.vec.push(ClosureCandidate); + } + } + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_unboxed_closure_candidates: ambiguous self-type"); + candidates.ambiguous = true; + } + _ => {} + } + + Ok(()) + } + + /// Implements one of the `Fn()` family for a fn pointer. + fn assemble_fn_pointer_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + // We provide impl of all fn traits for fn pointers. + if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() { + return Ok(()); + } + + // Okay to skip binder because what we are inspecting doesn't involve bound regions. + let self_ty = *obligation.self_ty().skip_binder(); + match self_ty.kind { + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_fn_pointer_candidates: ambiguous self-type"); + candidates.ambiguous = true; // Could wind up being a fn() type. + } + // Provide an impl, but only for suitable `fn` pointers. + ty::FnDef(..) | ty::FnPtr(_) => { + if let ty::FnSig { + unsafety: hir::Unsafety::Normal, + abi: Abi::Rust, + c_variadic: false, + .. + } = self_ty.fn_sig(self.tcx()).skip_binder() + { + candidates.vec.push(FnPointerCandidate); + } + } + _ => {} + } + + Ok(()) + } + + /// Searches for impls that might apply to `obligation`. + fn assemble_candidates_from_impls( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + debug!("assemble_candidates_from_impls(obligation={:?})", obligation); + + self.tcx().for_each_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + |impl_def_id| { + self.infcx.probe(|snapshot| { + if let Ok(_substs) = self.match_impl(impl_def_id, obligation, snapshot) { + candidates.vec.push(ImplCandidate(impl_def_id)); + } + }); + }, + ); + + Ok(()) + } + + fn assemble_candidates_from_auto_impls( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + // Okay to skip binder here because the tests we do below do not involve bound regions. + let self_ty = *obligation.self_ty().skip_binder(); + debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty); + + let def_id = obligation.predicate.def_id(); + + if self.tcx().trait_is_auto(def_id) { + match self_ty.kind { + ty::Dynamic(..) => { + // For object types, we don't know what the closed + // over types are. This means we conservatively + // say nothing; a candidate may be added by + // `assemble_candidates_from_object_ty`. + } + ty::Foreign(..) => { + // Since the contents of foreign types is unknown, + // we don't add any `..` impl. Default traits could + // still be provided by a manual implementation for + // this trait and type. + } + ty::Param(..) | ty::Projection(..) => { + // In these cases, we don't know what the actual + // type is. Therefore, we cannot break it down + // into its constituent types. So we don't + // consider the `..` impl but instead just add no + // candidates: this means that typeck will only + // succeed if there is another reason to believe + // that this obligation holds. That could be a + // where-clause or, in the case of an object type, + // it could be that the object type lists the + // trait (e.g., `Foo+Send : Send`). See + // `compile-fail/typeck-default-trait-impl-send-param.rs` + // for an example of a test case that exercises + // this path. + } + ty::Infer(ty::TyVar(_)) => { + // The auto impl might apply; we don't know. + candidates.ambiguous = true; + } + ty::Generator(_, _, movability) + if self.tcx().lang_items().unpin_trait() == Some(def_id) => + { + match movability { + hir::Movability::Static => { + // Immovable generators are never `Unpin`, so + // suppress the normal auto-impl candidate for it. + } + hir::Movability::Movable => { + // Movable generators are always `Unpin`, so add an + // unconditional builtin candidate. + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + } + } + + _ => candidates.vec.push(AutoImplCandidate(def_id)), + } + } + + Ok(()) + } + + /// Searches for impls that might apply to `obligation`. + fn assemble_candidates_from_object_ty( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + debug!( + "assemble_candidates_from_object_ty(self_ty={:?})", + obligation.self_ty().skip_binder() + ); + + self.infcx.probe(|_snapshot| { + // The code below doesn't care about regions, and the + // self-ty here doesn't escape this probe, so just erase + // any LBR. + let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty()); + let poly_trait_ref = match self_ty.kind { + ty::Dynamic(ref data, ..) => { + if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { + debug!( + "assemble_candidates_from_object_ty: matched builtin bound, \ + pushing candidate" + ); + candidates.vec.push(BuiltinObjectCandidate); + return; + } + + if let Some(principal) = data.principal() { + if !self.infcx.tcx.features().object_safe_for_dispatch { + principal.with_self_ty(self.tcx(), self_ty) + } else if self.tcx().is_object_safe(principal.def_id()) { + principal.with_self_ty(self.tcx(), self_ty) + } else { + return; + } + } else { + // Only auto trait bounds exist. + return; + } + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_candidates_from_object_ty: ambiguous"); + candidates.ambiguous = true; // could wind up being an object type + return; + } + _ => return, + }; + + debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}", poly_trait_ref); + + // Count only those upcast versions that match the trait-ref + // we are looking for. Specifically, do not only check for the + // correct trait, but also the correct type parameters. + // For example, we may be trying to upcast `Foo` to `Bar`, + // but `Foo` is declared as `trait Foo: Bar`. + let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref) + .filter(|upcast_trait_ref| { + self.infcx + .probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok()) + }) + .count(); + + if upcast_trait_refs > 1 { + // Can be upcast in many ways; need more type information. + candidates.ambiguous = true; + } else if upcast_trait_refs == 1 { + candidates.vec.push(ObjectCandidate); + } + }) + } + + /// Searches for unsizing that might apply to `obligation`. + fn assemble_candidates_for_unsizing( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // We currently never consider higher-ranked obligations e.g. + // `for<'a> &'a T: Unsize` to be implemented. This is not + // because they are a priori invalid, and we could potentially add support + // for them later, it's just that there isn't really a strong need for it. + // A `T: Unsize` obligation is always used as part of a `T: CoerceUnsize` + // impl, and those are generally applied to concrete types. + // + // That said, one might try to write a fn with a where clause like + // for<'a> Foo<'a, T>: Unsize> + // where the `'a` is kind of orthogonal to the relevant part of the `Unsize`. + // Still, you'd be more likely to write that where clause as + // T: Trait + // so it seems ok if we (conservatively) fail to accept that `Unsize` + // obligation above. Should be possible to extend this in the future. + let source = match obligation.self_ty().no_bound_vars() { + Some(t) => t, + None => { + // Don't add any candidates if there are bound regions. + return; + } + }; + let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); + + debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); + + let may_apply = match (&source.kind, &target.kind) { + // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { + // Upcasts permit two things: + // + // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` + // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` + // + // Note that neither of these changes requires any + // change at runtime. Eventually this will be + // generalized. + // + // We always upcast when we can because of reason + // #2 (region bounds). + data_a.principal_def_id() == data_b.principal_def_id() + && data_b + .auto_traits() + // All of a's auto traits need to be in b's auto traits. + .all(|b| data_a.auto_traits().any(|a| a == b)) + } + + // `T` -> `Trait` + (_, &ty::Dynamic(..)) => true, + + // Ambiguous handling is below `T` -> `Trait`, because inference + // variables can still implement `Unsize` and nested + // obligations will have the final say (likely deferred). + (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { + debug!("assemble_candidates_for_unsizing: ambiguous"); + candidates.ambiguous = true; + false + } + + // `[T; n]` -> `[T]` + (&ty::Array(..), &ty::Slice(_)) => true, + + // `Struct` -> `Struct` + (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { + def_id_a == def_id_b + } + + // `(.., T)` -> `(.., U)` + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(), + + _ => false, + }; + + if may_apply { + candidates.vec.push(BuiltinUnsizeCandidate); + } + } + + fn assemble_candidates_for_trait_alias( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + // Okay to skip binder here because the tests we do below do not involve bound regions. + let self_ty = *obligation.self_ty().skip_binder(); + debug!("assemble_candidates_for_trait_alias(self_ty={:?})", self_ty); + + let def_id = obligation.predicate.def_id(); + + if self.tcx().is_trait_alias(def_id) { + candidates.vec.push(TraitAliasCandidate(def_id)); + } + + Ok(()) + } + + /////////////////////////////////////////////////////////////////////////// + // WINNOW + // + // Winnowing is the process of attempting to resolve ambiguity by + // probing further. During the winnowing process, we unify all + // type variables and then we also attempt to evaluate recursive + // bounds to see if they are satisfied. + + /// Returns `true` if `victim` should be dropped in favor of + /// `other`. Generally speaking we will drop duplicate + /// candidates and prefer where-clause candidates. + /// + /// See the comment for "SelectionCandidate" for more details. + fn candidate_should_be_dropped_in_favor_of( + &mut self, + victim: &EvaluatedCandidate<'tcx>, + other: &EvaluatedCandidate<'tcx>, + needs_infer: bool, + ) -> bool { + if victim.candidate == other.candidate { + return true; + } + + // Check if a bound would previously have been removed when normalizing + // the param_env so that it can be given the lowest priority. See + // #50825 for the motivation for this. + let is_global = + |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); + + match other.candidate { + // Prefer `BuiltinCandidate { has_nested: false }` to anything else. + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. + BuiltinCandidate { has_nested: false } => true, + ParamCandidate(ref cand) => match victim.candidate { + AutoImplCandidate(..) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // Prefer `BuiltinCandidate { has_nested: false }` to anything else. + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. + BuiltinCandidate { has_nested: false } => false, + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..) => { + // Global bounds from the where clause should be ignored + // here (see issue #50825). Otherwise, we have a where + // clause so don't go around looking for impls. + !is_global(cand) + } + ObjectCandidate | ProjectionCandidate => { + // Arbitrarily give param candidates priority + // over projection and object candidates. + !is_global(cand) + } + ParamCandidate(..) => false, + }, + ObjectCandidate | ProjectionCandidate => match victim.candidate { + AutoImplCandidate(..) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // Prefer `BuiltinCandidate { has_nested: false }` to anything else. + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. + BuiltinCandidate { has_nested: false } => false, + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..) => true, + ObjectCandidate | ProjectionCandidate => { + // Arbitrarily give param candidates priority + // over projection and object candidates. + true + } + ParamCandidate(ref cand) => is_global(cand), + }, + ImplCandidate(other_def) => { + // See if we can toss out `victim` based on specialization. + // This requires us to know *for sure* that the `other` impl applies + // i.e., `EvaluatedToOk`. + if other.evaluation.must_apply_modulo_regions() { + match victim.candidate { + ImplCandidate(victim_def) => { + let tcx = self.tcx(); + if tcx.specializes((other_def, victim_def)) { + return true; + } + return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { + Some(ty::ImplOverlapKind::Permitted { marker: true }) => { + // Subtle: If the predicate we are evaluating has inference + // variables, do *not* allow discarding candidates due to + // marker trait impls. + // + // Without this restriction, we could end up accidentally + // constrainting inference variables based on an arbitrarily + // chosen trait impl. + // + // Imagine we have the following code: + // + // ```rust + // #[marker] trait MyTrait {} + // impl MyTrait for u8 {} + // impl MyTrait for bool {} + // ``` + // + // And we are evaluating the predicate `<_#0t as MyTrait>`. + // + // During selection, we will end up with one candidate for each + // impl of `MyTrait`. If we were to discard one impl in favor + // of the other, we would be left with one candidate, causing + // us to "successfully" select the predicate, unifying + // _#0t with (for example) `u8`. + // + // However, we have no reason to believe that this unification + // is correct - we've essentially just picked an arbitrary + // *possibility* for _#0t, and required that this be the *only* + // possibility. + // + // Eventually, we will either: + // 1) Unify all inference variables in the predicate through + // some other means (e.g. type-checking of a function). We will + // then be in a position to drop marker trait candidates + // without constraining inference variables (since there are + // none left to constrin) + // 2) Be left with some unconstrained inference variables. We + // will then correctly report an inference error, since the + // existence of multiple marker trait impls tells us nothing + // about which one should actually apply. + !needs_infer + } + Some(_) => true, + None => false, + }; + } + ParamCandidate(ref cand) => { + // Prefer the impl to a global where clause candidate. + return is_global(cand); + } + _ => (), + } + } + + false + } + ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } => { + match victim.candidate { + ParamCandidate(ref cand) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) && other.evaluation.must_apply_modulo_regions() + } + _ => false, + } + } + _ => false, + } + } + + /////////////////////////////////////////////////////////////////////////// + // BUILTIN BOUNDS + // + // These cover the traits that are built-in to the language + // itself: `Copy`, `Clone` and `Sized`. + + fn assemble_builtin_bound_candidates( + &mut self, + conditions: BuiltinImplConditions<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + match conditions { + BuiltinImplConditions::Where(nested) => { + debug!("builtin_bound: nested={:?}", nested); + candidates + .vec + .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); + } + BuiltinImplConditions::None => {} + BuiltinImplConditions::Ambiguous => { + debug!("assemble_builtin_bound_candidates: ambiguous builtin"); + candidates.ambiguous = true; + } + } + + Ok(()) + } + + fn sized_conditions( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + match self_ty.kind { + ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::Never + | ty::Error => { + // safe for everything + Where(ty::Binder::dummy(Vec::new())) + } + + ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, + + ty::Tuple(tys) => { + Where(ty::Binder::bind(tys.last().into_iter().map(|k| k.expect_ty()).collect())) + } + + ty::Adt(def, substs) => { + let sized_crit = def.sized_constraint(self.tcx()); + // (*) binder moved here + Where(ty::Binder::bind( + sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect(), + )) + } + + ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, + ty::Infer(ty::TyVar(_)) => Ambiguous, + + ty::UnnormalizedProjection(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_)) + | ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + fn copy_clone_conditions( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + match self_ty.kind { + ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error => Where(ty::Binder::dummy(Vec::new())), + + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) => { + // Implementations provided in libcore + None + } + + ty::Dynamic(..) + | ty::Str + | ty::Slice(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Ref(_, _, hir::Mutability::Mut) => None, + + ty::Array(element_ty, _) => { + // (*) binder moved here + Where(ty::Binder::bind(vec![element_ty])) + } + + ty::Tuple(tys) => { + // (*) binder moved here + Where(ty::Binder::bind(tys.iter().map(|k| k.expect_ty()).collect())) + } + + ty::Closure(def_id, substs) => { + // (*) binder moved here + Where(ty::Binder::bind(substs.as_closure().upvar_tys(def_id, self.tcx()).collect())) + } + + ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => { + // Fallback to whatever user-defined impls exist in this case. + None + } + + ty::Infer(ty::TyVar(_)) => { + // Unbound type variable. Might or might not have + // applicable impls and so forth, depending on what + // those type variables wind up being bound to. + Ambiguous + } + + ty::UnnormalizedProjection(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_)) + | ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + /// For default impls, we need to break apart a type into its + /// "constituent types" -- meaning, the types that it contains. + /// + /// Here are some (simple) examples: + /// + /// ``` + /// (i32, u32) -> [i32, u32] + /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] + /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] + /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] + /// ``` + fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { + match t.kind { + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Str + | ty::Error + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Never + | ty::Char => Vec::new(), + + ty::UnnormalizedProjection(..) + | ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Param(..) + | ty::Foreign(..) + | ty::Projection(..) + | ty::Bound(..) + | ty::Infer(ty::TyVar(_)) + | ty::Infer(ty::FreshTy(_)) + | ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) => { + bug!("asked to assemble constituent types of unexpected type: {:?}", t); + } + + ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + vec![element_ty] + } + + ty::Array(element_ty, _) | ty::Slice(element_ty) => vec![element_ty], + + ty::Tuple(ref tys) => { + // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet + tys.iter().map(|k| k.expect_ty()).collect() + } + + ty::Closure(def_id, ref substs) => { + substs.as_closure().upvar_tys(def_id, self.tcx()).collect() + } + + ty::Generator(def_id, ref substs, _) => { + let witness = substs.as_generator().witness(def_id, self.tcx()); + substs + .as_generator() + .upvar_tys(def_id, self.tcx()) + .chain(iter::once(witness)) + .collect() + } + + ty::GeneratorWitness(types) => { + // This is sound because no regions in the witness can refer to + // the binder outside the witness. So we'll effectivly reuse + // the implicit binder around the witness. + types.skip_binder().to_vec() + } + + // For `PhantomData`, we pass `T`. + ty::Adt(def, substs) if def.is_phantom_data() => substs.types().collect(), + + ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect(), + + ty::Opaque(def_id, substs) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)] + } + } + } + + fn collect_predicates_for_types( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + trait_def_id: DefId, + types: ty::Binder>>, + ) -> Vec> { + // Because the types were potentially derived from + // higher-ranked obligations they may reference late-bound + // regions. For example, `for<'a> Foo<&'a int> : Copy` would + // yield a type like `for<'a> &'a int`. In general, we + // maintain the invariant that we never manipulate bound + // regions, so we have to process these bound regions somehow. + // + // The strategy is to: + // + // 1. Instantiate those regions to placeholder regions (e.g., + // `for<'a> &'a int` becomes `&0 int`. + // 2. Produce something like `&'0 int : Copy` + // 3. Re-bind the regions back to `for<'a> &'a int : Copy` + + types + .skip_binder() + .iter() + .flat_map(|ty| { + // binder moved -\ + let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ + + self.infcx.commit_unconditionally(|_| { + let (skol_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); + let Normalized { value: normalized_ty, mut obligations } = + project::normalize_with_depth( + self, + param_env, + cause.clone(), + recursion_depth, + &skol_ty, + ); + let skol_obligation = predicate_for_trait_def( + self.tcx(), + param_env, + cause.clone(), + trait_def_id, + recursion_depth, + normalized_ty, + &[], + ); + obligations.push(skol_obligation); + obligations + }) + }) + .collect() + } + + /////////////////////////////////////////////////////////////////////////// + // CONFIRMATION + // + // Confirmation unifies the output type parameters of the trait + // with the values found in the obligation, possibly yielding a + // type error. See the [rustc dev guide] for more details. + // + // [rustc dev guide]: + // https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation + + fn confirm_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + candidate: SelectionCandidate<'tcx>, + ) -> Result, SelectionError<'tcx>> { + debug!("confirm_candidate({:?}, {:?})", obligation, candidate); + + match candidate { + BuiltinCandidate { has_nested } => { + let data = self.confirm_builtin_candidate(obligation, has_nested); + Ok(VtableBuiltin(data)) + } + + ParamCandidate(param) => { + let obligations = self.confirm_param_candidate(obligation, param); + Ok(VtableParam(obligations)) + } + + ImplCandidate(impl_def_id) => { + Ok(VtableImpl(self.confirm_impl_candidate(obligation, impl_def_id))) + } + + AutoImplCandidate(trait_def_id) => { + let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); + Ok(VtableAutoImpl(data)) + } + + ProjectionCandidate => { + self.confirm_projection_candidate(obligation); + Ok(VtableParam(Vec::new())) + } + + ClosureCandidate => { + let vtable_closure = self.confirm_closure_candidate(obligation)?; + Ok(VtableClosure(vtable_closure)) + } + + GeneratorCandidate => { + let vtable_generator = self.confirm_generator_candidate(obligation)?; + Ok(VtableGenerator(vtable_generator)) + } + + FnPointerCandidate => { + let data = self.confirm_fn_pointer_candidate(obligation)?; + Ok(VtableFnPointer(data)) + } + + TraitAliasCandidate(alias_def_id) => { + let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); + Ok(VtableTraitAlias(data)) + } + + ObjectCandidate => { + let data = self.confirm_object_candidate(obligation); + Ok(VtableObject(data)) + } + + BuiltinObjectCandidate => { + // This indicates something like `Trait + Send: Send`. In this case, we know that + // this holds because that's what the object type is telling us, and there's really + // no additional obligations to prove and no types in particular to unify, etc. + Ok(VtableParam(Vec::new())) + } + + BuiltinUnsizeCandidate => { + let data = self.confirm_builtin_unsize_candidate(obligation)?; + Ok(VtableBuiltin(data)) + } + } + } + + fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { + self.infcx.commit_unconditionally(|snapshot| { + let result = + self.match_projection_obligation_against_definition_bounds(obligation, snapshot); + assert!(result); + }) + } + + fn confirm_param_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + param: ty::PolyTraitRef<'tcx>, + ) -> Vec> { + debug!("confirm_param_candidate({:?},{:?})", obligation, param); + + // During evaluation, we already checked that this + // where-clause trait-ref could be unified with the obligation + // trait-ref. Repeat that unification now without any + // transactional boundary; it should not fail. + match self.match_where_clause_trait_ref(obligation, param.clone()) { + Ok(obligations) => obligations, + Err(()) => { + bug!( + "Where clause `{:?}` was applicable to `{:?}` but now is not", + param, + obligation + ); + } + } + } + + fn confirm_builtin_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + has_nested: bool, + ) -> VtableBuiltinData> { + debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested); + + let lang_items = self.tcx().lang_items(); + let obligations = if has_nested { + let trait_def = obligation.predicate.def_id(); + let conditions = if Some(trait_def) == lang_items.sized_trait() { + self.sized_conditions(obligation) + } else if Some(trait_def) == lang_items.copy_trait() { + self.copy_clone_conditions(obligation) + } else if Some(trait_def) == lang_items.clone_trait() { + self.copy_clone_conditions(obligation) + } else { + bug!("unexpected builtin trait {:?}", trait_def) + }; + let nested = match conditions { + BuiltinImplConditions::Where(nested) => nested, + _ => bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation), + }; + + let cause = obligation.derived_cause(BuiltinDerivedObligation); + self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def, + nested, + ) + } else { + vec![] + }; + + debug!("confirm_builtin_candidate: obligations={:?}", obligations); + + VtableBuiltinData { nested: obligations } + } + + /// This handles the case where a `auto trait Foo` impl is being used. + /// The idea is that the impl applies to `X : Foo` if the following conditions are met: + /// + /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds + /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. + fn confirm_auto_impl_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + trait_def_id: DefId, + ) -> VtableAutoImplData> { + debug!("confirm_auto_impl_candidate({:?}, {:?})", obligation, trait_def_id); + + let types = obligation.predicate.map_bound(|inner| { + let self_ty = self.infcx.shallow_resolve(inner.self_ty()); + self.constituent_types_for_ty(self_ty) + }); + self.vtable_auto_impl(obligation, trait_def_id, types) + } + + /// See `confirm_auto_impl_candidate`. + fn vtable_auto_impl( + &mut self, + obligation: &TraitObligation<'tcx>, + trait_def_id: DefId, + nested: ty::Binder>>, + ) -> VtableAutoImplData> { + debug!("vtable_auto_impl: nested={:?}", nested); + + let cause = obligation.derived_cause(BuiltinDerivedObligation); + let mut obligations = self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def_id, + nested, + ); + + let trait_obligations: Vec> = + self.infcx.commit_unconditionally(|_| { + let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); + let (trait_ref, _) = + self.infcx.replace_bound_vars_with_placeholders(&poly_trait_ref); + let cause = obligation.derived_cause(ImplDerivedObligation); + self.impl_or_trait_obligations( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + trait_def_id, + &trait_ref.substs, + ) + }); + + // Adds the predicates from the trait. Note that this contains a `Self: Trait` + // predicate as usual. It won't have any effect since auto traits are coinductive. + obligations.extend(trait_obligations); + + debug!("vtable_auto_impl: obligations={:?}", obligations); + + VtableAutoImplData { trait_def_id, nested: obligations } + } + + fn confirm_impl_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + impl_def_id: DefId, + ) -> VtableImplData<'tcx, PredicateObligation<'tcx>> { + debug!("confirm_impl_candidate({:?},{:?})", obligation, impl_def_id); + + // First, create the substitutions by matching the impl again, + // this time not in a probe. + self.infcx.commit_unconditionally(|snapshot| { + let substs = self.rematch_impl(impl_def_id, obligation, snapshot); + debug!("confirm_impl_candidate: substs={:?}", substs); + let cause = obligation.derived_cause(ImplDerivedObligation); + self.vtable_impl( + impl_def_id, + substs, + cause, + obligation.recursion_depth + 1, + obligation.param_env, + ) + }) + } + + fn vtable_impl( + &mut self, + impl_def_id: DefId, + mut substs: Normalized<'tcx, SubstsRef<'tcx>>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + ) -> VtableImplData<'tcx, PredicateObligation<'tcx>> { + debug!( + "vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})", + impl_def_id, substs, recursion_depth, + ); + + let mut impl_obligations = self.impl_or_trait_obligations( + cause, + recursion_depth, + param_env, + impl_def_id, + &substs.value, + ); + + debug!( + "vtable_impl: impl_def_id={:?} impl_obligations={:?}", + impl_def_id, impl_obligations + ); + + // Because of RFC447, the impl-trait-ref and obligations + // are sufficient to determine the impl substs, without + // relying on projections in the impl-trait-ref. + // + // e.g., `impl> Foo<::T> for V` + impl_obligations.append(&mut substs.obligations); + + VtableImplData { impl_def_id, substs: substs.value, nested: impl_obligations } + } + + fn confirm_object_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> VtableObjectData<'tcx, PredicateObligation<'tcx>> { + debug!("confirm_object_candidate({:?})", obligation); + + // FIXME(nmatsakis) skipping binder here seems wrong -- we should + // probably flatten the binder from the obligation and the binder + // from the object. Have to try to make a broken test case that + // results. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let poly_trait_ref = match self_ty.kind { + ty::Dynamic(ref data, ..) => data + .principal() + .unwrap_or_else(|| { + span_bug!(obligation.cause.span, "object candidate with no principal") + }) + .with_self_ty(self.tcx(), self_ty), + _ => span_bug!(obligation.cause.span, "object candidate with non-object"), + }; + + let mut upcast_trait_ref = None; + let mut nested = vec![]; + let vtable_base; + + { + let tcx = self.tcx(); + + // We want to find the first supertrait in the list of + // supertraits that we can unify with, and do that + // unification. We know that there is exactly one in the list + // where we can unify, because otherwise select would have + // reported an ambiguity. (When we do find a match, also + // record it for later.) + let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| { + match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) { + Ok(obligations) => { + upcast_trait_ref = Some(t); + nested.extend(obligations); + false + } + Err(_) => true, + } + }); + + // Additionally, for each of the non-matching predicates that + // we pass over, we sum up the set of number of vtable + // entries, so that we can compute the offset for the selected + // trait. + vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum(); + } + + VtableObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested } + } + + fn confirm_fn_pointer_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + debug!("confirm_fn_pointer_candidate({:?})", obligation); + + // Okay to skip binder; it is reintroduced below. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let sig = self_ty.fn_sig(self.tcx()); + let trait_ref = closure_trait_ref_and_return_type( + self.tcx(), + obligation.predicate.def_id(), + self_ty, + sig, + util::TupleArgumentsFlag::Yes, + ) + .map_bound(|(trait_ref, _)| trait_ref); + + let Normalized { value: trait_ref, obligations } = project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_ref, + ); + + self.confirm_poly_trait_refs( + obligation.cause.clone(), + obligation.param_env, + obligation.predicate.to_poly_trait_ref(), + trait_ref, + )?; + Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations }) + } + + fn confirm_trait_alias_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + alias_def_id: DefId, + ) -> VtableTraitAliasData<'tcx, PredicateObligation<'tcx>> { + debug!("confirm_trait_alias_candidate({:?}, {:?})", obligation, alias_def_id); + + self.infcx.commit_unconditionally(|_| { + let (predicate, _) = + self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); + let trait_ref = predicate.trait_ref; + let trait_def_id = trait_ref.def_id; + let substs = trait_ref.substs; + + let trait_obligations = self.impl_or_trait_obligations( + obligation.cause.clone(), + obligation.recursion_depth, + obligation.param_env, + trait_def_id, + &substs, + ); + + debug!( + "confirm_trait_alias_candidate: trait_def_id={:?} trait_obligations={:?}", + trait_def_id, trait_obligations + ); + + VtableTraitAliasData { alias_def_id, substs: substs, nested: trait_obligations } + }) + } + + fn confirm_generator_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + // Okay to skip binder because the substs on generator types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let (generator_def_id, substs) = match self_ty.kind { + ty::Generator(id, substs, _) => (id, substs), + _ => bug!("closure candidate for non-closure {:?}", obligation), + }; + + debug!("confirm_generator_candidate({:?},{:?},{:?})", obligation, generator_def_id, substs); + + let trait_ref = self.generator_trait_ref_unnormalized(obligation, generator_def_id, substs); + let Normalized { value: trait_ref, mut obligations } = normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_ref, + ); + + debug!( + "confirm_generator_candidate(generator_def_id={:?}, \ + trait_ref={:?}, obligations={:?})", + generator_def_id, trait_ref, obligations + ); + + obligations.extend(self.confirm_poly_trait_refs( + obligation.cause.clone(), + obligation.param_env, + obligation.predicate.to_poly_trait_ref(), + trait_ref, + )?); + + Ok(VtableGeneratorData { generator_def_id, substs, nested: obligations }) + } + + fn confirm_closure_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + debug!("confirm_closure_candidate({:?})", obligation); + + let kind = self + .tcx() + .fn_trait_kind_from_lang_item(obligation.predicate.def_id()) + .unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation)); + + // Okay to skip binder because the substs on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let (closure_def_id, substs) = match self_ty.kind { + ty::Closure(id, substs) => (id, substs), + _ => bug!("closure candidate for non-closure {:?}", obligation), + }; + + let trait_ref = self.closure_trait_ref_unnormalized(obligation, closure_def_id, substs); + let Normalized { value: trait_ref, mut obligations } = normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_ref, + ); + + debug!( + "confirm_closure_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})", + closure_def_id, trait_ref, obligations + ); + + obligations.extend(self.confirm_poly_trait_refs( + obligation.cause.clone(), + obligation.param_env, + obligation.predicate.to_poly_trait_ref(), + trait_ref, + )?); + + obligations.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env, + ty::Predicate::ClosureKind(closure_def_id, substs, kind), + )); + + Ok(VtableClosureData { closure_def_id, substs, nested: obligations }) + } + + /// In the case of closure types and fn pointers, + /// we currently treat the input type parameters on the trait as + /// outputs. This means that when we have a match we have only + /// considered the self type, so we have to go back and make sure + /// to relate the argument types too. This is kind of wrong, but + /// since we control the full set of impls, also not that wrong, + /// and it DOES yield better error messages (since we don't report + /// errors as if there is no applicable impl, but rather report + /// errors are about mismatched argument types. + /// + /// Here is an example. Imagine we have a closure expression + /// and we desugared it so that the type of the expression is + /// `Closure`, and `Closure` expects an int as argument. Then it + /// is "as if" the compiler generated this impl: + /// + /// impl Fn(int) for Closure { ... } + /// + /// Now imagine our obligation is `Fn(usize) for Closure`. So far + /// we have matched the self type `Closure`. At this point we'll + /// compare the `int` to `usize` and generate an error. + /// + /// Note that this checking occurs *after* the impl has selected, + /// because these output type parameters should not affect the + /// selection of the impl. Therefore, if there is a mismatch, we + /// report an error to the user. + fn confirm_poly_trait_refs( + &mut self, + obligation_cause: ObligationCause<'tcx>, + obligation_param_env: ty::ParamEnv<'tcx>, + obligation_trait_ref: ty::PolyTraitRef<'tcx>, + expected_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + self.infcx + .at(&obligation_cause, obligation_param_env) + .sup(obligation_trait_ref, expected_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) + } + + fn confirm_builtin_unsize_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); + let target = self.infcx.shallow_resolve(target); + + debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); + + let mut nested = vec![]; + match (&source.kind, &target.kind) { + // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + // See `assemble_candidates_for_unsizing` for more info. + let existential_predicates = data_a.map_bound(|data_a| { + let iter = data_a + .principal() + .map(|x| ty::ExistentialPredicate::Trait(x)) + .into_iter() + .chain( + data_a + .projection_bounds() + .map(|x| ty::ExistentialPredicate::Projection(x)), + ) + .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); + tcx.mk_existential_predicates(iter) + }); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b); + + // Require that the traits involved in this upcast are **equal**; + // only the **lifetime bound** is changed. + // + // FIXME: This condition is arguably too strong -- it would + // suffice for the source trait to be a *subtype* of the target + // trait. In particular, changing from something like + // `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be + // permitted. And, indeed, in the in commit + // 904a0bde93f0348f69914ee90b1f8b6e4e0d7cbc, this + // condition was loosened. However, when the leak check was + // added back, using subtype here actually guides the coercion + // code in such a way that it accepts `old-lub-glb-object.rs`. + // This is probably a good thing, but I've modified this to `.eq` + // because I want to continue rejecting that test (as we have + // done for quite some time) before we are firmly comfortable + // with what our behavior should be there. -nikomatsakis + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(target, source_trait) // FIXME -- see below + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Register one obligation for 'a: 'b. + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + let outlives = ty::OutlivesPredicate(r_a, r_b); + nested.push(Obligation::with_depth( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + ty::Binder::bind(outlives).to_predicate(), + )); + } + + // `T` -> `Trait` + (_, &ty::Dynamic(ref data, r)) => { + let mut object_dids = data.auto_traits().chain(data.principal_def_id()); + if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) { + return Err(TraitNotObjectSafe(did)); + } + + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + + let predicate_to_obligation = |predicate| { + Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + ) + }; + + // Create obligations: + // - Casting `T` to `Trait` + // - For all the various builtin bounds attached to the object cast. (In other + // words, if the object type is `Foo + Send`, this would create an obligation for + // the `Send` check.) + // - Projection predicates + nested.extend( + data.iter().map(|predicate| { + predicate_to_obligation(predicate.with_self_ty(tcx, source)) + }), + ); + + // We can only make objects from sized types. + let tr = ty::TraitRef::new( + tcx.require_lang_item(lang_items::SizedTraitLangItem, None), + tcx.mk_substs_trait(source, &[]), + ); + nested.push(predicate_to_obligation(tr.without_const().to_predicate())); + + // If the type is `Foo + 'a`, ensure that the type + // being cast to `Foo + 'a` outlives `'a`: + let outlives = ty::OutlivesPredicate(source, r); + nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate())); + } + + // `[T; n]` -> `[T]` + (&ty::Array(a, _), &ty::Slice(b)) => { + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(b, a) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + } + + // `Struct` -> `Struct` + (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => { + let fields = + def.all_fields().map(|field| tcx.type_of(field.did)).collect::>(); + + // The last field of the structure has to exist and contain type parameters. + let field = if let Some(&field) = fields.last() { + field + } else { + return Err(Unimplemented); + }; + let mut ty_params = GrowableBitSet::new_empty(); + let mut found = false; + for ty in field.walk() { + if let ty::Param(p) = ty.kind { + ty_params.insert(p.index as usize); + found = true; + } + } + if !found { + return Err(Unimplemented); + } + + // Replace type parameters used in unsizing with + // Error and ensure they do not affect any other fields. + // This could be checked after type collection for any struct + // with a potentially unsized trailing field. + let params = substs_a + .iter() + .enumerate() + .map(|(i, &k)| if ty_params.contains(i) { tcx.types.err.into() } else { k }); + let substs = tcx.mk_substs(params); + for &ty in fields.split_last().unwrap().1 { + if ty.subst(tcx, substs).references_error() { + return Err(Unimplemented); + } + } + + // Extract `Field` and `Field` from `Struct` and `Struct`. + let inner_source = field.subst(tcx, substs_a); + let inner_target = field.subst(tcx, substs_b); + + // Check that the source struct with the target's + // unsized parameters is equal to the target. + let params = substs_a.iter().enumerate().map(|(i, &k)| { + if ty_params.contains(i) { substs_b.type_at(i).into() } else { k } + }); + let new_struct = tcx.mk_adt(def, tcx.mk_substs(params)); + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(target, new_struct) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Construct the nested `Field: Unsize>` predicate. + nested.push(predicate_for_trait_def( + tcx, + obligation.param_env, + obligation.cause.clone(), + obligation.predicate.def_id(), + obligation.recursion_depth + 1, + inner_source, + &[inner_target.into()], + )); + } + + // `(.., T)` -> `(.., U)` + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + assert_eq!(tys_a.len(), tys_b.len()); + + // The last field of the tuple has to exist. + let (&a_last, a_mid) = if let Some(x) = tys_a.split_last() { + x + } else { + return Err(Unimplemented); + }; + let &b_last = tys_b.last().unwrap(); + + // Check that the source tuple with the target's + // last element is equal to the target. + let new_tuple = tcx.mk_tup( + a_mid.iter().map(|k| k.expect_ty()).chain(iter::once(b_last.expect_ty())), + ); + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(target, new_tuple) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Construct the nested `T: Unsize` predicate. + nested.push(predicate_for_trait_def( + tcx, + obligation.param_env, + obligation.cause.clone(), + obligation.predicate.def_id(), + obligation.recursion_depth + 1, + a_last.expect_ty(), + &[b_last], + )); + } + + _ => bug!(), + }; + + Ok(VtableBuiltinData { nested }) + } + + /////////////////////////////////////////////////////////////////////////// + // Matching + // + // Matching is a common path used for both evaluation and + // confirmation. It basically unifies types that appear in impls + // and traits. This does affect the surrounding environment; + // therefore, when used during evaluation, match routines must be + // run inside of a `probe()` so that their side-effects are + // contained. + + fn rematch_impl( + &mut self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + snapshot: &CombinedSnapshot<'_, 'tcx>, + ) -> Normalized<'tcx, SubstsRef<'tcx>> { + match self.match_impl(impl_def_id, obligation, snapshot) { + Ok(substs) => substs, + Err(()) => { + bug!( + "Impl {:?} was matchable against {:?} but now is not", + impl_def_id, + obligation + ); + } + } + } + + fn match_impl( + &mut self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + snapshot: &CombinedSnapshot<'_, 'tcx>, + ) -> Result>, ()> { + let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); + + // Before we create the substitutions and everything, first + // consider a "quick reject". This avoids creating more types + // and so forth that we need to. + if self.fast_reject_trait_refs(obligation, &impl_trait_ref) { + return Err(()); + } + + let (skol_obligation, placeholder_map) = + self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); + let skol_obligation_trait_ref = skol_obligation.trait_ref; + + let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); + + let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); + + let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &impl_trait_ref, + ); + + debug!( + "match_impl(impl_def_id={:?}, obligation={:?}, \ + impl_trait_ref={:?}, skol_obligation_trait_ref={:?})", + impl_def_id, obligation, impl_trait_ref, skol_obligation_trait_ref + ); + + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(skol_obligation_trait_ref, impl_trait_ref) + .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; + nested_obligations.extend(obligations); + + if let Err(e) = self.infcx.leak_check(false, &placeholder_map, snapshot) { + debug!("match_impl: failed leak check due to `{}`", e); + return Err(()); + } + + if !self.intercrate + && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation + { + debug!("match_impl: reservation impls only apply in intercrate mode"); + return Err(()); + } + + debug!("match_impl: success impl_substs={:?}", impl_substs); + Ok(Normalized { value: impl_substs, obligations: nested_obligations }) + } + + fn fast_reject_trait_refs( + &mut self, + obligation: &TraitObligation<'_>, + impl_trait_ref: &ty::TraitRef<'_>, + ) -> bool { + // We can avoid creating type variables and doing the full + // substitution if we find that any of the input types, when + // simplified, do not match. + + obligation.predicate.skip_binder().input_types().zip(impl_trait_ref.input_types()).any( + |(obligation_ty, impl_ty)| { + let simplified_obligation_ty = + fast_reject::simplify_type(self.tcx(), obligation_ty, true); + let simplified_impl_ty = fast_reject::simplify_type(self.tcx(), impl_ty, false); + + simplified_obligation_ty.is_some() + && simplified_impl_ty.is_some() + && simplified_obligation_ty != simplified_impl_ty + }, + ) + } + + /// Normalize `where_clause_trait_ref` and try to match it against + /// `obligation`. If successful, return any predicates that + /// result from the normalization. Normalization is necessary + /// because where-clauses are stored in the parameter environment + /// unnormalized. + fn match_where_clause_trait_ref( + &mut self, + obligation: &TraitObligation<'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { + self.match_poly_trait_ref(obligation, where_clause_trait_ref) + } + + /// Returns `Ok` if `poly_trait_ref` being true implies that the + /// obligation is satisfied. + fn match_poly_trait_ref( + &mut self, + obligation: &TraitObligation<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { + debug!( + "match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}", + obligation, poly_trait_ref + ); + + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| ()) + } + + /////////////////////////////////////////////////////////////////////////// + // Miscellany + + fn match_fresh_trait_refs( + &self, + previous: &ty::PolyTraitRef<'tcx>, + current: &ty::PolyTraitRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + let mut matcher = ty::_match::Match::new(self.tcx(), param_env); + matcher.relate(previous, current).is_ok() + } + + fn push_stack<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: &'o TraitObligation<'tcx>, + ) -> TraitObligationStack<'o, 'tcx> { + let fresh_trait_ref = + obligation.predicate.to_poly_trait_ref().fold_with(&mut self.freshener); + + let dfn = previous_stack.cache.next_dfn(); + let depth = previous_stack.depth() + 1; + TraitObligationStack { + obligation, + fresh_trait_ref, + reached_depth: Cell::new(depth), + previous: previous_stack, + dfn, + depth, + } + } + + fn closure_trait_ref_unnormalized( + &mut self, + obligation: &TraitObligation<'tcx>, + closure_def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> ty::PolyTraitRef<'tcx> { + debug!( + "closure_trait_ref_unnormalized(obligation={:?}, closure_def_id={:?}, substs={:?})", + obligation, closure_def_id, substs, + ); + let closure_type = self.infcx.closure_sig(closure_def_id, substs); + + debug!("closure_trait_ref_unnormalized: closure_type = {:?}", closure_type); + + // (1) Feels icky to skip the binder here, but OTOH we know + // that the self-type is an unboxed closure type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). Still probably some + // refactoring could make this nicer. + closure_trait_ref_and_return_type( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.skip_binder().self_ty(), // (1) + closure_type, + util::TupleArgumentsFlag::No, + ) + .map_bound(|(trait_ref, _)| trait_ref) + } + + fn generator_trait_ref_unnormalized( + &mut self, + obligation: &TraitObligation<'tcx>, + closure_def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> ty::PolyTraitRef<'tcx> { + let gen_sig = substs.as_generator().poly_sig(closure_def_id, self.tcx()); + + // (1) Feels icky to skip the binder here, but OTOH we know + // that the self-type is an generator type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). Still probably some + // refactoring could make this nicer. + + super::util::generator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.skip_binder().self_ty(), // (1) + gen_sig, + ) + .map_bound(|(trait_ref, ..)| trait_ref) + } + + /// Returns the obligations that are implied by instantiating an + /// impl or trait. The obligations are substituted and fully + /// normalized. This is used when confirming an impl or default + /// impl. + fn impl_or_trait_obligations( + &mut self, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, // of impl or trait + substs: SubstsRef<'tcx>, // for impl or trait + ) -> Vec> { + debug!("impl_or_trait_obligations(def_id={:?})", def_id); + let tcx = self.tcx(); + + // To allow for one-pass evaluation of the nested obligation, + // each predicate must be preceded by the obligations required + // to normalize it. + // for example, if we have: + // impl, V: Iterator> Foo for V + // the impl will have the following predicates: + // ::Item = U, + // U: Iterator, U: Sized, + // V: Iterator, V: Sized, + // ::Item: Copy + // When we substitute, say, `V => IntoIter, U => $0`, the last + // obligation will normalize to `<$0 as Iterator>::Item = $1` and + // `$1: Copy`, so we must ensure the obligations are emitted in + // that order. + let predicates = tcx.predicates_of(def_id); + assert_eq!(predicates.parent, None); + let mut obligations = Vec::with_capacity(predicates.predicates.len()); + for (predicate, _) in predicates.predicates { + let predicate = normalize_with_depth_to( + self, + param_env, + cause.clone(), + recursion_depth, + &predicate.subst(tcx, substs), + &mut obligations, + ); + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }); + } + + // We are performing deduplication here to avoid exponential blowups + // (#38528) from happening, but the real cause of the duplication is + // unknown. What we know is that the deduplication avoids exponential + // amount of predicates being propagated when processing deeply nested + // types. + // + // This code is hot enough that it's worth avoiding the allocation + // required for the FxHashSet when possible. Special-casing lengths 0, + // 1 and 2 covers roughly 75-80% of the cases. + if obligations.len() <= 1 { + // No possibility of duplicates. + } else if obligations.len() == 2 { + // Only two elements. Drop the second if they are equal. + if obligations[0] == obligations[1] { + obligations.truncate(1); + } + } else { + // Three or more elements. Use a general deduplication process. + let mut seen = FxHashSet::default(); + obligations.retain(|i| seen.insert(i.clone())); + } + + obligations + } +} + +trait TraitObligationExt<'tcx> { + fn derived_cause( + &self, + variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx>; +} + +impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> { + #[allow(unused_comparisons)] + fn derived_cause( + &self, + variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + /*! + * Creates a cause for obligations that are derived from + * `obligation` by a recursive search (e.g., for a builtin + * bound, or eventually a `auto trait Foo`). If `obligation` + * is itself a derived obligation, this is just a clone, but + * otherwise we create a "derived obligation" cause so as to + * keep track of the original root obligation for error + * reporting. + */ + + let obligation = self; + + // NOTE(flaper87): As of now, it keeps track of the whole error + // chain. Ideally, we should have a way to configure this either + // by using -Z verbose or just a CLI argument. + let derived_cause = DerivedObligationCause { + parent_trait_ref: obligation.predicate.to_poly_trait_ref(), + parent_code: Rc::new(obligation.cause.code.clone()), + }; + let derived_code = variant(derived_cause); + ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code) + } +} + +impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { + fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList::with(self) + } + + fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> { + self.previous.cache + } + + fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> { + self.list() + } + + /// Indicates that attempting to evaluate this stack entry + /// required accessing something from the stack at depth `reached_depth`. + fn update_reached_depth(&self, reached_depth: usize) { + assert!( + self.depth > reached_depth, + "invoked `update_reached_depth` with something under this stack: \ + self.depth={} reached_depth={}", + self.depth, + reached_depth, + ); + debug!("update_reached_depth(reached_depth={})", reached_depth); + let mut p = self; + while reached_depth < p.depth { + debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref); + p.reached_depth.set(p.reached_depth.get().min(reached_depth)); + p = p.previous.head.unwrap(); + } + } +} + +/// The "provisional evaluation cache" is used to store intermediate cache results +/// when solving auto traits. Auto traits are unusual in that they can support +/// cycles. So, for example, a "proof tree" like this would be ok: +/// +/// - `Foo: Send` :- +/// - `Bar: Send` :- +/// - `Foo: Send` -- cycle, but ok +/// - `Baz: Send` +/// +/// Here, to prove `Foo: Send`, we have to prove `Bar: Send` and +/// `Baz: Send`. Proving `Bar: Send` in turn required `Foo: Send`. +/// For non-auto traits, this cycle would be an error, but for auto traits (because +/// they are coinductive) it is considered ok. +/// +/// However, there is a complication: at the point where we have +/// "proven" `Bar: Send`, we have in fact only proven it +/// *provisionally*. In particular, we proved that `Bar: Send` +/// *under the assumption* that `Foo: Send`. But what if we later +/// find out this assumption is wrong? Specifically, we could +/// encounter some kind of error proving `Baz: Send`. In that case, +/// `Bar: Send` didn't turn out to be true. +/// +/// In Issue #60010, we found a bug in rustc where it would cache +/// these intermediate results. This was fixed in #60444 by disabling +/// *all* caching for things involved in a cycle -- in our example, +/// that would mean we don't cache that `Bar: Send`. But this led +/// to large slowdowns. +/// +/// Specifically, imagine this scenario, where proving `Baz: Send` +/// first requires proving `Bar: Send` (which is true: +/// +/// - `Foo: Send` :- +/// - `Bar: Send` :- +/// - `Foo: Send` -- cycle, but ok +/// - `Baz: Send` +/// - `Bar: Send` -- would be nice for this to be a cache hit! +/// - `*const T: Send` -- but what if we later encounter an error? +/// +/// The *provisional evaluation cache* resolves this issue. It stores +/// cache results that we've proven but which were involved in a cycle +/// in some way. We track the minimal stack depth (i.e., the +/// farthest from the top of the stack) that we are dependent on. +/// The idea is that the cache results within are all valid -- so long as +/// none of the nodes in between the current node and the node at that minimum +/// depth result in an error (in which case the cached results are just thrown away). +/// +/// During evaluation, we consult this provisional cache and rely on +/// it. Accessing a cached value is considered equivalent to accessing +/// a result at `reached_depth`, so it marks the *current* solution as +/// provisional as well. If an error is encountered, we toss out any +/// provisional results added from the subtree that encountered the +/// error. When we pop the node at `reached_depth` from the stack, we +/// can commit all the things that remain in the provisional cache. +struct ProvisionalEvaluationCache<'tcx> { + /// next "depth first number" to issue -- just a counter + dfn: Cell, + + /// Stores the "coldest" depth (bottom of stack) reached by any of + /// the evaluation entries. The idea here is that all things in the provisional + /// cache are always dependent on *something* that is colder in the stack: + /// therefore, if we add a new entry that is dependent on something *colder still*, + /// we have to modify the depth for all entries at once. + /// + /// Example: + /// + /// Imagine we have a stack `A B C D E` (with `E` being the top of + /// the stack). We cache something with depth 2, which means that + /// it was dependent on C. Then we pop E but go on and process a + /// new node F: A B C D F. Now F adds something to the cache with + /// depth 1, meaning it is dependent on B. Our original cache + /// entry is also dependent on B, because there is a path from E + /// to C and then from C to F and from F to B. + reached_depth: Cell, + + /// Map from cache key to the provisionally evaluated thing. + /// The cache entries contain the result but also the DFN in which they + /// were added. The DFN is used to clear out values on failure. + /// + /// Imagine we have a stack like: + /// + /// - `A B C` and we add a cache for the result of C (DFN 2) + /// - Then we have a stack `A B D` where `D` has DFN 3 + /// - We try to solve D by evaluating E: `A B D E` (DFN 4) + /// - `E` generates various cache entries which have cyclic dependices on `B` + /// - `A B D E F` and so forth + /// - the DFN of `F` for example would be 5 + /// - then we determine that `E` is in error -- we will then clear + /// all cache values whose DFN is >= 4 -- in this case, that + /// means the cached value for `F`. + map: RefCell, ProvisionalEvaluation>>, +} + +/// A cache value for the provisional cache: contains the depth-first +/// number (DFN) and result. +#[derive(Copy, Clone, Debug)] +struct ProvisionalEvaluation { + from_dfn: usize, + result: EvaluationResult, +} + +impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { + fn default() -> Self { + Self { + dfn: Cell::new(0), + reached_depth: Cell::new(std::usize::MAX), + map: Default::default(), + } + } +} + +impl<'tcx> ProvisionalEvaluationCache<'tcx> { + /// Get the next DFN in sequence (basically a counter). + fn next_dfn(&self) -> usize { + let result = self.dfn.get(); + self.dfn.set(result + 1); + result + } + + /// Check the provisional cache for any result for + /// `fresh_trait_ref`. If there is a hit, then you must consider + /// it an access to the stack slots at depth + /// `self.current_reached_depth()` and above. + fn get_provisional(&self, fresh_trait_ref: ty::PolyTraitRef<'tcx>) -> Option { + debug!( + "get_provisional(fresh_trait_ref={:?}) = {:#?} with reached-depth {}", + fresh_trait_ref, + self.map.borrow().get(&fresh_trait_ref), + self.reached_depth.get(), + ); + Some(self.map.borrow().get(&fresh_trait_ref)?.result) + } + + /// Current value of the `reached_depth` counter -- all the + /// provisional cache entries are dependent on the item at this + /// depth. + fn current_reached_depth(&self) -> usize { + self.reached_depth.get() + } + + /// Insert a provisional result into the cache. The result came + /// from the node with the given DFN. It accessed a minimum depth + /// of `reached_depth` to compute. It evaluated `fresh_trait_ref` + /// and resulted in `result`. + fn insert_provisional( + &self, + from_dfn: usize, + reached_depth: usize, + fresh_trait_ref: ty::PolyTraitRef<'tcx>, + result: EvaluationResult, + ) { + debug!( + "insert_provisional(from_dfn={}, reached_depth={}, fresh_trait_ref={:?}, result={:?})", + from_dfn, reached_depth, fresh_trait_ref, result, + ); + let r_d = self.reached_depth.get(); + self.reached_depth.set(r_d.min(reached_depth)); + + debug!("insert_provisional: reached_depth={:?}", self.reached_depth.get()); + + self.map.borrow_mut().insert(fresh_trait_ref, ProvisionalEvaluation { from_dfn, result }); + } + + /// Invoked when the node with dfn `dfn` does not get a successful + /// result. This will clear out any provisional cache entries + /// that were added since `dfn` was created. This is because the + /// provisional entries are things which must assume that the + /// things on the stack at the time of their creation succeeded -- + /// since the failing node is presently at the top of the stack, + /// these provisional entries must either depend on it or some + /// ancestor of it. + fn on_failure(&self, dfn: usize) { + debug!("on_failure(dfn={:?})", dfn,); + self.map.borrow_mut().retain(|key, eval| { + if !eval.from_dfn >= dfn { + debug!("on_failure: removing {:?}", key); + false + } else { + true + } + }); + } + + /// Invoked when the node at depth `depth` completed without + /// depending on anything higher in the stack (if that completion + /// was a failure, then `on_failure` should have been invoked + /// already). The callback `op` will be invoked for each + /// provisional entry that we can now confirm. + fn on_completion( + &self, + depth: usize, + mut op: impl FnMut(ty::PolyTraitRef<'tcx>, EvaluationResult), + ) { + debug!("on_completion(depth={}, reached_depth={})", depth, self.reached_depth.get(),); + + if self.reached_depth.get() < depth { + debug!("on_completion: did not yet reach depth to complete"); + return; + } + + for (fresh_trait_ref, eval) in self.map.borrow_mut().drain() { + debug!("on_completion: fresh_trait_ref={:?} eval={:?}", fresh_trait_ref, eval,); + + op(fresh_trait_ref, eval.result); + } + + self.reached_depth.set(std::usize::MAX); + } +} + +#[derive(Copy, Clone)] +struct TraitObligationStackList<'o, 'tcx> { + cache: &'o ProvisionalEvaluationCache<'tcx>, + head: Option<&'o TraitObligationStack<'o, 'tcx>>, +} + +impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { + fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache, head: None } + } + + fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache: r.cache(), head: Some(r) } + } + + fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + self.head + } + + fn depth(&self) -> usize { + if let Some(head) = self.head { head.depth } else { 0 } + } +} + +impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> { + type Item = &'o TraitObligationStack<'o, 'tcx>; + + fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + match self.head { + Some(o) => { + *self = o.previous; + Some(o) + } + None => None, + } + } +} + +impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraitObligationStack({:?})", self.obligation) + } +} diff --git a/src/librustc_trait_selection/traits/specialize/mod.rs b/src/librustc_trait_selection/traits/specialize/mod.rs new file mode 100644 index 00000000000..b763851b86e --- /dev/null +++ b/src/librustc_trait_selection/traits/specialize/mod.rs @@ -0,0 +1,473 @@ +//! Logic and data structures related to impl specialization, explained in +//! greater detail below. +//! +//! At the moment, this implementation support only the simple "chain" rule: +//! If any two impls overlap, one must be a strict subset of the other. +//! +//! See the [rustc dev guide] for a bit more detail on how specialization +//! fits together with the rest of the trait machinery. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html + +pub mod specialization_graph; +use specialization_graph::GraphExt; + +use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use crate::traits::select::IntercrateAmbiguityCause; +use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; +use rustc::lint::LintDiagnosticBuilder; +use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc::ty::{self, TyCtxt, TypeFoldable}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::struct_span_err; +use rustc_hir::def_id::DefId; +use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; +use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; +use rustc_span::DUMMY_SP; + +use super::util::impl_trait_ref_and_oblig; +use super::{FulfillmentContext, SelectionContext}; + +/// Information pertinent to an overlapping impl error. +#[derive(Debug)] +pub struct OverlapError { + pub with_impl: DefId, + pub trait_desc: String, + pub self_desc: Option, + pub intercrate_ambiguity_causes: Vec, + pub involves_placeholder: bool, +} + +/// Given a subst for the requested impl, translate it to a subst +/// appropriate for the actual item definition (whether it be in that impl, +/// a parent impl, or the trait). +/// +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_substs` is a substitution of its generics. +/// And `target_node` is the impl/trait we're actually going to get the +/// definition from. The resulting substitution will map from `target_node`'s +/// generics to `source_impl`'s generics as instantiated by `source_subst`. +/// +/// For example, consider the following scenario: +/// +/// ```rust +/// trait Foo { ... } +/// impl Foo for (T, U) { ... } // target impl +/// impl Foo for (V, V) { ... } // source impl +/// ``` +/// +/// Suppose we have selected "source impl" with `V` instantiated with `u32`. +/// This function will produce a substitution with `T` and `U` both mapping to `u32`. +/// +/// where-clauses add some trickiness here, because they can be used to "define" +/// an argument indirectly: +/// +/// ```rust +/// impl<'a, I, T: 'a> Iterator for Cloned +/// where I: Iterator, T: Clone +/// ``` +/// +/// In a case like this, the substitution for `T` is determined indirectly, +/// through associated type projection. We deal with such cases by using +/// *fulfillment* to relate the two impls, requiring that all projections are +/// resolved. +pub fn translate_substs<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_substs: SubstsRef<'tcx>, + target_node: specialization_graph::Node, +) -> SubstsRef<'tcx> { + debug!( + "translate_substs({:?}, {:?}, {:?}, {:?})", + param_env, source_impl, source_substs, target_node + ); + let source_trait_ref = + infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs); + + // translate the Self and Param parts of the substitution, since those + // vary across impls + let target_substs = match target_node { + specialization_graph::Node::Impl(target_impl) => { + // no need to translate if we're targeting the impl we started with + if source_impl == target_impl { + return source_substs; + } + + fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( + |_| { + bug!( + "When translating substitutions for specialization, the expected \ + specialization failed to hold" + ) + }, + ) + } + specialization_graph::Node::Trait(..) => source_trait_ref.substs, + }; + + // directly inherent the method generics, since those do not vary across impls + source_substs.rebase_onto(infcx.tcx, source_impl, target_substs) +} + +/// Given a selected impl described by `impl_data`, returns the +/// definition and substitutions for the method with the name `name` +/// the kind `kind`, and trait method substitutions `substs`, in +/// that impl, a less specialized impl, or the trait default, +/// whichever applies. +pub fn find_associated_item<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + item: &ty::AssocItem, + substs: SubstsRef<'tcx>, + impl_data: &super::VtableImplData<'tcx, ()>, +) -> (DefId, SubstsRef<'tcx>) { + debug!("find_associated_item({:?}, {:?}, {:?}, {:?})", param_env, item, substs, impl_data); + assert!(!substs.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); + let trait_def = tcx.trait_def(trait_def_id); + + if let Ok(ancestors) = trait_def.ancestors(tcx, impl_data.impl_def_id) { + match ancestors.leaf_def(tcx, item.ident, item.kind) { + Some(node_item) => { + let substs = tcx.infer_ctxt().enter(|infcx| { + let param_env = param_env.with_reveal_all(); + let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs); + let substs = translate_substs( + &infcx, + param_env, + impl_data.impl_def_id, + substs, + node_item.node, + ); + infcx.tcx.erase_regions(&substs) + }); + (node_item.item.def_id, substs) + } + None => bug!("{:?} not found in {:?}", item, impl_data.impl_def_id), + } + } else { + (item.def_id, substs) + } +} + +/// Is `impl1` a specialization of `impl2`? +/// +/// Specialization is determined by the sets of types to which the impls apply; +/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies +/// to. +pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { + debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id); + + // The feature gate should prevent introducing new specializations, but not + // taking advantage of upstream ones. + let features = tcx.features(); + let specialization_enabled = features.specialization || features.min_specialization; + if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) { + return false; + } + + // We determine whether there's a subset relationship by: + // + // - skolemizing impl1, + // - assuming the where clauses for impl1, + // - instantiating impl2 with fresh inference variables, + // - unifying, + // - attempting to prove the where clauses for impl2 + // + // The last three steps are encapsulated in `fulfill_implication`. + // + // See RFC 1210 for more details and justification. + + // Currently we do not allow e.g., a negative impl to specialize a positive one + if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) { + return false; + } + + // create a parameter environment corresponding to a (placeholder) instantiation of impl1 + let penv = tcx.param_env(impl1_def_id); + let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); + + // Create a infcx, taking the predicates of impl1 as assumptions: + tcx.infer_ctxt().enter(|infcx| { + // Normalize the trait reference. The WF rules ought to ensure + // that this always succeeds. + let impl1_trait_ref = match traits::fully_normalize( + &infcx, + FulfillmentContext::new(), + ObligationCause::dummy(), + penv, + &impl1_trait_ref, + ) { + Ok(impl1_trait_ref) => impl1_trait_ref, + Err(err) => { + bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err); + } + }; + + // Attempt to prove that impl2 applies, given all of the above. + fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() + }) +} + +/// Attempt to fulfill all obligations of `target_impl` after unification with +/// `source_trait_ref`. If successful, returns a substitution for *all* the +/// generics of `target_impl`, including both those needed to unify with +/// `source_trait_ref` and those whose identity is determined via a where +/// clause in the impl. +fn fulfill_implication<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_trait_ref: ty::TraitRef<'tcx>, + target_impl: DefId, +) -> Result, ()> { + debug!( + "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", + param_env, source_trait_ref, target_impl + ); + + let selcx = &mut SelectionContext::new(&infcx); + let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); + let (target_trait_ref, mut obligations) = + impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs); + debug!( + "fulfill_implication: target_trait_ref={:?}, obligations={:?}", + target_trait_ref, obligations + ); + + // do the impls unify? If not, no specialization. + match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) { + Ok(InferOk { obligations: o, .. }) => { + obligations.extend(o); + } + Err(_) => { + debug!( + "fulfill_implication: {:?} does not unify with {:?}", + source_trait_ref, target_trait_ref + ); + return Err(()); + } + } + + // attempt to prove all of the predicates for impl2 given those for impl1 + // (which are packed up in penv) + + infcx.save_and_restore_in_snapshot_flag(|infcx| { + // If we came from `translate_substs`, we already know that the + // predicates for our impl hold (after all, we know that a more + // specialized impl holds, so our impl must hold too), and + // we only want to process the projections to determine the + // the types in our substs using RFC 447, so we can safely + // ignore region obligations, which allows us to avoid threading + // a node-id to assign them with. + // + // If we came from specialization graph construction, then + // we already make a mockery out of the region system, so + // why not ignore them a bit earlier? + let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + for oblig in obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, oblig); + } + match fulfill_cx.select_all_or_error(infcx) { + Err(errors) => { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + source_trait_ref, target_trait_ref, errors, param_env.caller_bounds + ); + Err(()) + } + + Ok(()) => { + debug!( + "fulfill_implication: an impl for {:?} specializes {:?}", + source_trait_ref, target_trait_ref + ); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_vars_if_possible(&target_substs)) + } + } + }) +} + +// Query provider for `specialization_graph_of`. +pub(super) fn specialization_graph_provider( + tcx: TyCtxt<'_>, + trait_id: DefId, +) -> &specialization_graph::Graph { + let mut sg = specialization_graph::Graph::new(); + + let mut trait_impls = tcx.all_impls(trait_id); + + // The coherence checking implementation seems to rely on impls being + // iterated over (roughly) in definition order, so we are sorting by + // negated `CrateNum` (so remote definitions are visited first) and then + // by a flattened version of the `DefIndex`. + trait_impls + .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); + + for impl_def_id in trait_impls { + if impl_def_id.is_local() { + // This is where impl overlap checking happens: + let insert_result = sg.insert(tcx, impl_def_id); + // Report error if there was one. + let (overlap, used_to_be_allowed) = match insert_result { + Err(overlap) => (Some(overlap), None), + Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)), + Ok(None) => (None, None), + }; + + if let Some(overlap) = overlap { + let impl_span = + tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap()); + + // Work to be done after we've built the DiagnosticBuilder. We have to define it + // now because the struct_lint methods don't return back the DiagnosticBuilder + // that's passed in. + let decorate = |err: LintDiagnosticBuilder<'_>| { + let msg = format!( + "conflicting implementations of trait `{}`{}:{}", + overlap.trait_desc, + overlap + .self_desc + .clone() + .map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", + _ => "", + } + ); + let mut err = err.build(&msg); + match tcx.span_of_impl(overlap.with_impl) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().def_span(span), + "first implementation here".to_string(), + ); + + err.span_label( + impl_span, + format!( + "conflicting implementation{}", + overlap + .self_desc + .map_or(String::new(), |ty| format!(" for `{}`", ty)) + ), + ); + } + Err(cname) => { + let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { + Some(s) => format!( + "conflicting implementation in crate `{}`:\n- {}", + cname, s + ), + None => format!("conflicting implementation in crate `{}`", cname), + }; + err.note(&msg); + } + } + + for cause in &overlap.intercrate_ambiguity_causes { + cause.add_intercrate_ambiguity_hint(&mut err); + } + + if overlap.involves_placeholder { + coherence::add_placeholder_note(&mut err); + } + err.emit() + }; + + match used_to_be_allowed { + None => { + sg.has_errored = true; + let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); + decorate(LintDiagnosticBuilder::new(err)); + } + Some(kind) => { + let lint = match kind { + FutureCompatOverlapErrorKind::Issue33140 => { + ORDER_DEPENDENT_TRAIT_OBJECTS + } + FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, + }; + tcx.struct_span_lint_hir( + lint, + tcx.hir().as_local_hir_id(impl_def_id).unwrap(), + impl_span, + decorate, + ) + } + }; + } + } else { + let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); + sg.record_impl_from_cstore(tcx, parent, impl_def_id) + } + } + + tcx.arena.alloc(sg) +} + +/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a +/// string. +fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option { + use std::fmt::Write; + + let trait_ref = tcx.impl_trait_ref(impl_def_id)?; + let mut w = "impl".to_owned(); + + let substs = InternalSubsts::identity_for_item(tcx, impl_def_id); + + // FIXME: Currently only handles ?Sized. + // Needs to support ?Move and ?DynSized when they are implemented. + let mut types_without_default_bounds = FxHashSet::default(); + let sized_trait = tcx.lang_items().sized_trait(); + + if !substs.is_noop() { + types_without_default_bounds.extend(substs.types()); + w.push('<'); + w.push_str( + &substs + .iter() + .map(|k| k.to_string()) + .filter(|k| k != "'_") + .collect::>() + .join(", "), + ); + w.push('>'); + } + + write!(w, " {} for {}", trait_ref.print_only_trait_path(), tcx.type_of(impl_def_id)).unwrap(); + + // The predicates will contain default bounds like `T: Sized`. We need to + // remove these bounds, and add `T: ?Sized` to any untouched type parameters. + let predicates = tcx.predicates_of(impl_def_id).predicates; + let mut pretty_predicates = + Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); + + for (p, _) in predicates { + if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { + if Some(poly_trait_ref.def_id()) == sized_trait { + types_without_default_bounds.remove(poly_trait_ref.self_ty()); + continue; + } + } + pretty_predicates.push(p.to_string()); + } + + pretty_predicates + .extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty))); + + if !pretty_predicates.is_empty() { + write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); + } + + w.push(';'); + Some(w) +} diff --git a/src/librustc_trait_selection/traits/specialize/specialization_graph.rs b/src/librustc_trait_selection/traits/specialize/specialization_graph.rs new file mode 100644 index 00000000000..17d4a22b9dd --- /dev/null +++ b/src/librustc_trait_selection/traits/specialize/specialization_graph.rs @@ -0,0 +1,379 @@ +use super::OverlapError; + +use crate::traits; +use rustc::ty::fast_reject::{self, SimplifiedType}; +use rustc::ty::{self, TyCtxt, TypeFoldable}; +use rustc_hir::def_id::DefId; + +pub use rustc::traits::specialization_graph::*; + +#[derive(Copy, Clone, Debug)] +pub enum FutureCompatOverlapErrorKind { + Issue33140, + LeakCheck, +} + +#[derive(Debug)] +pub struct FutureCompatOverlapError { + pub error: OverlapError, + pub kind: FutureCompatOverlapErrorKind, +} + +/// The result of attempting to insert an impl into a group of children. +enum Inserted { + /// The impl was inserted as a new child in this group of children. + BecameNewSibling(Option), + + /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc. + ReplaceChildren(Vec), + + /// The impl is a specialization of an existing child. + ShouldRecurseOn(DefId), +} + +trait ChildrenExt { + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); + + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + simplified_self: Option, + ) -> Result; +} + +impl ChildrenExt for Children { + /// Insert an impl into this set of children without comparing to any existing impls. + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { + debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); + self.nonblanket_impls.entry(st).or_default().push(impl_def_id) + } else { + debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id); + self.blanket_impls.push(impl_def_id) + } + } + + /// Removes an impl from this set of children. Used when replacing + /// an impl with a parent. The impl must be present in the list of + /// children already. + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let vec: &mut Vec; + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { + debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); + vec = self.nonblanket_impls.get_mut(&st).unwrap(); + } else { + debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id); + vec = &mut self.blanket_impls; + } + + let index = vec.iter().position(|d| *d == impl_def_id).unwrap(); + vec.remove(index); + } + + /// Attempt to insert an impl into this set of children, while comparing for + /// specialization relationships. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + simplified_self: Option, + ) -> Result { + let mut last_lint = None; + let mut replace_children = Vec::new(); + + debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,); + + let possible_siblings = match simplified_self { + Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)), + None => PotentialSiblings::Unfiltered(iter_children(self)), + }; + + for possible_sibling in possible_siblings { + debug!( + "insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}", + impl_def_id, simplified_self, possible_sibling, + ); + + let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| { + let trait_ref = overlap.impl_header.trait_ref.unwrap(); + let self_ty = trait_ref.self_ty(); + + OverlapError { + with_impl: possible_sibling, + trait_desc: trait_ref.print_only_trait_path().to_string(), + // Only report the `Self` type if it has at least + // some outer concrete shell; otherwise, it's + // not adding much information. + self_desc: if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }, + intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, + involves_placeholder: overlap.involves_placeholder, + } + }; + + let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>, + last_lint: &mut _| { + // Found overlap, but no specialization; error out or report future-compat warning. + + // Do we *still* get overlap if we disable the future-incompatible modes? + let should_err = traits::overlapping_impls( + tcx, + possible_sibling, + impl_def_id, + traits::SkipLeakCheck::default(), + |_| true, + || false, + ); + + let error = create_overlap_error(overlap); + + if should_err { + Err(error) + } else { + *last_lint = Some(FutureCompatOverlapError { + error, + kind: FutureCompatOverlapErrorKind::LeakCheck, + }); + + Ok((false, false)) + } + }; + + let last_lint_mut = &mut last_lint; + let (le, ge) = traits::overlapping_impls( + tcx, + possible_sibling, + impl_def_id, + traits::SkipLeakCheck::Yes, + |overlap| { + if let Some(overlap_kind) = + tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) + { + match overlap_kind { + ty::ImplOverlapKind::Permitted { marker: _ } => {} + ty::ImplOverlapKind::Issue33140 => { + *last_lint_mut = Some(FutureCompatOverlapError { + error: create_overlap_error(overlap), + kind: FutureCompatOverlapErrorKind::Issue33140, + }); + } + } + + return Ok((false, false)); + } + + let le = tcx.specializes((impl_def_id, possible_sibling)); + let ge = tcx.specializes((possible_sibling, impl_def_id)); + + if le == ge { + report_overlap_error(overlap, last_lint_mut) + } else { + Ok((le, ge)) + } + }, + || Ok((false, false)), + )?; + + if le && !ge { + debug!( + "descending as child of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap() + ); + + // The impl specializes `possible_sibling`. + return Ok(Inserted::ShouldRecurseOn(possible_sibling)); + } else if ge && !le { + debug!( + "placing as parent of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap() + ); + + replace_children.push(possible_sibling); + } else { + // Either there's no overlap, or the overlap was already reported by + // `overlap_error`. + } + } + + if !replace_children.is_empty() { + return Ok(Inserted::ReplaceChildren(replace_children)); + } + + // No overlap with any potential siblings, so add as a new sibling. + debug!("placing as new sibling"); + self.insert_blindly(tcx, impl_def_id); + Ok(Inserted::BecameNewSibling(last_lint)) + } +} + +fn iter_children(children: &mut Children) -> impl Iterator + '_ { + let nonblanket = children.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter()); + children.blanket_impls.iter().chain(nonblanket).cloned() +} + +fn filtered_children( + children: &mut Children, + st: SimplifiedType, +) -> impl Iterator + '_ { + let nonblanket = children.nonblanket_impls.entry(st).or_default().iter(); + children.blanket_impls.iter().chain(nonblanket).cloned() +} + +// A custom iterator used by Children::insert +enum PotentialSiblings +where + I: Iterator, + J: Iterator, +{ + Unfiltered(I), + Filtered(J), +} + +impl Iterator for PotentialSiblings +where + I: Iterator, + J: Iterator, +{ + type Item = DefId; + + fn next(&mut self) -> Option { + match *self { + PotentialSiblings::Unfiltered(ref mut iter) => iter.next(), + PotentialSiblings::Filtered(ref mut iter) => iter.next(), + } + } +} + +pub trait GraphExt { + /// Insert a local impl into the specialization graph. If an existing impl + /// conflicts with it (has overlap, but neither specializes the other), + /// information about the area of overlap is returned in the `Err`. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + ) -> Result, OverlapError>; + + /// Insert cached metadata mapping from a child impl back to its parent. + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId); +} + +impl GraphExt for Graph { + /// Insert a local impl into the specialization graph. If an existing impl + /// conflicts with it (has overlap, but neither specializes the other), + /// information about the area of overlap is returned in the `Err`. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + ) -> Result, OverlapError> { + assert!(impl_def_id.is_local()); + + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let trait_def_id = trait_ref.def_id; + + debug!( + "insert({:?}): inserting TraitRef {:?} into specialization graph", + impl_def_id, trait_ref + ); + + // If the reference itself contains an earlier error (e.g., due to a + // resolution failure), then we just insert the impl at the top level of + // the graph and claim that there's no overlap (in order to suppress + // bogus errors). + if trait_ref.references_error() { + debug!( + "insert: inserting dummy node for erroneous TraitRef {:?}, \ + impl_def_id={:?}, trait_def_id={:?}", + trait_ref, impl_def_id, trait_def_id + ); + + self.parent.insert(impl_def_id, trait_def_id); + self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id); + return Ok(None); + } + + let mut parent = trait_def_id; + let mut last_lint = None; + let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false); + + // Descend the specialization tree, where `parent` is the current parent node. + loop { + use self::Inserted::*; + + let insert_result = + self.children.entry(parent).or_default().insert(tcx, impl_def_id, simplified)?; + + match insert_result { + BecameNewSibling(opt_lint) => { + last_lint = opt_lint; + break; + } + ReplaceChildren(grand_children_to_be) => { + // We currently have + // + // P + // | + // G + // + // and we are inserting the impl N. We want to make it: + // + // P + // | + // N + // | + // G + + // Adjust P's list of children: remove G and then add N. + { + let siblings = self.children.get_mut(&parent).unwrap(); + for &grand_child_to_be in &grand_children_to_be { + siblings.remove_existing(tcx, grand_child_to_be); + } + siblings.insert_blindly(tcx, impl_def_id); + } + + // Set G's parent to N and N's parent to P. + for &grand_child_to_be in &grand_children_to_be { + self.parent.insert(grand_child_to_be, impl_def_id); + } + self.parent.insert(impl_def_id, parent); + + // Add G as N's child. + for &grand_child_to_be in &grand_children_to_be { + self.children + .entry(impl_def_id) + .or_default() + .insert_blindly(tcx, grand_child_to_be); + } + break; + } + ShouldRecurseOn(new_parent) => { + parent = new_parent; + } + } + } + + self.parent.insert(impl_def_id, parent); + Ok(last_lint) + } + + /// Insert cached metadata mapping from a child impl back to its parent. + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) { + if self.parent.insert(child, parent).is_some() { + bug!( + "When recording an impl from the crate store, information about its parent \ + was already present." + ); + } + + self.children.entry(parent).or_default().insert_blindly(tcx, child); + } +} diff --git a/src/librustc_trait_selection/traits/structural_match.rs b/src/librustc_trait_selection/traits/structural_match.rs new file mode 100644 index 00000000000..60682f58129 --- /dev/null +++ b/src/librustc_trait_selection/traits/structural_match.rs @@ -0,0 +1,216 @@ +use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::traits::ObligationCause; +use crate::traits::{self, ConstPatternStructural, TraitEngine}; + +use rustc::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_span::Span; + +#[derive(Debug)] +pub enum NonStructuralMatchTy<'tcx> { + Adt(&'tcx AdtDef), + Param, +} + +/// This method traverses the structure of `ty`, trying to find an +/// instance of an ADT (i.e. struct or enum) that was declared without +/// the `#[structural_match]` attribute, or a generic type parameter +/// (which cannot be determined to be `structural_match`). +/// +/// The "structure of a type" includes all components that would be +/// considered when doing a pattern match on a constant of that +/// type. +/// +/// * This means this method descends into fields of structs/enums, +/// and also descends into the inner type `T` of `&T` and `&mut T` +/// +/// * The traversal doesn't dereference unsafe pointers (`*const T`, +/// `*mut T`), and it does not visit the type arguments of an +/// instantiated generic like `PhantomData`. +/// +/// The reason we do this search is Rust currently require all ADTs +/// reachable from a constant's type to be annotated with +/// `#[structural_match]`, an attribute which essentially says that +/// the implementation of `PartialEq::eq` behaves *equivalently* to a +/// comparison against the unfolded structure. +/// +/// For more background on why Rust has this requirement, and issues +/// that arose when the requirement was not enforced completely, see +/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. +pub fn search_for_structural_match_violation<'tcx>( + id: hir::HirId, + span: Span, + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option> { + // FIXME: we should instead pass in an `infcx` from the outside. + tcx.infer_ctxt().enter(|infcx| { + let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() }; + ty.visit_with(&mut search); + search.found + }) +} + +/// This method returns true if and only if `adt_ty` itself has been marked as +/// eligible for structural-match: namely, if it implements both +/// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by +/// `#[derive(PartialEq)]` and `#[derive(Eq)]`). +/// +/// Note that this does *not* recursively check if the substructure of `adt_ty` +/// implements the traits. +pub fn type_marked_structural( + id: hir::HirId, + span: Span, + infcx: &InferCtxt<'_, 'tcx>, + adt_ty: Ty<'tcx>, +) -> bool { + let mut fulfillment_cx = traits::FulfillmentContext::new(); + let cause = ObligationCause::new(span, id, ConstPatternStructural); + // require `#[derive(PartialEq)]` + let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap(); + fulfillment_cx.register_bound( + infcx, + ty::ParamEnv::empty(), + adt_ty, + structural_peq_def_id, + cause, + ); + // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around + // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) + let cause = ObligationCause::new(span, id, ConstPatternStructural); + let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap(); + fulfillment_cx.register_bound( + infcx, + ty::ParamEnv::empty(), + adt_ty, + structural_teq_def_id, + cause, + ); + + // We deliberately skip *reporting* fulfillment errors (via + // `report_fulfillment_errors`), for two reasons: + // + // 1. The error messages would mention `std::marker::StructuralPartialEq` + // (a trait which is solely meant as an implementation detail + // for now), and + // + // 2. We are sometimes doing future-incompatibility lints for + // now, so we do not want unconditional errors here. + fulfillment_cx.select_all_or_error(infcx).is_ok() +} + +/// This implements the traversal over the structure of a given type to try to +/// find instances of ADTs (specifically structs or enums) that do not implement +/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). +struct Search<'a, 'tcx> { + id: hir::HirId, + span: Span, + + infcx: InferCtxt<'a, 'tcx>, + + /// Records first ADT that does not implement a structural-match trait. + found: Option>, + + /// Tracks ADTs previously encountered during search, so that + /// we will not recur on them again. + seen: FxHashSet, +} + +impl Search<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { + type_marked_structural(self.id, self.span, &self.infcx, adt_ty) + } +} + +impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("Search visiting ty: {:?}", ty); + + let (adt_def, substs) = match ty.kind { + ty::Adt(adt_def, substs) => (adt_def, substs), + ty::Param(_) => { + self.found = Some(NonStructuralMatchTy::Param); + return true; // Stop visiting. + } + ty::RawPtr(..) => { + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + + // (But still tell caller to continue search.) + return false; + } + ty::FnDef(..) | ty::FnPtr(..) => { + // types of formals and return in `fn(_) -> _` are also irrelevant; + // so we do not recur into them via `super_visit_with` + // + // (But still tell caller to continue search.) + return false; + } + ty::Array(_, n) + if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } => + { + // rust-lang/rust#62336: ignore type of contents + // for empty array. + return false; + } + _ => { + ty.super_visit_with(self); + return false; + } + }; + + if !self.seen.insert(adt_def.did) { + debug!("Search already seen adt_def: {:?}", adt_def); + // let caller continue its search + return false; + } + + if !self.type_marked_structural(ty) { + debug!("Search found ty: {:?}", ty); + self.found = Some(NonStructuralMatchTy::Adt(&adt_def)); + return true; // Halt visiting! + } + + // structural-match does not care about the + // instantiation of the generics in an ADT (it + // instead looks directly at its fields outside + // this match), so we skip super_visit_with. + // + // (Must not recur on substs for `PhantomData` cf + // rust-lang/rust#55028 and rust-lang/rust#55837; but also + // want to skip substs when only uses of generic are + // behind unsafe pointers `*const T`/`*mut T`.) + + // even though we skip super_visit_with, we must recur on + // fields of ADT. + let tcx = self.tcx(); + for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) { + if field_ty.visit_with(self) { + // found an ADT without structural-match; halt visiting! + assert!(self.found.is_some()); + return true; + } + } + + // Even though we do not want to recur on substs, we do + // want our caller to continue its own search. + false + } +} diff --git a/src/librustc_trait_selection/traits/util.rs b/src/librustc_trait_selection/traits/util.rs new file mode 100644 index 00000000000..cd4595e76cc --- /dev/null +++ b/src/librustc_trait_selection/traits/util.rs @@ -0,0 +1,675 @@ +use rustc_errors::DiagnosticBuilder; +use rustc_span::Span; +use smallvec::smallvec; +use smallvec::SmallVec; + +use rustc::ty::outlives::Component; +use rustc::ty::subst::{GenericArg, Subst, SubstsRef}; +use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; + +use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; + +fn anonymize_predicate<'tcx>(tcx: TyCtxt<'tcx>, pred: &ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + match *pred { + ty::Predicate::Trait(ref data, constness) => { + ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data), constness) + } + + ty::Predicate::RegionOutlives(ref data) => { + ty::Predicate::RegionOutlives(tcx.anonymize_late_bound_regions(data)) + } + + ty::Predicate::TypeOutlives(ref data) => { + ty::Predicate::TypeOutlives(tcx.anonymize_late_bound_regions(data)) + } + + ty::Predicate::Projection(ref data) => { + ty::Predicate::Projection(tcx.anonymize_late_bound_regions(data)) + } + + ty::Predicate::WellFormed(data) => ty::Predicate::WellFormed(data), + + ty::Predicate::ObjectSafe(data) => ty::Predicate::ObjectSafe(data), + + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) + } + + ty::Predicate::Subtype(ref data) => { + ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)) + } + + ty::Predicate::ConstEvaluatable(def_id, substs) => { + ty::Predicate::ConstEvaluatable(def_id, substs) + } + } +} + +struct PredicateSet<'tcx> { + tcx: TyCtxt<'tcx>, + set: FxHashSet>, +} + +impl PredicateSet<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx, set: Default::default() } + } + + fn insert(&mut self, pred: &ty::Predicate<'tcx>) -> bool { + // We have to be careful here because we want + // + // for<'a> Foo<&'a int> + // + // and + // + // for<'b> Foo<&'b int> + // + // to be considered equivalent. So normalize all late-bound + // regions before we throw things into the underlying set. + self.set.insert(anonymize_predicate(self.tcx, pred)) + } +} + +impl>> Extend for PredicateSet<'tcx> { + fn extend>(&mut self, iter: I) { + for pred in iter { + self.insert(pred.as_ref()); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// `Elaboration` iterator +/////////////////////////////////////////////////////////////////////////// + +/// "Elaboration" is the process of identifying all the predicates that +/// are implied by a source predicate. Currently, this basically means +/// walking the "supertraits" and other similar assumptions. For example, +/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` +/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that +/// `T: Foo`, then we know that `T: 'static`. +pub struct Elaborator<'tcx> { + stack: Vec>, + visited: PredicateSet<'tcx>, +} + +pub fn elaborate_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Elaborator<'tcx> { + elaborate_predicates(tcx, vec![trait_ref.without_const().to_predicate()]) +} + +pub fn elaborate_trait_refs<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl Iterator>, +) -> Elaborator<'tcx> { + let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate()).collect(); + elaborate_predicates(tcx, predicates) +} + +pub fn elaborate_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + mut predicates: Vec>, +) -> Elaborator<'tcx> { + let mut visited = PredicateSet::new(tcx); + predicates.retain(|pred| visited.insert(pred)); + Elaborator { stack: predicates, visited } +} + +impl Elaborator<'tcx> { + pub fn filter_to_traits(self) -> FilterToTraits { + FilterToTraits::new(self) + } + + fn elaborate(&mut self, predicate: &ty::Predicate<'tcx>) { + let tcx = self.visited.tcx; + match *predicate { + ty::Predicate::Trait(ref data, _) => { + // Get predicates declared on the trait. + let predicates = tcx.super_predicates_of(data.def_id()); + + let predicates = predicates + .predicates + .iter() + .map(|(pred, _)| pred.subst_supertrait(tcx, &data.to_poly_trait_ref())); + debug!("super_predicates: data={:?} predicates={:?}", data, predicates.clone()); + + // Only keep those bounds that we haven't already seen. + // This is necessary to prevent infinite recursion in some + // cases. One common case is when people define + // `trait Sized: Sized { }` rather than `trait Sized { }`. + let visited = &mut self.visited; + let predicates = predicates.filter(|pred| visited.insert(pred)); + + self.stack.extend(predicates); + } + ty::Predicate::WellFormed(..) => { + // Currently, we do not elaborate WF predicates, + // although we easily could. + } + ty::Predicate::ObjectSafe(..) => { + // Currently, we do not elaborate object-safe + // predicates. + } + ty::Predicate::Subtype(..) => { + // Currently, we do not "elaborate" predicates like `X <: Y`, + // though conceivably we might. + } + ty::Predicate::Projection(..) => { + // Nothing to elaborate in a projection predicate. + } + ty::Predicate::ClosureKind(..) => { + // Nothing to elaborate when waiting for a closure's kind to be inferred. + } + ty::Predicate::ConstEvaluatable(..) => { + // Currently, we do not elaborate const-evaluatable + // predicates. + } + ty::Predicate::RegionOutlives(..) => { + // Nothing to elaborate from `'a: 'b`. + } + ty::Predicate::TypeOutlives(ref data) => { + // We know that `T: 'a` for some type `T`. We can + // often elaborate this. For example, if we know that + // `[U]: 'a`, that implies that `U: 'a`. Similarly, if + // we know `&'a U: 'b`, then we know that `'a: 'b` and + // `U: 'b`. + // + // We can basically ignore bound regions here. So for + // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to + // `'a: 'b`. + + // Ignore `for<'a> T: 'a` -- we might in the future + // consider this as evidence that `T: 'static`, but + // I'm a bit wary of such constructions and so for now + // I want to be conservative. --nmatsakis + let ty_max = data.skip_binder().0; + let r_min = data.skip_binder().1; + if r_min.is_late_bound() { + return; + } + + let visited = &mut self.visited; + let mut components = smallvec![]; + tcx.push_outlives_components(ty_max, &mut components); + self.stack.extend( + components + .into_iter() + .filter_map(|component| match component { + Component::Region(r) => { + if r.is_late_bound() { + None + } else { + Some(ty::Predicate::RegionOutlives(ty::Binder::dummy( + ty::OutlivesPredicate(r, r_min), + ))) + } + } + + Component::Param(p) => { + let ty = tcx.mk_ty_param(p.index, p.name); + Some(ty::Predicate::TypeOutlives(ty::Binder::dummy( + ty::OutlivesPredicate(ty, r_min), + ))) + } + + Component::UnresolvedInferenceVariable(_) => None, + + Component::Projection(_) | Component::EscapingProjection(_) => { + // We can probably do more here. This + // corresponds to a case like `>::U: 'b`. + None + } + }) + .filter(|p| visited.insert(p)), + ); + } + } + } +} + +impl Iterator for Elaborator<'tcx> { + type Item = ty::Predicate<'tcx>; + + fn size_hint(&self) -> (usize, Option) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option> { + // Extract next item from top-most stack frame, if any. + if let Some(pred) = self.stack.pop() { + self.elaborate(&pred); + Some(pred) + } else { + None + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Supertrait iterator +/////////////////////////////////////////////////////////////////////////// + +pub type Supertraits<'tcx> = FilterToTraits>; + +pub fn supertraits<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Supertraits<'tcx> { + elaborate_trait_ref(tcx, trait_ref).filter_to_traits() +} + +pub fn transitive_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: impl Iterator>, +) -> Supertraits<'tcx> { + elaborate_trait_refs(tcx, bounds).filter_to_traits() +} + +/////////////////////////////////////////////////////////////////////////// +// `TraitAliasExpander` iterator +/////////////////////////////////////////////////////////////////////////// + +/// "Trait alias expansion" is the process of expanding a sequence of trait +/// references into another sequence by transitively following all trait +/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias +/// `trait Foo = Bar + Sync;`, and another trait alias +/// `trait Bar = Read + Write`, then the bounds would expand to +/// `Read + Write + Sync + Send`. +/// Expansion is done via a DFS (depth-first search), and the `visited` field +/// is used to avoid cycles. +pub struct TraitAliasExpander<'tcx> { + tcx: TyCtxt<'tcx>, + stack: Vec>, +} + +/// Stores information about the expansion of a trait via a path of zero or more trait aliases. +#[derive(Debug, Clone)] +pub struct TraitAliasExpansionInfo<'tcx> { + pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, +} + +impl<'tcx> TraitAliasExpansionInfo<'tcx> { + fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { + Self { path: smallvec![(trait_ref, span)] } + } + + /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate + /// trait aliases. + pub fn label_with_exp_info( + &self, + diag: &mut DiagnosticBuilder<'_>, + top_label: &str, + use_desc: &str, + ) { + diag.span_label(self.top().1, top_label); + if self.path.len() > 1 { + for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { + diag.span_label(*sp, format!("referenced here ({})", use_desc)); + } + } + diag.span_label( + self.bottom().1, + format!("trait alias used in trait object type ({})", use_desc), + ); + } + + pub fn trait_ref(&self) -> &ty::PolyTraitRef<'tcx> { + &self.top().0 + } + + pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.path.last().unwrap() + } + + pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.path.first().unwrap() + } + + fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { + let mut path = self.path.clone(); + path.push((trait_ref, span)); + + Self { path } + } +} + +pub fn expand_trait_aliases<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl IntoIterator, Span)>, +) -> TraitAliasExpander<'tcx> { + let items: Vec<_> = trait_refs + .into_iter() + .map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)) + .collect(); + TraitAliasExpander { tcx, stack: items } +} + +impl<'tcx> TraitAliasExpander<'tcx> { + /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` + /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. + /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a + /// trait alias. + /// The return value indicates whether `item` should be yielded to the user. + fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { + let tcx = self.tcx; + let trait_ref = item.trait_ref(); + let pred = trait_ref.without_const().to_predicate(); + + debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); + + // Don't recurse if this bound is not a trait alias. + let is_alias = tcx.is_trait_alias(trait_ref.def_id()); + if !is_alias { + return true; + } + + // Don't recurse if this trait alias is already on the stack for the DFS search. + let anon_pred = anonymize_predicate(tcx, &pred); + if item.path.iter().rev().skip(1).any(|(tr, _)| { + anonymize_predicate(tcx, &tr.without_const().to_predicate()) == anon_pred + }) { + return false; + } + + // Get components of trait alias. + let predicates = tcx.super_predicates_of(trait_ref.def_id()); + + let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { + pred.subst_supertrait(tcx, &trait_ref) + .to_opt_poly_trait_ref() + .map(|trait_ref| item.clone_and_push(trait_ref, *span)) + }); + debug!("expand_trait_aliases: items={:?}", items.clone()); + + self.stack.extend(items); + + false + } +} + +impl<'tcx> Iterator for TraitAliasExpander<'tcx> { + type Item = TraitAliasExpansionInfo<'tcx>; + + fn size_hint(&self) -> (usize, Option) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option> { + while let Some(item) = self.stack.pop() { + if self.expand(&item) { + return Some(item); + } + } + None + } +} + +/////////////////////////////////////////////////////////////////////////// +// Iterator over def-IDs of supertraits +/////////////////////////////////////////////////////////////////////////// + +pub struct SupertraitDefIds<'tcx> { + tcx: TyCtxt<'tcx>, + stack: Vec, + visited: FxHashSet, +} + +pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> { + SupertraitDefIds { + tcx, + stack: vec![trait_def_id], + visited: Some(trait_def_id).into_iter().collect(), + } +} + +impl Iterator for SupertraitDefIds<'tcx> { + type Item = DefId; + + fn next(&mut self) -> Option { + let def_id = self.stack.pop()?; + let predicates = self.tcx.super_predicates_of(def_id); + let visited = &mut self.visited; + self.stack.extend( + predicates + .predicates + .iter() + .filter_map(|(pred, _)| pred.to_opt_poly_trait_ref()) + .map(|trait_ref| trait_ref.def_id()) + .filter(|&super_def_id| visited.insert(super_def_id)), + ); + Some(def_id) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Other +/////////////////////////////////////////////////////////////////////////// + +/// A filter around an iterator of predicates that makes it yield up +/// just trait references. +pub struct FilterToTraits { + base_iterator: I, +} + +impl FilterToTraits { + fn new(base: I) -> FilterToTraits { + FilterToTraits { base_iterator: base } + } +} + +impl<'tcx, I: Iterator>> Iterator for FilterToTraits { + type Item = ty::PolyTraitRef<'tcx>; + + fn next(&mut self) -> Option> { + while let Some(pred) = self.base_iterator.next() { + if let ty::Predicate::Trait(data, _) = pred { + return Some(data.to_poly_trait_ref()); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.base_iterator.size_hint(); + (0, upper) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Other +/////////////////////////////////////////////////////////////////////////// + +/// Instantiate all bound parameters of the impl with the given substs, +/// returning the resulting trait ref and all obligations that arise. +/// The obligations are closed under normalization. +pub fn impl_trait_ref_and_oblig<'a, 'tcx>( + selcx: &mut SelectionContext<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl_def_id: DefId, + impl_substs: SubstsRef<'tcx>, +) -> (ty::TraitRef<'tcx>, Vec>) { + let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); + let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs); + let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = + super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref); + + let predicates = selcx.tcx().predicates_of(impl_def_id); + let predicates = predicates.instantiate(selcx.tcx(), impl_substs); + let Normalized { value: predicates, obligations: normalization_obligations2 } = + super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates); + let impl_obligations = + predicates_for_generics(ObligationCause::dummy(), 0, param_env, &predicates); + + let impl_obligations: Vec<_> = impl_obligations + .into_iter() + .chain(normalization_obligations1) + .chain(normalization_obligations2) + .collect(); + + (impl_trait_ref, impl_obligations) +} + +/// See [`super::obligations_for_generics`]. +pub fn predicates_for_generics<'tcx>( + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + generic_bounds: &ty::InstantiatedPredicates<'tcx>, +) -> Vec> { + debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); + + generic_bounds + .predicates + .iter() + .map(|&predicate| Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }) + .collect() +} + +pub fn predicate_for_trait_ref<'tcx>( + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + recursion_depth: usize, +) -> PredicateObligation<'tcx> { + Obligation { + cause, + param_env, + recursion_depth, + predicate: trait_ref.without_const().to_predicate(), + } +} + +pub fn predicate_for_trait_def( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + trait_def_id: DefId, + recursion_depth: usize, + self_ty: Ty<'tcx>, + params: &[GenericArg<'tcx>], +) -> PredicateObligation<'tcx> { + let trait_ref = + ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) }; + predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth) +} + +/// Casts a trait reference into a reference to one of its super +/// traits; returns `None` if `target_trait_def_id` is not a +/// supertrait. +pub fn upcast_choices( + tcx: TyCtxt<'tcx>, + source_trait_ref: ty::PolyTraitRef<'tcx>, + target_trait_def_id: DefId, +) -> Vec> { + if source_trait_ref.def_id() == target_trait_def_id { + return vec![source_trait_ref]; // Shortcut the most common case. + } + + supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() +} + +/// Given a trait `trait_ref`, returns the number of vtable entries +/// that come from `trait_ref`, excluding its supertraits. Used in +/// computing the vtable base for an upcast trait of a trait object. +pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize { + let mut entries = 0; + // Count number of methods and add them to the total offset. + // Skip over associated types and constants. + for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() { + if trait_item.kind == ty::AssocKind::Method { + entries += 1; + } + } + entries +} + +/// Given an upcast trait object described by `object`, returns the +/// index of the method `method_def_id` (which should be part of +/// `object.upcast_trait_ref`) within the vtable for `object`. +pub fn get_vtable_index_of_object_method( + tcx: TyCtxt<'tcx>, + object: &super::VtableObjectData<'tcx, N>, + method_def_id: DefId, +) -> usize { + // Count number of methods preceding the one we are selecting and + // add them to the total offset. + // Skip over associated types and constants. + let mut entries = object.vtable_base; + for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() { + if trait_item.def_id == method_def_id { + // The item with the ID we were given really ought to be a method. + assert_eq!(trait_item.kind, ty::AssocKind::Method); + return entries; + } + if trait_item.kind == ty::AssocKind::Method { + entries += 1; + } + } + + bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id); +} + +pub fn closure_trait_ref_and_return_type( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::PolyFnSig<'tcx>, + tuple_arguments: TupleArgumentsFlag, +) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> { + let arguments_tuple = match tuple_arguments { + TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], + TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()), + }; + let trait_ref = ty::TraitRef { + def_id: fn_trait_def_id, + substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]), + }; + ty::Binder::bind((trait_ref, sig.skip_binder().output())) +} + +pub fn generator_trait_ref_and_outputs( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::PolyGenSig<'tcx>, +) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { + let trait_ref = ty::TraitRef { + def_id: fn_trait_def_id, + substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]), + }; + ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty)) +} + +pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool { + match tcx.hir().as_local_hir_id(node_item_def_id) { + Some(hir_id) => { + let item = tcx.hir().expect_item(hir_id); + if let hir::ItemKind::Impl { defaultness, .. } = item.kind { + defaultness.is_default() + } else { + false + } + } + None => tcx.impl_defaultness(node_item_def_id).is_default(), + } +} + +pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { + assoc_item.defaultness.is_final() && !impl_is_default(tcx, assoc_item.container.id()) +} + +pub enum TupleArgumentsFlag { + Yes, + No, +} diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs new file mode 100644 index 00000000000..b69c5bdce2a --- /dev/null +++ b/src/librustc_trait_selection/traits/wf.rs @@ -0,0 +1,753 @@ +use crate::infer::InferCtxt; +use crate::opaque_types::required_region_bounds; +use crate::traits::{self, AssocTypeBoundData}; +use rustc::middle::lang_items; +use rustc::ty::subst::SubstsRef; +use rustc::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::{kw, Ident}; +use rustc_span::Span; + +/// Returns the set of obligations needed to make `ty` well-formed. +/// If `ty` contains unresolved inference variables, this may include +/// further WF obligations. However, if `ty` IS an unresolved +/// inference variable, returns `None`, because we are not able to +/// make any progress at all. This is to prevent "livelock" where we +/// say "$0 is WF if $0 is WF". +pub fn obligations<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ty: Ty<'tcx>, + span: Span, +) -> Option>> { + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; + if wf.compute(ty) { + debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); + + let result = wf.normalize(); + debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result); + Some(result) + } else { + None // no progress made, return None + } +} + +/// Returns the obligations that make this trait reference +/// well-formed. For example, if there is a trait `Set` defined like +/// `trait Set`, then the trait reference `Foo: Set` is WF +/// if `Bar: Eq`. +pub fn trait_obligations<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + trait_ref: &ty::TraitRef<'tcx>, + span: Span, + item: Option<&'tcx hir::Item<'tcx>>, +) -> Vec> { + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item }; + wf.compute_trait_ref(trait_ref, Elaborate::All); + wf.normalize() +} + +pub fn predicate_obligations<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + predicate: &ty::Predicate<'tcx>, + span: Span, +) -> Vec> { + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; + + // (*) ok to skip binders, because wf code is prepared for it + match *predicate { + ty::Predicate::Trait(ref t, _) => { + wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*) + } + ty::Predicate::RegionOutlives(..) => {} + ty::Predicate::TypeOutlives(ref t) => { + wf.compute(t.skip_binder().0); + } + ty::Predicate::Projection(ref t) => { + let t = t.skip_binder(); // (*) + wf.compute_projection(t.projection_ty); + wf.compute(t.ty); + } + ty::Predicate::WellFormed(t) => { + wf.compute(t); + } + ty::Predicate::ObjectSafe(_) => {} + ty::Predicate::ClosureKind(..) => {} + ty::Predicate::Subtype(ref data) => { + wf.compute(data.skip_binder().a); // (*) + wf.compute(data.skip_binder().b); // (*) + } + ty::Predicate::ConstEvaluatable(def_id, substs) => { + let obligations = wf.nominal_obligations(def_id, substs); + wf.out.extend(obligations); + + for ty in substs.types() { + wf.compute(ty); + } + } + } + + wf.normalize() +} + +struct WfPredicates<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + span: Span, + out: Vec>, + item: Option<&'tcx hir::Item<'tcx>>, +} + +/// Controls whether we "elaborate" supertraits and so forth on the WF +/// predicates. This is a kind of hack to address #43784. The +/// underlying problem in that issue was a trait structure like: +/// +/// ``` +/// trait Foo: Copy { } +/// trait Bar: Foo { } +/// impl Foo for T { } +/// impl Bar for T { } +/// ``` +/// +/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but +/// we decide that this is true because `T: Bar` is in the +/// where-clauses (and we can elaborate that to include `T: +/// Copy`). This wouldn't be a problem, except that when we check the +/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo` +/// impl. And so nowhere did we check that `T: Copy` holds! +/// +/// To resolve this, we elaborate the WF requirements that must be +/// proven when checking impls. This means that (e.g.) the `impl Bar +/// for T` will be forced to prove not only that `T: Foo` but also `T: +/// Copy` (which it won't be able to do, because there is no `Copy` +/// impl for `T`). +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum Elaborate { + All, + None, +} + +impl<'a, 'tcx> WfPredicates<'a, 'tcx> { + fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { + traits::ObligationCause::new(self.span, self.body_id, code) + } + + fn normalize(&mut self) -> Vec> { + let cause = self.cause(traits::MiscObligation); + let infcx = &mut self.infcx; + let param_env = self.param_env; + let mut obligations = Vec::with_capacity(self.out.len()); + for pred in &self.out { + assert!(!pred.has_escaping_bound_vars()); + let mut selcx = traits::SelectionContext::new(infcx); + let i = obligations.len(); + let value = + traits::normalize_to(&mut selcx, param_env, cause.clone(), pred, &mut obligations); + obligations.insert(i, value); + } + obligations + } + + /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. + fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) { + let tcx = self.infcx.tcx; + let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); + + let cause = self.cause(traits::MiscObligation); + let param_env = self.param_env; + + let item = &self.item; + let extend_cause_with_original_assoc_item_obligation = + |cause: &mut traits::ObligationCause<'_>, + pred: &ty::Predicate<'_>, + trait_assoc_items: &[ty::AssocItem]| { + let trait_item = tcx + .hir() + .as_local_hir_id(trait_ref.def_id) + .and_then(|trait_id| tcx.hir().find(trait_id)); + let (trait_name, trait_generics) = match trait_item { + Some(hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Trait(.., generics, _, _), + .. + })) + | Some(hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::TraitAlias(generics, _), + .. + })) => (Some(ident), Some(generics)), + _ => (None, None), + }; + + let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span)); + match pred { + ty::Predicate::Projection(proj) => { + // The obligation comes not from the current `impl` nor the `trait` being + // implemented, but rather from a "second order" obligation, like in + // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`: + // + // error[E0271]: type mismatch resolving `::Ok == ()` + // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + // | + // LL | type Ok; + // | -- associated type defined here + // ... + // LL | impl Bar for Foo { + // | ---------------- in this `impl` item + // LL | type Ok = (); + // | ^^^^^^^^^^^^^ expected `u32`, found `()` + // | + // = note: expected type `u32` + // found type `()` + // + // FIXME: we would want to point a span to all places that contributed to this + // obligation. In the case above, it should be closer to: + // + // error[E0271]: type mismatch resolving `::Ok == ()` + // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + // | + // LL | type Ok; + // | -- associated type defined here + // LL | type Sibling: Bar2; + // | -------------------------------- obligation set here + // ... + // LL | impl Bar for Foo { + // | ---------------- in this `impl` item + // LL | type Ok = (); + // | ^^^^^^^^^^^^^ expected `u32`, found `()` + // ... + // LL | impl Bar2 for Foo2 { + // | ---------------- in this `impl` item + // LL | type Ok = u32; + // | -------------- obligation set here + // | + // = note: expected type `u32` + // found type `()` + if let Some(hir::ItemKind::Impl { items, .. }) = item.map(|i| &i.kind) { + let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); + if let Some(impl_item) = + items.iter().find(|item| item.ident == trait_assoc_item.ident) + { + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData { + impl_span: item_span, + original: trait_assoc_item.ident.span, + bounds: vec![], + })); + } + } + } + ty::Predicate::Trait(proj, _) => { + // An associated item obligation born out of the `trait` failed to be met. + // Point at the `impl` that failed the obligation, the associated item that + // needed to meet the obligation, and the definition of that associated item, + // which should hold the obligation in most cases. An example can be seen in + // `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`: + // + // error[E0277]: the trait bound `bool: Bar` is not satisfied + // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + // | + // LL | type Assoc: Bar; + // | ----- associated type defined here + // ... + // LL | impl Foo for () { + // | --------------- in this `impl` item + // LL | type Assoc = bool; + // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + // + // If the obligation comes from the where clause in the `trait`, we point at it: + // + // error[E0277]: the trait bound `bool: Bar` is not satisfied + // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + // | + // | trait Foo where >::Assoc: Bar { + // | -------------------------- restricted in this bound + // LL | type Assoc; + // | ----- associated type defined here + // ... + // LL | impl Foo for () { + // | --------------- in this `impl` item + // LL | type Assoc = bool; + // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + if let ( + ty::Projection(ty::ProjectionTy { item_def_id, .. }), + Some(hir::ItemKind::Impl { items, .. }), + ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) + { + if let Some((impl_item, trait_assoc_item)) = trait_assoc_items + .iter() + .find(|i| i.def_id == *item_def_id) + .and_then(|trait_assoc_item| { + items + .iter() + .find(|i| i.ident == trait_assoc_item.ident) + .map(|impl_item| (impl_item, trait_assoc_item)) + }) + { + let bounds = trait_generics + .map(|generics| { + get_generic_bound_spans( + &generics, + trait_name, + trait_assoc_item.ident, + ) + }) + .unwrap_or_else(Vec::new); + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData { + impl_span: item_span, + original: trait_assoc_item.ident.span, + bounds, + })); + } + } + } + _ => {} + } + }; + + if let Elaborate::All = elaborate { + // FIXME: Make `extend_cause_with_original_assoc_item_obligation` take an iterator + // instead of a slice. + let trait_assoc_items: Vec<_> = + tcx.associated_items(trait_ref.def_id).in_definition_order().copied().collect(); + + let predicates = obligations.iter().map(|obligation| obligation.predicate).collect(); + let implied_obligations = traits::elaborate_predicates(tcx, predicates); + let implied_obligations = implied_obligations.map(|pred| { + let mut cause = cause.clone(); + extend_cause_with_original_assoc_item_obligation( + &mut cause, + &pred, + &*trait_assoc_items, + ); + traits::Obligation::new(cause, param_env, pred) + }); + self.out.extend(implied_obligations); + } + + self.out.extend(obligations); + + self.out.extend(trait_ref.substs.types().filter(|ty| !ty.has_escaping_bound_vars()).map( + |ty| traits::Obligation::new(cause.clone(), param_env, ty::Predicate::WellFormed(ty)), + )); + } + + /// Pushes the obligations required for `trait_ref::Item` to be WF + /// into `self.out`. + fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) { + // A projection is well-formed if (a) the trait ref itself is + // WF and (b) the trait-ref holds. (It may also be + // normalizable and be WF that way.) + let trait_ref = data.trait_ref(self.infcx.tcx); + self.compute_trait_ref(&trait_ref, Elaborate::None); + + if !data.has_escaping_bound_vars() { + let predicate = trait_ref.without_const().to_predicate(); + let cause = self.cause(traits::ProjectionWf(data)); + self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); + } + } + + /// Pushes the obligations required for an array length to be WF + /// into `self.out`. + fn compute_array_len(&mut self, constant: ty::Const<'tcx>) { + if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.val { + assert!(promoted.is_none()); + + let obligations = self.nominal_obligations(def_id, substs); + self.out.extend(obligations); + + let predicate = ty::Predicate::ConstEvaluatable(def_id, substs); + let cause = self.cause(traits::MiscObligation); + self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); + } + } + + fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { + if !subty.has_escaping_bound_vars() { + let cause = self.cause(cause); + let trait_ref = ty::TraitRef { + def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None), + substs: self.infcx.tcx.mk_substs_trait(subty, &[]), + }; + self.out.push(traits::Obligation::new( + cause, + self.param_env, + trait_ref.without_const().to_predicate(), + )); + } + } + + /// Pushes new obligations into `out`. Returns `true` if it was able + /// to generate all the predicates needed to validate that `ty0` + /// is WF. Returns false if `ty0` is an unresolved type variable, + /// in which case we are not able to simplify at all. + fn compute(&mut self, ty0: Ty<'tcx>) -> bool { + let mut subtys = ty0.walk(); + let param_env = self.param_env; + while let Some(ty) = subtys.next() { + match ty.kind { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Error + | ty::Str + | ty::GeneratorWitness(..) + | ty::Never + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Foreign(..) => { + // WfScalar, WfParameter, etc + } + + ty::Slice(subty) => { + self.require_sized(subty, traits::SliceOrArrayElem); + } + + ty::Array(subty, len) => { + self.require_sized(subty, traits::SliceOrArrayElem); + self.compute_array_len(*len); + } + + ty::Tuple(ref tys) => { + if let Some((_last, rest)) = tys.split_last() { + for elem in rest { + self.require_sized(elem.expect_ty(), traits::TupleElem); + } + } + } + + ty::RawPtr(_) => { + // simple cases that are WF if their type args are WF + } + + ty::Projection(data) => { + subtys.skip_current_subtree(); // subtree handled by compute_projection + self.compute_projection(data); + } + + ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), + + ty::Adt(def, substs) => { + // WfNominalType + let obligations = self.nominal_obligations(def.did, substs); + self.out.extend(obligations); + } + + ty::FnDef(did, substs) => { + let obligations = self.nominal_obligations(did, substs); + self.out.extend(obligations); + } + + ty::Ref(r, rty, _) => { + // WfReference + if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { + let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); + self.out.push(traits::Obligation::new( + cause, + param_env, + ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate( + rty, r, + ))), + )); + } + } + + ty::Generator(..) => { + // Walk ALL the types in the generator: this will + // include the upvar types as well as the yield + // type. Note that this is mildly distinct from + // the closure case, where we have to be careful + // about the signature of the closure. We don't + // have the problem of implied bounds here since + // generators don't take arguments. + } + + ty::Closure(def_id, substs) => { + // Only check the upvar types for WF, not the rest + // of the types within. This is needed because we + // capture the signature and it may not be WF + // without the implied bounds. Consider a closure + // like `|x: &'a T|` -- it may be that `T: 'a` is + // not known to hold in the creator's context (and + // indeed the closure may not be invoked by its + // creator, but rather turned to someone who *can* + // verify that). + // + // The special treatment of closures here really + // ought not to be necessary either; the problem + // is related to #25860 -- there is no way for us + // to express a fn type complete with the implied + // bounds that it is assuming. I think in reality + // the WF rules around fn are a bit messed up, and + // that is the rot problem: `fn(&'a T)` should + // probably always be WF, because it should be + // shorthand for something like `where(T: 'a) { + // fn(&'a T) }`, as discussed in #25860. + // + // Note that we are also skipping the generic + // types. This is consistent with the `outlives` + // code, but anyway doesn't matter: within the fn + // body where they are created, the generics will + // always be WF, and outside of that fn body we + // are not directly inspecting closure types + // anyway, except via auto trait matching (which + // only inspects the upvar types). + subtys.skip_current_subtree(); // subtree handled by compute_projection + for upvar_ty in substs.as_closure().upvar_tys(def_id, self.infcx.tcx) { + self.compute(upvar_ty); + } + } + + ty::FnPtr(_) => { + // let the loop iterate into the argument/return + // types appearing in the fn signature + } + + ty::Opaque(did, substs) => { + // all of the requirements on type parameters + // should've been checked by the instantiation + // of whatever returned this exact `impl Trait`. + + // for named opaque `impl Trait` types we still need to check them + if ty::is_impl_trait_defn(self.infcx.tcx, did).is_none() { + let obligations = self.nominal_obligations(did, substs); + self.out.extend(obligations); + } + } + + ty::Dynamic(data, r) => { + // WfObject + // + // Here, we defer WF checking due to higher-ranked + // regions. This is perhaps not ideal. + self.from_object_ty(ty, data, r); + + // FIXME(#27579) RFC also considers adding trait + // obligations that don't refer to Self and + // checking those + + let defer_to_coercion = self.infcx.tcx.features().object_safe_for_dispatch; + + if !defer_to_coercion { + let cause = self.cause(traits::MiscObligation); + let component_traits = data.auto_traits().chain(data.principal_def_id()); + self.out.extend(component_traits.map(|did| { + traits::Obligation::new( + cause.clone(), + param_env, + ty::Predicate::ObjectSafe(did), + ) + })); + } + } + + // Inference variables are the complicated case, since we don't + // know what type they are. We do two things: + // + // 1. Check if they have been resolved, and if so proceed with + // THAT type. + // 2. If not, check whether this is the type that we + // started with (ty0). In that case, we've made no + // progress at all, so return false. Otherwise, + // we've at least simplified things (i.e., we went + // from `Vec<$0>: WF` to `$0: WF`, so we can + // register a pending obligation and keep + // moving. (Goal is that an "inductive hypothesis" + // is satisfied to ensure termination.) + ty::Infer(_) => { + let ty = self.infcx.shallow_resolve(ty); + if let ty::Infer(_) = ty.kind { + // not yet resolved... + if ty == ty0 { + // ...this is the type we started from! no progress. + return false; + } + + let cause = self.cause(traits::MiscObligation); + self.out.push( + // ...not the type we started from, so we made progress. + traits::Obligation::new( + cause, + self.param_env, + ty::Predicate::WellFormed(ty), + ), + ); + } else { + // Yes, resolved, proceed with the + // result. Should never return false because + // `ty` is not a Infer. + assert!(self.compute(ty)); + } + } + } + } + + // if we made it through that loop above, we made progress! + return true; + } + + fn nominal_obligations( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Vec> { + let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs); + let cause = self.cause(traits::ItemObligation(def_id)); + predicates + .predicates + .into_iter() + .map(|pred| traits::Obligation::new(cause.clone(), self.param_env, pred)) + .filter(|pred| !pred.has_escaping_bound_vars()) + .collect() + } + + fn from_object_ty( + &mut self, + ty: Ty<'tcx>, + data: ty::Binder<&'tcx ty::List>>, + region: ty::Region<'tcx>, + ) { + // Imagine a type like this: + // + // trait Foo { } + // trait Bar<'c> : 'c { } + // + // &'b (Foo+'c+Bar<'d>) + // ^ + // + // In this case, the following relationships must hold: + // + // 'b <= 'c + // 'd <= 'c + // + // The first conditions is due to the normal region pointer + // rules, which say that a reference cannot outlive its + // referent. + // + // The final condition may be a bit surprising. In particular, + // you may expect that it would have been `'c <= 'd`, since + // usually lifetimes of outer things are conservative + // approximations for inner things. However, it works somewhat + // differently with trait objects: here the idea is that if the + // user specifies a region bound (`'c`, in this case) it is the + // "master bound" that *implies* that bounds from other traits are + // all met. (Remember that *all bounds* in a type like + // `Foo+Bar+Zed` must be met, not just one, hence if we write + // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and + // 'y.) + // + // Note: in fact we only permit builtin traits, not `Bar<'d>`, I + // am looking forward to the future here. + if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { + let implicit_bounds = object_region_bounds(self.infcx.tcx, data); + + let explicit_bound = region; + + self.out.reserve(implicit_bounds.len()); + for implicit_bound in implicit_bounds { + let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound)); + let outlives = + ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); + self.out.push(traits::Obligation::new( + cause, + self.param_env, + outlives.to_predicate(), + )); + } + } + } +} + +/// Given an object type like `SomeTrait + Send`, computes the lifetime +/// bounds that must hold on the elided self type. These are derived +/// from the declarations of `SomeTrait`, `Send`, and friends -- if +/// they declare `trait SomeTrait : 'static`, for example, then +/// `'static` would appear in the list. The hard work is done by +/// `infer::required_region_bounds`, see that for more information. +pub fn object_region_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + existential_predicates: ty::Binder<&'tcx ty::List>>, +) -> Vec> { + // Since we don't actually *know* the self type for an object, + // this "open(err)" serves as a kind of dummy standin -- basically + // a placeholder type. + let open_ty = tcx.mk_ty_infer(ty::FreshTy(0)); + + let predicates = existential_predicates + .iter() + .filter_map(|predicate| { + if let ty::ExistentialPredicate::Projection(_) = *predicate.skip_binder() { + None + } else { + Some(predicate.with_self_ty(tcx, open_ty)) + } + }) + .collect(); + + required_region_bounds(tcx, open_ty, predicates) +} + +/// Find the span of a generic bound affecting an associated type. +fn get_generic_bound_spans( + generics: &hir::Generics<'_>, + trait_name: Option<&Ident>, + assoc_item_name: Ident, +) -> Vec { + let mut bounds = vec![]; + for clause in generics.where_clause.predicates.iter() { + if let hir::WherePredicate::BoundPredicate(pred) = clause { + match &pred.bounded_ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => { + let mut s = path.segments.iter(); + if let (a, Some(b), None) = (s.next(), s.next(), s.next()) { + if a.map(|s| &s.ident) == trait_name + && b.ident == assoc_item_name + && is_self_path(&ty.kind) + { + // `::Bar` + bounds.push(pred.span); + } + } + } + hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => { + if segment.ident == assoc_item_name { + if is_self_path(&ty.kind) { + // `Self::Bar` + bounds.push(pred.span); + } + } + } + _ => {} + } + } + } + bounds +} + +fn is_self_path(kind: &hir::TyKind<'_>) -> bool { + match kind { + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { + let mut s = path.segments.iter(); + if let (Some(segment), None) = (s.next(), s.next()) { + if segment.ident.name == kw::SelfUpper { + // `type(Self)` + return true; + } + } + } + _ => {} + } + false +} diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml index 3fdbb4e6805..5e33efb1cf9 100644 --- a/src/librustc_traits/Cargo.toml +++ b/src/librustc_traits/Cargo.toml @@ -17,6 +17,6 @@ rustc_macros = { path = "../librustc_macros" } rustc_target = { path = "../librustc_target" } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } -chalk-engine = { version = "0.9.0", default-features=false } smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_infer = { path = "../librustc_infer" } +rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs deleted file mode 100644 index 240a93f0900..00000000000 --- a/src/librustc_traits/chalk_context/mod.rs +++ /dev/null @@ -1,638 +0,0 @@ -mod program_clauses; -mod resolvent_ops; -mod unify; - -use chalk_engine::fallible::Fallible; -use chalk_engine::forest::Forest; -use chalk_engine::{context, hh::HhGoal, DelayedLiteral, ExClause, Literal}; -use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use rustc::ty::query::Providers; -use rustc::ty::subst::{GenericArg, GenericArgKind}; -use rustc::ty::{self, TyCtxt}; -use rustc_infer::infer::canonical::{ - Canonical, CanonicalVarValues, Certainty, OriginalQueryValues, QueryRegionConstraints, - QueryResponse, -}; -use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime, TyCtxtInferExt}; -use rustc_infer::traits::{ - self, ChalkCanonicalGoal, ChalkContextLift, Clause, DomainGoal, Environment, ExClauseFold, - Goal, GoalKind, InEnvironment, QuantifierKind, -}; -use rustc_macros::{Lift, TypeFoldable}; -use rustc_span::DUMMY_SP; - -use std::fmt::{self, Debug}; -use std::marker::PhantomData; - -use self::unify::*; - -#[derive(Copy, Clone, Debug)] -crate struct ChalkArenas<'tcx> { - _phantom: PhantomData<&'tcx ()>, -} - -#[derive(Copy, Clone)] -crate struct ChalkContext<'tcx> { - _arenas: ChalkArenas<'tcx>, - tcx: TyCtxt<'tcx>, -} - -#[derive(Copy, Clone)] -crate struct ChalkInferenceContext<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, -} - -#[derive(Copy, Clone, Debug)] -crate struct UniverseMap; - -crate type RegionConstraint<'tcx> = ty::OutlivesPredicate, ty::Region<'tcx>>; - -#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, Lift)] -crate struct ConstrainedSubst<'tcx> { - subst: CanonicalVarValues<'tcx>, - constraints: Vec>, -} - -impl context::Context for ChalkArenas<'tcx> { - type CanonicalExClause = Canonical<'tcx, ChalkExClause<'tcx>>; - - type CanonicalGoalInEnvironment = Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>; - - // u-canonicalization not yet implemented - type UCanonicalGoalInEnvironment = Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>; - - type CanonicalConstrainedSubst = Canonical<'tcx, ConstrainedSubst<'tcx>>; - - // u-canonicalization not yet implemented - type UniverseMap = UniverseMap; - - type Solution = Canonical<'tcx, QueryResponse<'tcx, ()>>; - - type InferenceNormalizedSubst = CanonicalVarValues<'tcx>; - - type GoalInEnvironment = InEnvironment<'tcx, Goal<'tcx>>; - - type RegionConstraint = RegionConstraint<'tcx>; - - type Substitution = CanonicalVarValues<'tcx>; - - type Environment = Environment<'tcx>; - - type Goal = Goal<'tcx>; - - type DomainGoal = DomainGoal<'tcx>; - - type BindersGoal = ty::Binder>; - - type Parameter = GenericArg<'tcx>; - - type ProgramClause = Clause<'tcx>; - - type ProgramClauses = Vec>; - - type UnificationResult = UnificationResult<'tcx>; - - type Variance = ty::Variance; - - fn goal_in_environment( - env: &Environment<'tcx>, - goal: Goal<'tcx>, - ) -> InEnvironment<'tcx, Goal<'tcx>> { - env.with(goal) - } -} - -impl context::AggregateOps> for ChalkContext<'tcx> { - fn make_solution( - &self, - root_goal: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, - mut simplified_answers: impl context::AnswerStream>, - ) -> Option>> { - use chalk_engine::SimplifiedAnswer; - - debug!("make_solution(root_goal = {:?})", root_goal); - - if simplified_answers.peek_answer().is_none() { - return None; - } - - let SimplifiedAnswer { subst: constrained_subst, ambiguous } = - simplified_answers.next_answer().unwrap(); - - debug!("make_solution: ambiguous flag = {}", ambiguous); - - let ambiguous = simplified_answers.peek_answer().is_some() || ambiguous; - - let solution = constrained_subst.unchecked_map(|cs| match ambiguous { - true => QueryResponse { - var_values: cs.subst.make_identity(self.tcx), - region_constraints: QueryRegionConstraints::default(), - certainty: Certainty::Ambiguous, - value: (), - }, - - false => QueryResponse { - var_values: cs.subst, - region_constraints: QueryRegionConstraints::default(), - - // FIXME: restore this later once we get better at handling regions - // region_constraints: cs.constraints - // .into_iter() - // .map(|c| ty::Binder::bind(c)) - // .collect(), - certainty: Certainty::Proven, - value: (), - }, - }); - - debug!("make_solution: solution = {:?}", solution); - - Some(solution) - } -} - -impl context::ContextOps> for ChalkContext<'tcx> { - /// Returns `true` if this is a coinductive goal: basically proving that an auto trait - /// is implemented or proving that a trait reference is well-formed. - fn is_coinductive(&self, goal: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>) -> bool { - use rustc::traits::{WellFormed, WhereClause}; - - let mut goal = goal.value.goal; - loop { - match goal { - GoalKind::DomainGoal(domain_goal) => match domain_goal { - DomainGoal::WellFormed(WellFormed::Trait(..)) => return true, - DomainGoal::Holds(WhereClause::Implemented(trait_predicate)) => { - return self.tcx.trait_is_auto(trait_predicate.def_id()); - } - _ => return false, - }, - - GoalKind::Quantified(_, bound_goal) => goal = *bound_goal.skip_binder(), - _ => return false, - } - } - } - - /// Creates an inference table for processing a new goal and instantiate that goal - /// in that context, returning "all the pieces". - /// - /// More specifically: given a u-canonical goal `arg`, creates a - /// new inference table `T` and populates it with the universes - /// found in `arg`. Then, creates a substitution `S` that maps - /// each bound variable in `arg` to a fresh inference variable - /// from T. Returns: - /// - /// - the table `T`, - /// - the substitution `S`, - /// - the environment and goal found by substitution `S` into `arg`. - fn instantiate_ucanonical_goal( - &self, - arg: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, - op: impl context::WithInstantiatedUCanonicalGoal, Output = R>, - ) -> R { - self.tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, arg, |ref infcx, arg, subst| { - let chalk_infcx = &mut ChalkInferenceContext { infcx }; - op.with(chalk_infcx, subst, arg.environment, arg.goal) - }) - } - - fn instantiate_ex_clause( - &self, - _num_universes: usize, - arg: &Canonical<'tcx, ChalkExClause<'tcx>>, - op: impl context::WithInstantiatedExClause, Output = R>, - ) -> R { - self.tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &arg.upcast(), |ref infcx, arg, _| { - let chalk_infcx = &mut ChalkInferenceContext { infcx }; - op.with(chalk_infcx, arg) - }) - } - - /// Returns `true` if this solution has no region constraints. - fn empty_constraints(ccs: &Canonical<'tcx, ConstrainedSubst<'tcx>>) -> bool { - ccs.value.constraints.is_empty() - } - - fn inference_normalized_subst_from_ex_clause( - canon_ex_clause: &'a Canonical<'tcx, ChalkExClause<'tcx>>, - ) -> &'a CanonicalVarValues<'tcx> { - &canon_ex_clause.value.subst - } - - fn inference_normalized_subst_from_subst( - canon_subst: &'a Canonical<'tcx, ConstrainedSubst<'tcx>>, - ) -> &'a CanonicalVarValues<'tcx> { - &canon_subst.value.subst - } - - fn canonical( - u_canon: &'a Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, - ) -> &'a Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>> { - u_canon - } - - fn is_trivial_substitution( - u_canon: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, - canonical_subst: &Canonical<'tcx, ConstrainedSubst<'tcx>>, - ) -> bool { - let subst = &canonical_subst.value.subst; - assert_eq!(u_canon.variables.len(), subst.var_values.len()); - subst.var_values.iter_enumerated().all(|(cvar, kind)| match kind.unpack() { - GenericArgKind::Lifetime(r) => match r { - &ty::ReLateBound(debruijn, br) => { - debug_assert_eq!(debruijn, ty::INNERMOST); - cvar == br.assert_bound_var() - } - _ => false, - }, - GenericArgKind::Type(ty) => match ty.kind { - ty::Bound(debruijn, bound_ty) => { - debug_assert_eq!(debruijn, ty::INNERMOST); - cvar == bound_ty.var - } - _ => false, - }, - GenericArgKind::Const(ct) => match ct.val { - ty::ConstKind::Bound(debruijn, bound_ct) => { - debug_assert_eq!(debruijn, ty::INNERMOST); - cvar == bound_ct - } - _ => false, - }, - }) - } - - fn num_universes(canon: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>) -> usize { - canon.max_universe.index() + 1 - } - - /// Convert a goal G *from* the canonical universes *into* our - /// local universes. This will yield a goal G' that is the same - /// but for the universes of universally quantified names. - fn map_goal_from_canonical( - _map: &UniverseMap, - value: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, - ) -> Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>> { - *value // FIXME universe maps not implemented yet - } - - fn map_subst_from_canonical( - _map: &UniverseMap, - value: &Canonical<'tcx, ConstrainedSubst<'tcx>>, - ) -> Canonical<'tcx, ConstrainedSubst<'tcx>> { - value.clone() // FIXME universe maps not implemented yet - } -} - -impl context::InferenceTable, ChalkArenas<'tcx>> - for ChalkInferenceContext<'cx, 'tcx> -{ - fn into_goal(&self, domain_goal: DomainGoal<'tcx>) -> Goal<'tcx> { - self.infcx.tcx.mk_goal(GoalKind::DomainGoal(domain_goal)) - } - - fn cannot_prove(&self) -> Goal<'tcx> { - self.infcx.tcx.mk_goal(GoalKind::CannotProve) - } - - fn into_hh_goal(&mut self, goal: Goal<'tcx>) -> ChalkHhGoal<'tcx> { - match *goal { - GoalKind::Implies(hypotheses, goal) => { - HhGoal::Implies(hypotheses.iter().cloned().collect(), goal) - } - GoalKind::And(left, right) => HhGoal::And(left, right), - GoalKind::Not(subgoal) => HhGoal::Not(subgoal), - GoalKind::DomainGoal(d) => HhGoal::DomainGoal(d), - GoalKind::Quantified(QuantifierKind::Universal, binder) => HhGoal::ForAll(binder), - GoalKind::Quantified(QuantifierKind::Existential, binder) => HhGoal::Exists(binder), - GoalKind::Subtype(a, b) => HhGoal::Unify(ty::Variance::Covariant, a.into(), b.into()), - GoalKind::CannotProve => HhGoal::CannotProve, - } - } - - fn add_clauses( - &mut self, - env: &Environment<'tcx>, - clauses: Vec>, - ) -> Environment<'tcx> { - Environment { - clauses: self - .infcx - .tcx - .mk_clauses(env.clauses.iter().cloned().chain(clauses.into_iter())), - } - } -} - -impl context::TruncateOps, ChalkArenas<'tcx>> - for ChalkInferenceContext<'cx, 'tcx> -{ - fn truncate_goal( - &mut self, - _subgoal: &InEnvironment<'tcx, Goal<'tcx>>, - ) -> Option>> { - None // FIXME we should truncate at some point! - } - - fn truncate_answer( - &mut self, - _subst: &CanonicalVarValues<'tcx>, - ) -> Option> { - None // FIXME we should truncate at some point! - } -} - -impl context::UnificationOps, ChalkArenas<'tcx>> - for ChalkInferenceContext<'cx, 'tcx> -{ - fn program_clauses( - &self, - environment: &Environment<'tcx>, - goal: &DomainGoal<'tcx>, - ) -> Vec> { - self.program_clauses_impl(environment, goal) - } - - fn instantiate_binders_universally(&mut self, arg: &ty::Binder>) -> Goal<'tcx> { - self.infcx.replace_bound_vars_with_placeholders(arg).0 - } - - fn instantiate_binders_existentially(&mut self, arg: &ty::Binder>) -> Goal<'tcx> { - self.infcx - .replace_bound_vars_with_fresh_vars( - DUMMY_SP, - LateBoundRegionConversionTime::HigherRankedType, - arg, - ) - .0 - } - - fn debug_ex_clause(&mut self, value: &'v ChalkExClause<'tcx>) -> Box { - let string = format!("{:?}", self.infcx.resolve_vars_if_possible(value)); - Box::new(string) - } - - fn canonicalize_goal( - &mut self, - value: &InEnvironment<'tcx, Goal<'tcx>>, - ) -> Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>> { - let mut _orig_values = OriginalQueryValues::default(); - self.infcx.canonicalize_query(value, &mut _orig_values) - } - - fn canonicalize_ex_clause( - &mut self, - value: &ChalkExClause<'tcx>, - ) -> Canonical<'tcx, ChalkExClause<'tcx>> { - self.infcx.canonicalize_response(value) - } - - fn canonicalize_constrained_subst( - &mut self, - subst: CanonicalVarValues<'tcx>, - constraints: Vec>, - ) -> Canonical<'tcx, ConstrainedSubst<'tcx>> { - self.infcx.canonicalize_response(&ConstrainedSubst { subst, constraints }) - } - - fn u_canonicalize_goal( - &mut self, - value: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, - ) -> (Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, UniverseMap) { - (*value, UniverseMap) - } - - fn invert_goal( - &mut self, - _value: &InEnvironment<'tcx, Goal<'tcx>>, - ) -> Option>> { - panic!("goal inversion not yet implemented") - } - - fn unify_parameters( - &mut self, - environment: &Environment<'tcx>, - variance: ty::Variance, - a: &GenericArg<'tcx>, - b: &GenericArg<'tcx>, - ) -> Fallible> { - self.infcx.commit_if_ok(|_| { - unify(self.infcx, *environment, variance, a, b) - .map_err(|_| chalk_engine::fallible::NoSolution) - }) - } - - fn sink_answer_subset( - &self, - value: &Canonical<'tcx, ConstrainedSubst<'tcx>>, - ) -> Canonical<'tcx, ConstrainedSubst<'tcx>> { - value.clone() - } - - fn lift_delayed_literal( - &self, - value: DelayedLiteral>, - ) -> DelayedLiteral> { - match self.infcx.tcx.lift(&value) { - Some(literal) => literal, - None => bug!("cannot lift {:?}", value), - } - } - - fn into_ex_clause( - &mut self, - result: UnificationResult<'tcx>, - ex_clause: &mut ChalkExClause<'tcx>, - ) { - into_ex_clause(result, ex_clause); - } -} - -crate fn into_ex_clause(result: UnificationResult<'tcx>, ex_clause: &mut ChalkExClause<'tcx>) { - ex_clause.subgoals.extend(result.goals.into_iter().map(Literal::Positive)); - - // FIXME: restore this later once we get better at handling regions - let _ = result.constraints.len(); // trick `-D dead-code` - // ex_clause.constraints.extend(result.constraints); -} - -type ChalkHhGoal<'tcx> = HhGoal>; - -type ChalkExClause<'tcx> = ExClause>; - -impl Debug for ChalkContext<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ChalkContext") - } -} - -impl Debug for ChalkInferenceContext<'cx, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ChalkInferenceContext") - } -} - -impl ChalkContextLift<'tcx> for ChalkArenas<'a> { - type LiftedExClause = ChalkExClause<'tcx>; - type LiftedDelayedLiteral = DelayedLiteral>; - type LiftedLiteral = Literal>; - - fn lift_ex_clause_to_tcx( - ex_clause: &ChalkExClause<'a>, - tcx: TyCtxt<'tcx>, - ) -> Option { - Some(ChalkExClause { - subst: tcx.lift(&ex_clause.subst)?, - delayed_literals: tcx.lift(&ex_clause.delayed_literals)?, - constraints: tcx.lift(&ex_clause.constraints)?, - subgoals: tcx.lift(&ex_clause.subgoals)?, - }) - } - - fn lift_delayed_literal_to_tcx( - literal: &DelayedLiteral>, - tcx: TyCtxt<'tcx>, - ) -> Option { - Some(match literal { - DelayedLiteral::CannotProve(()) => DelayedLiteral::CannotProve(()), - DelayedLiteral::Negative(index) => DelayedLiteral::Negative(*index), - DelayedLiteral::Positive(index, subst) => { - DelayedLiteral::Positive(*index, tcx.lift(subst)?) - } - }) - } - - fn lift_literal_to_tcx( - literal: &Literal>, - tcx: TyCtxt<'tcx>, - ) -> Option { - Some(match literal { - Literal::Negative(goal) => Literal::Negative(tcx.lift(goal)?), - Literal::Positive(goal) => Literal::Positive(tcx.lift(goal)?), - }) - } -} - -impl ExClauseFold<'tcx> for ChalkArenas<'tcx> { - fn fold_ex_clause_with>( - ex_clause: &ChalkExClause<'tcx>, - folder: &mut F, - ) -> ChalkExClause<'tcx> { - ExClause { - subst: ex_clause.subst.fold_with(folder), - delayed_literals: ex_clause.delayed_literals.fold_with(folder), - constraints: ex_clause.constraints.fold_with(folder), - subgoals: ex_clause.subgoals.fold_with(folder), - } - } - - fn visit_ex_clause_with>( - ex_clause: &ExClause, - visitor: &mut V, - ) -> bool { - let ExClause { subst, delayed_literals, constraints, subgoals } = ex_clause; - subst.visit_with(visitor) - || delayed_literals.visit_with(visitor) - || constraints.visit_with(visitor) - || subgoals.visit_with(visitor) - } -} - -trait Upcast<'tcx>: 'tcx { - type Upcasted: 'tcx; - - fn upcast(&self) -> Self::Upcasted; -} - -impl<'tcx> Upcast<'tcx> for DelayedLiteral> { - type Upcasted = DelayedLiteral>; - - fn upcast(&self) -> Self::Upcasted { - match self { - &DelayedLiteral::CannotProve(..) => DelayedLiteral::CannotProve(()), - &DelayedLiteral::Negative(index) => DelayedLiteral::Negative(index), - DelayedLiteral::Positive(index, subst) => { - DelayedLiteral::Positive(*index, subst.clone()) - } - } - } -} - -impl<'tcx> Upcast<'tcx> for Literal> { - type Upcasted = Literal>; - - fn upcast(&self) -> Self::Upcasted { - match self { - &Literal::Negative(goal) => Literal::Negative(goal), - &Literal::Positive(goal) => Literal::Positive(goal), - } - } -} - -impl<'tcx> Upcast<'tcx> for ExClause> { - type Upcasted = ExClause>; - - fn upcast(&self) -> Self::Upcasted { - ExClause { - subst: self.subst.clone(), - delayed_literals: self.delayed_literals.iter().map(|l| l.upcast()).collect(), - constraints: self.constraints.clone(), - subgoals: self.subgoals.iter().map(|g| g.upcast()).collect(), - } - } -} - -impl<'tcx, T> Upcast<'tcx> for Canonical<'tcx, T> -where - T: Upcast<'tcx>, -{ - type Upcasted = Canonical<'tcx, T::Upcasted>; - - fn upcast(&self) -> Self::Upcasted { - Canonical { - max_universe: self.max_universe, - value: self.value.upcast(), - variables: self.variables, - } - } -} - -crate fn provide(p: &mut Providers<'_>) { - *p = Providers { evaluate_goal, ..*p }; -} - -crate fn evaluate_goal<'tcx>( - tcx: TyCtxt<'tcx>, - goal: ChalkCanonicalGoal<'tcx>, -) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> { - use crate::lowering::Lower; - use rustc::traits::WellFormed; - - let goal = goal.unchecked_map(|goal| InEnvironment { - environment: goal.environment, - goal: match goal.goal { - ty::Predicate::WellFormed(ty) => { - tcx.mk_goal(GoalKind::DomainGoal(DomainGoal::WellFormed(WellFormed::Ty(ty)))) - } - - ty::Predicate::Subtype(predicate) => tcx.mk_goal(GoalKind::Quantified( - QuantifierKind::Universal, - predicate.map_bound(|pred| tcx.mk_goal(GoalKind::Subtype(pred.a, pred.b))), - )), - - other => tcx.mk_goal(GoalKind::from_poly_domain_goal(other.lower(), tcx)), - }, - }); - - debug!("evaluate_goal(goal = {:?})", goal); - - let context = ChalkContext { _arenas: ChalkArenas { _phantom: PhantomData }, tcx }; - - let mut forest = Forest::new(context); - let solution = forest.solve(&goal); - - debug!("evaluate_goal: solution = {:?}", solution); - - solution.map(|ok| Ok(&*tcx.arena.alloc(ok))).unwrap_or(Err(traits::query::NoSolution)) -} diff --git a/src/librustc_traits/chalk_context/program_clauses/builtin.rs b/src/librustc_traits/chalk_context/program_clauses/builtin.rs deleted file mode 100644 index 7512cbbd882..00000000000 --- a/src/librustc_traits/chalk_context/program_clauses/builtin.rs +++ /dev/null @@ -1,316 +0,0 @@ -use crate::generic_types; -use crate::lowering::Lower; -use rustc::traits::{Clause, GoalKind, ProgramClause, ProgramClauseCategory}; -use rustc::ty::subst::{GenericArg, InternalSubsts, Subst}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; - -/// Returns a predicate of the form -/// `Implemented(ty: Trait) :- Implemented(nested: Trait)...` -/// where `Trait` is specified by `trait_def_id`. -fn builtin_impl_clause( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - nested: &[GenericArg<'tcx>], - trait_def_id: DefId, -) -> ProgramClause<'tcx> { - ProgramClause { - goal: ty::TraitPredicate { - trait_ref: ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) }, - } - .lower(), - hypotheses: tcx.mk_goals( - nested - .iter() - .cloned() - .map(|nested_ty| ty::TraitRef { - def_id: trait_def_id, - substs: tcx.mk_substs_trait(nested_ty.expect_ty(), &[]), - }) - .map(|trait_ref| ty::TraitPredicate { trait_ref }) - .map(|pred| GoalKind::DomainGoal(pred.lower())) - .map(|goal_kind| tcx.mk_goal(goal_kind)), - ), - category: ProgramClauseCategory::Other, - } -} - -crate fn assemble_builtin_unsize_impls<'tcx>( - tcx: TyCtxt<'tcx>, - unsize_def_id: DefId, - source: Ty<'tcx>, - target: Ty<'tcx>, - clauses: &mut Vec>, -) { - match (&source.kind, &target.kind) { - (ty::Dynamic(data_a, ..), ty::Dynamic(data_b, ..)) => { - if data_a.principal_def_id() != data_b.principal_def_id() - || data_b.auto_traits().any(|b| data_a.auto_traits().all(|a| a != b)) - { - return; - } - - // FIXME: rules for trait upcast - } - - (_, &ty::Dynamic(..)) => { - // FIXME: basically, we should have something like: - // ``` - // forall { - // Implemented(T: Unsize< for<...> dyn Trait<...> >) :- - // for<...> Implemented(T: Trait<...>). - // } - // ``` - // The question is: how to correctly handle the higher-ranked - // `for<...>` binder in order to have a generic rule? - // (Having generic rules is useful for caching, as we may be able - // to turn this function and others into tcx queries later on). - } - - (ty::Array(_, length), ty::Slice(_)) => { - let ty_param = generic_types::bound(tcx, 0); - let array_ty = tcx.mk_ty(ty::Array(ty_param, length)); - let slice_ty = tcx.mk_ty(ty::Slice(ty_param)); - - // `forall { Implemented([T; N]: Unsize<[T]>). }` - let clause = ProgramClause { - goal: ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: unsize_def_id, - substs: tcx.mk_substs_trait(array_ty, &[slice_ty.into()]), - }, - } - .lower(), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::Other, - }; - - clauses.push(Clause::ForAll(ty::Binder::bind(clause))); - } - - (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => { - // FIXME: ambiguous - } - - (ty::Adt(def_id_a, ..), ty::Adt(def_id_b, ..)) => { - if def_id_a != def_id_b { - return; - } - - // FIXME: rules for struct unsizing - } - - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { - if tys_a.len() != tys_b.len() { - return; - } - - // FIXME: rules for tuple unsizing - } - - _ => (), - } -} - -crate fn assemble_builtin_sized_impls<'tcx>( - tcx: TyCtxt<'tcx>, - sized_def_id: DefId, - ty: Ty<'tcx>, - clauses: &mut Vec>, -) { - let mut push_builtin_impl = |ty: Ty<'tcx>, nested: &[GenericArg<'tcx>]| { - let clause = builtin_impl_clause(tcx, ty, nested, sized_def_id); - // Bind innermost bound vars that may exist in `ty` and `nested`. - clauses.push(Clause::ForAll(ty::Binder::bind(clause))); - }; - - match &ty.kind { - // Non parametric primitive types. - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Error - | ty::Never => push_builtin_impl(ty, &[]), - - // These ones are always `Sized`. - &ty::Array(_, length) => { - push_builtin_impl(tcx.mk_ty(ty::Array(generic_types::bound(tcx, 0), length)), &[]); - } - ty::RawPtr(ptr) => { - push_builtin_impl(generic_types::raw_ptr(tcx, ptr.mutbl), &[]); - } - &ty::Ref(_, _, mutbl) => { - push_builtin_impl(generic_types::ref_ty(tcx, mutbl), &[]); - } - ty::FnPtr(fn_ptr) => { - let fn_ptr = fn_ptr.skip_binder(); - let fn_ptr = generic_types::fn_ptr( - tcx, - fn_ptr.inputs_and_output.len(), - fn_ptr.c_variadic, - fn_ptr.unsafety, - fn_ptr.abi, - ); - push_builtin_impl(fn_ptr, &[]); - } - &ty::FnDef(def_id, ..) => { - push_builtin_impl(generic_types::fn_def(tcx, def_id), &[]); - } - &ty::Closure(def_id, ..) => { - push_builtin_impl(generic_types::closure(tcx, def_id), &[]); - } - &ty::Generator(def_id, ..) => { - push_builtin_impl(generic_types::generator(tcx, def_id), &[]); - } - - // `Sized` if the last type is `Sized` (because else we will get a WF error anyway). - &ty::Tuple(type_list) => { - let type_list = generic_types::type_list(tcx, type_list.len()); - push_builtin_impl(tcx.mk_ty(ty::Tuple(type_list)), &type_list); - } - - // Struct def - ty::Adt(adt_def, _) => { - let substs = InternalSubsts::bound_vars_for_item(tcx, adt_def.did); - let adt = tcx.mk_ty(ty::Adt(adt_def, substs)); - let sized_constraint = adt_def - .sized_constraint(tcx) - .iter() - .map(|ty| GenericArg::from(ty.subst(tcx, substs))) - .collect::>(); - push_builtin_impl(adt, &sized_constraint); - } - - // Artificially trigger an ambiguity by adding two possible types to - // unify against. - ty::Infer(ty::TyVar(_)) => { - push_builtin_impl(tcx.types.i32, &[]); - push_builtin_impl(tcx.types.f32, &[]); - } - - ty::Projection(_projection_ty) => { - // FIXME: add builtin impls from the associated type values found in - // trait impls of `projection_ty.trait_ref(tcx)`. - } - - // The `Sized` bound can only come from the environment. - ty::Param(..) | ty::Placeholder(..) | ty::UnnormalizedProjection(..) => (), - - // Definitely not `Sized`. - ty::Foreign(..) | ty::Str | ty::Slice(..) | ty::Dynamic(..) | ty::Opaque(..) => (), - - ty::Bound(..) - | ty::GeneratorWitness(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => bug!("unexpected type {:?}", ty), - } -} - -crate fn assemble_builtin_copy_clone_impls<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - ty: Ty<'tcx>, - clauses: &mut Vec>, -) { - let mut push_builtin_impl = |ty: Ty<'tcx>, nested: &[GenericArg<'tcx>]| { - let clause = builtin_impl_clause(tcx, ty, nested, trait_def_id); - // Bind innermost bound vars that may exist in `ty` and `nested`. - clauses.push(Clause::ForAll(ty::Binder::bind(clause))); - }; - - match &ty.kind { - // Implementations provided in libcore. - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => (), - - // Non parametric primitive types. - ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Error => { - push_builtin_impl(ty, &[]) - } - - // These implement `Copy`/`Clone` if their element types do. - &ty::Array(_, length) => { - let element_ty = generic_types::bound(tcx, 0); - push_builtin_impl( - tcx.mk_ty(ty::Array(element_ty, length)), - &[GenericArg::from(element_ty)], - ); - } - &ty::Tuple(type_list) => { - let type_list = generic_types::type_list(tcx, type_list.len()); - push_builtin_impl(tcx.mk_ty(ty::Tuple(type_list)), &**type_list); - } - &ty::Closure(def_id, ..) => { - let closure_ty = generic_types::closure(tcx, def_id); - let upvar_tys: Vec<_> = match &closure_ty.kind { - ty::Closure(_, substs) => substs - .as_closure() - .upvar_tys(def_id, tcx) - .map(|ty| GenericArg::from(ty)) - .collect(), - _ => bug!(), - }; - push_builtin_impl(closure_ty, &upvar_tys); - } - - // These ones are always `Clone`. - ty::FnPtr(fn_ptr) => { - let fn_ptr = fn_ptr.skip_binder(); - let fn_ptr = generic_types::fn_ptr( - tcx, - fn_ptr.inputs_and_output.len(), - fn_ptr.c_variadic, - fn_ptr.unsafety, - fn_ptr.abi, - ); - push_builtin_impl(fn_ptr, &[]); - } - &ty::FnDef(def_id, ..) => { - push_builtin_impl(generic_types::fn_def(tcx, def_id), &[]); - } - - // These depend on whatever user-defined impls might exist. - ty::Adt(_, _) => (), - - // Artificially trigger an ambiguity by adding two possible types to - // unify against. - ty::Infer(ty::TyVar(_)) => { - push_builtin_impl(tcx.types.i32, &[]); - push_builtin_impl(tcx.types.f32, &[]); - } - - ty::Projection(_projection_ty) => { - // FIXME: add builtin impls from the associated type values found in - // trait impls of `projection_ty.trait_ref(tcx)`. - } - - // The `Copy`/`Clone` bound can only come from the environment. - ty::Param(..) | ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Opaque(..) => (), - - // Definitely not `Copy`/`Clone`. - ty::Dynamic(..) - | ty::Foreign(..) - | ty::Generator(..) - | ty::Str - | ty::Slice(..) - | ty::Ref(_, _, hir::Mutability::Mut) => (), - - ty::Bound(..) - | ty::GeneratorWitness(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => bug!("unexpected type {:?}", ty), - } -} diff --git a/src/librustc_traits/chalk_context/program_clauses/mod.rs b/src/librustc_traits/chalk_context/program_clauses/mod.rs deleted file mode 100644 index 38a4a729648..00000000000 --- a/src/librustc_traits/chalk_context/program_clauses/mod.rs +++ /dev/null @@ -1,300 +0,0 @@ -mod builtin; -mod primitive; - -use super::ChalkInferenceContext; -use rustc::traits::{ - Clause, DomainGoal, Environment, FromEnv, ProgramClause, ProgramClauseCategory, WellFormed, -}; -use rustc::ty::{self, TyCtxt}; -use rustc_hir::def_id::DefId; -use std::iter; - -use self::builtin::*; -use self::primitive::*; - -fn assemble_clauses_from_impls<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - clauses: &mut Vec>, -) { - tcx.for_each_impl(trait_def_id, |impl_def_id| { - clauses.extend(tcx.program_clauses_for(impl_def_id).into_iter().cloned()); - }); -} - -fn assemble_clauses_from_assoc_ty_values<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - clauses: &mut Vec>, -) { - tcx.for_each_impl(trait_def_id, |impl_def_id| { - for def_id in tcx.associated_item_def_ids(impl_def_id).iter() { - clauses.extend(tcx.program_clauses_for(*def_id).into_iter().cloned()); - } - }); -} - -impl ChalkInferenceContext<'cx, 'tcx> { - pub(super) fn program_clauses_impl( - &self, - environment: &Environment<'tcx>, - goal: &DomainGoal<'tcx>, - ) -> Vec> { - use rustc::infer::canonical::OriginalQueryValues; - use rustc::traits::WhereClause::*; - - let goal = self.infcx.resolve_vars_if_possible(goal); - - debug!("program_clauses(goal = {:?})", goal); - - let mut clauses = match goal { - DomainGoal::Holds(Implemented(trait_predicate)) => { - // These come from: - // * implementations of the trait itself (rule `Implemented-From-Impl`) - // * the trait decl (rule `Implemented-From-Env`) - - let mut clauses = vec![]; - - assemble_clauses_from_impls(self.infcx.tcx, trait_predicate.def_id(), &mut clauses); - - if Some(trait_predicate.def_id()) == self.infcx.tcx.lang_items().sized_trait() { - assemble_builtin_sized_impls( - self.infcx.tcx, - trait_predicate.def_id(), - trait_predicate.self_ty(), - &mut clauses, - ); - } - - if Some(trait_predicate.def_id()) == self.infcx.tcx.lang_items().unsize_trait() { - let source = trait_predicate.self_ty(); - let target = trait_predicate.trait_ref.substs.type_at(1); - assemble_builtin_unsize_impls( - self.infcx.tcx, - trait_predicate.def_id(), - source, - target, - &mut clauses, - ); - } - - if Some(trait_predicate.def_id()) == self.infcx.tcx.lang_items().copy_trait() { - assemble_builtin_copy_clone_impls( - self.infcx.tcx, - trait_predicate.def_id(), - trait_predicate.self_ty(), - &mut clauses, - ); - } - - if Some(trait_predicate.def_id()) == self.infcx.tcx.lang_items().clone_trait() { - // For all builtin impls, the conditions for `Copy` and - // `Clone` are the same. - assemble_builtin_copy_clone_impls( - self.infcx.tcx, - trait_predicate.def_id(), - trait_predicate.self_ty(), - &mut clauses, - ); - } - - // FIXME: we need to add special rules for other builtin impls: - // * `Generator` - // * `FnOnce` / `FnMut` / `Fn` - // * trait objects - // * auto traits - - // Rule `Implemented-From-Env` will be computed from the environment. - clauses - } - - DomainGoal::Holds(ProjectionEq(projection_predicate)) => { - // These come from: - // * the assoc type definition (rule `ProjectionEq-Placeholder`) - // * normalization of the assoc ty values (rule `ProjectionEq-Normalize`) - // * implied bounds from trait definitions (rule `Implied-Bound-From-Trait`) - // * implied bounds from type definitions (rule `Implied-Bound-From-Type`) - - let clauses = self - .infcx - .tcx - .program_clauses_for(projection_predicate.projection_ty.item_def_id) - .into_iter() - // only select `ProjectionEq-Placeholder` and `ProjectionEq-Normalize` - .filter(|clause| clause.category() == ProgramClauseCategory::Other) - .cloned() - .collect::>(); - - // Rules `Implied-Bound-From-Trait` and `Implied-Bound-From-Type` will be computed - // from the environment. - clauses - } - - // For outlive requirements, just assume they hold. `ResolventOps::resolvent_clause` - // will register them as actual region constraints later. - DomainGoal::Holds(RegionOutlives(..)) | DomainGoal::Holds(TypeOutlives(..)) => { - vec![Clause::Implies(ProgramClause { - goal, - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::Other, - })] - } - - DomainGoal::WellFormed(WellFormed::Trait(trait_predicate)) => { - // These come from -- the trait decl (rule `WellFormed-TraitRef`). - self.infcx - .tcx - .program_clauses_for(trait_predicate.def_id()) - .into_iter() - // only select `WellFormed-TraitRef` - .filter(|clause| clause.category() == ProgramClauseCategory::WellFormed) - .cloned() - .collect() - } - - DomainGoal::WellFormed(WellFormed::Ty(ty)) => { - // These come from: - // * the associated type definition if `ty` refers to an unnormalized - // associated type (rule `WellFormed-AssocTy`) - // * custom rules for built-in types - // * the type definition otherwise (rule `WellFormed-Type`) - let clauses = match ty.kind { - ty::Projection(data) => self.infcx.tcx.program_clauses_for(data.item_def_id), - - // These types are always WF. - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::Param(..) - | ty::Placeholder(..) - | ty::Error - | ty::Never => { - let wf_clause = ProgramClause { - goal, - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::Implies(wf_clause); - - self.infcx.tcx.mk_clauses(iter::once(wf_clause)) - } - - // Always WF (recall that we do not check for parameters to be WF). - ty::RawPtr(ptr) => wf_clause_for_raw_ptr(self.infcx.tcx, ptr.mutbl), - - // Always WF (recall that we do not check for parameters to be WF). - ty::FnPtr(fn_ptr) => { - let fn_ptr = fn_ptr.skip_binder(); - wf_clause_for_fn_ptr( - self.infcx.tcx, - fn_ptr.inputs_and_output.len(), - fn_ptr.c_variadic, - fn_ptr.unsafety, - fn_ptr.abi, - ) - } - - // WF if inner type is `Sized`. - ty::Slice(..) => wf_clause_for_slice(self.infcx.tcx), - - // WF if inner type is `Sized`. - ty::Array(_, length) => wf_clause_for_array(self.infcx.tcx, length), - - // WF if all types but the last one are `Sized`. - ty::Tuple(types) => wf_clause_for_tuple(self.infcx.tcx, types.len()), - - // WF if `sub_ty` outlives `region`. - ty::Ref(_, _, mutbl) => wf_clause_for_ref(self.infcx.tcx, mutbl), - - ty::FnDef(def_id, ..) => wf_clause_for_fn_def(self.infcx.tcx, def_id), - - ty::Dynamic(..) => { - // FIXME: no rules yet for trait objects - ty::List::empty() - } - - ty::Adt(def, ..) => self.infcx.tcx.program_clauses_for(def.did), - - // FIXME: these are probably wrong - ty::Foreign(def_id) - | ty::Closure(def_id, ..) - | ty::Generator(def_id, ..) - | ty::Opaque(def_id, ..) => self.infcx.tcx.program_clauses_for(def_id), - - // Artificially trigger an ambiguity. - ty::Infer(..) => { - let tcx = self.infcx.tcx; - let types = [tcx.types.i32, tcx.types.u32, tcx.types.f32, tcx.types.f64]; - let clauses = types - .iter() - .cloned() - .map(|ty| ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(ty)), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }) - .map(|clause| Clause::Implies(clause)); - tcx.mk_clauses(clauses) - } - - ty::GeneratorWitness(..) | ty::UnnormalizedProjection(..) | ty::Bound(..) => { - bug!("unexpected type {:?}", ty) - } - }; - - clauses - .into_iter() - .filter(|clause| clause.category() == ProgramClauseCategory::WellFormed) - .cloned() - .collect() - } - - DomainGoal::FromEnv(FromEnv::Trait(..)) => { - // These come from: - // * implied bounds from trait definitions (rule `Implied-Bound-From-Trait`) - // * implied bounds from type definitions (rule `Implied-Bound-From-Type`) - // * implied bounds from assoc type defs (rules `Implied-Trait-From-AssocTy`, - // `Implied-Bound-From-AssocTy` and `Implied-WC-From-AssocTy`) - - // All of these rules are computed in the environment. - vec![] - } - - DomainGoal::FromEnv(FromEnv::Ty(..)) => { - // There are no `FromEnv::Ty(..) :- ...` rules (this predicate only - // comes from the environment). - vec![] - } - - DomainGoal::Normalize(projection_predicate) => { - // These come from -- assoc ty values (rule `Normalize-From-Impl`). - let mut clauses = vec![]; - - assemble_clauses_from_assoc_ty_values( - self.infcx.tcx, - projection_predicate.projection_ty.trait_ref(self.infcx.tcx).def_id, - &mut clauses, - ); - - clauses - } - }; - - debug!("program_clauses: clauses = {:?}", clauses); - debug!("program_clauses: adding clauses from environment = {:?}", environment); - - let mut _orig_query_values = OriginalQueryValues::default(); - let canonical_environment = - self.infcx.canonicalize_query(environment, &mut _orig_query_values).value; - let env_clauses = self.infcx.tcx.program_clauses_for_env(canonical_environment); - - debug!("program_clauses: env_clauses = {:?}", env_clauses); - - clauses.extend(env_clauses.into_iter().cloned()); - clauses.extend(environment.clauses.iter().cloned()); - clauses - } -} diff --git a/src/librustc_traits/chalk_context/program_clauses/primitive.rs b/src/librustc_traits/chalk_context/program_clauses/primitive.rs deleted file mode 100644 index ae4afe58436..00000000000 --- a/src/librustc_traits/chalk_context/program_clauses/primitive.rs +++ /dev/null @@ -1,168 +0,0 @@ -use crate::generic_types; -use crate::lowering::Lower; -use rustc::traits::{ - Clause, Clauses, DomainGoal, GoalKind, ProgramClause, ProgramClauseCategory, WellFormed, -}; -use rustc::ty::{self, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_target::spec::abi; -use std::iter; - -crate fn wf_clause_for_raw_ptr(tcx: TyCtxt<'_>, mutbl: hir::Mutability) -> Clauses<'_> { - let ptr_ty = generic_types::raw_ptr(tcx, mutbl); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(ptr_ty)), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::Implies(wf_clause); - - // `forall { WellFormed(*const T). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -crate fn wf_clause_for_fn_ptr( - tcx: TyCtxt<'_>, - arity_and_output: usize, - variadic: bool, - unsafety: hir::Unsafety, - abi: abi::Abi, -) -> Clauses<'_> { - let fn_ptr = generic_types::fn_ptr(tcx, arity_and_output, variadic, unsafety, abi); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(fn_ptr)), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed(for<> fn(T1, ..., Tn) -> Tn+1). }` - // where `n + 1` == `arity_and_output` - tcx.mk_clauses(iter::once(wf_clause)) -} - -crate fn wf_clause_for_slice(tcx: TyCtxt<'_>) -> Clauses<'_> { - let ty = generic_types::bound(tcx, 0); - let slice_ty = tcx.mk_slice(ty); - - let sized_trait = match tcx.lang_items().sized_trait() { - Some(def_id) => def_id, - None => return ty::List::empty(), - }; - let sized_implemented = - ty::TraitRef { def_id: sized_trait, substs: tcx.mk_substs_trait(ty, ty::List::empty()) }; - let sized_implemented: DomainGoal<'_> = - ty::TraitPredicate { trait_ref: sized_implemented }.lower(); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(slice_ty)), - hypotheses: tcx.mk_goals(iter::once(tcx.mk_goal(GoalKind::DomainGoal(sized_implemented)))), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed([T]) :- Implemented(T: Sized). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -crate fn wf_clause_for_array<'tcx>( - tcx: TyCtxt<'tcx>, - length: &'tcx ty::Const<'tcx>, -) -> Clauses<'tcx> { - let ty = generic_types::bound(tcx, 0); - let array_ty = tcx.mk_ty(ty::Array(ty, length)); - - let sized_trait = match tcx.lang_items().sized_trait() { - Some(def_id) => def_id, - None => return ty::List::empty(), - }; - let sized_implemented = - ty::TraitRef { def_id: sized_trait, substs: tcx.mk_substs_trait(ty, ty::List::empty()) }; - let sized_implemented: DomainGoal<'_> = - ty::TraitPredicate { trait_ref: sized_implemented }.lower(); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(array_ty)), - hypotheses: tcx.mk_goals(iter::once(tcx.mk_goal(GoalKind::DomainGoal(sized_implemented)))), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed([T; length]) :- Implemented(T: Sized). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -crate fn wf_clause_for_tuple(tcx: TyCtxt<'_>, arity: usize) -> Clauses<'_> { - let type_list = generic_types::type_list(tcx, arity); - let tuple_ty = tcx.mk_ty(ty::Tuple(type_list)); - - let sized_trait = match tcx.lang_items().sized_trait() { - Some(def_id) => def_id, - None => return ty::List::empty(), - }; - - // If `arity == 0` (i.e. the unit type) or `arity == 1`, this list of - // hypotheses is actually empty. - let sized_implemented = type_list[0..std::cmp::max(arity, 1) - 1] - .iter() - .map(|ty| ty::TraitRef { - def_id: sized_trait, - substs: tcx.mk_substs_trait(ty.expect_ty(), ty::List::empty()), - }) - .map(|trait_ref| ty::TraitPredicate { trait_ref }) - .map(|predicate| predicate.lower()); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(tuple_ty)), - hypotheses: tcx.mk_goals( - sized_implemented.map(|domain_goal| tcx.mk_goal(GoalKind::DomainGoal(domain_goal))), - ), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // ``` - // forall { - // WellFormed((T1, ..., Tn)) :- - // Implemented(T1: Sized), - // ... - // Implemented(Tn-1: Sized). - // } - // ``` - tcx.mk_clauses(iter::once(wf_clause)) -} - -crate fn wf_clause_for_ref(tcx: TyCtxt<'_>, mutbl: hir::Mutability) -> Clauses<'_> { - let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0))); - let ty = generic_types::bound(tcx, 1); - let ref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty, mutbl }); - - let outlives: DomainGoal<'_> = ty::OutlivesPredicate(ty, region).lower(); - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(ref_ty)), - hypotheses: tcx.mk_goals(iter::once(tcx.mk_goal(outlives.into_goal()))), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall<'a, T> { WellFormed(&'a T) :- Outlives(T: 'a). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -crate fn wf_clause_for_fn_def(tcx: TyCtxt<'_>, def_id: DefId) -> Clauses<'_> { - let fn_def = generic_types::fn_def(tcx, def_id); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(fn_def)), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed(fn some_fn(T1, ..., Tn) -> Tn+1). }` - // where `def_id` maps to the `some_fn` function definition - tcx.mk_clauses(iter::once(wf_clause)) -} diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs deleted file mode 100644 index 796ce6085fd..00000000000 --- a/src/librustc_traits/chalk_context/resolvent_ops.rs +++ /dev/null @@ -1,297 +0,0 @@ -use chalk_engine::fallible::{Fallible, NoSolution}; -use chalk_engine::{context, ExClause, Literal}; -use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc::ty::subst::GenericArg; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; -use rustc_infer::traits::{ - Clause, DomainGoal, Environment, Goal, GoalKind, InEnvironment, ProgramClause, WhereClause, -}; -use rustc_span::DUMMY_SP; - -use super::unify::*; -use super::{ChalkArenas, ChalkExClause, ChalkInferenceContext, ConstrainedSubst}; - -impl context::ResolventOps, ChalkArenas<'tcx>> - for ChalkInferenceContext<'cx, 'tcx> -{ - fn resolvent_clause( - &mut self, - environment: &Environment<'tcx>, - goal: &DomainGoal<'tcx>, - subst: &CanonicalVarValues<'tcx>, - clause: &Clause<'tcx>, - ) -> Fallible>> { - use chalk_engine::context::UnificationOps; - - debug!("resolvent_clause(goal = {:?}, clause = {:?})", goal, clause); - - let result = self.infcx.probe(|_| { - let ProgramClause { goal: consequence, hypotheses, .. } = match clause { - Clause::Implies(program_clause) => *program_clause, - Clause::ForAll(program_clause) => { - self.infcx - .replace_bound_vars_with_fresh_vars( - DUMMY_SP, - LateBoundRegionConversionTime::HigherRankedType, - program_clause, - ) - .0 - } - }; - - let result = - unify(self.infcx, *environment, ty::Variance::Invariant, goal, &consequence) - .map_err(|_| NoSolution)?; - - let mut ex_clause = ExClause { - subst: subst.clone(), - delayed_literals: vec![], - constraints: vec![], - subgoals: vec![], - }; - - self.into_ex_clause(result, &mut ex_clause); - - ex_clause.subgoals.extend(hypotheses.iter().map(|g| match g { - GoalKind::Not(g) => Literal::Negative(environment.with(*g)), - g => Literal::Positive(environment.with(*g)), - })); - - // If we have a goal of the form `T: 'a` or `'a: 'b`, then just - // assume it is true (no subgoals) and register it as a constraint - // instead. - match goal { - DomainGoal::Holds(WhereClause::RegionOutlives(pred)) => { - assert_eq!(ex_clause.subgoals.len(), 0); - ex_clause.constraints.push(ty::OutlivesPredicate(pred.0.into(), pred.1)); - } - - DomainGoal::Holds(WhereClause::TypeOutlives(pred)) => { - assert_eq!(ex_clause.subgoals.len(), 0); - ex_clause.constraints.push(ty::OutlivesPredicate(pred.0.into(), pred.1)); - } - - _ => (), - }; - - let canonical_ex_clause = self.canonicalize_ex_clause(&ex_clause); - Ok(canonical_ex_clause) - }); - - debug!("resolvent_clause: result = {:?}", result); - result - } - - fn apply_answer_subst( - &mut self, - ex_clause: ChalkExClause<'tcx>, - selected_goal: &InEnvironment<'tcx, Goal<'tcx>>, - answer_table_goal: &Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>, - canonical_answer_subst: &Canonical<'tcx, ConstrainedSubst<'tcx>>, - ) -> Fallible> { - debug!( - "apply_answer_subst(ex_clause = {:?}, selected_goal = {:?})", - self.infcx.resolve_vars_if_possible(&ex_clause), - self.infcx.resolve_vars_if_possible(selected_goal) - ); - - let (answer_subst, _) = self - .infcx - .instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, canonical_answer_subst); - - let mut substitutor = AnswerSubstitutor { - infcx: self.infcx, - environment: selected_goal.environment, - answer_subst: answer_subst.subst, - binder_index: ty::INNERMOST, - ex_clause, - }; - - substitutor.relate(&answer_table_goal.value, &selected_goal).map_err(|_| NoSolution)?; - - let mut ex_clause = substitutor.ex_clause; - ex_clause.constraints.extend(answer_subst.constraints); - - debug!("apply_answer_subst: ex_clause = {:?}", ex_clause); - Ok(ex_clause) - } -} - -struct AnswerSubstitutor<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, - environment: Environment<'tcx>, - answer_subst: CanonicalVarValues<'tcx>, - binder_index: ty::DebruijnIndex, - ex_clause: ChalkExClause<'tcx>, -} - -impl AnswerSubstitutor<'cx, 'tcx> { - fn unify_free_answer_var( - &mut self, - answer_var: ty::BoundVar, - pending: GenericArg<'tcx>, - ) -> RelateResult<'tcx, ()> { - let answer_param = &self.answer_subst.var_values[answer_var]; - let pending = - &ty::fold::shift_out_vars(self.infcx.tcx, &pending, self.binder_index.as_u32()); - - super::into_ex_clause( - unify(self.infcx, self.environment, ty::Variance::Invariant, answer_param, pending)?, - &mut self.ex_clause, - ); - - Ok(()) - } -} - -impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - // FIXME(oli-obk): learn chalk and create param envs - ty::ParamEnv::empty() - } - - fn tag(&self) -> &'static str { - "chalk_context::answer_substitutor" - } - - fn a_is_expected(&self) -> bool { - true - } - - fn relate_with_variance>( - &mut self, - _variance: ty::Variance, - a: &T, - b: &T, - ) -> RelateResult<'tcx, T> { - // We don't care about variance. - self.relate(a, b) - } - - fn binders>( - &mut self, - a: &ty::Binder, - b: &ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> { - self.binder_index.shift_in(1); - let result = self.relate(a.skip_binder(), b.skip_binder())?; - self.binder_index.shift_out(1); - Ok(ty::Binder::bind(result)) - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let b = self.infcx.shallow_resolve(b); - debug!("AnswerSubstitutor::tys(a = {:?}, b = {:?})", a, b); - - if let &ty::Bound(debruijn, bound_ty) = &a.kind { - // Free bound var - if debruijn == self.binder_index { - self.unify_free_answer_var(bound_ty.var, b.into())?; - return Ok(b); - } - } - - match (&a.kind, &b.kind) { - (&ty::Bound(a_debruijn, a_bound), &ty::Bound(b_debruijn, b_bound)) => { - assert_eq!(a_debruijn, b_debruijn); - assert_eq!(a_bound.var, b_bound.var); - Ok(a) - } - - // Those should have been canonicalized away. - (ty::Placeholder(..), _) => { - bug!("unexpected placeholder ty in `AnswerSubstitutor`: {:?} ", a); - } - - // Everything else should just be a perfect match as well, - // and we forbid inference variables. - _ => match ty::relate::super_relate_tys(self, a, b) { - Ok(ty) => Ok(ty), - Err(err) => bug!("type mismatch in `AnswerSubstitutor`: {}", err), - }, - } - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - let b = match b { - &ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid), - - other => other, - }; - - if let &ty::ReLateBound(debruijn, bound) = a { - // Free bound region - if debruijn == self.binder_index { - self.unify_free_answer_var(bound.assert_bound_var(), b.into())?; - return Ok(b); - } - } - - match (a, b) { - (&ty::ReLateBound(a_debruijn, a_bound), &ty::ReLateBound(b_debruijn, b_bound)) => { - assert_eq!(a_debruijn, b_debruijn); - assert_eq!(a_bound.assert_bound_var(), b_bound.assert_bound_var()); - } - - (ty::ReStatic, ty::ReStatic) | (ty::ReErased, ty::ReErased) => (), - - (ty::ReEmpty(a_ui), ty::ReEmpty(b_ui)) => { - assert_eq!(a_ui, b_ui); - } - - (&ty::ReFree(a_free), &ty::ReFree(b_free)) => { - assert_eq!(a_free, b_free); - } - - _ => bug!("unexpected regions in `AnswerSubstitutor`: {:?}, {:?}", a, b), - } - - Ok(a) - } - - fn consts( - &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - if let ty::Const { val: ty::ConstKind::Bound(debruijn, bound_ct), .. } = a { - if *debruijn == self.binder_index { - self.unify_free_answer_var(*bound_ct, b.into())?; - return Ok(b); - } - } - - match (a, b) { - ( - ty::Const { val: ty::ConstKind::Bound(a_debruijn, a_bound), .. }, - ty::Const { val: ty::ConstKind::Bound(b_debruijn, b_bound), .. }, - ) => { - assert_eq!(a_debruijn, b_debruijn); - assert_eq!(a_bound, b_bound); - Ok(a) - } - - // Everything else should just be a perfect match as well, - // and we forbid inference variables. - _ => match ty::relate::super_relate_consts(self, a, b) { - Ok(ct) => Ok(ct), - Err(err) => bug!("const mismatch in `AnswerSubstitutor`: {}", err), - }, - } - } -} diff --git a/src/librustc_traits/chalk_context/unify.rs b/src/librustc_traits/chalk_context/unify.rs deleted file mode 100644 index 3274a301bb6..00000000000 --- a/src/librustc_traits/chalk_context/unify.rs +++ /dev/null @@ -1,85 +0,0 @@ -use rustc::ty; -use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; -use rustc_infer::infer::{InferCtxt, RegionVariableOrigin}; -use rustc_infer::traits::{DomainGoal, Environment, Goal, InEnvironment}; -use rustc_span::DUMMY_SP; - -crate struct UnificationResult<'tcx> { - crate goals: Vec>>, - crate constraints: Vec>, -} - -crate fn unify<'me, 'tcx, T: Relate<'tcx>>( - infcx: &'me InferCtxt<'me, 'tcx>, - environment: Environment<'tcx>, - variance: ty::Variance, - a: &T, - b: &T, -) -> RelateResult<'tcx, UnificationResult<'tcx>> { - debug!( - "unify( - a = {:?}, - b = {:?}, - environment = {:?}, - )", - a, b, environment - ); - - let mut delegate = ChalkTypeRelatingDelegate::new(infcx, environment); - - TypeRelating::new(infcx, &mut delegate, variance).relate(a, b)?; - - debug!("unify: goals = {:?}, constraints = {:?}", delegate.goals, delegate.constraints); - - Ok(UnificationResult { goals: delegate.goals, constraints: delegate.constraints }) -} - -struct ChalkTypeRelatingDelegate<'me, 'tcx> { - infcx: &'me InferCtxt<'me, 'tcx>, - environment: Environment<'tcx>, - goals: Vec>>, - constraints: Vec>, -} - -impl ChalkTypeRelatingDelegate<'me, 'tcx> { - fn new(infcx: &'me InferCtxt<'me, 'tcx>, environment: Environment<'tcx>) -> Self { - Self { infcx, environment, goals: Vec::new(), constraints: Vec::new() } - } -} - -impl TypeRelatingDelegate<'tcx> for &mut ChalkTypeRelatingDelegate<'_, 'tcx> { - fn create_next_universe(&mut self) -> ty::UniverseIndex { - self.infcx.create_next_universe() - } - - fn next_existential_region_var(&mut self, _was_placeholder: bool) -> ty::Region<'tcx> { - self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) - } - - fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> { - self.infcx.tcx.mk_region(ty::RePlaceholder(placeholder)) - } - - fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { - self.infcx - .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(DUMMY_SP), universe) - } - - fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { - self.constraints.push(ty::OutlivesPredicate(sup.into(), sub)); - } - - fn push_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>) { - let goal = self.environment.with(self.infcx.tcx.mk_goal(domain_goal.into_goal())); - self.goals.push(goal); - } - - fn normalization() -> NormalizationStrategy { - NormalizationStrategy::Lazy - } - - fn forbid_inference_vars() -> bool { - false - } -} diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index 2f854c045e5..b13a7a3acb1 100644 --- a/src/librustc_traits/dropck_outlives.rs +++ b/src/librustc_traits/dropck_outlives.rs @@ -5,11 +5,17 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::query::dropck_outlives::trivial_dropck_outlives; -use rustc_infer::traits::query::dropck_outlives::{DropckOutlivesResult, DtorckConstraint}; -use rustc_infer::traits::query::{CanonicalTyGoal, NoSolution}; -use rustc_infer::traits::{Normalized, ObligationCause, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::TraitEngineExt as _; use rustc_span::source_map::{Span, DUMMY_SP}; +use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives; +use rustc_trait_selection::traits::query::dropck_outlives::{ + DropckOutlivesResult, DtorckConstraint, +}; +use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc_trait_selection::traits::{ + Normalized, ObligationCause, TraitEngine, TraitEngineExt as _, +}; crate fn provide(p: &mut Providers<'_>) { *p = Providers { dropck_outlives, adt_dtorck_constraint, ..*p }; diff --git a/src/librustc_traits/evaluate_obligation.rs b/src/librustc_traits/evaluate_obligation.rs index 4cf5b66b3cb..87895d8e384 100644 --- a/src/librustc_traits/evaluate_obligation.rs +++ b/src/librustc_traits/evaluate_obligation.rs @@ -1,11 +1,11 @@ use rustc::ty::query::Providers; use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::query::CanonicalPredicateGoal; -use rustc_infer::traits::{ +use rustc_span::source_map::DUMMY_SP; +use rustc_trait_selection::traits::query::CanonicalPredicateGoal; +use rustc_trait_selection::traits::{ EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode, }; -use rustc_span::source_map::DUMMY_SP; crate fn provide(p: &mut Providers<'_>) { *p = Providers { evaluate_obligation, ..*p }; diff --git a/src/librustc_traits/generic_types.rs b/src/librustc_traits/generic_types.rs deleted file mode 100644 index 44a2c5464cd..00000000000 --- a/src/librustc_traits/generic_types.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Utilities for creating generic types with bound vars in place of parameter values. - -use rustc::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_target::spec::abi; - -crate fn bound(tcx: TyCtxt<'tcx>, index: u32) -> Ty<'tcx> { - let ty = ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(index).into()); - tcx.mk_ty(ty) -} - -crate fn raw_ptr(tcx: TyCtxt<'tcx>, mutbl: hir::Mutability) -> Ty<'tcx> { - tcx.mk_ptr(ty::TypeAndMut { ty: bound(tcx, 0), mutbl }) -} - -crate fn fn_ptr( - tcx: TyCtxt<'tcx>, - arity_and_output: usize, - c_variadic: bool, - unsafety: hir::Unsafety, - abi: abi::Abi, -) -> Ty<'tcx> { - let inputs_and_output = tcx.mk_type_list( - (0..arity_and_output) - .map(|i| ty::BoundVar::from(i)) - // DebruijnIndex(1) because we are going to inject these in a `PolyFnSig` - .map(|var| tcx.mk_ty(ty::Bound(ty::DebruijnIndex::from(1usize), var.into()))), - ); - - let fn_sig = ty::Binder::bind(ty::FnSig { inputs_and_output, c_variadic, unsafety, abi }); - tcx.mk_fn_ptr(fn_sig) -} - -crate fn type_list(tcx: TyCtxt<'tcx>, arity: usize) -> SubstsRef<'tcx> { - tcx.mk_substs( - (0..arity) - .map(|i| ty::BoundVar::from(i)) - .map(|var| tcx.mk_ty(ty::Bound(ty::INNERMOST, var.into()))) - .map(|ty| GenericArg::from(ty)), - ) -} - -crate fn ref_ty(tcx: TyCtxt<'tcx>, mutbl: hir::Mutability) -> Ty<'tcx> { - let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0))); - - tcx.mk_ref(region, ty::TypeAndMut { ty: bound(tcx, 1), mutbl }) -} - -crate fn fn_def(tcx: TyCtxt<'tcx>, def_id: DefId) -> Ty<'tcx> { - tcx.mk_ty(ty::FnDef(def_id, InternalSubsts::bound_vars_for_item(tcx, def_id))) -} - -crate fn closure(tcx: TyCtxt<'tcx>, def_id: DefId) -> Ty<'tcx> { - tcx.mk_closure(def_id, InternalSubsts::bound_vars_for_item(tcx, def_id)) -} - -crate fn generator(tcx: TyCtxt<'tcx>, def_id: DefId) -> Ty<'tcx> { - tcx.mk_generator( - def_id, - InternalSubsts::bound_vars_for_item(tcx, def_id), - hir::Movability::Movable, - ) -} diff --git a/src/librustc_traits/implied_outlives_bounds.rs b/src/librustc_traits/implied_outlives_bounds.rs index 69424e3fac7..4505a1e59d9 100644 --- a/src/librustc_traits/implied_outlives_bounds.rs +++ b/src/librustc_traits/implied_outlives_bounds.rs @@ -7,12 +7,14 @@ use rustc_hir as hir; use rustc_infer::infer::canonical::{self, Canonical}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::query::outlives_bounds::OutlivesBound; -use rustc_infer::traits::query::{CanonicalTyGoal, Fallible, NoSolution}; -use rustc_infer::traits::wf; -use rustc_infer::traits::FulfillmentContext; -use rustc_infer::traits::{TraitEngine, TraitEngineExt}; +use rustc_infer::traits::TraitEngineExt as _; use rustc_span::source_map::DUMMY_SP; +use rustc_trait_selection::infer::InferCtxtBuilderExt; +use rustc_trait_selection::traits::query::outlives_bounds::OutlivesBound; +use rustc_trait_selection::traits::query::{CanonicalTyGoal, Fallible, NoSolution}; +use rustc_trait_selection::traits::wf; +use rustc_trait_selection::traits::FulfillmentContext; +use rustc_trait_selection::traits::TraitEngine; use smallvec::{smallvec, SmallVec}; crate fn provide(p: &mut Providers<'_>) { @@ -84,7 +86,7 @@ fn compute_implied_outlives_bounds<'tcx>( // to avoids duplicate errors that otherwise show up. fulfill_cx.register_predicate_obligations( infcx, - obligations.iter().filter(|o| o.predicate.has_infer_types()).cloned(), + obligations.iter().filter(|o| o.predicate.has_infer_types_or_consts()).cloned(), ); // From the full set of obligations, just filter down to the diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index fefe82fdece..894e3ef3a8f 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -11,10 +11,8 @@ #[macro_use] extern crate rustc; -mod chalk_context; mod dropck_outlives; mod evaluate_obligation; -mod generic_types; mod implied_outlives_bounds; pub mod lowering; mod normalize_erasing_regions; @@ -28,7 +26,6 @@ pub fn provide(p: &mut Providers<'_>) { evaluate_obligation::provide(p); implied_outlives_bounds::provide(p); lowering::provide(p); - chalk_context::provide(p); normalize_projection_ty::provide(p); normalize_erasing_regions::provide(p); type_op::provide(p); diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs index 0e26e9461f4..69d0bd09296 100644 --- a/src/librustc_traits/lowering/environment.rs +++ b/src/librustc_traits/lowering/environment.rs @@ -185,12 +185,12 @@ enum NodeKind { let node_kind = match node { Node::TraitItem(item) => match item.kind { - TraitItemKind::Method(..) => NodeKind::Fn, + TraitItemKind::Fn(..) => NodeKind::Fn, _ => NodeKind::Other, }, Node::ImplItem(item) => match item.kind { - ImplItemKind::Method(..) => NodeKind::Fn, + ImplItemKind::Fn(..) => NodeKind::Fn, _ => NodeKind::Other, }, diff --git a/src/librustc_traits/lowering/mod.rs b/src/librustc_traits/lowering/mod.rs index 97b81c224d5..3a0c36a84ae 100644 --- a/src/librustc_traits/lowering/mod.rs +++ b/src/librustc_traits/lowering/mod.rs @@ -108,13 +108,13 @@ fn lower(&self) -> PolyDomainGoal<'tcx> { } } -/// Used for implied bounds related rules (see rustc guide). +/// Used for implied bounds related rules (see rustc dev guide). trait IntoFromEnvGoal { /// Transforms an existing goal into a `FromEnv` goal. fn into_from_env_goal(self) -> Self; } -/// Used for well-formedness related rules (see rustc guide). +/// Used for well-formedness related rules (see rustc dev guide). trait IntoWellFormedGoal { /// Transforms an existing goal into a `WellFormed` goal. fn into_well_formed_goal(self) -> Self; @@ -178,7 +178,7 @@ fn into_well_formed_goal(self) -> DomainGoal<'tcx> { fn program_clauses_for_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Clauses<'_> { // `trait Trait where WC { .. } // P0 == Self` - // Rule Implemented-From-Env (see rustc guide) + // Rule Implemented-From-Env (see rustc dev guide) // // ``` // forall { @@ -282,7 +282,7 @@ fn program_clauses_for_impl(tcx: TyCtxt<'tcx>, def_id: DefId) -> Clauses<'tcx> { return List::empty(); } - // Rule Implemented-From-Impl (see rustc guide) + // Rule Implemented-From-Impl (see rustc dev guide) // // `impl Trait for A0 where WC { .. }` // @@ -501,7 +501,7 @@ pub fn program_clauses_for_associated_type_def(tcx: TyCtxt<'_>, item_id: DefId) } pub fn program_clauses_for_associated_type_value(tcx: TyCtxt<'_>, item_id: DefId) -> Clauses<'_> { - // Rule Normalize-From-Impl (see rustc guide) + // Rule Normalize-From-Impl (see rustc dev guide) // // ``` // impl Trait for A0 { @@ -603,8 +603,8 @@ fn process_attrs(&mut self, hir_id: hir::HirId, attrs: &[ast::Attribute]) { impl Visitor<'tcx> for ClauseDumper<'tcx> { type Map = Map<'tcx>; - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.tcx.hir()) } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs index 4e5f20d80b0..c2fb237a05b 100644 --- a/src/librustc_traits/normalize_erasing_regions.rs +++ b/src/librustc_traits/normalize_erasing_regions.rs @@ -2,7 +2,8 @@ use rustc::ty::query::Providers; use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::{Normalized, ObligationCause}; +use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_trait_selection::traits::{Normalized, ObligationCause}; use std::sync::atomic::Ordering; crate fn provide(p: &mut Providers<'_>) { diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index b5678956347..57abff769de 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -3,11 +3,13 @@ use rustc_hir as hir; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::query::{ +use rustc_infer::traits::TraitEngineExt as _; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::infer::InferCtxtBuilderExt; +use rustc_trait_selection::traits::query::{ normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution, }; -use rustc_infer::traits::{self, ObligationCause, SelectionContext, TraitEngineExt}; -use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext}; use std::sync::atomic::Ordering; crate fn provide(p: &mut Providers<'_>) { diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs index 41181338061..e174c040e0d 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -8,14 +8,18 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::query::type_op::ascribe_user_type::AscribeUserType; -use rustc_infer::traits::query::type_op::eq::Eq; -use rustc_infer::traits::query::type_op::normalize::Normalize; -use rustc_infer::traits::query::type_op::prove_predicate::ProvePredicate; -use rustc_infer::traits::query::type_op::subtype::Subtype; -use rustc_infer::traits::query::{Fallible, NoSolution}; -use rustc_infer::traits::{Normalized, Obligation, ObligationCause, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::TraitEngineExt as _; use rustc_span::DUMMY_SP; +use rustc_trait_selection::infer::InferCtxtBuilderExt; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_trait_selection::traits::query::type_op::ascribe_user_type::AscribeUserType; +use rustc_trait_selection::traits::query::type_op::eq::Eq; +use rustc_trait_selection::traits::query::type_op::normalize::Normalize; +use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate; +use rustc_trait_selection::traits::query::type_op::subtype::Subtype; +use rustc_trait_selection::traits::query::{Fallible, NoSolution}; +use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, TraitEngine}; use std::fmt; crate fn provide(p: &mut Providers<'_>) { diff --git a/src/librustc_ty/Cargo.toml b/src/librustc_ty/Cargo.toml index 6e64df3492b..cf0b4b82eea 100644 --- a/src/librustc_ty/Cargo.toml +++ b/src/librustc_ty/Cargo.toml @@ -15,4 +15,6 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_hir = { path = "../librustc_hir" } rustc_infer = { path = "../librustc_infer" } rustc_span = { path = "../librustc_span" } +rustc_session = { path = "../librustc_session" } rustc_target = { path = "../librustc_target" } +rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_ty/common_traits.rs b/src/librustc_ty/common_traits.rs index e0ce6ad23a6..311ba383f30 100644 --- a/src/librustc_ty/common_traits.rs +++ b/src/librustc_ty/common_traits.rs @@ -3,8 +3,8 @@ use rustc::middle::lang_items; use rustc::ty::{self, Ty, TyCtxt}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits; fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { is_item_raw(tcx, query, lang_items::CopyTraitLangItem) diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index 484b774add4..a5abe7b6413 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -1,8 +1,9 @@ use rustc::ty::subst::SubstsRef; use rustc::ty::{self, Instance, TyCtxt, TypeFoldable}; use rustc_hir::def_id::DefId; -use rustc_infer::traits; +use rustc_span::sym; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits; use log::debug; @@ -31,23 +32,28 @@ pub fn resolve_instance<'tcx>( debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def_id) } - _ => { - if Some(def_id) == tcx.lang_items().drop_in_place_fn() { - let ty = substs.type_at(0); - if ty.needs_drop(tcx, param_env.with_reveal_all()) { - debug!(" => nontrivial drop glue"); - ty::InstanceDef::DropGlue(def_id, Some(ty)) - } else { - debug!(" => trivial drop glue"); - ty::InstanceDef::DropGlue(def_id, None) + ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { + let ty = substs.type_at(0); + + if ty.needs_drop(tcx, param_env) { + // `DropGlue` requires a monomorphic aka concrete type. + if ty.needs_subst() { + return None; } + + debug!(" => nontrivial drop glue"); + ty::InstanceDef::DropGlue(def_id, Some(ty)) } else { - debug!(" => free item"); - ty::InstanceDef::Item(def_id) + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) } } + _ => { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } }; - Some(Instance { def: def, substs: substs }) + Some(Instance { def, substs }) }; debug!("resolve(def_id={:?}, substs={:?}) = {:?}", def_id, substs, result); result @@ -70,7 +76,7 @@ fn resolve_associated_item<'tcx>( ); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref))); + let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref)))?; // Now that we know which impl is being used, we can dispatch to // the actual function: @@ -113,20 +119,44 @@ fn resolve_associated_item<'tcx>( trait_closure_kind, )) } - traits::VtableFnPointer(ref data) => Some(Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), - substs: rcvr_substs, - }), + traits::VtableFnPointer(ref data) => { + // `FnPtrShim` requires a monomorphic aka concrete type. + if data.fn_ty.needs_subst() { + return None; + } + + Some(Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs, + }) + } traits::VtableObject(ref data) => { let index = traits::get_vtable_index_of_object_method(tcx, data, def_id); Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs }) } traits::VtableBuiltin(..) => { - if tcx.lang_items().clone_trait().is_some() { - Some(Instance { - def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), - substs: rcvr_substs, - }) + if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() { + // FIXME(eddyb) use lang items for methods instead of names. + let name = tcx.item_name(def_id); + if name == sym::clone { + let self_ty = trait_ref.self_ty(); + + // `CloneShim` requires a monomorphic aka concrete type. + if self_ty.needs_subst() { + return None; + } + + Some(Instance { + def: ty::InstanceDef::CloneShim(def_id, self_ty), + substs: rcvr_substs, + }) + } else { + assert_eq!(name, sym::clone_from); + + // Use the default `fn clone_from` from `trait Clone`. + let substs = tcx.erase_regions(&rcvr_substs); + Some(ty::Instance::new(def_id, substs)) + } } else { None } diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs index d466bbcca79..4b522997537 100644 --- a/src/librustc_ty/ty.rs +++ b/src/librustc_ty/ty.rs @@ -1,13 +1,13 @@ use rustc::hir::map as hir_map; -use rustc::session::CrateDisambiguator; use rustc::ty::subst::Subst; use rustc::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_infer::traits; +use rustc_session::CrateDisambiguator; use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_trait_selection::traits; fn sized_constraint_for_ty<'tcx>( tcx: TyCtxt<'tcx>, @@ -210,7 +210,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { } } -fn associated_items<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ty::AssociatedItems { +fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AssociatedItems { let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); tcx.arena.alloc(ty::AssociatedItems::new(items)) } @@ -252,11 +252,8 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // are any errors at that point, so after type checking you can be // sure that this will succeed without errors anyway. - let unnormalized_env = ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - traits::Reveal::UserFacing, - tcx.sess.opts.debugging_opts.chalk.then_some(def_id), - ); + let unnormalized_env = + ty::ParamEnv::new(tcx.intern_predicates(&predicates), traits::Reveal::UserFacing, None); let body_id = tcx.hir().as_local_hir_id(def_id).map_or(hir::DUMMY_HIR_ID, |id| { tcx.hir().maybe_body_owned_by(id).map_or(id, |body| body.hir_id) @@ -276,8 +273,7 @@ fn original_crate_name(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Symbol { } fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { - assert_eq!(crate_num, LOCAL_CRATE); - tcx.hir().crate_hash + tcx.index_hir(crate_num).crate_hash } fn instance_def_size_estimate<'tcx>( diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index 51a9b259c8f..e61a36f844f 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -19,8 +19,10 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_hir = { path = "../librustc_hir" } rustc_target = { path = "../librustc_target" } +rustc_session = { path = "../librustc_session" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } rustc_index = { path = "../librustc_index" } rustc_infer = { path = "../librustc_infer" } +rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_typeck/README.md b/src/librustc_typeck/README.md index fdcbd935524..b61dbd8c964 100644 --- a/src/librustc_typeck/README.md +++ b/src/librustc_typeck/README.md @@ -1,5 +1,5 @@ For high-level intro to how type checking works in rustc, see the -[type checking] chapter of the [rustc guide]. +[type checking] chapter of the [rustc dev guide]. -[type checking]: https://rust-lang.github.io/rustc-guide/type-checking.html -[rustc guide]: https://rust-lang.github.io/rustc-guide/ +[type checking]: https://rustc-dev-guide.rust-lang.org/type-checking.html +[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index bef14d3f4a3..9a8d161572b 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -6,13 +6,10 @@ // ignore-tidy-filelength use crate::collect::PlaceholderHirTyCollector; -use crate::lint; use crate::middle::lang_items::SizedTraitLangItem; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; use crate::util::common::ErrorReported; -use rustc::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; -use rustc::session::{parse::feature_err, Session}; use rustc::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; use rustc::ty::{self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc::ty::{GenericParamDef, GenericParamDefKind}; @@ -26,15 +23,18 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::print; use rustc_hir::{Constness, ExprKind, GenericArg, GenericArgs}; -use rustc_infer::traits; -use rustc_infer::traits::astconv_object_safety_violations; -use rustc_infer::traits::error_reporting::report_object_safety_error; -use rustc_infer::traits::wf::object_region_bounds; +use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, LATE_BOUND_LIFETIME_ARGUMENTS}; +use rustc_session::parse::feature_err; +use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use rustc_target::spec::abi; -use smallvec::SmallVec; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::astconv_object_safety_violations; +use rustc_trait_selection::traits::error_reporting::report_object_safety_error; +use rustc_trait_selection::traits::wf::object_region_bounds; +use smallvec::SmallVec; use std::collections::BTreeSet; use std::iter; use std::slice; @@ -340,7 +340,7 @@ fn check_generic_arg_count( let mut multispan = MultiSpan::from_span(span); multispan.push_span_label(span_late, note.to_string()); tcx.struct_span_lint_hir( - lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, + LATE_BOUND_LIFETIME_ARGUMENTS, args.args[0].id(), multispan, |lint| lint.build(msg).emit(), @@ -661,7 +661,7 @@ pub fn create_substs_for_generic_args<'b>( /// Given the type/lifetime/const arguments provided to some path (along with /// an implicit `Self`, if this is a trait reference), returns the complete /// set of substitutions. This may involve applying defaulted type parameters. - /// Also returns back constriants on associated types. + /// Also returns back constraints on associated types. /// /// Example: /// @@ -1441,12 +1441,14 @@ fn add_predicates_for_ast_type_binding( let (assoc_ident, def_scope) = tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); - // We have already adjusted the item name above, so compare with `ident.modern()` instead + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. let assoc_ty = tcx .associated_items(candidate.def_id()) .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| i.kind == ty::AssocKind::Type && i.ident.modern() == assoc_ident) + .find(|i| { + i.kind == ty::AssocKind::Type && i.ident.normalize_to_macros_2_0() == assoc_ident + }) .expect("missing associated type"); if !assoc_ty.vis.is_accessible_from(def_scope, tcx) { @@ -1652,7 +1654,7 @@ fn conv_object_ty_poly_trait_ref( } for (projection_bound, _) in &bounds.projection_bounds { - for (_, def_ids) in &mut associated_types { + for def_ids in associated_types.values_mut() { def_ids.remove(&projection_bound.projection_def_id()); } } @@ -2298,12 +2300,15 @@ pub fn associated_path_to_ty( let (assoc_ident, def_scope) = tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id); - // We have already adjusted the item name above, so compare with `ident.modern()` instead + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. let item = tcx .associated_items(trait_did) .in_definition_order() - .find(|i| i.kind.namespace() == Namespace::TypeNS && i.ident.modern() == assoc_ident) + .find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident.normalize_to_macros_2_0() == assoc_ident + }) .expect("missing associated type"); let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); @@ -2588,7 +2593,7 @@ pub fn def_ids_for_value_path_segments( } // Case 4. Reference to a method or associated const. - DefKind::Method | DefKind::AssocConst => { + DefKind::AssocFn | DefKind::AssocConst => { if segments.len() >= 2 { let generics = tcx.generics_of(def_id); path_segs.push(PathSeg(generics.parent.unwrap(), last - 1)); @@ -2924,7 +2929,7 @@ pub fn ty_of_fn( let tcx = self.tcx(); - // We proactively collect all the infered type params to emit a single error per fn def. + // We proactively collect all the inferred type params to emit a single error per fn def. let mut visitor = PlaceholderHirTyCollector::default(); for ty in decl.inputs { visitor.visit_ty(ty); diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 2c71fec6809..20737b44e7c 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -4,9 +4,9 @@ use rustc_hir as hir; use rustc_hir::ExprKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::ObligationCauseCode; -use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause}; use rustc_span::Span; +use rustc_trait_selection::traits::ObligationCauseCode; +use rustc_trait_selection::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_match( diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 67bfb090253..2315b42aec5 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -1,17 +1,17 @@ use super::method::MethodCallee; use super::{FnCtxt, Needs, PlaceOp}; -use rustc::session::DiagnosticMessageId; use rustc::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; use rustc::ty::{ToPredicate, TypeFoldable}; +use rustc_ast::ast::Ident; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_infer::infer::{InferCtxt, InferOk}; -use rustc_infer::traits::{self, TraitEngine}; - -use rustc_ast::ast::Ident; +use rustc_session::DiagnosticMessageId; use rustc_span::Span; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{self, TraitEngine}; use std::iter; diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index ff100c261f1..2875d38a996 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -31,11 +31,9 @@ use super::FnCtxt; use crate::hir::def_id::DefId; -use crate::lint; use crate::type_error_struct; use crate::util::common::ErrorReported; use rustc::middle::lang_items; -use rustc::session::Session; use rustc::ty::adjustment::AllowTwoPhase; use rustc::ty::cast::{CastKind, CastTy}; use rustc::ty::error::TypeError; @@ -44,9 +42,11 @@ use rustc_ast::ast; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_infer::traits; -use rustc_infer::traits::error_reporting::report_object_safety_error; +use rustc_session::lint; +use rustc_session::Session; use rustc_span::Span; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::error_reporting::report_object_safety_error; /// Reifies a cast check to be checked once we have full type information for /// a function context. diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 816de5dadbc..49b7a997311 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -12,10 +12,11 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; -use rustc_infer::traits::error_reporting::ArgKind; -use rustc_infer::traits::Obligation; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::ArgKind; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::Obligation; use std::cmp; use std::iter; @@ -675,7 +676,7 @@ fn deduce_future_output_from_projection( // The `Future` trait has only one associted item, `Output`, // so check that this is what we see. let output_assoc_item = - self.tcx.associated_items(future_trait).in_definition_order().nth(0).unwrap().def_id; + self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id; if output_assoc_item != predicate.projection_ty.item_def_id { span_bug!( cause_span, diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 3720b74d92e..b0d74651847 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -52,7 +52,6 @@ use crate::astconv::AstConv; use crate::check::{FnCtxt, Needs}; -use rustc::session::parse::feature_err; use rustc::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, }; @@ -66,10 +65,13 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; -use rustc_infer::traits::{self, ObligationCause, ObligationCauseCode}; +use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, Span}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; + use smallvec::{smallvec, SmallVec}; use std::ops::Deref; @@ -111,7 +113,7 @@ fn identity(_: Ty<'_>) -> Vec> { vec![] } -fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec> { +fn simple(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec> { move |target| vec![Adjustment { kind, target }] } diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 8b54b534375..a2832d92d4a 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -1,4 +1,3 @@ -use rustc::hir::map::Map; use rustc::ty::error::{ExpectedFound, TypeError}; use rustc::ty::subst::{InternalSubsts, Subst}; use rustc::ty::util::ExplicitSelf; @@ -10,8 +9,9 @@ use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; -use rustc_infer::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use rustc_span::Span; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use super::{potentially_plural_count, FnCtxt, Inherited}; @@ -402,7 +402,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( let tcx = infcx.tcx; let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id).unwrap(); let (impl_m_output, impl_m_iter) = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { - ImplItemKind::Method(ref impl_m_sig, _) => { + ImplItemKind::Fn(ref impl_m_sig, _) => { (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) } _ => bug!("{:?} is not a method", impl_m), @@ -412,8 +412,8 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( TypeError::Mutability => { if let Some(trait_m_hir_id) = tcx.hir().as_local_hir_id(trait_m.def_id) { let trait_m_iter = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { - TraitItemKind::Method(ref trait_m_sig, _) => trait_m_sig.decl.inputs.iter(), - _ => bug!("{:?} is not a TraitItemKind::Method", trait_m), + TraitItemKind::Fn(ref trait_m_sig, _) => trait_m_sig.decl.inputs.iter(), + _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), }; impl_m_iter @@ -440,10 +440,10 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( if let Some(trait_m_hir_id) = tcx.hir().as_local_hir_id(trait_m.def_id) { let (trait_m_output, trait_m_iter) = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { - TraitItemKind::Method(ref trait_m_sig, _) => { + TraitItemKind::Fn(ref trait_m_sig, _) => { (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter()) } - _ => bug!("{:?} is not a TraitItemKind::Method", trait_m), + _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), }; let impl_iter = impl_sig.inputs().iter(); @@ -708,7 +708,7 @@ fn compare_number_of_method_arguments<'tcx>( let trait_m_hir_id = tcx.hir().as_local_hir_id(trait_m.def_id); let trait_span = if let Some(trait_id) = trait_m_hir_id { match tcx.hir().expect_trait_item(trait_id).kind { - TraitItemKind::Method(ref trait_m_sig, _) => { + TraitItemKind::Fn(ref trait_m_sig, _) => { let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 }; if let Some(arg) = trait_m_sig.decl.inputs.get(pos) { Some(if pos == 0 { @@ -731,7 +731,7 @@ fn compare_number_of_method_arguments<'tcx>( }; let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id).unwrap(); let impl_span = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { - ImplItemKind::Method(ref impl_m_sig, _) => { + ImplItemKind::Fn(ref impl_m_sig, _) => { let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 }; if let Some(arg) = impl_m_sig.decl.inputs.get(pos) { if pos == 0 { @@ -872,7 +872,7 @@ fn compare_synthetic_generics<'tcx>( let impl_m = tcx.hir().as_local_hir_id(impl_m.def_id)?; let impl_m = tcx.hir().impl_item(hir::ImplItemId { hir_id: impl_m }); let input_tys = match impl_m.kind { - hir::ImplItemKind::Method(ref sig, _) => sig.decl.inputs, + hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, _ => unreachable!(), }; struct Visitor(Option, hir::def_id::DefId); @@ -889,10 +889,10 @@ fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { } } } - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; fn nested_visit_map( &mut self, - ) -> intravisit::NestedVisitorMap<'_, Self::Map> + ) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 0c5f3d3e99d..0556c80e4f7 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -1,6 +1,8 @@ use crate::check::FnCtxt; use rustc_infer::infer::InferOk; -use rustc_infer::traits::{self, ObligationCause}; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{self, ObligationCause}; use rustc::ty::adjustment::AllowTwoPhase; use rustc::ty::{self, AssocItem, Ty}; @@ -236,8 +238,7 @@ pub fn get_conversion_methods( // // FIXME? Other potential candidate methods: `as_ref` and // `as_mut`? - .find(|a| a.check_name(sym::rustc_conversion_suggestion)) - .is_some() + .any(|a| a.check_name(sym::rustc_conversion_suggestion)) }); methods @@ -374,7 +375,7 @@ pub fn check_ref( ) -> Option<(Span, &'static str, String)> { let sm = self.sess().source_map(); let sp = expr.span; - if !sm.span_to_filename(sp).is_real() { + if sm.is_imported(sp) { // Ignore if span is from within a macro #41858, #58298. We previously used the macro // call span, but that breaks down when the type error comes from multiple calls down. return None; @@ -524,9 +525,9 @@ pub fn check_ref( { // We have `&T`, check if what was expected was `T`. If so, // we may want to suggest removing a `&`. - if !sm.span_to_filename(expr.span).is_real() { + if sm.is_imported(expr.span) { if let Ok(code) = sm.span_to_snippet(sp) { - if code.chars().next() == Some('&') { + if code.starts_with('&') { return Some(( sp, "consider removing the borrow", @@ -602,7 +603,7 @@ pub fn check_for_cast( // FIXME(estebank): modify once we decide to suggest `as` casts return false; } - if !self.tcx.sess.source_map().span_to_filename(expr.span).is_real() { + if self.tcx.sess.source_map().is_imported(expr.span) { // Ignore if span is from within a macro. return false; } diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index ead7536f8c6..e48ebbbb235 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -9,9 +9,12 @@ use rustc::ty::{self, Predicate, Ty, TyCtxt}; use rustc_errors::struct_span_err; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{InferOk, SuppressRegionErrors, TyCtxtInferExt}; -use rustc_infer::traits::{ObligationCause, TraitEngine, TraitEngineExt}; +use rustc_infer::infer::{InferOk, RegionckMode, TyCtxtInferExt}; +use rustc_infer::traits::TraitEngineExt as _; use rustc_span::Span; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::query::dropck_outlives::AtExt; +use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt}; /// This function confirms that the `Drop` implementation identified by /// `drop_impl_did` is not any more specialized than the type it is @@ -136,7 +139,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( drop_impl_did, ®ion_scope_tree, &outlives_env, - SuppressRegionErrors::default(), + RegionckMode::default(), ); Ok(()) }) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index ff9fec004bb..617c54a738e 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -18,6 +18,7 @@ use crate::util::common::ErrorReported; use rustc::middle::lang_items; +use rustc::mir::interpret::ErrorHandled; use rustc::ty; use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc::ty::Ty; @@ -33,10 +34,10 @@ use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::{self, ObligationCauseCode}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_trait_selection::traits::{self, ObligationCauseCode}; use std::fmt::Display; @@ -404,7 +405,7 @@ fn check_expr_addr_of( let needs = Needs::maybe_mut_place(mutbl); let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs); - let tm = ty::TypeAndMut { ty: ty, mutbl: mutbl }; + let tm = ty::TypeAndMut { ty, mutbl }; match kind { _ if tm.ty.references_error() => self.tcx.types.err, hir::BorrowKind::Raw => { @@ -1039,11 +1040,18 @@ fn check_expr_repeat( }; if element_ty.references_error() { - tcx.types.err - } else if let Ok(count) = count { - tcx.mk_ty(ty::Array(t, count)) - } else { - tcx.types.err + return tcx.types.err; + } + match count { + Ok(count) => tcx.mk_ty(ty::Array(t, count)), + Err(ErrorHandled::TooGeneric) => { + self.tcx.sess.span_err( + tcx.def_span(count_def_id), + "array lengths can't depend on generic parameters", + ); + tcx.types.err + } + Err(ErrorHandled::Reported) => tcx.types.err, } } @@ -1195,7 +1203,7 @@ fn check_expr_struct_fields( .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.modern(), (i, field))) + .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) .collect::>(); let mut seen_fields = FxHashMap::default(); @@ -1461,7 +1469,9 @@ fn check_field( let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); let fields = &base_def.non_enum_variant().fields; - if let Some(index) = fields.iter().position(|f| f.ident.modern() == ident) { + if let Some(index) = + fields.iter().position(|f| f.ident.normalize_to_macros_2_0() == ident) + { let field = &fields[index]; let field_ty = self.field_ty(expr.span, field, substs); // Save the index of all fields regardless of their visibility in case @@ -1621,7 +1631,7 @@ fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field } fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) { - let generics = self.tcx.generics_of(self.body_id.owner_def_id()); + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); let generic_param = generics.type_param(¶m, self.tcx); if let ty::GenericParamDefKind::Type { synthetic: Some(..), .. } = generic_param.kind { return; diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs index 50692e0f104..cdf68256a7a 100644 --- a/src/librustc_typeck/check/generator_interior.rs +++ b/src/librustc_typeck/check/generator_interior.rs @@ -4,7 +4,6 @@ //! types computed here. use super::FnCtxt; -use rustc::hir::map::Map; use rustc::middle::region::{self, YieldData}; use rustc::ty::{self, Ty}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -209,9 +208,9 @@ pub fn resolve_interior<'a, 'tcx>( // librustc/middle/region.rs since `expr_count` is compared against the results // there. impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } @@ -237,7 +236,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { // ZST in a temporary, so skip its type, just in case it // can significantly complicate the generator type. Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::Method, _) + | Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => { // NOTE(eddyb) this assumes a path expression has // no nested expressions to keep track of. diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 3572eda5c13..dd5e5726e83 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -147,9 +147,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ), "rustc_peek" => (1, vec![param(0)], param(0)), "caller_location" => (0, vec![], tcx.caller_location_ty()), - "panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()), - "init" => (1, Vec::new(), param(0)), - "uninit" => (1, Vec::new(), param(0)), + "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => { + (1, Vec::new(), tcx.mk_unit()) + } "forget" => (1, vec![param(0)], tcx.mk_unit()), "transmute" => (2, vec![param(0)], param(1)), "move_val_init" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), @@ -297,14 +297,25 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { "try" => { let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); - let fn_ty = ty::Binder::bind(tcx.mk_fn_sig( + let try_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( iter::once(mut_u8), tcx.mk_unit(), false, hir::Unsafety::Normal, Abi::Rust, )); - (0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32) + let catch_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( + [mut_u8, mut_u8].iter().cloned(), + tcx.mk_unit(), + false, + hir::Unsafety::Normal, + Abi::Rust, + )); + ( + 0, + vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)], + tcx.types.i32, + ) } "va_start" | "va_end" => match mk_va_list_ty(hir::Mutability::Mut) { diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 108affe5a86..48c72567b5c 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -11,8 +11,8 @@ use rustc::ty::{self, GenericParamDefKind, Ty}; use rustc_hir as hir; use rustc_infer::infer::{self, InferOk}; -use rustc_infer::traits; use rustc_span::Span; +use rustc_trait_selection::traits; use std::ops::Deref; diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 9b8d88e94b6..3cf7b65e30f 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -1,6 +1,6 @@ -//! Method lookup: the secret sauce of Rust. See the [rustc guide] for more information. +//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information. //! -//! [rustc guide]: https://rust-lang.github.io/rustc-guide/method-lookup.html +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html mod confirm; pub mod probe; @@ -22,8 +22,9 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; -use rustc_infer::traits; use rustc_span::Span; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use self::probe::{IsSuggestion, ProbeScope}; @@ -459,12 +460,14 @@ pub fn resolve_ufcs( ProbeScope::TraitsInScope, )?; debug!("resolve_ufcs: pick={:?}", pick); - for import_id in pick.import_ids { - let import_def_id = tcx.hir().local_def_id(import_id); - debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id); - Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) - .unwrap() - .insert(import_def_id); + { + let mut tables = self.tables.borrow_mut(); + let used_trait_imports = Lrc::get_mut(&mut tables.used_trait_imports).unwrap(); + for import_id in pick.import_ids { + let import_def_id = tcx.hir().local_def_id(import_id); + debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id); + used_trait_imports.insert(import_def_id); + } } let def_kind = pick.item.def_kind(); diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index a52cabd8894..45b1c7d6ea7 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -8,9 +8,7 @@ use crate::hir::def::DefKind; use crate::hir::def_id::DefId; -use rustc::lint; use rustc::middle::stability; -use rustc::session::config::nightly_options; use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc::ty::GenericParamDefKind; use rustc::ty::{ @@ -28,11 +26,16 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; -use rustc_infer::traits::query::method_autoderef::MethodAutoderefBadTy; -use rustc_infer::traits::query::method_autoderef::{CandidateStep, MethodAutoderefStepsResult}; -use rustc_infer::traits::query::CanonicalTyGoal; -use rustc_infer::traits::{self, ObligationCause}; +use rustc_session::config::nightly_options; +use rustc_session::lint; use rustc_span::{symbol::Symbol, Span, DUMMY_SP}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; +use rustc_trait_selection::traits::query::method_autoderef::{ + CandidateStep, MethodAutoderefStepsResult, +}; +use rustc_trait_selection::traits::query::CanonicalTyGoal; +use rustc_trait_selection::traits::{self, ObligationCause}; use std::cmp::max; use std::iter; use std::mem; @@ -572,7 +575,7 @@ fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) { } fn assemble_inherent_candidates(&mut self) { - let steps = self.steps.clone(); + let steps = Lrc::clone(&self.steps); for step in steps.iter() { self.assemble_probe(&step.self_ty); } @@ -635,87 +638,51 @@ fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'t self.assemble_inherent_impl_for_primitive(lang_def_id); } ty::Slice(_) => { - let lang_def_id = lang_items.slice_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - - let lang_def_id = lang_items.slice_u8_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - - let lang_def_id = lang_items.slice_alloc_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - - let lang_def_id = lang_items.slice_u8_alloc_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::RawPtr(ty::TypeAndMut { ty: _, mutbl: hir::Mutability::Not }) => { - let lang_def_id = lang_items.const_ptr_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::RawPtr(ty::TypeAndMut { ty: _, mutbl: hir::Mutability::Mut }) => { - let lang_def_id = lang_items.mut_ptr_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Int(ast::IntTy::I8) => { - let lang_def_id = lang_items.i8_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Int(ast::IntTy::I16) => { - let lang_def_id = lang_items.i16_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Int(ast::IntTy::I32) => { - let lang_def_id = lang_items.i32_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Int(ast::IntTy::I64) => { - let lang_def_id = lang_items.i64_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Int(ast::IntTy::I128) => { - let lang_def_id = lang_items.i128_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Int(ast::IntTy::Isize) => { - let lang_def_id = lang_items.isize_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Uint(ast::UintTy::U8) => { - let lang_def_id = lang_items.u8_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Uint(ast::UintTy::U16) => { - let lang_def_id = lang_items.u16_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Uint(ast::UintTy::U32) => { - let lang_def_id = lang_items.u32_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - } - ty::Uint(ast::UintTy::U64) => { - let lang_def_id = lang_items.u64_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); + for &lang_def_id in &[ + lang_items.slice_impl(), + lang_items.slice_u8_impl(), + lang_items.slice_alloc_impl(), + lang_items.slice_u8_alloc_impl(), + ] { + self.assemble_inherent_impl_for_primitive(lang_def_id); + } } - ty::Uint(ast::UintTy::U128) => { - let lang_def_id = lang_items.u128_impl(); + ty::RawPtr(ty::TypeAndMut { ty: _, mutbl }) => { + let lang_def_id = match mutbl { + hir::Mutability::Not => lang_items.const_ptr_impl(), + hir::Mutability::Mut => lang_items.mut_ptr_impl(), + }; self.assemble_inherent_impl_for_primitive(lang_def_id); } - ty::Uint(ast::UintTy::Usize) => { - let lang_def_id = lang_items.usize_impl(); + ty::Int(i) => { + let lang_def_id = match i { + ast::IntTy::I8 => lang_items.i8_impl(), + ast::IntTy::I16 => lang_items.i16_impl(), + ast::IntTy::I32 => lang_items.i32_impl(), + ast::IntTy::I64 => lang_items.i64_impl(), + ast::IntTy::I128 => lang_items.i128_impl(), + ast::IntTy::Isize => lang_items.isize_impl(), + }; self.assemble_inherent_impl_for_primitive(lang_def_id); } - ty::Float(ast::FloatTy::F32) => { - let lang_def_id = lang_items.f32_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - - let lang_def_id = lang_items.f32_runtime_impl(); + ty::Uint(i) => { + let lang_def_id = match i { + ast::UintTy::U8 => lang_items.u8_impl(), + ast::UintTy::U16 => lang_items.u16_impl(), + ast::UintTy::U32 => lang_items.u32_impl(), + ast::UintTy::U64 => lang_items.u64_impl(), + ast::UintTy::U128 => lang_items.u128_impl(), + ast::UintTy::Usize => lang_items.usize_impl(), + }; self.assemble_inherent_impl_for_primitive(lang_def_id); } - ty::Float(ast::FloatTy::F64) => { - let lang_def_id = lang_items.f64_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); - - let lang_def_id = lang_items.f64_runtime_impl(); - self.assemble_inherent_impl_for_primitive(lang_def_id); + ty::Float(f) => { + let (lang_def_id1, lang_def_id2) = match f { + ast::FloatTy::F32 => (lang_items.f32_impl(), lang_items.f32_runtime_impl()), + ast::FloatTy::F64 => (lang_items.f64_impl(), lang_items.f64_runtime_impl()), + }; + self.assemble_inherent_impl_for_primitive(lang_def_id1); + self.assemble_inherent_impl_for_primitive(lang_def_id2); } _ => {} } @@ -1040,7 +1007,7 @@ fn pick(mut self) -> PickResult<'tcx> { return r; } - debug!("pick: actual search failed, assemble diagnotics"); + debug!("pick: actual search failed, assemble diagnostics"); let static_candidates = mem::take(&mut self.static_candidates); let private_candidate = self.private_candidate.take(); @@ -1403,6 +1370,7 @@ fn consider_probe( let predicate = trait_ref.without_const().to_predicate(); let obligation = traits::Obligation::new(cause, self.param_env, predicate); if !self.predicate_may_hold(&obligation) { + result = ProbeResult::NoMatch; if self.probe(|_| { match self.select_trait_candidate(trait_ref) { Err(_) => return true, @@ -1413,7 +1381,6 @@ fn consider_probe( // Determine exactly which obligation wasn't met, so // that we can give more context in the error. if !self.predicate_may_hold(&obligation) { - result = ProbeResult::NoMatch; let o = self.resolve_vars_if_possible(obligation); let predicate = self.resolve_vars_if_possible(&predicate); @@ -1431,7 +1398,6 @@ fn consider_probe( _ => { // Some nested subobligation of this predicate // failed. - result = ProbeResult::NoMatch; let predicate = self.resolve_vars_if_possible(&predicate); possibly_unsatisfied_predicates.push((predicate, None)); } @@ -1551,21 +1517,18 @@ fn probe_for_lev_candidate(&mut self) -> Result, MethodErr let method_names = pcx.candidate_method_names(); pcx.allow_similar_names = false; - let applicable_close_candidates: Vec = - method_names - .iter() - .filter_map(|&method_name| { - pcx.reset(); - pcx.method_name = Some(method_name); - pcx.assemble_inherent_candidates(); - pcx.assemble_extension_candidates_for_traits_in_scope(hir::DUMMY_HIR_ID) - .map_or(None, |_| { - pcx.pick_core() - .and_then(|pick| pick.ok()) - .and_then(|pick| Some(pick.item)) - }) - }) - .collect(); + let applicable_close_candidates: Vec = method_names + .iter() + .filter_map(|&method_name| { + pcx.reset(); + pcx.method_name = Some(method_name); + pcx.assemble_inherent_candidates(); + pcx.assemble_extension_candidates_for_traits_in_scope(hir::DUMMY_HIR_ID) + .map_or(None, |_| { + pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) + }) + }) + .collect(); if applicable_close_candidates.is_empty() { Ok(None) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 95faa353e9b..2f0eb5e0670 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -4,7 +4,6 @@ use crate::check::FnCtxt; use crate::middle::lang_items::FnOnceTraitLangItem; use rustc::hir::map as hir_map; -use rustc::hir::map::Map; use rustc::ty::print::with_crate_prefix; use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_ast::ast; @@ -17,9 +16,10 @@ use rustc_hir::intravisit; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::Obligation; use rustc_span::symbol::kw; use rustc_span::{source_map, FileName, Span}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::Obligation; use std::cmp::Ordering; @@ -427,7 +427,7 @@ pub fn report_method_error<'b>( }); if let Some((field, field_ty)) = field_receiver { - let scope = self.tcx.parent_module(self.body_id); + let scope = self.tcx.parent_module(self.body_id).to_def_id(); let is_accessible = field.vis.is_accessible_from(scope, self.tcx); if is_accessible { @@ -547,10 +547,13 @@ macro_rules! report_function { (&self_ty.kind, parent_pred) { if let ty::Adt(def, _) = p.skip_binder().trait_ref.self_ty().kind { - let id = self.tcx.hir().as_local_hir_id(def.did).unwrap(); - let node = self.tcx.hir().get(id); + let node = self + .tcx + .hir() + .as_local_hir_id(def.did) + .map(|id| self.tcx.hir().get(id)); match node { - hir::Node::Item(hir::Item { kind, .. }) => { + Some(hir::Node::Item(hir::Item { kind, .. })) => { if let Some(g) = kind.generics() { let key = match &g.where_clause.predicates[..] { [.., pred] => { @@ -825,7 +828,7 @@ fn suggest_use_candidates( candidates: Vec, ) { let module_did = self.tcx.parent_module(self.body_id); - let module_id = self.tcx.hir().as_local_hir_id(module_did).unwrap(); + let module_id = self.tcx.hir().as_local_hir_id(module_did.to_def_id()).unwrap(); let krate = self.tcx.hir().krate(); let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id); if let Some(span) = span { @@ -930,15 +933,15 @@ fn suggest_traits_to_import<'b>( if let ty::AssocKind::Method = item.kind { let id = self.tcx.hir().as_local_hir_id(item.def_id); if let Some(hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Method(fn_sig, method), + kind: hir::TraitItemKind::Fn(fn_sig, method), .. })) = id.map(|id| self.tcx.hir().get(id)) { let self_first_arg = match method { - hir::TraitMethod::Required([ident, ..]) => { + hir::TraitFn::Required([ident, ..]) => { ident.name == kw::SelfLower } - hir::TraitMethod::Provided(body_id) => { + hir::TraitFn::Provided(body_id) => { match &self.tcx.hir().body(*body_id).params[..] { [hir::Param { pat: @@ -1343,9 +1346,9 @@ fn visit_mod(&mut self, module: &'tcx hir::Mod<'tcx>, _: Span, hir_id: hir::HirI } } - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4c214bd15e8..368f64e4d41 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -90,10 +90,8 @@ use crate::astconv::{AstConv, GenericArgCountMismatch, PathSeg}; use crate::middle::lang_items; use rustc::hir::map::blocks::FnLikeNode; -use rustc::hir::map::Map; use rustc::middle::region; use rustc::mir::interpret::ConstValue; -use rustc::session::parse::feature_err; use rustc::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, }; @@ -121,17 +119,26 @@ use rustc_index::vec::Idx; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::opaque_types::OpaqueTypeDecl; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_infer::infer::{self, InferCtxt, InferOk, InferResult, TyCtxtInferExt}; -use rustc_infer::traits::error_reporting::recursive_type_with_infinite_size_error; -use rustc_infer::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine}; +use rustc_session::config::{self, EntryFnType}; +use rustc_session::lint; +use rustc_session::parse::feature_err; +use rustc_session::Session; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{self, BytePos, MultiSpan, Span}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl}; +use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{ + self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, +}; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::cmp; @@ -141,10 +148,7 @@ use std::ops::{self, Deref}; use std::slice; -use crate::lint; use crate::require_c_abi_if_c_variadic; -use crate::session::config::EntryFnType; -use crate::session::Session; use crate::util::common::{indenter, ErrorReported}; use crate::TypeAndSubsts; @@ -457,7 +461,7 @@ pub enum Diverges { /// where all arms diverge), we may be /// able to provide a more informative /// message to the user. - /// If this is `None`, a default messsage + /// If this is `None`, a default message /// will be generated, which is suitable /// for most cases. custom_note: Option<&'static str>, @@ -634,9 +638,8 @@ pub struct InheritedBuilder<'tcx> { impl Inherited<'_, 'tcx> { pub fn build(tcx: TyCtxt<'tcx>, def_id: DefId) -> InheritedBuilder<'tcx> { - let hir_id_root = if def_id.is_local() { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - DefId::local(hir_id.owner) + let hir_id_root = if let Some(def_id) = def_id.as_local() { + tcx.hir().local_def_id_to_hir_id(def_id).owner.to_def_id() } else { def_id }; @@ -737,8 +740,8 @@ fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} } pub fn check_wf_new(tcx: TyCtxt<'_>) { - let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); - tcx.hir().krate().par_visit_all_item_likes(&mut visit); + let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); + tcx.hir().krate().par_visit_all_item_likes(&visit); } fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: DefId) { @@ -811,14 +814,14 @@ fn primary_body_of( }, Node::TraitItem(item) => match item.kind { hir::TraitItemKind::Const(ref ty, Some(body)) => Some((body, Some(ty), None, None)), - hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) => { + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { Some((body, None, Some(&sig.header), Some(&sig.decl))) } _ => None, }, Node::ImplItem(item) => match item.kind { hir::ImplItemKind::Const(ref ty, body) => Some((body, Some(ty), None, None)), - hir::ImplItemKind::Method(ref sig, body) => { + hir::ImplItemKind::Fn(ref sig, body) => { Some((body, None, Some(&sig.header), Some(&sig.decl))) } _ => None, @@ -896,7 +899,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { ty::Opaque(def_id, substs) => { debug!("fixup_opaque_types: found type {:?}", ty); // Here, we replace any inference variables that occur within - // the substs of an opaque type. By definition, any type occuring + // the substs of an opaque type. By definition, any type occurring // in the substs has a corresponding generic parameter, which is what // we replace it with. // This replacement is only run on the function signature, so any @@ -1124,7 +1127,7 @@ fn typeck_tables_of_with_fallback<'tcx>( // Consistency check our TypeckTables instance can hold all ItemLocalIds // it will need to hold. - assert_eq!(tables.local_id_root, Some(DefId::local(id.owner))); + assert_eq!(tables.local_id_root, Some(id.owner.to_def_id())); tables } @@ -1172,9 +1175,9 @@ fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option>) } impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } @@ -1733,7 +1736,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { for item in items.iter() { let item = tcx.hir().trait_item(item.id); - if let hir::TraitItemKind::Method(sig, _) = &item.kind { + if let hir::TraitItemKind::Fn(sig, _) = &item.kind { let abi = sig.header.abi; fn_maybe_err(tcx, item.ident.span, abi); } @@ -1891,13 +1894,16 @@ fn check_specialization_validity<'tcx>( ) { let kind = match impl_item.kind { hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Method(..) => ty::AssocKind::Method, + hir::ImplItemKind::Fn(..) => ty::AssocKind::Method, hir::ImplItemKind::OpaqueTy(..) => ty::AssocKind::OpaqueTy, hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, }; - let mut ancestor_impls = trait_def - .ancestors(tcx, impl_id) + let ancestors = match trait_def.ancestors(tcx, impl_id) { + Ok(ancestors) => ancestors, + Err(_) => return, + }; + let mut ancestor_impls = ancestors .skip(1) .filter_map(|parent| { if parent.is_from_trait() { @@ -1937,7 +1943,7 @@ fn check_specialization_validity<'tcx>( } }); - // If `opt_result` is `None`, we have only encoutered `default impl`s that don't contain the + // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the // item. This is allowed, the item isn't actually getting specialized here. let result = opt_result.unwrap_or(Ok(())); @@ -2014,7 +2020,7 @@ fn check_impl_items_against_trait<'tcx>( err.emit() } } - hir::ImplItemKind::Method(..) => { + hir::ImplItemKind::Fn(..) => { let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); if ty_trait_item.kind == ty::AssocKind::Method { compare_impl_method( @@ -2078,16 +2084,17 @@ fn check_impl_items_against_trait<'tcx>( // Check for missing items from trait let mut missing_items = Vec::new(); - for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { - let is_implemented = trait_def - .ancestors(tcx, impl_id) - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.node.is_from_trait()) - .unwrap_or(false); - - if !is_implemented && !traits::impl_is_default(tcx, impl_id) { - if !trait_item.defaultness.has_value() { - missing_items.push(*trait_item); + if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id) { + for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { + let is_implemented = ancestors + .leaf_def(tcx, trait_item.ident, trait_item.kind) + .map(|node_item| !node_item.node.is_from_trait()) + .unwrap_or(false); + + if !is_implemented && !traits::impl_is_default(tcx, impl_id) { + if !trait_item.defaultness.has_value() { + missing_items.push(*trait_item); + } } } } @@ -2900,14 +2907,14 @@ fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { debug!("resolve_vars_with_obligations(ty={:?})", ty); // No Infer()? Nothing needs doing. - if !ty.has_infer_types() && !ty.has_infer_consts() { + if !ty.has_infer_types_or_consts() { debug!("resolve_vars_with_obligations: ty={:?}", ty); return ty; } // If `ty` is a type variable, see whether we already know what it is. ty = self.resolve_vars_if_possible(&ty); - if !ty.has_infer_types() && !ty.has_infer_consts() { + if !ty.has_infer_types_or_consts() { debug!("resolve_vars_with_obligations: ty={:?}", ty); return ty; } @@ -2976,7 +2983,7 @@ fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), Error pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method); - self.write_resolution(hir_id, Ok((DefKind::Method, method.def_id))); + self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); self.write_substs(hir_id, method.substs); // When the method is confirmed, the `method.substs` includes @@ -3452,7 +3459,7 @@ fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { // } // ``` // - // In the above snippet, the inference varaible created by + // In the above snippet, the inference variable created by // instantiating `Option` will be completely unconstrained. // We treat this as a non-defining use by making the inference // variable fall back to the opaque type itself. @@ -4234,7 +4241,7 @@ pub fn impl_self_ty( let substs = self.fresh_substs_for_item(span, did); let substd_ty = self.instantiate_type_scheme(span, &substs, &ity); - TypeAndSubsts { substs: substs, ty: substd_ty } + TypeAndSubsts { substs, ty: substd_ty } } /// Unifies the output type with the expected type early, for more coercions @@ -4733,9 +4740,7 @@ fn parent_item_span(&self, id: hir::HirId) -> Option { let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id)); match node { Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) - | Node::ImplItem(&hir::ImplItem { - kind: hir::ImplItemKind::Method(_, body_id), .. - }) => { + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { let body = self.tcx.hir().body(body_id); if let ExprKind::Block(block, _) = &body.value.kind { return Some(block.span); @@ -4769,12 +4774,12 @@ fn get_node_fn_decl( } Node::TraitItem(&hir::TraitItem { ident, - kind: hir::TraitItemKind::Method(ref sig, ..), + kind: hir::TraitItemKind::Fn(ref sig, ..), .. }) => Some((&sig.decl, ident, true)), Node::ImplItem(&hir::ImplItem { ident, - kind: hir::ImplItemKind::Method(ref sig, ..), + kind: hir::ImplItemKind::Fn(ref sig, ..), .. }) => Some((&sig.decl, ident, false)), _ => None, @@ -4859,11 +4864,11 @@ fn suggest_fn_call( match hir.get_if_local(def_id) { Some(Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })) | Some(Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Method(_, body_id), + kind: hir::ImplItemKind::Fn(_, body_id), .. })) | Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Method(.., hir::TraitMethod::Provided(body_id)), + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), .. })) => { let body = hir.body(*body_id); @@ -4934,7 +4939,7 @@ fn suggest_fn_call( .join(", ") } Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Method(.., hir::TraitMethod::Required(idents)), + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), .. })) => { sugg_call = idents @@ -5244,7 +5249,7 @@ fn suggest_missing_await( .tcx .associated_items(future_trait) .in_definition_order() - .nth(0) + .next() .unwrap() .def_id; let predicate = @@ -5364,7 +5369,7 @@ pub fn instantiate_value_path( is_alias_variant_ctor = true; } } - Res::Def(DefKind::Method, def_id) | Res::Def(DefKind::AssocConst, def_id) => { + Res::Def(DefKind::AssocFn, def_id) | Res::Def(DefKind::AssocConst, def_id) => { let container = tcx.associated_item(def_id).container; debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); match container { @@ -5783,7 +5788,7 @@ fn fatally_break_rust(sess: &Session) { handler.note_without_error(&format!( "rustc {} running on {}", option_env!("CFG_VERSION").unwrap_or("unknown_version"), - crate::session::config::host_triple(), + config::host_triple(), )); } diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index bf3511a2614..f589805e1e2 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -10,6 +10,7 @@ use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_span::Span; +use rustc_trait_selection::infer::InferCtxtExt; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` @@ -860,7 +861,7 @@ enum Op { } /// Dereferences a single level of immutable referencing. -fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> { +fn deref_ty_if_possible(ty: Ty<'tcx>) -> Ty<'tcx> { match ty.kind { ty::Ref(_, ty, hir::Mutability::Not) => ty, _ => ty, diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index b7aac707a98..0f3884de84e 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -11,9 +11,9 @@ use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::{ObligationCause, Pattern}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{Span, Spanned}; +use rustc_trait_selection::traits::{ObligationCause, Pattern}; use std::cmp; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -63,6 +63,22 @@ struct TopInfo<'tcx> { /// found type `std::result::Result<_, _>` /// ``` span: Option, + /// This refers to the parent pattern. Used to provide extra diagnostic information on errors. + /// ```text + /// error[E0308]: mismatched types + /// --> $DIR/const-in-struct-pat.rs:8:17 + /// | + /// L | struct f; + /// | --------- unit struct defined here + /// ... + /// L | let Thing { f } = t; + /// | ^ + /// | | + /// | expected struct `std::string::String`, found struct `f` + /// | `f` is interpreted as a unit struct, not a new binding + /// | help: bind the struct field to a different name instead: `f: other_f` + /// ``` + parent_pat: Option<&'tcx Pat<'tcx>>, } impl<'tcx> FnCtxt<'_, 'tcx> { @@ -98,7 +114,7 @@ fn demand_eqtype_pat( enum AdjustMode { /// Peel off all immediate reference types. Peel, - /// Reset binding mode to the inital mode. + /// Reset binding mode to the initial mode. Reset, /// Pass on the input binding mode and expected type. Pass, @@ -120,7 +136,8 @@ pub fn check_pat_top( span: Option, origin_expr: bool, ) { - self.check_pat(pat, expected, INITIAL_BM, TopInfo { expected, origin_expr, span }); + let info = TopInfo { expected, origin_expr, span, parent_pat: None }; + self.check_pat(pat, expected, INITIAL_BM, info); } /// Type check the given `pat` against the `expected` type @@ -161,8 +178,9 @@ fn check_pat( self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti) } PatKind::Or(pats) => { + let parent_pat = Some(pat); for pat in pats { - self.check_pat(pat, expected, def_bm, ti); + self.check_pat(pat, expected, def_bm, TopInfo { parent_pat, ..ti }); } expected } @@ -501,7 +519,7 @@ fn emit_err_pat_range( fn check_pat_ident( &self, - pat: &Pat<'_>, + pat: &'tcx Pat<'tcx>, ba: hir::BindingAnnotation, var_id: HirId, sub: Option<&'tcx Pat<'tcx>>, @@ -546,7 +564,7 @@ fn check_pat_ident( } if let Some(p) = sub { - self.check_pat(&p, expected, def_bm, ti); + self.check_pat(&p, expected, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); } local_ty @@ -559,8 +577,16 @@ fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: T let var_ty = self.resolve_vars_with_obligations(var_ty); let msg = format!("first introduced with type `{}` here", var_ty); err.span_label(hir.span(var_id), msg); - let in_arm = hir.parent_iter(var_id).any(|(_, n)| matches!(n, hir::Node::Arm(..))); - let pre = if in_arm { "in the same arm, " } else { "" }; + let in_match = hir.parent_iter(var_id).any(|(_, n)| { + matches!( + n, + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Match(.., hir::MatchSource::Normal), + .. + }) + ) + }); + let pre = if in_match { "in the same arm, " } else { "" }; err.note(&format!("{}a binding must have the same type in all alternatives", pre)); err.emit(); } @@ -647,6 +673,7 @@ fn check_pat_struct( variant_ty } else { for field in fields { + let ti = TopInfo { parent_pat: Some(&pat), ..ti }; self.check_pat(&field.pat, self.tcx.types.err, def_bm, ti); } return self.tcx.types.err; @@ -656,9 +683,7 @@ fn check_pat_struct( self.demand_eqtype_pat(pat.span, expected, pat_ty, ti); // Type-check subpatterns. - if self - .check_struct_pat_fields(pat_ty, pat.hir_id, pat.span, variant, fields, etc, def_bm, ti) - { + if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, etc, def_bm, ti) { pat_ty } else { self.tcx.types.err @@ -682,7 +707,7 @@ fn check_pat_path( self.set_tainted_by_errors(); return tcx.types.err; } - Res::Def(DefKind::Method, _) + Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => { report_unexpected_variant_res(tcx, res, pat.span, qpath); @@ -691,23 +716,62 @@ fn check_pat_path( Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..) | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) => {} // OK + | Res::Def(DefKind::AssocConst, _) + | Res::Def(DefKind::ConstParam, _) => {} // OK _ => bug!("unexpected pattern resolution: {:?}", res), } // Type-check the path. - let pat_ty = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id).0; - if let Some(mut err) = + let (pat_ty, pat_res) = + self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); + if let Some(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) { - err.emit(); + self.emit_bad_pat_path(err, pat.span, res, pat_res, segments, ti.parent_pat); } pat_ty } + fn emit_bad_pat_path( + &self, + mut e: DiagnosticBuilder<'_>, + pat_span: Span, + res: Res, + pat_res: Res, + segments: &'b [hir::PathSegment<'b>], + parent_pat: Option<&Pat<'_>>, + ) { + if let Some(span) = self.tcx.hir().res_span(pat_res) { + e.span_label(span, &format!("{} defined here", res.descr())); + if let [hir::PathSegment { ident, .. }] = &*segments { + e.span_label( + pat_span, + &format!( + "`{}` is interpreted as {} {}, not a new binding", + ident, + res.article(), + res.descr(), + ), + ); + let (msg, sugg) = match parent_pat { + Some(Pat { kind: hir::PatKind::Struct(..), .. }) => ( + "bind the struct field to a different name instead", + format!("{}: other_{}", ident, ident.as_str().to_lowercase()), + ), + _ => ( + "introduce a new binding instead", + format!("other_{}", ident.as_str().to_lowercase()), + ), + }; + e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders); + } + } + e.emit(); + } + fn check_pat_tuple_struct( &self, - pat: &Pat<'_>, + pat: &'tcx Pat<'tcx>, qpath: &hir::QPath<'_>, subpats: &'tcx [&'tcx Pat<'tcx>], ddpos: Option, @@ -717,8 +781,9 @@ fn check_pat_tuple_struct( ) -> Ty<'tcx> { let tcx = self.tcx; let on_error = || { + let parent_pat = Some(pat); for pat in subpats { - self.check_pat(&pat, tcx.types.err, def_bm, ti); + self.check_pat(&pat, tcx.types.err, def_bm, TopInfo { parent_pat, ..ti }); } }; let report_unexpected_res = |res: Res| { @@ -729,7 +794,7 @@ fn check_pat_tuple_struct( ); let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{}", msg); match (res, &pat.kind) { - (Res::Def(DefKind::Fn, _), _) | (Res::Def(DefKind::Method, _), _) => { + (Res::Def(DefKind::Fn, _), _) | (Res::Def(DefKind::AssocFn, _), _) => { err.span_label(pat.span, "`fn` calls are not allowed in patterns"); err.help( "for more information, visit \ @@ -766,7 +831,7 @@ fn check_pat_tuple_struct( on_error(); return tcx.types.err; } - Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::Method, _) => { + Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::AssocFn, _) => { report_unexpected_res(res); return tcx.types.err; } @@ -793,7 +858,7 @@ fn check_pat_tuple_struct( }; for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); - self.check_pat(&subpat, field_ty, def_bm, ti); + self.check_pat(&subpat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span); } @@ -841,7 +906,7 @@ fn e0023( // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`. let missing_parenthesis = match (&expected.kind, fields, had_err) { - // #67037: only do this if we could sucessfully type-check the expected type against + // #67037: only do this if we could successfully type-check the expected type against // the tuple struct pattern. Otherwise the substs could get out of range on e.g., // `let P() = U;` where `P != U` with `struct P(T);`. (ty::Adt(_, substs), [field], false) => { @@ -938,8 +1003,7 @@ fn check_pat_tuple( fn check_struct_pat_fields( &self, adt_ty: Ty<'tcx>, - pat_id: HirId, - span: Span, + pat: &'tcx Pat<'tcx>, variant: &'tcx ty::VariantDef, fields: &'tcx [hir::FieldPat<'tcx>], etc: bool, @@ -950,7 +1014,7 @@ fn check_struct_pat_fields( let (substs, adt) = match adt_ty.kind { ty::Adt(adt, substs) => (substs, adt), - _ => span_bug!(span, "struct pattern is not an ADT"), + _ => span_bug!(pat.span, "struct pattern is not an ADT"), }; let kind_name = adt.variant_descr(); @@ -959,7 +1023,7 @@ fn check_struct_pat_fields( .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.modern(), (i, field))) + .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) .collect::>(); // Keep track of which fields have already appeared in the pattern. @@ -983,7 +1047,7 @@ fn check_struct_pat_fields( .get(&ident) .map(|(i, f)| { self.write_field_index(field.hir_id, *i); - self.tcx.check_stability(f.did, Some(pat_id), span); + self.tcx.check_stability(f.did, Some(pat.hir_id), span); self.field_ty(span, f, substs) }) .unwrap_or_else(|| { @@ -994,13 +1058,13 @@ fn check_struct_pat_fields( } }; - self.check_pat(&field.pat, field_ty, def_bm, ti); + self.check_pat(&field.pat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); } let mut unmentioned_fields = variant .fields .iter() - .map(|field| field.ident.modern()) + .map(|field| field.ident.normalize_to_macros_2_0()) .filter(|ident| !used_fields.contains_key(&ident)) .collect::>(); @@ -1017,7 +1081,7 @@ fn check_struct_pat_fields( if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc { struct_span_err!( tcx.sess, - span, + pat.span, E0638, "`..` required with {} marked as non-exhaustive", kind_name @@ -1029,14 +1093,14 @@ fn check_struct_pat_fields( if kind_name == "union" { if fields.len() != 1 { tcx.sess - .struct_span_err(span, "union patterns should have exactly one field") + .struct_span_err(pat.span, "union patterns should have exactly one field") .emit(); } if etc { - tcx.sess.struct_span_err(span, "`..` cannot be used in union patterns").emit(); + tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); } } else if !etc && !unmentioned_fields.is_empty() { - self.error_unmentioned_fields(span, &unmentioned_fields, variant); + self.error_unmentioned_fields(pat.span, &unmentioned_fields, variant); } no_field_errors } @@ -1196,7 +1260,7 @@ fn check_pat_box( fn check_pat_ref( &self, - pat: &Pat<'_>, + pat: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>, mutbl: hir::Mutability, expected: Ty<'tcx>, @@ -1236,7 +1300,7 @@ fn check_pat_ref( } else { (tcx.types.err, tcx.types.err) }; - self.check_pat(&inner, inner_ty, def_bm, ti); + self.check_pat(&inner, inner_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); rptr_ty } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index c0e33637fd0..57a89614eb1 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -76,7 +76,6 @@ use crate::check::FnCtxt; use crate::mem_categorization as mc; use crate::middle::region; -use rustc::hir::map::Map; use rustc::ty::adjustment; use rustc::ty::subst::{GenericArgKind, SubstsRef}; use rustc::ty::{self, Ty}; @@ -85,8 +84,10 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{self, RegionObligation, SuppressRegionErrors}; +use rustc_infer::infer::{self, RegionObligation, RegionckMode}; use rustc_span::Span; +use rustc_trait_selection::infer::OutlivesEnvironmentExt; +use rustc_trait_selection::opaque_types::InferCtxtExt; use std::mem; use std::ops::Deref; @@ -122,10 +123,7 @@ pub fn regionck_expr(&self, body: &'tcx hir::Body<'tcx>) { rcx.visit_body(body); rcx.visit_region_obligations(id); } - rcx.resolve_regions_and_report_errors(SuppressRegionErrors::when_nll_is_enabled(self.tcx)); - - assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); + rcx.resolve_regions_and_report_errors(RegionckMode::for_item_body(self.tcx)); } /// Region checking during the WF phase for items. `wf_tys` are the @@ -143,7 +141,7 @@ pub fn regionck_item(&self, item_id: hir::HirId, span: Span, wf_tys: &[Ty<'tcx>] rcx.outlives_environment.add_implied_bounds(self, wf_tys, item_id, span); rcx.outlives_environment.save_implied_bounds(item_id); rcx.visit_region_obligations(item_id); - rcx.resolve_regions_and_report_errors(SuppressRegionErrors::default()); + rcx.resolve_regions_and_report_errors(RegionckMode::default()); } /// Region check a function body. Not invoked on closures, but @@ -166,13 +164,7 @@ pub fn regionck_fn(&self, fn_id: hir::HirId, body: &'tcx hir::Body<'tcx>) { rcx.visit_fn_body(fn_id, body, self.tcx.hir().span(fn_id)); } - rcx.resolve_regions_and_report_errors(SuppressRegionErrors::when_nll_is_enabled(self.tcx)); - - // In this mode, we also copy the free-region-map into the - // tables of the enclosing fcx. In the other regionck modes - // (e.g., `regionck_item`), we don't have an enclosing tables. - assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); + rcx.resolve_regions_and_report_errors(RegionckMode::for_item_body(self.tcx)); } } @@ -353,7 +345,7 @@ fn visit_region_obligations(&mut self, hir_id: hir::HirId) { self.select_all_obligations_or_error(); } - fn resolve_regions_and_report_errors(&self, suppress: SuppressRegionErrors) { + fn resolve_regions_and_report_errors(&self, mode: RegionckMode) { self.infcx.process_registered_region_obligations( self.outlives_environment.region_bound_pairs_map(), self.implicit_region_bound, @@ -364,7 +356,7 @@ fn resolve_regions_and_report_errors(&self, suppress: SuppressRegionErrors) { self.subject_def_id, &self.region_scope_tree, &self.outlives_environment, - suppress, + mode, ); } @@ -415,9 +407,9 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { // hierarchy, and in particular the relationships between free // regions, until regionck, as described in #3238. - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index b5ed6335dc0..1b5f151870c 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -35,7 +35,6 @@ use crate::expr_use_visitor as euv; use crate::mem_categorization as mc; use crate::mem_categorization::PlaceBase; -use rustc::hir::map::Map; use rustc::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_ast::ast; use rustc_data_structures::fx::FxIndexMap; @@ -60,9 +59,9 @@ struct InferBorrowKindVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } @@ -119,7 +118,7 @@ fn analyze_closure( for (&var_hir_id, _) in upvars.iter() { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: LocalDefId::from_def_id(closure_def_id), + closure_expr_id: closure_def_id.expect_local(), }; debug!("seed upvar_id {:?}", upvar_id); // Adding the upvar Id to the list of Upvars, which will be added @@ -229,7 +228,7 @@ fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec> { let upvar_ty = self.node_ty(var_hir_id); let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: LocalDefId::from_def_id(closure_def_id), + closure_expr_id: closure_def_id.expect_local(), }; let capture = self.tables.borrow().upvar_capture(upvar_id); diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index e8e34a4e8f0..3255d7b435c 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -2,23 +2,24 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; use rustc::middle::lang_items; -use rustc::session::parse::feature_err; use rustc::ty::subst::{InternalSubsts, Subst}; +use rustc::ty::trait_def::TraitSpecializationKind; use rustc::ty::{ self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, }; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::itemlikevisit::ParItemLikeVisitor; use rustc_hir::ItemKind; -use rustc_infer::infer::opaque_types::may_define_opaque_type; -use rustc_infer::traits::{self, ObligationCause, ObligationCauseCode}; +use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::Span; - -use rustc_hir as hir; -use rustc_hir::itemlikevisit::ParItemLikeVisitor; +use rustc_trait_selection::opaque_types::may_define_opaque_type; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; /// Helper type of a temporary returned by `.for_item(...)`. /// This is necessary because we can't write the following bound: @@ -173,7 +174,7 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) { let trait_item = tcx.hir().expect_trait_item(hir_id); let method_sig = match trait_item.kind { - hir::TraitItemKind::Method(ref sig, _) => Some(sig), + hir::TraitItemKind::Fn(ref sig, _) => Some(sig), _ => None, }; check_object_unsafe_self_trait_by_name(tcx, &trait_item); @@ -207,7 +208,7 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem { trait_should_be_self.push(ty.span) } - hir::TraitItemKind::Method(sig, _) => { + hir::TraitItemKind::Fn(sig, _) => { for ty in sig.decl.inputs { if could_be_self(trait_def_id, ty) { trait_should_be_self.push(ty.span); @@ -247,7 +248,7 @@ pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) { let impl_item = tcx.hir().expect_impl_item(hir_id); let method_sig = match impl_item.kind { - hir::ImplItemKind::Method(ref sig, _) => Some(sig), + hir::ImplItemKind::Fn(ref sig, _) => Some(sig), _ => None, }; @@ -411,7 +412,9 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { let trait_def_id = tcx.hir().local_def_id(item.hir_id); let trait_def = tcx.trait_def(trait_def_id); - if trait_def.is_marker { + if trait_def.is_marker + || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) + { for associated_def_id in &*tcx.associated_item_def_ids(trait_def_id) { struct_span_err!( tcx.sess, diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 380e256c9fc..fd92284effb 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -4,18 +4,18 @@ use crate::check::FnCtxt; -use rustc::hir::map::Map; use rustc::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc::ty::fold::{TypeFoldable, TypeFolder}; use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, DefIdSet, DefIndex}; +use rustc_hir::def_id::DefIdSet; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::InferCtxt; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_trait_selection::opaque_types::InferCtxtExt; use std::mem; @@ -61,7 +61,6 @@ pub fn resolve_type_vars_in_body( wbcx.visit_fru_field_types(); wbcx.visit_opaque_types(body.value.span); wbcx.visit_coercion_casts(); - wbcx.visit_free_region_map(); wbcx.visit_user_provided_tys(); wbcx.visit_user_provided_sigs(); wbcx.visit_generator_interior_types(); @@ -108,11 +107,11 @@ fn new( body: &'tcx hir::Body<'tcx>, rustc_dump_user_substs: bool, ) -> WritebackCx<'cx, 'tcx> { - let owner = body.id().hir_id; + let owner = body.id().hir_id.owner; WritebackCx { fcx, - tables: ty::TypeckTables::empty(Some(DefId::local(owner.owner))), + tables: ty::TypeckTables::empty(Some(owner.to_def_id())), body, rustc_dump_user_substs, } @@ -124,7 +123,7 @@ fn tcx(&self) -> TyCtxt<'tcx> { fn write_ty_to_tables(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) { debug!("write_ty_to_tables({:?}, {:?})", hir_id, ty); - assert!(!ty.needs_infer() && !ty.has_placeholders()); + assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions()); self.tables.node_types_mut().insert(hir_id, ty); } @@ -243,9 +242,9 @@ fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { // traffic in node-ids or update tables in the type context etc. impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } @@ -326,9 +325,10 @@ fn visit_upvar_capture_map(&mut self) { let new_upvar_capture = match *upvar_capture { ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue, ty::UpvarCapture::ByRef(ref upvar_borrow) => { - let r = upvar_borrow.region; - let r = self.resolve(&r, &upvar_id.var_path.hir_id); - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: upvar_borrow.kind, region: r }) + ty::UpvarCapture::ByRef(ty::UpvarBorrow { + kind: upvar_borrow.kind, + region: self.tcx().lifetimes.re_erased, + }) } }; debug!("Upvar capture for {:?} resolved to {:?}", upvar_id, new_upvar_capture); @@ -342,7 +342,7 @@ fn visit_closures(&mut self) { let common_local_id_root = fcx_tables.local_id_root.unwrap(); for (&id, &origin) in fcx_tables.closure_kind_origins().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id: id }; + let hir_id = hir::HirId { owner: common_local_id_root.expect_local(), local_id: id }; self.tables.closure_kind_origins_mut().insert(hir_id, origin); } } @@ -357,11 +357,6 @@ fn visit_coercion_casts(&mut self) { } } - fn visit_free_region_map(&mut self) { - self.tables.free_region_map = self.fcx.tables.borrow().free_region_map.clone(); - debug_assert!(!self.tables.free_region_map.elements().any(|r| r.has_local_value())); - } - fn visit_user_provided_tys(&mut self) { let fcx_tables = self.fcx.tables.borrow(); debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); @@ -369,7 +364,7 @@ fn visit_user_provided_tys(&mut self) { let mut errors_buffer = Vec::new(); for (&local_id, c_ty) in fcx_tables.user_provided_types().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id }; + let hir_id = hir::HirId { owner: common_local_id_root.expect_local(), local_id }; if cfg!(debug_assertions) && c_ty.has_local_value() { span_bug!(hir_id.to_span(self.fcx.tcx), "writeback: `{:?}` is a local value", c_ty); @@ -426,8 +421,7 @@ fn visit_generator_interior_types(&mut self) { fn visit_opaque_types(&mut self, span: Span) { for (&def_id, opaque_defn) in self.fcx.opaque_types.borrow().iter() { let hir_id = self.tcx().hir().as_local_hir_id(def_id).unwrap(); - let instantiated_ty = - self.tcx().erase_regions(&self.resolve(&opaque_defn.concrete_ty, &hir_id)); + let instantiated_ty = self.resolve(&opaque_defn.concrete_ty, &hir_id); debug_assert!(!instantiated_ty.has_escaping_bound_vars()); @@ -563,7 +557,7 @@ fn visit_liberated_fn_sigs(&mut self) { let common_local_id_root = fcx_tables.local_id_root.unwrap(); for (&local_id, fn_sig) in fcx_tables.liberated_fn_sigs().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id }; + let hir_id = hir::HirId { owner: common_local_id_root.expect_local(), local_id }; let fn_sig = self.resolve(fn_sig, &hir_id); self.tables.liberated_fn_sigs_mut().insert(hir_id, fn_sig.clone()); } @@ -575,7 +569,7 @@ fn visit_fru_field_types(&mut self) { let common_local_id_root = fcx_tables.local_id_root.unwrap(); for (&local_id, ftys) in fcx_tables.fru_field_types().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id }; + let hir_id = hir::HirId { owner: common_local_id_root.expect_local(), local_id }; let ftys = self.resolve(ftys, &hir_id); self.tables.fru_field_types_mut().insert(hir_id, ftys); } @@ -603,23 +597,14 @@ fn to_span(&self, _: TyCtxt<'_>) -> Span { } } -impl Locatable for DefIndex { - fn to_span(&self, tcx: TyCtxt<'_>) -> Span { - let hir_id = tcx.hir().def_index_to_hir_id(*self); - tcx.hir().span(hir_id) - } -} - impl Locatable for hir::HirId { fn to_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.hir().span(*self) } } -/////////////////////////////////////////////////////////////////////////// -// The Resolver. This is the type folding engine that detects -// unresolved types and so forth. - +/// The Resolver. This is the type folding engine that detects +/// unresolved types and so forth. struct Resolver<'cx, 'tcx> { tcx: TyCtxt<'tcx>, infcx: &'cx InferCtxt<'cx, 'tcx>, @@ -652,7 +637,7 @@ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match self.infcx.fully_resolve(&t) { - Ok(t) => t, + Ok(t) => self.infcx.tcx.erase_regions(&t), Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); self.report_error(t); @@ -661,15 +646,14 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { } } - // FIXME This should be carefully checked - // We could use `self.report_error` but it doesn't accept a ty::Region, right now. fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - self.infcx.fully_resolve(&r).unwrap_or(self.tcx.lifetimes.re_static) + debug_assert!(!r.is_late_bound(), "Should not be resolving bound region."); + self.tcx.lifetimes.re_erased } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { match self.infcx.fully_resolve(&ct) { - Ok(ct) => ct, + Ok(ct) => self.infcx.tcx.erase_regions(&ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); // FIXME: we'd like to use `self.report_error`, but it doesn't yet diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs index 3517e09133c..d0414af5b21 100644 --- a/src/librustc_typeck/check_unused.rs +++ b/src/librustc_typeck/check_unused.rs @@ -1,4 +1,3 @@ -use crate::lint; use rustc::ty::TyCtxt; use rustc_ast::ast; use rustc_data_structures::fx::FxHashMap; @@ -7,6 +6,7 @@ use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::print::visibility_qualified; +use rustc_session::lint; use rustc_span::Span; pub fn check_crate(tcx: TyCtxt<'_>) { diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index aa39a191b3d..e24d9bebf65 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -12,10 +12,11 @@ use rustc_hir::ItemKind; use rustc_infer::infer; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{SuppressRegionErrors, TyCtxtInferExt}; -use rustc_infer::traits::misc::{can_type_implement_copy, CopyImplementationError}; -use rustc_infer::traits::predicate_for_trait_def; -use rustc_infer::traits::{self, ObligationCause, TraitEngine}; +use rustc_infer::infer::{RegionckMode, TyCtxtInferExt}; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; +use rustc_trait_selection::traits::predicate_for_trait_def; +use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt}; pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { let lang_items = tcx.lang_items(); @@ -306,7 +307,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: DefId) { impl_did, ®ion_scope_tree, &outlives_env, - SuppressRegionErrors::default(), + RegionckMode::default(), ); } } @@ -322,7 +323,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: DefId) { } } -pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { +pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); let coerce_unsized_trait = tcx.lang_items().coerce_unsized_trait().unwrap(); @@ -567,7 +568,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn impl_did, ®ion_scope_tree, &outlives_env, - SuppressRegionErrors::default(), + RegionckMode::default(), ); CoerceUnsizedInfo { custom_kind: kind } diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs index fcded27463e..7513759c76b 100644 --- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs +++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_infer::traits::{self, SkipLeakCheck}; +use rustc_trait_selection::traits::{self, SkipLeakCheck}; pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) { assert_eq!(crate_num, LOCAL_CRATE); @@ -23,14 +23,12 @@ fn impls_have_common_items(&self, impl1: DefId, impl2: DefId) -> bool { let impl_items2 = self.tcx.associated_items(impl2); for item1 in impl_items1.in_definition_order() { - let collision = impl_items2 - .filter_by_name_unhygienic(item1.ident.name) - .find(|item2| { - // Symbols and namespace match, compare hygienically. - item1.kind.namespace() == item2.kind.namespace() - && item1.ident.modern() == item2.ident.modern() - }) - .is_some(); + let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).any(|item2| { + // Symbols and namespace match, compare hygienically. + item1.kind.namespace() == item2.kind.namespace() + && item1.ident.normalize_to_macros_2_0() + == item2.ident.normalize_to_macros_2_0() + }); if collision { return true; @@ -53,11 +51,12 @@ fn check_for_common_items_in_impls( let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).find(|item2| { // Symbols and namespace match, compare hygienically. item1.kind.namespace() == item2.kind.namespace() - && item1.ident.modern() == item2.ident.modern() + && item1.ident.normalize_to_macros_2_0() + == item2.ident.normalize_to_macros_2_0() }); if let Some(item2) = collision { - let name = item1.ident.modern(); + let name = item1.ident.normalize_to_macros_2_0(); let mut err = struct_span_err!( self.tcx.sess, self.tcx.span_of_impl(item1.def_id).unwrap(), diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index d24ee5f156b..27b2c19499c 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -9,8 +9,8 @@ use rustc::ty::{self, TyCtxt, TypeFoldable}; use rustc_errors::struct_span_err; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_infer::traits; use rustc_span::Span; +use rustc_trait_selection::traits; mod builtin; mod inherent_impls; @@ -76,6 +76,22 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, tra return; } + if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = + tcx.trait_def(trait_def_id).specialization_kind + { + if !tcx.features().specialization && !tcx.features().min_specialization { + let span = impl_header_span(tcx, impl_def_id); + tcx.sess + .struct_span_err( + span, + "implementing `rustc_specialization_trait` traits is unstable", + ) + .help("add `#![feature(min_specialization)]` to the crate attributes to enable") + .emit(); + return; + } + } + let trait_name = if did == li.fn_trait() { "Fn" } else if did == li.fn_mut_trait() { diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs index a8e5a0ddf26..fc77aad8688 100644 --- a/src/librustc_typeck/coherence/orphan.rs +++ b/src/librustc_typeck/coherence/orphan.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits; +use rustc_trait_selection::traits; pub fn check(tcx: TyCtxt<'_>) { let mut orphan = OrphanChecker { tcx }; @@ -174,7 +174,7 @@ fn visit_item(&mut self, item: &hir::Item<'_>) { // impl !Send for (A, B) { } // ``` // - // This final impl is legal according to the orpan + // This final impl is legal according to the orphan // rules, but it invalidates the reasoning from // `two_foos` above. debug!( diff --git a/src/librustc_typeck/coherence/unsafety.rs b/src/librustc_typeck/coherence/unsafety.rs index a6044217403..3b25f67aacc 100644 --- a/src/librustc_typeck/coherence/unsafety.rs +++ b/src/librustc_typeck/coherence/unsafety.rs @@ -69,11 +69,11 @@ fn check_unsafety_coherence( .emit(); } - (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative) => { + (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { // Reported in AST validation self.tcx.sess.delay_span_bug(item.span, "unsafe negative impl"); } - (_, _, Unsafety::Normal, hir::ImplPolarity::Negative) + (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) | (Unsafety::Normal, None, Unsafety::Normal, _) => { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2dad3d1d6d7..7145b948f2f 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -17,14 +17,12 @@ use crate::astconv::{AstConv, Bounds, SizedByDefault}; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::constrained_generic_params as cgp; -use crate::lint; use crate::middle::lang_items; use crate::middle::resolve_lifetime as rl; use rustc::hir::map::blocks::FnLikeNode; use rustc::hir::map::Map; use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc::mir::mono::Linkage; -use rustc::session::parse::feature_err; use rustc::ty::query::Providers; use rustc::ty::subst::{InternalSubsts, Subst}; use rustc::ty::util::Discr; @@ -42,6 +40,8 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{GenericParamKind, Node, Unsafety}; +use rustc_session::lint; +use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; @@ -105,9 +105,9 @@ pub struct ItemCtxt<'tcx> { crate struct PlaceholderHirTyCollector(crate Vec); impl<'v> Visitor<'v> for PlaceholderHirTyCollector { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { @@ -151,7 +151,7 @@ struct CollectItemTypesVisitor<'tcx> { .unwrap_or(&"ParamName"); let mut sugg: Vec<_> = - placeholder_types.iter().map(|sp| (*sp, type_name.to_string())).collect(); + placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); if generics.is_empty() { sugg.push((span, format!("<{}>", type_name))); } else if let Some(arg) = generics.iter().find(|arg| match arg.name { @@ -160,7 +160,7 @@ struct CollectItemTypesVisitor<'tcx> { }) { // Account for `_` already present in cases like `struct S<_>(_);` and suggest // `struct S(T);` instead of `struct S<_, T>(T);`. - sugg.push((arg.span, type_name.to_string())); + sugg.push((arg.span, (*type_name).to_string())); } else { sugg.push(( generics.iter().last().unwrap().span.shrink_to_hi(), @@ -201,8 +201,8 @@ fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.tcx.hir()) } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -715,7 +715,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) { tcx.generics_of(def_id); match trait_item.kind { - hir::TraitItemKind::Method(..) => { + hir::TraitItemKind::Fn(..) => { tcx.type_of(def_id); tcx.fn_sig(def_id); } @@ -745,7 +745,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::HirId) { tcx.predicates_of(def_id); let impl_item = tcx.hir().expect_impl_item(impl_item_id); match impl_item.kind { - hir::ImplItemKind::Method(..) => { + hir::ImplItemKind::Fn(..) => { tcx.fn_sig(def_id); } hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => { @@ -828,7 +828,7 @@ fn convert_variant( .iter() .map(|f| { let fid = tcx.hir().local_def_id(f.hir_id); - let dup_span = seen_fields.get(&f.ident.modern()).cloned(); + let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); if let Some(prev_span) = dup_span { struct_span_err!( tcx.sess, @@ -841,7 +841,7 @@ fn convert_variant( .span_label(prev_span, format!("`{}` first declared here", f.ident)) .emit(); } else { - seen_fields.insert(f.ident.modern(), f.span); + seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } ty::FieldDef { @@ -1032,8 +1032,23 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TraitDef { } let is_marker = tcx.has_attr(def_id, sym::marker); + let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { + ty::trait_def::TraitSpecializationKind::Marker + } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { + ty::trait_def::TraitSpecializationKind::AlwaysApplicable + } else { + ty::trait_def::TraitSpecializationKind::None + }; let def_path_hash = tcx.def_path_hash(def_id); - let def = ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, def_path_hash); + let def = ty::TraitDef::new( + def_id, + unsafety, + paren_sugar, + is_auto, + is_marker, + spec_kind, + def_path_hash, + ); tcx.arena.alloc(def) } @@ -1045,9 +1060,9 @@ struct LateBoundRegionsDetector<'tcx> { } impl Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } @@ -1121,13 +1136,13 @@ fn has_late_bound_regions<'tcx>( match node { Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Method(ref sig, _) => { + hir::TraitItemKind::Fn(ref sig, _) => { has_late_bound_regions(tcx, &item.generics, &sig.decl) } _ => None, }, Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Method(ref sig, _) => { + hir::ImplItemKind::Fn(ref sig, _) => { has_late_bound_regions(tcx, &item.generics, &sig.decl) } _ => None, @@ -1395,7 +1410,7 @@ fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { .any(is_suggestable_infer_ty) } -/// Whether `ty` is a type with `_` placeholders that can be infered. Used in diagnostics only to +/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to /// use inference to provide suggestions for the appropriate type if possible. fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { use hir::TyKind::*; @@ -1437,12 +1452,12 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { match tcx.hir().get(hir_id) { TraitItem(hir::TraitItem { - kind: TraitItemKind::Method(sig, TraitMethod::Provided(_)), + kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), ident, generics, .. }) - | ImplItem(hir::ImplItem { kind: ImplItemKind::Method(sig, _), ident, generics, .. }) + | ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => { match get_infer_ret_ty(&sig.decl.output) { Some(ty) => { @@ -1474,7 +1489,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { } TraitItem(hir::TraitItem { - kind: TraitItemKind::Method(FnSig { header, decl }, _), + kind: TraitItemKind::Fn(FnSig { header, decl }, _), ident, generics, .. @@ -1548,7 +1563,7 @@ fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity { let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); let item = tcx.hir().expect_item(hir_id); match &item.kind { - hir::ItemKind::Impl { polarity: hir::ImplPolarity::Negative, .. } => { + hir::ItemKind::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => { if is_rustc_reservation { tcx.sess.span_err(item.span, "reservation impls can't be negative"); } @@ -2341,8 +2356,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; } else if attr.check_name(sym::rustc_std_internal_symbol) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - } else if attr.check_name(sym::no_debug) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_DEBUG; } else if attr.check_name(sym::used) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; } else if attr.check_name(sym::thread_local) { diff --git a/src/librustc_typeck/collect/type_of.rs b/src/librustc_typeck/collect/type_of.rs index ec87112b7a8..41c205bc11b 100644 --- a/src/librustc_typeck/collect/type_of.rs +++ b/src/librustc_typeck/collect/type_of.rs @@ -1,5 +1,4 @@ use rustc::hir::map::Map; -use rustc::session::parse::feature_err; use rustc::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc::ty::util::IntTypeExt; use rustc::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; @@ -11,9 +10,10 @@ use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; -use rustc_infer::traits; +use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::traits; use super::ItemCtxt; use super::{bad_placeholder_type, is_suggestable_infer_ty}; @@ -27,7 +27,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { match tcx.hir().get(hir_id) { Node::TraitItem(item) => match item.kind { - TraitItemKind::Method(..) => { + TraitItemKind::Fn(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id); tcx.mk_fn_def(def_id, substs) } @@ -47,7 +47,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { }, Node::ImplItem(item) => match item.kind { - ImplItemKind::Method(..) => { + ImplItemKind::Fn(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id); tcx.mk_fn_def(def_id, substs) } @@ -529,8 +529,8 @@ fn check(&mut self, def_id: DefId) { impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { - intravisit::NestedVisitorMap::All(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.tcx.hir()) } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let hir::ExprKind::Closure(..) = ex.kind { diff --git a/src/librustc_typeck/constrained_generic_params.rs b/src/librustc_typeck/constrained_generic_params.rs index 9b187d461cd..b16aa6ff3b2 100644 --- a/src/librustc_typeck/constrained_generic_params.rs +++ b/src/librustc_typeck/constrained_generic_params.rs @@ -36,7 +36,7 @@ pub fn parameters_for_impl<'tcx>( vec.into_iter().collect() } -/// If `include_projections` is false, returns the list of parameters that are +/// If `include_nonconstraining` is false, returns the list of parameters that are /// constrained by `t` - i.e., the value of each parameter in the list is /// uniquely determined by `t` (see RFC 447). If it is true, return the list /// of parameters whose values are needed in order to constrain `ty` - these @@ -79,10 +79,18 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { } fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { - if let ty::ConstKind::Param(data) = c.val { - self.parameters.push(Parameter::from(data)); + match c.val { + ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { + // Constant expressions are not injective + return c.ty.visit_with(self); + } + ty::ConstKind::Param(data) => { + self.parameters.push(Parameter::from(data)); + } + _ => {} } - false + + c.super_visit_with(self) } } diff --git a/src/librustc_typeck/expr_use_visitor.rs b/src/librustc_typeck/expr_use_visitor.rs index 6666b169994..a45d8ce6823 100644 --- a/src/librustc_typeck/expr_use_visitor.rs +++ b/src/librustc_typeck/expr_use_visitor.rs @@ -519,7 +519,7 @@ fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, fn_decl_span: Span) { for &var_id in upvars.keys() { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_def_id.to_local(), + closure_expr_id: closure_def_id.expect_local(), }; let upvar_capture = self.mc.tables.upvar_capture(upvar_id); let captured_place = return_if_err!(self.cat_captured_var( diff --git a/src/librustc_typeck/impl_wf_check.rs b/src/librustc_typeck/impl_wf_check.rs index 0a765a1f9c9..42cb4fcf85d 100644 --- a/src/librustc_typeck/impl_wf_check.rs +++ b/src/librustc_typeck/impl_wf_check.rs @@ -9,6 +9,8 @@ //! fixed, but for the moment it's easier to do these checks early. use crate::constrained_generic_params as cgp; +use min_specialization::check_min_specialization; + use rustc::ty::query::Providers; use rustc::ty::{self, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -16,9 +18,11 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_span::Span; + use std::collections::hash_map::Entry::{Occupied, Vacant}; -use rustc_span::Span; +mod min_specialization; /// Checks that all the type/lifetime parameters on an impl also /// appear in the trait ref or self type (or are constrained by a @@ -60,7 +64,9 @@ pub fn impl_wf_check(tcx: TyCtxt<'_>) { } fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: DefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx }); + let min_specialization = tcx.features().min_specialization; + tcx.hir() + .visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx, min_specialization }); } pub fn provide(providers: &mut Providers<'_>) { @@ -69,6 +75,7 @@ pub fn provide(providers: &mut Providers<'_>) { struct ImplWfCheck<'tcx> { tcx: TyCtxt<'tcx>, + min_specialization: bool, } impl ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { @@ -77,6 +84,9 @@ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { let impl_def_id = self.tcx.hir().local_def_id(item.hir_id); enforce_impl_params_are_constrained(self.tcx, impl_def_id, items); enforce_impl_items_are_distinct(self.tcx, items); + if self.min_specialization { + check_min_specialization(self.tcx, impl_def_id, item.span); + } } } @@ -227,7 +237,7 @@ fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_item_refs: &[hir::ImplI hir::ImplItemKind::TyAlias(_) => &mut seen_type_items, _ => &mut seen_value_items, }; - match seen_items.entry(impl_item.ident.modern()) { + match seen_items.entry(impl_item.ident.normalize_to_macros_2_0()) { Occupied(entry) => { let mut err = struct_span_err!( tcx.sess, diff --git a/src/librustc_typeck/impl_wf_check/min_specialization.rs b/src/librustc_typeck/impl_wf_check/min_specialization.rs new file mode 100644 index 00000000000..cae88376118 --- /dev/null +++ b/src/librustc_typeck/impl_wf_check/min_specialization.rs @@ -0,0 +1,409 @@ +//! # Minimal Specialization +//! +//! This module contains the checks for sound specialization used when the +//! `min_specialization` feature is enabled. This requires that the impl is +//! *always applicable*. +//! +//! If `impl1` specializes `impl2` then `impl1` is always applicable if we know +//! that all the bounds of `impl2` are satisfied, and all of the bounds of +//! `impl1` are satisfied for some choice of lifetimes then we know that +//! `impl1` applies for any choice of lifetimes. +//! +//! ## Basic approach +//! +//! To enforce this requirement on specializations we take the following +//! approach: +//! +//! 1. Match up the substs for `impl2` so that the implemented trait and +//! self-type match those for `impl1`. +//! 2. Check for any direct use of `'static` in the substs of `impl2`. +//! 3. Check that all of the generic parameters of `impl1` occur at most once +//! in the *unconstrained* substs for `impl2`. A parameter is constrained if +//! its value is completely determined by an associated type projection +//! predicate. +//! 4. Check that all predicates on `impl1` either exist on `impl2` (after +//! matching substs), or are well-formed predicates for the trait's type +//! arguments. +//! +//! ## Example +//! +//! Suppose we have the following always applicable impl: +//! +//! ```rust +//! impl SpecExtend for std::vec::IntoIter { /* specialized impl */ } +//! impl> SpecExtend for I { /* default impl */ } +//! ``` +//! +//! We get that the subst for `impl2` are `[T, std::vec::IntoIter]`. `T` is +//! constrained to be `::Item`, so we check only +//! `std::vec::IntoIter` for repeated parameters, which it doesn't have. The +//! predicates of `impl1` are only `T: Sized`, which is also a predicate of +//! `impl2`. So this specialization is sound. +//! +//! ## Extensions +//! +//! Unfortunately not all specializations in the standard library are allowed +//! by this. So there are two extensions to these rules that allow specializing +//! on some traits: that is, using them as bounds on the specializing impl, +//! even when they don't occur in the base impl. +//! +//! ### rustc_specialization_trait +//! +//! If a trait is always applicable, then it's sound to specialize on it. We +//! check trait is always applicable in the same way as impls, except that step +//! 4 is now "all predicates on `impl1` are always applicable". We require that +//! `specialization` or `min_specialization` is enabled to implement these +//! traits. +//! +//! ### rustc_unsafe_specialization_marker +//! +//! There are also some specialization on traits with no methods, including the +//! stable `FusedIterator` trait. We allow marking marker traits with an +//! unstable attribute that means we ignore them in point 3 of the checks +//! above. This is unsound, in the sense that the specialized impl may be used +//! when it doesn't apply, but we allow it in the short term since it can't +//! cause use after frees with purely safe code in the same way as specializing +//! on traits with methods can. + +use crate::constrained_generic_params as cgp; + +use rustc::middle::region::ScopeTree; +use rustc::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; +use rustc::ty::trait_def::TraitSpecializationKind; +use rustc::ty::{self, InstantiatedPredicates, TyCtxt, TypeFoldable}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{InferCtxt, RegionckMode, TyCtxtInferExt}; +use rustc_infer::traits::specialization_graph::Node; +use rustc_span::Span; +use rustc_trait_selection::traits::{self, translate_substs, wf}; + +pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: DefId, span: Span) { + if let Some(node) = parent_specialization_node(tcx, impl_def_id) { + tcx.infer_ctxt().enter(|infcx| { + check_always_applicable(&infcx, impl_def_id, node, span); + }); + } +} + +fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: DefId) -> Option { + let trait_ref = tcx.impl_trait_ref(impl1_def_id)?; + let trait_def = tcx.trait_def(trait_ref.def_id); + + let impl2_node = trait_def.ancestors(tcx, impl1_def_id).ok()?.nth(1)?; + + let always_applicable_trait = + matches!(trait_def.specialization_kind, TraitSpecializationKind::AlwaysApplicable); + if impl2_node.is_from_trait() && !always_applicable_trait { + // Implementing a normal trait isn't a specialization. + return None; + } + Some(impl2_node) +} + +/// Check that `impl1` is a sound specialization +fn check_always_applicable( + infcx: &InferCtxt<'_, '_>, + impl1_def_id: DefId, + impl2_node: Node, + span: Span, +) { + if let Some((impl1_substs, impl2_substs)) = + get_impl_substs(infcx, impl1_def_id, impl2_node, span) + { + let impl2_def_id = impl2_node.def_id(); + debug!( + "check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)", + impl1_def_id, impl2_def_id, impl2_substs + ); + + let tcx = infcx.tcx; + + let parent_substs = if impl2_node.is_from_trait() { + impl2_substs.to_vec() + } else { + unconstrained_parent_impl_substs(tcx, impl2_def_id, impl2_substs) + }; + + check_static_lifetimes(tcx, &parent_substs, span); + check_duplicate_params(tcx, impl1_substs, &parent_substs, span); + + check_predicates(infcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); + } +} + +/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two +/// substitutions `(S1, S2)` that equate their trait references. The returned +/// types are expressed in terms of the generics of `impl1`. +/// +/// Example +/// +/// impl Foo for B { /* impl2 */ } +/// impl Foo> for C { /* impl1 */ } +/// +/// Would return `S1 = [C]` and `S2 = [Vec, C]`. +fn get_impl_substs<'tcx>( + infcx: &InferCtxt<'_, 'tcx>, + impl1_def_id: DefId, + impl2_node: Node, + span: Span, +) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> { + let tcx = infcx.tcx; + let param_env = tcx.param_env(impl1_def_id); + + let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id); + let impl2_substs = translate_substs(infcx, param_env, impl1_def_id, impl1_substs, impl2_node); + + // Conservatively use an empty `ParamEnv`. + let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); + infcx.resolve_regions_and_report_errors( + impl1_def_id, + &ScopeTree::default(), + &outlives_env, + RegionckMode::default(), + ); + let impl2_substs = match infcx.fully_resolve(&impl2_substs) { + Ok(s) => s, + Err(_) => { + tcx.sess.struct_span_err(span, "could not resolve substs on overridden impl").emit(); + return None; + } + }; + Some((impl1_substs, impl2_substs)) +} + +/// Returns a list of all of the unconstrained subst of the given impl. +/// +/// For example given the impl: +/// +/// impl<'a, T, I> ... where &'a I: IntoIterator +/// +/// This would return the substs corresponding to `['a, I]`, because knowing +/// `'a` and `I` determines the value of `T`. +fn unconstrained_parent_impl_substs<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + impl_substs: SubstsRef<'tcx>, +) -> Vec> { + let impl_generic_predicates = tcx.predicates_of(impl_def_id); + let mut unconstrained_parameters = FxHashSet::default(); + let mut constrained_params = FxHashSet::default(); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); + + // Unfortunately the functions in `constrained_generic_parameters` don't do + // what we want here. We want only a list of constrained parameters while + // the functions in `cgp` add the constrained parameters to a list of + // unconstrained parameters. + for (predicate, _) in impl_generic_predicates.predicates.iter() { + if let ty::Predicate::Projection(proj) = predicate { + let projection_ty = proj.skip_binder().projection_ty; + let projected_ty = proj.skip_binder().ty; + + let unbound_trait_ref = projection_ty.trait_ref(tcx); + if Some(unbound_trait_ref) == impl_trait_ref { + continue; + } + + unconstrained_parameters.extend(cgp::parameters_for(&projection_ty, true)); + + for param in cgp::parameters_for(&projected_ty, false) { + if !unconstrained_parameters.contains(¶m) { + constrained_params.insert(param.0); + } + } + + unconstrained_parameters.extend(cgp::parameters_for(&projected_ty, true)); + } + } + + impl_substs + .iter() + .enumerate() + .filter(|&(idx, _)| !constrained_params.contains(&(idx as u32))) + .map(|(_, arg)| *arg) + .collect() +} + +/// Check that parameters of the derived impl don't occur more than once in the +/// equated substs of the base impl. +/// +/// For example forbid the following: +/// +/// impl Tr for A { } +/// impl Tr for (B, B) { } +/// +/// Note that only consider the unconstrained parameters of the base impl: +/// +/// impl> Tr for I { } +/// impl Tr for Vec { } +/// +/// The substs for the parent impl here are `[T, Vec]`, which repeats `T`, +/// but `S` is constrained in the parent impl, so `parent_substs` is only +/// `[Vec]`. This means we allow this impl. +fn check_duplicate_params<'tcx>( + tcx: TyCtxt<'tcx>, + impl1_substs: SubstsRef<'tcx>, + parent_substs: &Vec>, + span: Span, +) { + let mut base_params = cgp::parameters_for(parent_substs, true); + base_params.sort_by_key(|param| param.0); + if let (_, [duplicate, ..]) = base_params.partition_dedup() { + let param = impl1_substs[duplicate.0 as usize]; + tcx.sess + .struct_span_err(span, &format!("specializing impl repeats parameter `{}`", param)) + .emit(); + } +} + +/// Check that `'static` lifetimes are not introduced by the specializing impl. +/// +/// For example forbid the following: +/// +/// impl Tr for A { } +/// impl Tr for &'static i32 { } +fn check_static_lifetimes<'tcx>( + tcx: TyCtxt<'tcx>, + parent_substs: &Vec>, + span: Span, +) { + if tcx.any_free_region_meets(parent_substs, |r| *r == ty::ReStatic) { + tcx.sess.struct_span_err(span, &format!("cannot specialize on `'static` lifetime")).emit(); + } +} + +/// Check whether predicates on the specializing impl (`impl1`) are allowed. +/// +/// Each predicate `P` must be: +/// +/// * global (not reference any parameters) +/// * `T: Tr` predicate where `Tr` is an always-applicable trait +/// * on the base `impl impl2` +/// * Currently this check is done using syntactic equality, which is +/// conservative but generally sufficient. +/// * a well-formed predicate of a type argument of the trait being implemented, +/// including the `Self`-type. +fn check_predicates<'tcx>( + infcx: &InferCtxt<'_, 'tcx>, + impl1_def_id: DefId, + impl1_substs: SubstsRef<'tcx>, + impl2_node: Node, + impl2_substs: SubstsRef<'tcx>, + span: Span, +) { + let tcx = infcx.tcx; + let impl1_predicates = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs); + let mut impl2_predicates = if impl2_node.is_from_trait() { + // Always applicable traits have to be always applicable without any + // assumptions. + InstantiatedPredicates::empty() + } else { + tcx.predicates_of(impl2_node.def_id()).instantiate(tcx, impl2_substs) + }; + debug!( + "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", + impl1_predicates, impl2_predicates, + ); + + // Since impls of always applicable traits don't get to assume anything, we + // can also assume their supertraits apply. + // + // For example, we allow: + // + // #[rustc_specialization_trait] + // trait AlwaysApplicable: Debug { } + // + // impl Tr for T { } + // impl Tr for T { } + // + // Specializing on `AlwaysApplicable` allows also specializing on `Debug` + // which is sound because we forbid impls like the following + // + // impl AlwaysApplicable for D { } + let always_applicable_traits: Vec<_> = impl1_predicates + .predicates + .iter() + .filter(|predicate| { + matches!( + trait_predicate_kind(tcx, predicate), + Some(TraitSpecializationKind::AlwaysApplicable) + ) + }) + .copied() + .collect(); + + // Include the well-formed predicates of the type parameters of the impl. + for ty in tcx.impl_trait_ref(impl1_def_id).unwrap().substs.types() { + if let Some(obligations) = wf::obligations( + infcx, + tcx.param_env(impl1_def_id), + tcx.hir().as_local_hir_id(impl1_def_id).unwrap(), + ty, + span, + ) { + impl2_predicates + .predicates + .extend(obligations.into_iter().map(|obligation| obligation.predicate)) + } + } + impl2_predicates.predicates.extend(traits::elaborate_predicates(tcx, always_applicable_traits)); + + for predicate in impl1_predicates.predicates { + if !impl2_predicates.predicates.contains(&predicate) { + check_specialization_on(tcx, &predicate, span) + } + } +} + +fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: &ty::Predicate<'tcx>, span: Span) { + debug!("can_specialize_on(predicate = {:?})", predicate); + match predicate { + // Global predicates are either always true or always false, so we + // are fine to specialize on. + _ if predicate.is_global() => (), + // We allow specializing on explicitly marked traits with no associated + // items. + ty::Predicate::Trait(pred, hir::Constness::NotConst) => { + if !matches!( + trait_predicate_kind(tcx, predicate), + Some(TraitSpecializationKind::Marker) + ) { + tcx.sess + .struct_span_err( + span, + &format!( + "cannot specialize on trait `{}`", + tcx.def_path_str(pred.def_id()), + ), + ) + .emit() + } + } + _ => tcx + .sess + .struct_span_err(span, &format!("cannot specialize on `{:?}`", predicate)) + .emit(), + } +} + +fn trait_predicate_kind<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: &ty::Predicate<'tcx>, +) -> Option { + match predicate { + ty::Predicate::Trait(pred, hir::Constness::NotConst) => { + Some(tcx.trait_def(pred.def_id()).specialization_kind) + } + ty::Predicate::Trait(_, hir::Constness::Const) + | ty::Predicate::RegionOutlives(_) + | ty::Predicate::TypeOutlives(_) + | ty::Predicate::Projection(_) + | ty::Predicate::WellFormed(_) + | ty::Predicate::Subtype(_) + | ty::Predicate::ObjectSafe(_) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::ConstEvaluatable(..) => None, + } +} diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index c5f339d6b76..e487e0d265c 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -64,6 +64,7 @@ #![feature(nll)] #![feature(try_blocks)] #![feature(never_type)] +#![feature(slice_partition_dedup)] #![recursion_limit = "256"] #[macro_use] @@ -87,10 +88,7 @@ mod structured_errors; mod variance; -use rustc::lint; use rustc::middle; -use rustc::session; -use rustc::session::config::EntryFnType; use rustc::ty::query::Providers; use rustc::ty::subst::SubstsRef; use rustc::ty::{self, Ty, TyCtxt}; @@ -101,9 +99,14 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::Node; use rustc_infer::infer::{InferOk, TyCtxtInferExt}; -use rustc_infer::traits::{ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::TraitEngineExt as _; +use rustc_session::config::EntryFnType; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::{ + ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _, +}; use std::iter; diff --git a/src/librustc_typeck/mem_categorization.rs b/src/librustc_typeck/mem_categorization.rs index a4569a14756..7d8bf71cf97 100644 --- a/src/librustc_typeck/mem_categorization.rs +++ b/src/librustc_typeck/mem_categorization.rs @@ -59,6 +59,7 @@ use rustc_hir::PatKind; use rustc_infer::infer::InferCtxt; use rustc_span::Span; +use rustc_trait_selection::infer::InferCtxtExt; #[derive(Clone, Debug)] pub enum PlaceBase { @@ -425,7 +426,7 @@ fn cat_expr_adjusted_with( | Res::Def(DefKind::ConstParam, _) | Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::Method, _) + | Res::Def(DefKind::AssocFn, _) | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, span, expr_ty)), Res::Def(DefKind::Static, _) => Ok(Place { @@ -469,7 +470,7 @@ fn cat_upvar( let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_expr_def_id.to_local(), + closure_expr_id: closure_expr_def_id.expect_local(), }; let var_ty = self.node_ty(var_id)?; diff --git a/src/librustc_typeck/outlives/implicit_infer.rs b/src/librustc_typeck/outlives/implicit_infer.rs index fcbeb5b210d..44473fee643 100644 --- a/src/librustc_typeck/outlives/implicit_infer.rs +++ b/src/librustc_typeck/outlives/implicit_infer.rs @@ -31,10 +31,10 @@ pub fn infer_predicates<'tcx>( predicates_added = false; let mut visitor = InferVisitor { - tcx: tcx, + tcx, global_inferred_outlives: &mut global_inferred_outlives, predicates_added: &mut predicates_added, - explicit_map: explicit_map, + explicit_map, }; // Visit all the crates and infer predicates diff --git a/src/librustc_typeck/structured_errors.rs b/src/librustc_typeck/structured_errors.rs index 99b7b2001a9..a4f8472ae28 100644 --- a/src/librustc_typeck/structured_errors.rs +++ b/src/librustc_typeck/structured_errors.rs @@ -1,6 +1,6 @@ -use rustc::session::Session; use rustc::ty::{Ty, TypeFoldable}; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_session::Session; use rustc_span::Span; pub trait StructuredDiagnostic<'tcx> { diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 6f5caea250b..54f84272ae8 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -105,13 +105,13 @@ fn visit_item(&mut self, item: &hir::Item<'_>) { } fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Method(..) = trait_item.kind { + if let hir::TraitItemKind::Fn(..) = trait_item.kind { self.visit_node_helper(trait_item.hir_id); } } fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Method(..) = impl_item.kind { + if let hir::ImplItemKind::Fn(..) = impl_item.kind { self.visit_node_helper(impl_item.hir_id); } } diff --git a/src/librustc_typeck/variance/mod.rs b/src/librustc_typeck/variance/mod.rs index ddde11b3844..3cbb42bb5f3 100644 --- a/src/librustc_typeck/variance/mod.rs +++ b/src/librustc_typeck/variance/mod.rs @@ -1,7 +1,7 @@ -//! Module for inferring the variance of type and lifetime parameters. See the [rustc guide] +//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide] //! chapter for more info. //! -//! [rustc guide]: https://rust-lang.github.io/rustc-guide/variance.html +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html use hir::Node; use rustc::ty::query::Providers; @@ -54,13 +54,13 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { }, Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Method(..) => {} + hir::TraitItemKind::Fn(..) => {} _ => unsupported(), }, Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Method(..) => {} + hir::ImplItemKind::Fn(..) => {} _ => unsupported(), }, diff --git a/src/librustc_typeck/variance/terms.rs b/src/librustc_typeck/variance/terms.rs index dd593a6abb4..7e6ec96b379 100644 --- a/src/librustc_typeck/variance/terms.rs +++ b/src/librustc_typeck/variance/terms.rs @@ -77,8 +77,8 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( // See the following for a discussion on dep-graph management. // - // - https://rust-lang.github.io/rustc-guide/query.html - // - https://rust-lang.github.io/rustc-guide/variance.html + // - https://rustc-dev-guide.rust-lang.org/query.html + // - https://rustc-dev-guide.rust-lang.org/variance.html tcx.hir().krate().visit_all_item_likes(&mut terms_cx); terms_cx @@ -164,13 +164,13 @@ fn visit_item(&mut self, item: &hir::Item<'_>) { } fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Method(..) = trait_item.kind { + if let hir::TraitItemKind::Fn(..) = trait_item.kind { self.add_inferreds_for_item(trait_item.hir_id); } } fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Method(..) = impl_item.kind { + if let hir::ImplItemKind::Fn(..) = impl_item.kind { self.add_inferreds_for_item(impl_item.hir_id); } } diff --git a/src/librustdoc/README.md b/src/librustdoc/README.md index e4f7bc30e3f..5a5f547068d 100644 --- a/src/librustdoc/README.md +++ b/src/librustdoc/README.md @@ -1,3 +1,3 @@ -For more information about how `librustdoc` works, see the [rustc guide]. +For more information about how `librustdoc` works, see the [rustc dev guide]. -[rustc guide]: https://rust-lang.github.io/rustc-guide/rustdoc.html +[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/rustdoc.html diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 289923b45e6..c85b21a5500 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -1,7 +1,7 @@ use rustc::ty::{self, Region, RegionVid, TypeFoldable}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_infer::traits::auto_trait::{self, AutoTraitResult}; +use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult}; use std::fmt::Debug; @@ -493,7 +493,7 @@ fn param_env_to_generics( // Writing a projection trait bound of the form // ::Name : ?Sized // is illegal, because ?Sized bounds can only - // be written in the (here, nonexistant) definition + // be written in the (here, nonexistent) definition // of the type. // Therefore, we make sure that we never add a ?Sized // bound for projections diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 4a1e2570d06..e66f8697717 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -1,3 +1,4 @@ +use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc::ty::subst::Subst; use rustc::ty::{ToPredicate, WithConstness}; use rustc_hir as hir; diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 618dfa0d33a..153f7af9f97 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -12,7 +12,6 @@ use rustc_metadata::creader::LoadedMacro; use rustc_mir::const_eval::is_min_const_fn; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::sym; use rustc_span::Span; use crate::clean::{self, GetDefId, ToSource, TypeKind}; @@ -42,11 +41,7 @@ pub fn try_inline( attrs: Option>, visited: &mut FxHashSet, ) -> Option> { - let did = if let Some(did) = res.opt_def_id() { - did - } else { - return None; - }; + let did = res.opt_def_id()?; if did.is_local() { return None; } @@ -198,7 +193,6 @@ pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait { let generics = (cx.tcx.generics_of(did), predicates).clean(cx); let generics = filter_non_trait_generics(did, generics); let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); - let is_spotlight = load_attrs(cx, did).clean(cx).has_doc_flag(sym::spotlight); let is_auto = cx.tcx.trait_is_auto(did); clean::Trait { auto: auto_trait, @@ -206,7 +200,6 @@ pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait { generics, items: trait_items, bounds: supertrait_bounds, - is_spotlight, is_auto, } } @@ -581,7 +574,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: name: ref _name, }, ref bounds, - } => !(*s == "Self" && did == trait_did) && !bounds.is_empty(), + } => !(bounds.is_empty() || *s == "Self" && did == trait_did), _ => true, }); g diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7c845a9b66b..e69d4ddc2d0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -50,7 +50,7 @@ pub use self::types::Visibility::{Inherited, Public}; pub use self::types::*; -const FN_OUTPUT_NAME: &'static str = "Output"; +const FN_OUTPUT_NAME: &str = "Output"; pub trait Clean { fn clean(&self, cx: &DocContext<'_>) -> T; @@ -141,6 +141,7 @@ fn clean(&self, cx: &DocContext<'_>) -> ExternalCrate { cx.tcx .hir() .krate() + .item .module .item_ids .iter() @@ -194,6 +195,7 @@ fn clean(&self, cx: &DocContext<'_>) -> ExternalCrate { cx.tcx .hir() .krate() + .item .module .item_ids .iter() @@ -1013,7 +1015,6 @@ fn clean(&self, cx: &DocContext<'_>) -> FnRetTy { impl Clean for doctree::Trait<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let attrs = self.attrs.clean(cx); - let is_spotlight = attrs.has_doc_flag(sym::spotlight); Item { name: Some(self.name.clean(cx)), attrs, @@ -1028,7 +1029,6 @@ fn clean(&self, cx: &DocContext<'_>) -> Item { items: self.items.iter().map(|ti| ti.clean(cx)).collect(), generics: self.generics.clean(cx), bounds: self.bounds.clean(cx), - is_spotlight, is_auto: self.is_auto.clean(cx), }), } @@ -1078,16 +1078,36 @@ fn clean(&self, cx: &DocContext<'_>) -> PolyTrait { } } +impl Clean for hir::def::DefKind { + fn clean(&self, _: &DocContext<'_>) -> TypeKind { + match *self { + hir::def::DefKind::Mod => TypeKind::Module, + hir::def::DefKind::Struct => TypeKind::Struct, + hir::def::DefKind::Union => TypeKind::Union, + hir::def::DefKind::Enum => TypeKind::Enum, + hir::def::DefKind::Trait => TypeKind::Trait, + hir::def::DefKind::TyAlias => TypeKind::Typedef, + hir::def::DefKind::ForeignTy => TypeKind::Foreign, + hir::def::DefKind::TraitAlias => TypeKind::TraitAlias, + hir::def::DefKind::Fn => TypeKind::Function, + hir::def::DefKind::Const => TypeKind::Const, + hir::def::DefKind::Static => TypeKind::Static, + hir::def::DefKind::Macro(_) => TypeKind::Macro, + _ => TypeKind::Foreign, + } + } +} + impl Clean for hir::TraitItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let inner = match self.kind { hir::TraitItemKind::Const(ref ty, default) => { AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e))) } - hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) => { + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { MethodItem((sig, &self.generics, body, None).clean(cx)) } - hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(ref names)) => { + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref names)) => { let (generics, decl) = enter_impl_trait(cx, || { (self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx)) }); @@ -1118,7 +1138,7 @@ fn clean(&self, cx: &DocContext<'_>) -> Item { hir::ImplItemKind::Const(ref ty, expr) => { AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr))) } - hir::ImplItemKind::Method(ref sig, body) => { + hir::ImplItemKind::Fn(ref sig, body) => { MethodItem((sig, &self.generics, body, Some(self.defaultness)).clean(cx)) } hir::ImplItemKind::TyAlias(ref ty) => { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 9c3bedfe37c..73f2c399e56 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -836,8 +836,8 @@ pub struct Method { pub decl: FnDecl, pub header: hir::FnHeader, pub defaultness: Option, - pub all_types: Vec, - pub ret_types: Vec, + pub all_types: Vec<(Type, TypeKind)>, + pub ret_types: Vec<(Type, TypeKind)>, } #[derive(Clone, Debug)] @@ -845,8 +845,8 @@ pub struct TyMethod { pub header: hir::FnHeader, pub decl: FnDecl, pub generics: Generics, - pub all_types: Vec, - pub ret_types: Vec, + pub all_types: Vec<(Type, TypeKind)>, + pub ret_types: Vec<(Type, TypeKind)>, } #[derive(Clone, Debug)] @@ -854,8 +854,8 @@ pub struct Function { pub decl: FnDecl, pub generics: Generics, pub header: hir::FnHeader, - pub all_types: Vec, - pub ret_types: Vec, + pub all_types: Vec<(Type, TypeKind)>, + pub ret_types: Vec<(Type, TypeKind)>, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -951,7 +951,6 @@ pub struct Trait { pub items: Vec, pub generics: Generics, pub bounds: Vec, - pub is_spotlight: bool, pub is_auto: bool, } @@ -1043,7 +1042,7 @@ pub enum PrimitiveType { Never, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)] pub enum TypeKind { Enum, Function, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 99e3d731d0d..b54af499187 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -121,9 +121,7 @@ pub fn external_generic_args( let args: Vec<_> = substs .iter() .filter_map(|kind| match kind.unpack() { - GenericArgKind::Lifetime(lt) => { - lt.clean(cx).and_then(|lt| Some(GenericArg::Lifetime(lt))) - } + GenericArgKind::Lifetime(lt) => lt.clean(cx).map(|lt| GenericArg::Lifetime(lt)), GenericArgKind::Type(_) if skip_self => { skip_self = false; None @@ -186,7 +184,7 @@ pub fn get_real_types( arg: &Type, cx: &DocContext<'_>, recurse: i32, -) -> FxHashSet { +) -> FxHashSet<(Type, TypeKind)> { let arg_s = arg.print().to_string(); let mut res = FxHashSet::default(); if recurse >= 10 { @@ -211,7 +209,11 @@ pub fn get_real_types( if !adds.is_empty() { res.extend(adds); } else if !ty.is_full_generic() { - res.insert(ty); + if let Some(did) = ty.def_id() { + if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { + res.insert((ty, kind)); + } + } } } } @@ -227,13 +229,21 @@ pub fn get_real_types( if !adds.is_empty() { res.extend(adds); } else if !ty.is_full_generic() { - res.insert(ty.clone()); + if let Some(did) = ty.def_id() { + if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { + res.insert((ty.clone(), kind)); + } + } } } } } } else { - res.insert(arg.clone()); + if let Some(did) = arg.def_id() { + if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { + res.insert((arg.clone(), kind)); + } + } if let Some(gens) = arg.generics() { for gen in gens.iter() { if gen.is_full_generic() { @@ -241,8 +251,10 @@ pub fn get_real_types( if !adds.is_empty() { res.extend(adds); } - } else { - res.insert(gen.clone()); + } else if let Some(did) = gen.def_id() { + if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { + res.insert((gen.clone(), kind)); + } } } } @@ -258,7 +270,7 @@ pub fn get_all_types( generics: &Generics, decl: &FnDecl, cx: &DocContext<'_>, -) -> (Vec, Vec) { +) -> (Vec<(Type, TypeKind)>, Vec<(Type, TypeKind)>) { let mut all_types = FxHashSet::default(); for arg in decl.inputs.values.iter() { if arg.type_.is_self_type() { @@ -268,7 +280,11 @@ pub fn get_all_types( if !args.is_empty() { all_types.extend(args); } else { - all_types.insert(arg.type_.clone()); + if let Some(did) = arg.type_.def_id() { + if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { + all_types.insert((arg.type_.clone(), kind)); + } + } } } @@ -276,7 +292,11 @@ pub fn get_all_types( FnRetTy::Return(ref return_type) => { let mut ret = get_real_types(generics, &return_type, cx, 0); if ret.is_empty() { - ret.insert(return_type.clone()); + if let Some(did) = return_type.def_id() { + if let Some(kind) = cx.tcx.def_kind(did).clean(cx) { + ret.insert((return_type.clone(), kind)); + } + } } ret.into_iter().collect() } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 33b3e800374..179c5bfacf3 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -1,17 +1,17 @@ use std::collections::BTreeMap; +use std::convert::TryFrom; use std::ffi::OsStr; use std::fmt; use std::path::PathBuf; -use rustc::lint::Level; -use rustc::session; -use rustc::session::config::{ +use rustc_session::config::{self, parse_crate_types_from_list, parse_externs, CrateType}; +use rustc_session::config::{ build_codegen_options, build_debugging_options, get_cmd_lint_options, host_triple, nightly_options, }; -use rustc::session::config::{parse_crate_types_from_list, parse_externs, CrateType}; -use rustc::session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs}; -use rustc::session::search_paths::SearchPath; +use rustc_session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs}; +use rustc_session::lint::Level; +use rustc_session::search_paths::SearchPath; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_target::spec::TargetTriple; @@ -24,6 +24,33 @@ use crate::passes::{self, Condition, DefaultPassOption}; use crate::theme; +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum OutputFormat { + Json, + Html, +} + +impl OutputFormat { + pub fn is_json(&self) -> bool { + match self { + OutputFormat::Json => true, + _ => false, + } + } +} + +impl TryFrom<&str> for OutputFormat { + type Error = String; + + fn try_from(value: &str) -> Result { + match value { + "json" => Ok(OutputFormat::Json), + "html" => Ok(OutputFormat::Html), + _ => Err(format!("unknown output format `{}`", value)), + } + } +} + /// Configuration options for rustdoc. #[derive(Clone)] pub struct Options { @@ -115,6 +142,8 @@ pub struct Options { pub crate_version: Option, /// Collected options specific to outputting final pages. pub render_options: RenderOptions, + /// Output format rendering (used only for "show-coverage" option for the moment) + pub output_format: Option, } impl fmt::Debug for Options { @@ -269,9 +298,9 @@ fn println_condition(condition: Condition) { return Err(0); } - let color = session::config::parse_color(&matches); - let (json_rendered, _artifacts) = session::config::parse_json(&matches); - let error_format = session::config::parse_error_format(&matches, color, json_rendered); + let color = config::parse_color(&matches); + let (json_rendered, _artifacts) = config::parse_json(&matches); + let error_format = config::parse_error_format(&matches, color, json_rendered); let codegen_options = build_codegen_options(matches, error_format); let debugging_options = build_debugging_options(matches, error_format); @@ -425,14 +454,6 @@ fn println_condition(condition: Condition) { } } - match matches.opt_str("w").as_ref().map(|s| &**s) { - Some("html") | None => {} - Some(s) => { - diag.struct_err(&format!("unknown output format: {}", s)).emit(); - return Err(1); - } - } - let index_page = matches.opt_str("index-page").map(|s| PathBuf::from(&s)); if let Some(ref index_page) = index_page { if !index_page.is_file() { @@ -469,6 +490,29 @@ fn println_condition(condition: Condition) { } }; + let output_format = match matches.opt_str("output-format") { + Some(s) => match OutputFormat::try_from(s.as_str()) { + Ok(o) => { + if o.is_json() && !show_coverage { + diag.struct_err("json output format isn't supported for doc generation") + .emit(); + return Err(1); + } else if !o.is_json() && show_coverage { + diag.struct_err( + "html output format isn't supported for the --show-coverage option", + ) + .emit(); + return Err(1); + } + Some(o) + } + Err(e) => { + diag.struct_err(&e).emit(); + return Err(1); + } + }, + None => None, + }; let crate_name = matches.opt_str("crate-name"); let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); let playground_url = matches.opt_str("playground-url"); @@ -553,6 +597,7 @@ fn println_condition(condition: Condition) { generate_search_filter, generate_redirect_pages, }, + output_format, }) } @@ -568,6 +613,9 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han for flag in deprecated_flags.iter() { if matches.opt_present(flag) { + if *flag == "output-format" && matches.opt_present("show-coverage") { + continue; + } let mut err = diag.struct_warn(&format!("the '{}' flag is considered deprecated", flag)); err.warn( diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 8bc34e949f1..f0b9ad2852f 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,23 +1,22 @@ use rustc::middle::cstore::CrateStore; use rustc::middle::privacy::AccessLevels; -use rustc::session::config::ErrorOutputType; -use rustc::session::DiagnosticOutput; -use rustc::session::{self, config}; use rustc::ty::{Ty, TyCtxt}; +use rustc_ast::ast::CRATE_NODE_ID; +use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_driver::abort_on_err; +use rustc_errors::emitter::{Emitter, EmitterWriter}; +use rustc_errors::json::JsonEmitter; use rustc_feature::UnstableFeatures; use rustc_hir::def::Namespace::TypeNS; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; use rustc_hir::HirId; use rustc_interface::interface; use rustc_resolve as resolve; +use rustc_session::config::ErrorOutputType; use rustc_session::lint; - -use rustc_ast::ast::CRATE_NODE_ID; -use rustc_attr as attr; -use rustc_errors::emitter::{Emitter, EmitterWriter}; -use rustc_errors::json::JsonEmitter; +use rustc_session::DiagnosticOutput; +use rustc_session::{config, Session}; use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; @@ -34,8 +33,8 @@ use crate::passes::{self, Condition::*, ConditionalPass}; -pub use rustc::session::config::{CodegenOptions, DebuggingOptions, Input, Options}; -pub use rustc::session::search_paths::SearchPath; +pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options}; +pub use rustc_session::search_paths::SearchPath; pub type ExternalPaths = FxHashMap, clean::TypeKind)>; @@ -68,7 +67,7 @@ pub struct DocContext<'tcx> { } impl<'tcx> DocContext<'tcx> { - pub fn sess(&self) -> &session::Session { + pub fn sess(&self) -> &Session { &self.tcx.sess } @@ -228,6 +227,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt mut manual_passes, display_warnings, render_options, + output_format, .. } = options; @@ -385,6 +385,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut renderinfo = RenderInfo::default(); renderinfo.access_levels = access_levels; + renderinfo.output_format = output_format; let mut ctxt = DocContext { tcx, diff --git a/src/librustdoc/docfs.rs b/src/librustdoc/docfs.rs index ecc394a2bc9..9c9a00295c3 100644 --- a/src/librustdoc/docfs.rs +++ b/src/librustdoc/docfs.rs @@ -90,14 +90,14 @@ pub fn write(&self, path: P, contents: C) -> Result<(), E> let sender = self.errors.sender.clone().unwrap(); rayon::spawn(move || match fs::write(&path, &contents) { Ok(_) => { - sender - .send(None) - .expect(&format!("failed to send error on \"{}\"", path.display())); + sender.send(None).unwrap_or_else(|_| { + panic!("failed to send error on \"{}\"", path.display()) + }); } Err(e) => { - sender - .send(Some(format!("\"{}\": {}", path.display(), e))) - .expect(&format!("failed to send non-error on \"{}\"", path.display())); + sender.send(Some(format!("\"{}\": {}", path.display(), e))).unwrap_or_else( + |_| panic!("failed to send non-error on \"{}\"", path.display()), + ); } }); Ok(()) diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index becdeaba50f..41b8e66d265 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -44,7 +44,7 @@ pub fn new( vis: &'hir hir::Visibility<'hir>, ) -> Module<'hir> { Module { - name: name, + name, id: hir::CRATE_HIR_ID, vis, where_outer: rustc_span::DUMMY_SP, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index ec615fc8589..e60ff37fd27 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -63,22 +63,10 @@ impl Buffer { Buffer { for_html: false, buffer: String::new() } } - crate fn is_empty(&self) -> bool { - self.buffer.is_empty() - } - crate fn into_inner(self) -> String { self.buffer } - crate fn insert_str(&mut self, idx: usize, s: &str) { - self.buffer.insert_str(idx, s); - } - - crate fn push_str(&mut self, s: &str) { - self.buffer.push_str(s); - } - // Intended for consumption by write! and writeln! (std::fmt) but without // the fmt::Result return type imposed by fmt::Write (and avoiding the trait // import). diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 0b4bb304fe9..02f1947c99e 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -135,7 +135,7 @@ trait Writer { fn string(&mut self, text: T, klass: Class) -> io::Result<()>; } -// Implement `Writer` for anthing that can be written to, this just implements +// Implement `Writer` for anything that can be written to, this just implements // the default rustdoc behaviour. impl Writer for U { fn string(&mut self, text: T, klass: Class) -> io::Result<()> { diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index 4a0f4e5a4c9..0b2b0cdc18b 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -62,7 +62,7 @@ impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { let inner = match item.inner { clean::StrippedItem(box ref item) => item, - ref inner @ _ => inner, + ref inner => inner, }; match *inner { @@ -194,7 +194,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } -pub const NAMESPACE_TYPE: &'static str = "t"; -pub const NAMESPACE_VALUE: &'static str = "v"; -pub const NAMESPACE_MACRO: &'static str = "m"; -pub const NAMESPACE_KEYWORD: &'static str = "k"; +pub const NAMESPACE_TYPE: &str = "t"; +pub const NAMESPACE_VALUE: &str = "v"; +pub const NAMESPACE_MACRO: &str = "m"; +pub const NAMESPACE_KEYWORD: &str = "k"; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index ed007fe383c..e13bf270440 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -296,7 +296,7 @@ fn dont_escape(c: u8) -> bool { "" } )), - playground_button.as_ref().map(String::as_str), + playground_button.as_deref(), Some((s1.as_str(), s2)), )); Some(Event::Html(s.into())) @@ -315,7 +315,7 @@ fn dont_escape(c: u8) -> bool { "" } )), - playground_button.as_ref().map(String::as_str), + playground_button.as_deref(), None, )); Some(Event::Html(s.into())) @@ -844,11 +844,7 @@ impl<'a> Iterator for ParserWrapper<'a> { type Item = String; fn next(&mut self) -> Option { - let next_event = self.inner.next(); - if next_event.is_none() { - return None; - } - let next_event = next_event.unwrap(); + let next_event = self.inner.next()?; let (ret, is_in) = match next_event { Event::Start(Tag::Paragraph) => (None, 1), Event::Start(Tag::Heading(_)) => (None, 1), @@ -869,12 +865,8 @@ fn next(&mut self) -> Option { } } let mut s = String::with_capacity(md.len() * 3 / 2); - let mut p = ParserWrapper { inner: Parser::new(md), is_in: 0, is_first: true }; - while let Some(t) = p.next() { - if !t.is_empty() { - s.push_str(&t); - } - } + let p = ParserWrapper { inner: Parser::new(md), is_in: 0, is_first: true }; + p.filter(|t| !t.is_empty()).for_each(|i| s.push_str(&i)); s } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 4aa3fa39fb4..eb7a367acf4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -44,7 +44,6 @@ use rustc::middle::privacy::AccessLevels; use rustc::middle::stability; -use rustc_ast::ast; use rustc_ast_pretty::pprust; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -59,8 +58,8 @@ use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; -use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy}; -use crate::config::RenderOptions; +use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; +use crate::config::{OutputFormat, RenderOptions}; use crate::docfs::{DocFS, ErrorStorage, PathError}; use crate::doctree; use crate::html::escape::Escape; @@ -270,6 +269,7 @@ pub struct RenderInfo { pub deref_trait_did: Option, pub deref_mut_trait_did: Option, pub owned_box_did: Option, + pub output_format: Option, } // Helper structs for rendering items/sidebars and carrying along contextual @@ -302,19 +302,25 @@ fn serialize(&self, serializer: S) -> Result /// A type used for the search index. #[derive(Debug)] -struct Type { +struct RenderType { + ty: Option, + idx: Option, name: Option, - generics: Option>, + generics: Option>, } -impl Serialize for Type { +impl Serialize for RenderType { fn serialize(&self, serializer: S) -> Result where S: Serializer, { if let Some(name) = &self.name { let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&name)?; + if let Some(id) = self.idx { + seq.serialize_element(&id)?; + } else { + seq.serialize_element(&name)?; + } if let Some(generics) = &self.generics { seq.serialize_element(&generics)?; } @@ -325,11 +331,32 @@ fn serialize(&self, serializer: S) -> Result } } +/// A type used for the search index. +#[derive(Debug)] +struct Generic { + name: String, + defid: Option, + idx: Option, +} + +impl Serialize for Generic { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if let Some(id) = self.idx { + serializer.serialize_some(&id) + } else { + serializer.serialize_some(&self.name) + } + } +} + /// Full type of functions/methods in the search index. #[derive(Debug)] struct IndexItemFunctionType { - inputs: Vec, - output: Option>, + inputs: Vec, + output: Option>, } impl Serialize for IndexItemFunctionType { @@ -340,8 +367,8 @@ fn serialize(&self, serializer: S) -> Result // If we couldn't figure out a type, just write `null`. let mut iter = self.inputs.iter(); if match self.output { - Some(ref output) => iter.chain(output.iter()).any(|ref i| i.name.is_none()), - None => iter.any(|ref i| i.name.is_none()), + Some(ref output) => iter.chain(output.iter()).any(|ref i| i.ty.name.is_none()), + None => iter.any(|ref i| i.ty.name.is_none()), } { serializer.serialize_none() } else { @@ -359,6 +386,31 @@ fn serialize(&self, serializer: S) -> Result } } +#[derive(Debug)] +pub struct TypeWithKind { + ty: RenderType, + kind: TypeKind, +} + +impl From<(RenderType, TypeKind)> for TypeWithKind { + fn from(x: (RenderType, TypeKind)) -> TypeWithKind { + TypeWithKind { ty: x.0, kind: x.1 } + } +} + +impl Serialize for TypeWithKind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(None)?; + seq.serialize_element(&self.ty.name)?; + let x: ItemType = self.kind.into(); + seq.serialize_element(&x)?; + seq.end() + } +} + thread_local!(static CACHE_KEY: RefCell> = Default::default()); thread_local!(pub static CURRENT_DEPTH: Cell = Cell::new(0)); @@ -1313,7 +1365,8 @@ fn krate(self, mut krate: clean::Crate) -> Result<(), Error> {