diff options
Diffstat (limited to 'compiler')
84 files changed, 3059 insertions, 1643 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 95abf552915..492d5788fc0 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1606,7 +1606,7 @@ pub enum LitKind { /// A string literal (`"foo"`). Str(Symbol, StrStyle), /// A byte string (`b"foo"`). - ByteStr(Lrc<Vec<u8>>), + ByteStr(Lrc<[u8]>), /// A byte char (`b'f'`). Byte(u8), /// A character literal (`'a'`). diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 597e5b437fc..f6f1ad0a9c3 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -4,7 +4,6 @@ use crate::ast::{self, Lit, LitKind}; use crate::token::{self, Token}; use crate::tokenstream::TokenTree; -use rustc_data_structures::sync::Lrc; use rustc_lexer::unescape::{unescape_byte, unescape_char}; use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode}; use rustc_span::symbol::{kw, sym, Symbol}; @@ -108,7 +107,7 @@ impl LitKind { }); error?; buf.shrink_to_fit(); - LitKind::ByteStr(Lrc::new(buf)) + LitKind::ByteStr(buf.into()) } token::ByteStrRaw(_) => { let s = symbol.as_str(); @@ -128,7 +127,7 @@ impl LitKind { symbol.to_string().into_bytes() }; - LitKind::ByteStr(Lrc::new(bytes)) + LitKind::ByteStr(bytes.into()) } token::Err => LitKind::Err(symbol), }) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c97f80cf09b..4676ad5c31f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -432,17 +432,25 @@ impl<'hir> LoweringContext<'_, 'hir> { self.with_catch_scope(body.id, |this| { let mut block = this.lower_block_noalloc(body, true); - let try_span = this.mark_span_with_reason( - DesugaringKind::TryBlock, - body.span, - this.allow_try_trait.clone(), - ); - // Final expression of the block (if present) or `()` with span at the end of block - let tail_expr = block - .expr - .take() - .unwrap_or_else(|| this.expr_unit(this.sess.source_map().end_point(try_span))); + let (try_span, tail_expr) = if let Some(expr) = block.expr.take() { + ( + this.mark_span_with_reason( + DesugaringKind::TryBlock, + expr.span, + this.allow_try_trait.clone(), + ), + expr, + ) + } else { + let try_span = this.mark_span_with_reason( + DesugaringKind::TryBlock, + this.sess.source_map().end_point(body.span), + this.allow_try_trait.clone(), + ); + + (try_span, this.expr_unit(try_span)) + }; let ok_wrapped_span = this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None); @@ -1553,7 +1561,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LangItem::TryFromError, unstable_span, from_expr, - try_span, + unstable_span, ); let thin_attrs = ThinVec::from(attrs); let catch_scope = self.catch_scopes.last().copied(); diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 70753208af3..f76bbd83819 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -13,8 +13,6 @@ use rustc_span::{self, Pos, Span}; use smallvec::SmallVec; use std::rc::Rc; -use rustc_data_structures::sync::Lrc; - // These macros all relate to the file system; they either return // the column/row/filename of the expression, or they include // a given file into the current one. @@ -216,7 +214,7 @@ pub fn expand_include_bytes( } }; match cx.source_map().load_binary_file(&file) { - Ok(bytes) => base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::new(bytes)))), + Ok(bytes) => base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(bytes.into()))), Err(e) => { cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e)); DummyResult::any(sp) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 0c172dc33ba..f496f3283da 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -16,7 +16,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::sym; +use rustc_span::{sym, Span}; use rustc_target::abi::{self, Align, Size}; use rustc_target::spec::{HasTargetSpec, Target}; use std::borrow::Cow; @@ -139,6 +139,8 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMGetInsertBlock(self.llbuilder) } } + fn set_span(&self, _span: Span) {} + fn position_at_end(&mut self, llbb: &'ll BasicBlock) { unsafe { llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index ec6c177614d..0098555a373 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -126,6 +126,7 @@ impl CoverageMapGenerator { let (filenames_index, _) = self.filenames.insert_full(c_filename); virtual_file_mapping.push(filenames_index as u32); } + debug!("Adding counter {:?} to map for {:?}", counter, region,); mapping_regions.push(CounterMappingRegion::code_region( counter, current_file_id, diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 814e43c5fa5..d8bde8ee705 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -143,7 +143,9 @@ impl FunctionCoverage { let id_to_counter = |new_indexes: &IndexVec<InjectedExpressionIndex, MappedExpressionIndex>, id: ExpressionOperandId| { - if id.index() < self.counters.len() { + if id == ExpressionOperandId::ZERO { + Some(Counter::zero()) + } else if id.index() < self.counters.len() { let index = CounterValueReference::from(id.index()); self.counters .get(index) @@ -179,14 +181,19 @@ impl FunctionCoverage { // been assigned a `new_index`. let mapped_expression_index = MappedExpressionIndex::from(counter_expressions.len()); - counter_expressions.push(CounterExpression::new( + let expression = CounterExpression::new( lhs_counter, match op { Op::Add => ExprKind::Add, Op::Subtract => ExprKind::Subtract, }, rhs_counter, - )); + ); + debug!( + "Adding expression {:?} = {:?} at {:?}", + mapped_expression_index, expression, region + ); + counter_expressions.push(expression); new_indexes[original_index] = mapped_expression_index; expression_regions.push((Counter::expression(mapped_expression_index), region)); } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index d8a530d98fa..26a646b0293 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -55,6 +55,7 @@ impl<D> DebugScope<D> { impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) { let (scope, span) = self.debug_loc(source_info); + bx.set_span(span); if let Some(scope) = scope { bx.set_source_location(scope, span); } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index b35b0f24208..0b8289a8dd9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -15,6 +15,7 @@ use crate::MemFlags; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::Ty; +use rustc_span::Span; use rustc_target::abi::{Abi, Align, Scalar, Size}; use rustc_target::spec::HasTargetSpec; @@ -44,6 +45,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn build_sibling_block(&self, name: &str) -> Self; fn cx(&self) -> &Self::CodegenCx; fn llbb(&self) -> Self::BasicBlock; + fn set_span(&self, span: Span); fn position_at_end(&mut self, llbb: Self::BasicBlock); fn ret_void(&mut self); diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 438a0d0c6ff..1cfbce2355e 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -9,6 +9,7 @@ use super::iterate::reverse_post_order; use super::ControlFlowGraph; use rustc_index::vec::{Idx, IndexVec}; use std::borrow::BorrowMut; +use std::cmp::Ordering; #[cfg(test)] mod tests; @@ -108,6 +109,14 @@ impl<Node: Idx> Dominators<Node> { // FIXME -- could be optimized by using post-order-rank self.dominators(node).any(|n| n == dom) } + + /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator + /// relationship, the dominator will always precede the dominated. (The relative ordering + /// of two unrelated nodes will also be consistent, but otherwise the order has no + /// meaning.) This method cannot be used to determine if either Node dominates the other. + pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option<Ordering> { + self.post_order_rank[lhs].partial_cmp(&self.post_order_rank[rhs]) + } } pub struct Iter<'dom, Node: Idx> { diff --git a/compiler/rustc_error_codes/src/error_codes/E0284.md b/compiler/rustc_error_codes/src/error_codes/E0284.md index a1ffa2bda00..82598aeec02 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0284.md +++ b/compiler/rustc_error_codes/src/error_codes/E0284.md @@ -5,37 +5,29 @@ as the `collect` method for `Iterator`s. For example: ```compile_fail,E0284 -fn foo() -> Result<bool, ()> { - let results = [Ok(true), Ok(false), Err(())].iter().cloned(); - let v: Vec<bool> = results.collect()?; - // Do things with v... - Ok(true) +fn main() { + let n: u32 = 1; + let mut d: u64 = 2; + d = d + n.into(); } ``` -Here we have an iterator `results` over `Result<bool, ()>`. -Hence, `results.collect()` can return any type implementing -`FromIterator<Result<bool, ()>>`. On the other hand, the -`?` operator can accept any type implementing `Try`. +Here we have an addition of `d` and `n.into()`. Hence, `n.into()` can return +any type `T` where `u64: Add<T>`. On the other hand, the `into` method can +return any type where `u32: Into<T>`. -The author of this code probably wants `collect()` to return a -`Result<Vec<bool>, ()>`, but the compiler can't be sure -that there isn't another type `T` implementing both `Try` and -`FromIterator<Result<bool, ()>>` in scope such that -`T::Ok == Vec<bool>`. Hence, this code is ambiguous and an error -is returned. +The author of this code probably wants `into()` to return a `u64`, but the +compiler can't be sure that there isn't another type `T` where both +`u32: Into<T>` and `u64: Add<T>`. To resolve this error, use a concrete type for the intermediate expression: ``` -fn foo() -> Result<bool, ()> { - let results = [Ok(true), Ok(false), Err(())].iter().cloned(); - let v = { - let temp: Result<Vec<bool>, ()> = results.collect(); - temp? - }; - // Do things with v... - Ok(true) +fn main() { + let n: u32 = 1; + let mut d: u64 = 2; + let m: u64 = n.into(); + d = d + m; } ``` diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index ea19dff7db1..e3365e8590b 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -2,7 +2,7 @@ //! the end of the file for details. use super::combine::CombineFields; -use super::{HigherRankedType, InferCtxt, PlaceholderMap}; +use super::{HigherRankedType, InferCtxt}; use crate::infer::CombinedSnapshot; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; @@ -33,7 +33,7 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> { self.infcx.commit_if_ok(|_| { // First, we instantiate each bound region in the supertype with a // fresh placeholder region. - let (b_prime, _) = self.infcx.replace_bound_vars_with_placeholders(&b); + let b_prime = self.infcx.replace_bound_vars_with_placeholders(&b); // Next, we instantiate each bound region in the subtype // with a fresh region variable. These region variables -- @@ -66,10 +66,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the [rustc dev guide]. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html - pub fn replace_bound_vars_with_placeholders<T>( - &self, - binder: &ty::Binder<T>, - ) -> (T, PlaceholderMap<'tcx>) + pub fn replace_bound_vars_with_placeholders<T>(&self, binder: &ty::Binder<T>) -> T where T: TypeFoldable<'tcx>, { @@ -122,7 +119,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { next_universe, binder, result, map, ); - (result, map) + result } /// See `infer::region_constraints::RegionConstraintCollector::leak_check`. diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 07a55c7f859..9742f5e2346 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -351,11 +351,6 @@ pub struct InferCtxt<'a, 'tcx> { universe: Cell<ty::UniverseIndex>, } -/// A map returned by `replace_bound_vars_with_placeholders()` -/// indicating the placeholder region that each late-bound region was -/// replaced with. -pub type PlaceholderMap<'tcx> = BTreeMap<ty::BoundRegion, ty::Region<'tcx>>; - /// See the `error_reporting` module for more details. #[derive(Clone, Debug, PartialEq, Eq, TypeFoldable)] pub enum ValuePairs<'tcx> { @@ -992,7 +987,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } Some(self.commit_if_ok(|_snapshot| { - let (ty::SubtypePredicate { a_is_expected, a, b }, _) = + let ty::SubtypePredicate { a_is_expected, a, b } = self.replace_bound_vars_with_placeholders(&predicate); let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?; @@ -1007,7 +1002,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { predicate: ty::PolyRegionOutlivesPredicate<'tcx>, ) -> UnitResult<'tcx> { self.commit_if_ok(|_snapshot| { - let (ty::OutlivesPredicate(r_a, r_b), _) = + let ty::OutlivesPredicate(r_a, r_b) = self.replace_bound_vars_with_placeholders(&predicate); let origin = SubregionOrigin::from_obligation_cause(cause, || { RelateRegionParamBound(cause.span) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 07924298c24..2b827f4f4ed 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -328,8 +328,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { assoc_item_def_id: DefId, ) -> impl Iterator<Item = ty::Region<'tcx>> { let tcx = self.tcx; - let predicates = tcx.projection_predicates(assoc_item_def_id); - predicates + let bounds = tcx.item_bounds(assoc_item_def_id); + bounds .into_iter() .filter_map(|p| p.to_opt_type_outlives()) .filter_map(|p| p.no_bound_vars()) diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 9c0d934a035..1b7269706a7 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -4,7 +4,6 @@ use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::outlives::Component; use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness}; -use rustc_span::Span; pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, @@ -94,7 +93,11 @@ pub fn elaborate_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: impl Iterator<Item = ty::Predicate<'tcx>>, ) -> Elaborator<'tcx> { - let obligations = predicates.map(|predicate| predicate_obligation(predicate, None)).collect(); + let obligations = predicates + .map(|predicate| { + predicate_obligation(predicate, ty::ParamEnv::empty(), ObligationCause::dummy()) + }) + .collect(); elaborate_obligations(tcx, obligations) } @@ -109,15 +112,10 @@ pub fn elaborate_obligations<'tcx>( fn predicate_obligation<'tcx>( predicate: ty::Predicate<'tcx>, - span: Option<Span>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, ) -> PredicateObligation<'tcx> { - let cause = if let Some(span) = span { - ObligationCause::dummy_with_span(span) - } else { - ObligationCause::dummy() - }; - - Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } + Obligation { cause, param_env, recursion_depth: 0, predicate } } impl Elaborator<'tcx> { @@ -133,10 +131,11 @@ impl Elaborator<'tcx> { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); - let obligations = predicates.predicates.iter().map(|&(pred, span)| { + let obligations = predicates.predicates.iter().map(|&(pred, _)| { predicate_obligation( pred.subst_supertrait(tcx, &ty::Binder::bind(data.trait_ref)), - Some(span), + obligation.param_env, + obligation.cause.clone(), ) }); debug!("super_predicates: data={:?}", data); @@ -233,7 +232,13 @@ impl Elaborator<'tcx> { }) .map(|predicate_kind| predicate_kind.to_predicate(tcx)) .filter(|&predicate| visited.insert(predicate)) - .map(|predicate| predicate_obligation(predicate, None)), + .map(|predicate| { + predicate_obligation( + predicate, + obligation.param_env, + obligation.cause.clone(), + ) + }), ); } ty::PredicateAtom::TypeWellFormedFromEnv(..) => { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 48254dcee82..222333a578b 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -10,6 +10,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::map::Map; +use rustc_middle::lint::LevelSource; use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; use rustc_middle::ty::query::Providers; @@ -95,6 +96,44 @@ impl<'s> LintLevelsBuilder<'s> { self.sets.list.push(LintSet::CommandLine { specs }); } + /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful + /// (e.g. if a forbid was already inserted on the same scope), then emits a + /// diagnostic with no change to `specs`. + fn insert_spec( + &mut self, + specs: &mut FxHashMap<LintId, LevelSource>, + id: LintId, + (level, src): LevelSource, + ) { + if let Some((old_level, old_src)) = specs.get(&id) { + if old_level == &Level::Forbid && level != Level::Forbid { + let mut diag_builder = struct_span_err!( + self.sess, + src.span(), + E0453, + "{}({}) incompatible with previous forbid in same scope", + level.as_str(), + src.name(), + ); + match *old_src { + LintSource::Default => {} + LintSource::Node(_, forbid_source_span, reason) => { + diag_builder.span_label(forbid_source_span, "`forbid` level set here"); + if let Some(rationale) = reason { + diag_builder.note(&rationale.as_str()); + } + } + LintSource::CommandLine(_) => { + diag_builder.note("`forbid` lint level was set on command line"); + } + } + diag_builder.emit(); + return; + } + } + specs.insert(id, (level, src)); + } + /// Pushes a list of AST lint attributes onto this context. /// /// This function will return a `BuilderPush` object which should be passed @@ -109,7 +148,7 @@ impl<'s> LintLevelsBuilder<'s> { /// `#[allow]` /// /// Don't forget to call `pop`! - pub fn push( + pub(crate) fn push( &mut self, attrs: &[ast::Attribute], store: &LintStore, @@ -221,7 +260,7 @@ impl<'s> LintLevelsBuilder<'s> { let src = LintSource::Node(name, li.span(), reason); for &id in ids { self.check_gated_lint(id, attr.span); - specs.insert(id, (level, src)); + self.insert_spec(&mut specs, id, (level, src)); } } @@ -235,7 +274,7 @@ impl<'s> LintLevelsBuilder<'s> { reason, ); for id in ids { - specs.insert(*id, (level, src)); + self.insert_spec(&mut specs, *id, (level, src)); } } Err((Some(ids), new_lint_name)) => { @@ -272,7 +311,7 @@ impl<'s> LintLevelsBuilder<'s> { reason, ); for id in ids { - specs.insert(*id, (level, src)); + self.insert_spec(&mut specs, *id, (level, src)); } } Err((None, _)) => { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 49e80f9d8a5..1db59bfc39d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -64,8 +64,8 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS, - EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_DOC_CODE_EXAMPLES, - PRIVATE_DOC_TESTS, + EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, INVALID_HTML_TAGS, + MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, }; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -311,7 +311,8 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { PRIVATE_INTRA_DOC_LINKS, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_DOC_CODE_EXAMPLES, - PRIVATE_DOC_TESTS + PRIVATE_DOC_TESTS, + INVALID_HTML_TAGS ); // Register renamed and removed lints. diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 1e8c30071e7..3abd9a6325d 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Adt(def, _) => check_must_use_def(cx, def.did, span, descr_pre, descr_post), ty::Opaque(def, _) => { let mut has_emitted = false; - for (predicate, _) in cx.tcx.predicates_of(def).predicates { + for &(predicate, _) in cx.tcx.explicit_item_bounds(def) { // We only look at the `DefId`, so it is safe to skip the binder here. if let ty::PredicateAtom::Trait(ref poly_trait_predicate, _) = predicate.skip_binders() diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index c31e941b3ff..2662f6b6ed6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -937,7 +937,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .inferred_outlives .get(self, item_id) - .map(|predicates| predicates.decode((self, tcx))) + .map(|predicates| tcx.arena.alloc_from_iter(predicates.decode((self, tcx)))) .unwrap_or_default() } @@ -949,6 +949,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.tables.super_predicates.get(self, item_id).unwrap().decode((self, tcx)) } + fn get_explicit_item_bounds( + &self, + item_id: DefIndex, + tcx: TyCtxt<'tcx>, + ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + self.root + .tables + .explicit_item_bounds + .get(self, item_id) + .map(|bounds| tcx.arena.alloc_from_iter(bounds.decode((self, tcx)))) + .unwrap_or_default() + } + fn get_generics(&self, item_id: DefIndex, sess: &Session) -> ty::Generics { self.root.tables.generics.get(self, item_id).unwrap().decode((self, sess)) } @@ -1011,6 +1024,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.tables.impl_trait_ref.get(self, id).map(|tr| tr.decode((self, tcx))) } + fn get_expn_that_defined(&self, id: DefIndex, sess: &Session) -> ExpnId { + self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess)) + } + /// Iterates over all the stability attributes in the given crate. fn get_lib_features(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option<Symbol>)] { // FIXME: For a proc macro crate, not sure whether we should return the "host" diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 4102cf84a67..60705f68681 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -89,6 +89,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, explicit_predicates_of => { cdata.get_explicit_predicates(def_id.index, tcx) } inferred_outlives_of => { cdata.get_inferred_outlives(def_id.index, tcx) } super_predicates_of => { cdata.get_super_predicates(def_id.index, tcx) } + explicit_item_bounds => { cdata.get_explicit_item_bounds(def_id.index, tcx) } trait_def => { cdata.get_trait_def(def_id.index, tcx.sess) } adt_def => { cdata.get_adt_def(def_id.index, tcx) } adt_destructor => { @@ -238,6 +239,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } crate_extern_paths => { cdata.source().paths().cloned().collect() } + expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) } } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index f58a792ef58..f0911928e81 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -747,6 +747,7 @@ impl EncodeContext<'a, 'tcx> { ty::Visibility::from_hir(enum_vis, enum_id, self.tcx)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- &self.tcx.get_attrs(def_id)[..]); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); record!(self.tables.children[def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); f.did.index @@ -883,6 +884,7 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.visibility[def_id] <- field.vis); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- variant_data.fields()[field_index].attrs); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); self.encode_ident_span(def_id, field.ident); self.encode_stability(def_id); self.encode_deprecation(def_id); @@ -924,6 +926,7 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data), adt_def.repr)); record!(self.tables.visibility[def_id] <- ctor_vis); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); self.encode_stability(def_id); self.encode_deprecation(def_id); self.encode_item_type(def_id); @@ -962,6 +965,14 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.super_predicates[def_id] <- self.tcx.super_predicates_of(def_id)); } + fn encode_explicit_item_bounds(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id); + let bounds = self.tcx.explicit_item_bounds(def_id); + if !bounds.is_empty() { + record!(self.tables.explicit_item_bounds[def_id] <- bounds); + } + } + fn encode_info_for_trait_item(&mut self, def_id: DefId) { debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id); let tcx = self.tcx; @@ -1014,7 +1025,10 @@ impl EncodeContext<'a, 'tcx> { has_self: trait_item.fn_has_self_parameter, })) } - ty::AssocKind::Type => EntryKind::AssocType(container), + ty::AssocKind::Type => { + self.encode_explicit_item_bounds(def_id); + EntryKind::AssocType(container) + } }); record!(self.tables.visibility[def_id] <- trait_item.vis); record!(self.tables.span[def_id] <- ast_item.span); @@ -1252,7 +1266,10 @@ impl EncodeContext<'a, 'tcx> { hir::ItemKind::ForeignMod(_) => EntryKind::ForeignMod, hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm, hir::ItemKind::TyAlias(..) => EntryKind::Type, - hir::ItemKind::OpaqueTy(..) => EntryKind::OpaqueTy, + hir::ItemKind::OpaqueTy(..) => { + self.encode_explicit_item_bounds(def_id); + EntryKind::OpaqueTy + } hir::ItemKind::Enum(..) => EntryKind::Enum(self.tcx.adt_def(def_id).repr), hir::ItemKind::Struct(ref struct_def, _) => { let adt_def = self.tcx.adt_def(def_id); @@ -1339,6 +1356,7 @@ impl EncodeContext<'a, 'tcx> { ty::Visibility::from_hir(&item.vis, item.hir_id, tcx)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- item.attrs); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); // FIXME(eddyb) there should be a nicer way to do this. match item.kind { hir::ItemKind::ForeignMod(ref fm) => record!(self.tables.children[def_id] <- diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 1a127035d4f..2bd2019d3cd 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -294,13 +294,12 @@ define_tables! { variances: Table<DefIndex, Lazy<[ty::Variance]>>, generics: Table<DefIndex, Lazy<ty::Generics>>, explicit_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>, - // FIXME(eddyb) this would ideally be `Lazy<[...]>` but `ty::Predicate` - // doesn't handle shorthands in its own (de)serialization impls, - // as it's an `enum` for which we want to derive (de)serialization, - // so the `ty::codec` APIs handle the whole `&'tcx [...]` at once. - // Also, as an optimization, a missing entry indicates an empty `&[]`. - inferred_outlives: Table<DefIndex, Lazy!(&'tcx [(ty::Predicate<'tcx>, Span)])>, + expn_that_defined: Table<DefIndex, Lazy<ExpnId>>, + // As an optimization, a missing entry indicates an empty `&[]`. + inferred_outlives: Table<DefIndex, Lazy!([(ty::Predicate<'tcx>, Span)])>, super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>, + // As an optimization, a missing entry indicates an empty `&[]`. + explicit_item_bounds: Table<DefIndex, Lazy!([(ty::Predicate<'tcx>, Span)])>, mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>, promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>, mir_abstract_consts: Table<DefIndex, Lazy!(&'tcx [mir::abstract_const::Node<'tcx>])>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index e8ace361b21..f9a450305d0 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -26,7 +26,7 @@ rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.29.0" +chalk-ir = "0.31.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } measureme = "0.7.1" rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index ceb873adf5c..57f03c2a5cf 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -535,15 +535,15 @@ impl<'hir> Map<'hir> { Some(Node::Binding(_)) => (), _ => return false, } - match self.find(self.get_parent_node(id)) { + matches!( + self.find(self.get_parent_node(id)), Some( Node::Item(_) | Node::TraitItem(_) | Node::ImplItem(_) | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), - ) => true, - _ => false, - } + ) + ) } /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. @@ -554,10 +554,10 @@ impl<'hir> Map<'hir> { /// Whether `hir_id` corresponds to a `mod` or a crate. pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { - match self.get_entry(hir_id).node { - Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) => true, - _ => false, - } + matches!( + self.get_entry(hir_id).node, + Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) + ) } /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 25e5379881e..91e1d6e0b0b 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -9,7 +9,7 @@ use rustc_session::lint::{builtin, Level, Lint, LintId}; use rustc_session::{DiagnosticMessageId, Session}; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan}; -use rustc_span::{Span, Symbol}; +use rustc_span::{symbol, Span, Symbol, DUMMY_SP}; /// How a lint level was set. #[derive(Clone, Copy, PartialEq, Eq, HashStable)] @@ -25,6 +25,24 @@ pub enum LintSource { CommandLine(Symbol), } +impl LintSource { + pub fn name(&self) -> Symbol { + match *self { + LintSource::Default => symbol::kw::Default, + LintSource::Node(name, _, _) => name, + LintSource::CommandLine(name) => name, + } + } + + pub fn span(&self) -> Span { + match *self { + LintSource::Default => DUMMY_SP, + LintSource::Node(_, span, _) => span, + LintSource::CommandLine(_) => DUMMY_SP, + } + } +} + pub type LevelSource = (Level, LintSource); pub struct LintLevelSets { diff --git a/compiler/rustc_middle/src/mir/coverage/mod.rs b/compiler/rustc_middle/src/mir/coverage/mod.rs index ce311c2ee52..0421eabc2dc 100644 --- a/compiler/rustc_middle/src/mir/coverage/mod.rs +++ b/compiler/rustc_middle/src/mir/coverage/mod.rs @@ -14,6 +14,20 @@ rustc_index::newtype_index! { } } +impl ExpressionOperandId { + /// An expression operand for a "zero counter", as described in the following references: + /// + /// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter + /// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#tag + /// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter-expressions + /// + /// This operand can be used to count two or more separate code regions with a single counter, + /// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for + /// one of the code regions, and inserting `CounterExpression`s ("add ZERO to the counter") in + /// the coverage map for the other code regions. + pub const ZERO: Self = Self::from_u32(0); +} + rustc_index::newtype_index! { pub struct CounterValueReference { derive [HashStable] @@ -22,6 +36,11 @@ rustc_index::newtype_index! { } } +impl CounterValueReference { + // Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO. + pub const START: Self = Self::from_u32(1); +} + rustc_index::newtype_index! { pub struct InjectedExpressionIndex { derive [HashStable] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 20363625e42..b5beb3babe2 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -486,10 +486,10 @@ impl<'tcx> TyCtxt<'tcx> { // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. // However, formatting code relies on function identity (see #58320), so we only do // this for generic functions. Lifetime parameters are ignored. - let is_generic = instance.substs.into_iter().any(|kind| match kind.unpack() { - GenericArgKind::Lifetime(_) => false, - _ => true, - }); + let is_generic = instance + .substs + .into_iter() + .any(|kind| !matches!(kind.unpack(), GenericArgKind::Lifetime(_))); if is_generic { // Get a fresh ID. let mut alloc_map = self.alloc_map.lock(); diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 206f01c2498..3751249853f 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -445,19 +445,13 @@ impl<'tcx, Tag> Scalar<Tag> { /// Do not call this method! Dispatch based on the type instead. #[inline] pub fn is_bits(self) -> bool { - match self { - Scalar::Raw { .. } => true, - _ => false, - } + matches!(self, Scalar::Raw { .. }) } /// Do not call this method! Dispatch based on the type instead. #[inline] pub fn is_ptr(self) -> bool { - match self { - Scalar::Ptr(_) => true, - _ => false, - } + matches!(self, Scalar::Ptr(_)) } pub fn to_bool(self) -> InterpResult<'tcx, bool> { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 2f306809f1f..da7677f802b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -971,67 +971,59 @@ impl<'tcx> LocalDecl<'tcx> { /// - `let x = ...`, /// - or `match ... { C(x) => ... }` pub fn can_be_made_mutable(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - })))) => true, - - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( - ImplicitSelfKind::Imm, - )))) => true, - - _ => false, - } + matches!( + self.local_info, + Some(box LocalInfo::User(ClearCrossCrate::Set( + BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + }) + | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm), + ))) + ) } /// Returns `true` if local is definitely not a `ref ident` or /// `ref mut ident` binding. (Such bindings cannot be made into /// mutable bindings, but the inverse does not necessarily hold). pub fn is_nonref_binding(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - })))) => true, - - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_)))) => true, - - _ => false, - } + matches!( + self.local_info, + Some(box LocalInfo::User(ClearCrossCrate::Set( + BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + }) + | BindingForm::ImplicitSelf(_), + ))) + ) } /// Returns `true` if this variable is a named variable or function /// parameter declared by the user. #[inline] pub fn is_user_variable(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(_)) => true, - _ => false, - } + matches!(self.local_info, Some(box LocalInfo::User(_))) } /// Returns `true` if this is a reference to a variable bound in a `match` /// expression that is used to access said variable for the guard of the /// match arm. pub fn is_ref_for_guard(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) => true, - _ => false, - } + matches!( + self.local_info, + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) + ) } /// Returns `Some` if this is a reference to a static item that is used to /// access that static pub fn is_ref_to_static(&self) -> bool { - match self.local_info { - Some(box LocalInfo::StaticRef { .. }) => true, - _ => false, - } + matches!(self.local_info, Some(box LocalInfo::StaticRef { .. })) } /// Returns `Some` if this is a reference to a static item that is used to @@ -2164,10 +2156,7 @@ pub enum BinOp { impl BinOp { pub fn is_checkable(self) -> bool { use self::BinOp::*; - match self { - Add | Sub | Mul | Shl | Shr => true, - _ => false, - } + matches!(self, Add | Sub | Mul | Shl | Shr) } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index a008bd5f75f..9a6bfa10189 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -752,7 +752,7 @@ macro_rules! make_mir_visitor { } fn super_coverage(&mut self, - _kind: & $($mutability)? Coverage, + _coverage: & $($mutability)? Coverage, _location: Location) { } @@ -1164,82 +1164,63 @@ pub enum PlaceContext { impl PlaceContext { /// Returns `true` if this place context represents a drop. pub fn is_drop(&self) -> bool { - match *self { - PlaceContext::MutatingUse(MutatingUseContext::Drop) => true, - _ => false, - } + matches!(self, PlaceContext::MutatingUse(MutatingUseContext::Drop)) } /// Returns `true` if this place context represents a borrow. pub fn is_borrow(&self) -> bool { - match *self { + matches!( + self, PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow - | NonMutatingUseContext::ShallowBorrow - | NonMutatingUseContext::UniqueBorrow, - ) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => true, - _ => false, - } + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::UniqueBorrow + ) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) } /// Returns `true` if this place context represents a storage live or storage dead marker. pub fn is_storage_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => true, - _ => false, - } + matches!( + self, + PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) + ) } /// Returns `true` if this place context represents a storage live marker. pub fn is_storage_live_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageLive) => true, - _ => false, - } + matches!(self, PlaceContext::NonUse(NonUseContext::StorageLive)) } /// Returns `true` if this place context represents a storage dead marker. pub fn is_storage_dead_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageDead) => true, - _ => false, - } + matches!(self, PlaceContext::NonUse(NonUseContext::StorageDead)) } /// Returns `true` if this place context represents a use that potentially changes the value. pub fn is_mutating_use(&self) -> bool { - match *self { - PlaceContext::MutatingUse(..) => true, - _ => false, - } + matches!(self, PlaceContext::MutatingUse(..)) } /// Returns `true` if this place context represents a use that does not change the value. pub fn is_nonmutating_use(&self) -> bool { - match *self { - PlaceContext::NonMutatingUse(..) => true, - _ => false, - } + matches!(self, PlaceContext::NonMutatingUse(..)) } /// Returns `true` if this place context represents a use. pub fn is_use(&self) -> bool { - match *self { - PlaceContext::NonUse(..) => false, - _ => true, - } + !matches!(self, PlaceContext::NonUse(..)) } /// Returns `true` if this place context represents an assignment statement. pub fn is_place_assignment(&self) -> bool { - match *self { + matches!( + self, PlaceContext::MutatingUse( MutatingUseContext::Store - | MutatingUseContext::Call - | MutatingUseContext::AsmOutput, - ) => true, - _ => false, - } + | MutatingUseContext::Call + | MutatingUseContext::AsmOutput, + ) + ) } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d5b99ea4d28..fc4c343372a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -156,12 +156,11 @@ rustc_queries! { cache_on_disk_if { key.is_local() } } - /// Returns the list of predicates that can be used for - /// `SelectionCandidate::ProjectionCandidate` and + /// Returns the list of bounds that can be used for + /// `SelectionCandidate::ProjectionCandidate(_)` and /// `ProjectionTyCandidate::TraitDef`. - /// Specifically this is the bounds (equivalent to) those - /// written on the trait's type definition, or those - /// after the `impl` keyword + /// Specifically this is the bounds written on the trait's type + /// definition, or those after the `impl` keyword /// /// type X: Bound + 'lt /// ^^^^^^^^^^^ @@ -169,8 +168,30 @@ rustc_queries! { /// ^^^^^^^^^^^^^^^ /// /// `key` is the `DefId` of the associated type or opaque type. - query projection_predicates(key: DefId) -> &'tcx ty::List<ty::Predicate<'tcx>> { - desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) } + /// + /// Bounds from the parent (e.g. with nested impl trait) are not included. + query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + } + + /// Elaborated version of the predicates from `explicit_item_bounds`. + /// + /// Example for + /// + /// trait MyTrait { + /// type MyAType: Eq + ?Sized` + /// } + /// + /// `explicit_item_bounds` returns `[<Self as MyTrait>::MyAType: Eq]`, + /// and `item_bounds` returns + /// [ + /// <Self as Trait>::MyAType: Eq, + /// <Self as Trait>::MyAType: PartialEq<<Self as Trait>::MyAType> + /// ] + /// + /// Bounds from the parent (e.g. with nested impl trait) are not included. + query item_bounds(key: DefId) -> &'tcx ty::List<ty::Predicate<'tcx>> { + desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } query projection_ty_from_predicates(key: (DefId, DefId)) -> Option<ty::ProjectionTy<'tcx>> { @@ -191,6 +212,11 @@ rustc_queries! { eval_always desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } } + + /// Internal helper query. Use `tcx.expansion_that_defined` instead + query expn_that_defined(key: DefId) -> rustc_span::ExpnId { + desc { |tcx| "expansion that defined `{}`", tcx.def_path_str(key) } + } } Codegen { @@ -365,6 +391,24 @@ rustc_queries! { desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } } + /// Returns everything that looks like a predicate written explicitly + /// by the user on a trait item. + /// + /// Traits are unusual, because predicates on associated types are + /// converted into bounds on that type for backwards compatibility: + /// + /// trait X where Self::U: Copy { type U; } + /// + /// becomes + /// + /// trait X { type U: Copy; } + /// + /// `explicit_predicates_of` and `explicit_item_bounds` will then take + /// the appropriate subsets of the predicates here. + query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key.to_def_id()) } + } + /// Returns the predicates written explicitly by the user. query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 6ad514c6be2..358ead507b4 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -105,9 +105,10 @@ pub enum SelectionCandidate<'tcx> { ImplCandidate(DefId), AutoImplCandidate(DefId), - /// This is a trait matching with a projected type as `Self`, and - /// we found an applicable bound in the trait definition. - ProjectionCandidate, + /// This is a trait matching with a projected type as `Self`, and we found + /// an applicable bound in the trait definition. The `usize` is an index + /// into the list returned by `tcx.item_bounds`. + ProjectionCandidate(usize), /// Implementation of a `Fn`-family trait by one of the anonymous types /// generated for a `||` expression. diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 969404c68ca..ec6010e6eec 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -79,10 +79,7 @@ pub enum Node { impl<'tcx> Node { pub fn is_from_trait(&self) -> bool { - match *self { - Node::Trait(..) => true, - _ => false, - } + matches!(self, Node::Trait(..)) } /// Iterate over the items defined directly by the given (impl or trait) node. diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 46ef5ff7dd8..89d0e139551 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -85,10 +85,7 @@ pub struct Adjustment<'tcx> { impl Adjustment<'tcx> { pub fn is_region_borrow(&self) -> bool { - match self.kind { - Adjust::Borrow(AutoBorrow::Ref(..)) => true, - _ => false, - } + matches!(self.kind, Adjust::Borrow(AutoBorrow::Ref(..))) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 22c3fd37be1..e67a76f0111 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -588,10 +588,7 @@ impl<'tcx> TypeckResults<'tcx> { return false; } - match self.type_dependent_defs().get(expr.hir_id) { - Some(Ok((DefKind::AssocFn, _))) => true, - _ => false, - } + matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option<BindingMode> { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 715319747e3..65703d04c70 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -11,21 +11,16 @@ use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; impl<'tcx> TyS<'tcx> { /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. pub fn is_primitive_ty(&self) -> bool { - match self.kind() { - Bool - | Char - | Str - | Int(_) - | Uint(_) - | Float(_) + matches!( + self.kind(), + Bool | Char | Str | Int(_) | Uint(_) | Float(_) | Infer( InferTy::IntVar(_) | InferTy::FloatVar(_) | InferTy::FreshIntTy(_) - | InferTy::FreshFloatTy(_), - ) => true, - _ => false, - } + | InferTy::FreshFloatTy(_) + ) + ) } /// Whether the type is succinctly representable as a type instead of just referred to with a @@ -64,11 +59,16 @@ impl<'tcx> TyS<'tcx> { /// Whether the type can be safely suggested during error recovery. pub fn is_suggestable(&self) -> bool { - match self.kind() { - Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) - | Projection(..) => false, - _ => true, - } + !matches!( + self.kind(), + Opaque(..) + | FnDef(..) + | FnPtr(..) + | Dynamic(..) + | Closure(..) + | Infer(..) + | Projection(..) + ) } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 9bb1c0ed7de..f8e8c209d37 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -184,10 +184,10 @@ impl<'tcx> InstanceDef<'tcx> { ty::InstanceDef::DropGlue(_, Some(_)) => return false, _ => return true, }; - match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::Ctor | DefPathData::ClosureExpr => true, - _ => false, - } + matches!( + tcx.def_key(def_id).disambiguated_data.data, + DefPathData::Ctor | DefPathData::ClosureExpr + ) } /// Returns `true` if the machine code for this instance is instantiated in diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index b61fde32c9c..f6f71d002a8 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2610,10 +2610,7 @@ where target.target_os == "linux" && target.arch == "sparc64" && target_env_gnu_like; let linux_powerpc_gnu_like = target.target_os == "linux" && target.arch == "powerpc" && target_env_gnu_like; - let rust_abi = match sig.abi { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, - _ => false, - }; + let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall); // Handle safe Rust thin and fat pointers. let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b7530c077cc..f069faed9e2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2681,15 +2681,15 @@ impl<'tcx> ClosureKind { /// Returns `true` if a type that impls this closure kind /// must also implement `other`. pub fn extends(self, other: ty::ClosureKind) -> bool { - match (self, other) { - (ClosureKind::Fn, ClosureKind::Fn) => true, - (ClosureKind::Fn, ClosureKind::FnMut) => true, - (ClosureKind::Fn, ClosureKind::FnOnce) => true, - (ClosureKind::FnMut, ClosureKind::FnMut) => true, - (ClosureKind::FnMut, ClosureKind::FnOnce) => true, - (ClosureKind::FnOnce, ClosureKind::FnOnce) => true, - _ => false, - } + matches!( + (self, other), + (ClosureKind::Fn, ClosureKind::Fn) + | (ClosureKind::Fn, ClosureKind::FnMut) + | (ClosureKind::Fn, ClosureKind::FnOnce) + | (ClosureKind::FnMut, ClosureKind::FnMut) + | (ClosureKind::FnMut, ClosureKind::FnOnce) + | (ClosureKind::FnOnce, ClosureKind::FnOnce) + ) } /// Returns the representative scalar type for this closure kind. @@ -2815,15 +2815,15 @@ impl<'tcx> TyCtxt<'tcx> { pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> { let is_associated_item = if let Some(def_id) = def_id.as_local() { - match self.hir().get(self.hir().local_def_id_to_hir_id(def_id)) { - Node::TraitItem(_) | Node::ImplItem(_) => true, - _ => false, - } + matches!( + self.hir().get(self.hir().local_def_id_to_hir_id(def_id)), + Node::TraitItem(_) | Node::ImplItem(_) + ) } else { - match self.def_kind(def_id) { - DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => true, - _ => false, - } + matches!( + self.def_kind(def_id), + DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy + ) }; is_associated_item.then(|| self.associated_item(def_id)) @@ -3034,10 +3034,12 @@ impl<'tcx> TyCtxt<'tcx> { .hygienic_eq(def_name.span.ctxt(), self.expansion_that_defined(def_parent_def_id)) } - fn expansion_that_defined(self, scope: DefId) -> ExpnId { + pub fn expansion_that_defined(self, scope: DefId) -> ExpnId { match scope.as_local() { + // Parsing and expansion aren't incremental, so we don't + // need to go through a query for the same-crate case. Some(scope) => self.hir().definitions().expansion_that_defined(scope), - None => ExpnId::root(), + None => self.expn_that_defined(scope), } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 238bce94cf5..e1f02d0f704 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -607,12 +607,13 @@ pub trait PrettyPrinter<'tcx>: } // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the projections associated with the def_id. - let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs); + let bounds = self.tcx().explicit_item_bounds(def_id); let mut first = true; let mut is_sized = false; p!("impl"); - for predicate in bounds.predicates { + for (predicate, _) in bounds { + let predicate = predicate.subst(self.tcx(), substs); // Note: We can't use `to_opt_poly_trait_ref` here as `predicate` // may contain unbound variables. We therefore do this manually. // diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 724ec101b23..1af56972ad0 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1513,6 +1513,9 @@ impl<'tcx> ExistentialProjection<'tcx> { /// then this function would return a `exists T. T: Iterator` existential trait /// reference. pub fn trait_ref(&self, tcx: TyCtxt<'_>) -> ty::ExistentialTraitRef<'tcx> { + // FIXME(generic_associated_types): substs is the substs of the + // associated type, which should be truncated to get the correct substs + // for the trait. let def_id = tcx.associated_item(self.item_def_id).container.id(); ty::ExistentialTraitRef { def_id, substs: self.substs } } @@ -1763,10 +1766,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_never(&self) -> bool { - match self.kind() { - Never => true, - _ => false, - } + matches!(self.kind(), Never) } /// Checks whether a type is definitely uninhabited. This is @@ -1823,34 +1823,22 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_adt(&self) -> bool { - match self.kind() { - Adt(..) => true, - _ => false, - } + matches!(self.kind(), Adt(..)) } #[inline] pub fn is_ref(&self) -> bool { - match self.kind() { - Ref(..) => true, - _ => false, - } + matches!(self.kind(), Ref(..)) } #[inline] pub fn is_ty_var(&self) -> bool { - match self.kind() { - Infer(TyVar(_)) => true, - _ => false, - } + matches!(self.kind(), Infer(TyVar(_))) } #[inline] pub fn is_ty_infer(&self) -> bool { - match self.kind() { - Infer(_) => true, - _ => false, - } + matches!(self.kind(), Infer(_)) } #[inline] @@ -1880,20 +1868,14 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_slice(&self) -> bool { match self.kind() { - RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind() { - Slice(_) | Str => true, - _ => false, - }, + RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_) | Str), _ => false, } } #[inline] pub fn is_array(&self) -> bool { - match self.kind() { - Array(..) => true, - _ => false, - } + matches!(self.kind(), Array(..)) } #[inline] @@ -1940,27 +1922,21 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_region_ptr(&self) -> bool { - match self.kind() { - Ref(..) => true, - _ => false, - } + matches!(self.kind(), Ref(..)) } #[inline] pub fn is_mutable_ptr(&self) -> bool { - match self.kind() { + matches!( + self.kind(), RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. }) - | Ref(_, _, hir::Mutability::Mut) => true, - _ => false, - } + | Ref(_, _, hir::Mutability::Mut) + ) } #[inline] pub fn is_unsafe_ptr(&self) -> bool { - match self.kind() { - RawPtr(_) => true, - _ => false, - } + matches!(self.kind(), RawPtr(_)) } /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). @@ -1990,35 +1966,22 @@ impl<'tcx> TyS<'tcx> { /// contents are abstract to rustc.) #[inline] pub fn is_scalar(&self) -> bool { - match self.kind() { - Bool - | Char - | Int(_) - | Float(_) - | Uint(_) + matches!( + self.kind(), + Bool | Char | Int(_) | Float(_) | Uint(_) | FnDef(..) | FnPtr(_) | RawPtr(_) | Infer(IntVar(_) | FloatVar(_)) - | FnDef(..) - | FnPtr(_) - | RawPtr(_) => true, - _ => false, - } + ) } /// Returns `true` if this type is a floating point type. #[inline] pub fn is_floating_point(&self) -> bool { - match self.kind() { - Float(_) | Infer(FloatVar(_)) => true, - _ => false, - } + matches!(self.kind(), Float(_) | Infer(FloatVar(_))) } #[inline] pub fn is_trait(&self) -> bool { - match self.kind() { - Dynamic(..) => true, - _ => false, - } + matches!(self.kind(), Dynamic(..)) } #[inline] @@ -2031,52 +1994,32 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_closure(&self) -> bool { - match self.kind() { - Closure(..) => true, - _ => false, - } + matches!(self.kind(), Closure(..)) } #[inline] pub fn is_generator(&self) -> bool { - match self.kind() { - Generator(..) => true, - _ => false, - } + matches!(self.kind(), Generator(..)) } #[inline] pub fn is_integral(&self) -> bool { - match self.kind() { - Infer(IntVar(_)) | Int(_) | Uint(_) => true, - _ => false, - } + matches!(self.kind(), Infer(IntVar(_)) | Int(_) | Uint(_)) } #[inline] pub fn is_fresh_ty(&self) -> bool { - match self.kind() { - Infer(FreshTy(_)) => true, - _ => false, - } + matches!(self.kind(), Infer(FreshTy(_))) } #[inline] pub fn is_fresh(&self) -> bool { - match self.kind() { - Infer(FreshTy(_)) => true, - Infer(FreshIntTy(_)) => true, - Infer(FreshFloatTy(_)) => true, - _ => false, - } + matches!(self.kind(), Infer(FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_))) } #[inline] pub fn is_char(&self) -> bool { - match self.kind() { - Char => true, - _ => false, - } + matches!(self.kind(), Char) } #[inline] @@ -2086,34 +2029,22 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_signed(&self) -> bool { - match self.kind() { - Int(_) => true, - _ => false, - } + matches!(self.kind(), Int(_)) } #[inline] pub fn is_ptr_sized_integral(&self) -> bool { - match self.kind() { - Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize) => true, - _ => false, - } + matches!(self.kind(), Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize)) } #[inline] pub fn is_machine(&self) -> bool { - match self.kind() { - Int(..) | Uint(..) | Float(..) => true, - _ => false, - } + matches!(self.kind(), Int(..) | Uint(..) | Float(..)) } #[inline] pub fn has_concrete_skeleton(&self) -> bool { - match self.kind() { - Param(_) | Infer(_) | Error(_) => false, - _ => true, - } + !matches!(self.kind(), Param(_) | Infer(_) | Error(_)) } /// Returns the type and mutability of `*ty`. @@ -2156,26 +2087,17 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fn(&self) -> bool { - match self.kind() { - FnDef(..) | FnPtr(_) => true, - _ => false, - } + matches!(self.kind(), FnDef(..) | FnPtr(_)) } #[inline] pub fn is_fn_ptr(&self) -> bool { - match self.kind() { - FnPtr(_) => true, - _ => false, - } + matches!(self.kind(), FnPtr(_)) } #[inline] pub fn is_impl_trait(&self) -> bool { - match self.kind() { - Opaque(..) => true, - _ => false, - } + matches!(self.kind(), Opaque(..)) } #[inline] diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs index b145e1d5fa3..e22dab01517 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs @@ -6,6 +6,7 @@ use rustc_infer::infer::{ error_reporting::unexpected_hidden_region_diagnostic, NLLRegionVariableOrigin, }; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; @@ -585,14 +586,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // // eg. check for `impl Trait + 'static` instead of `impl Trait`. let has_static_predicate = { - let predicates_of = self.infcx.tcx.predicates_of(did); - let bounds = predicates_of.instantiate(self.infcx.tcx, substs); + let bounds = self.infcx.tcx.explicit_item_bounds(did); let mut found = false; - for predicate in bounds.predicates { + for (bound, _) in bounds { if let ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(_, r)) = - predicate.skip_binders() + bound.skip_binders() { + let r = r.subst(self.infcx.tcx, substs); if let ty::RegionKind::ReStatic = r { found = true; break; diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs index 5492e328535..47726632727 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs @@ -1225,7 +1225,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// it. However, it works pretty well in practice. In particular, /// this is needed to deal with projection outlives bounds like /// - /// <T as Foo<'0>>::Item: '1 + /// ```ignore (internal compiler representation so lifetime syntax is invalid) + /// <T as Foo<'0>>::Item: '1 + /// ``` /// /// In particular, this routine winds up being important when /// there are bounds like `where <T as Foo<'a>>::Item: 'b` in the diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs index a45b0e86fff..237a5c7864b 100644 --- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -26,7 +26,7 @@ pub struct EarlyOtherwiseBranch; impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 1 { + if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { return; } trace!("running EarlyOtherwiseBranch on {:?}", body.source); diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index ced8a2289d5..bec1eb79047 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -45,8 +45,12 @@ impl<'tcx> MirPass<'tcx> for Inline { // based function. debug!("function inlining is disabled when compiling with `instrument_coverage`"); } else { - Inliner { tcx, codegen_fn_attrs: tcx.codegen_fn_attrs(body.source.def_id()) } - .run_pass(body); + Inliner { + tcx, + param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()), + codegen_fn_attrs: tcx.codegen_fn_attrs(body.source.def_id()), + } + .run_pass(body); } } } @@ -54,6 +58,7 @@ impl<'tcx> MirPass<'tcx> for Inline { struct Inliner<'tcx> { tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, codegen_fn_attrs: &'tcx CodegenFnAttrs, } @@ -75,17 +80,13 @@ impl Inliner<'tcx> { let def_id = caller_body.source.def_id(); - let param_env = self.tcx.param_env_reveal_all_normalized(def_id); - // Only do inlining into fn bodies. let self_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); if self.tcx.hir().body_owner_kind(self_hir_id).is_fn_or_closure() && caller_body.source.promoted.is_none() { for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated() { - if let Some(callsite) = - self.get_valid_function_call(bb, bb_data, caller_body, param_env) - { + if let Some(callsite) = self.get_valid_function_call(bb, bb_data, caller_body) { callsites.push_back(callsite); } } @@ -131,7 +132,7 @@ impl Inliner<'tcx> { let callee_body = if self.consider_optimizing(callsite, callee_body) { self.tcx.subst_and_normalize_erasing_regions( &callsite.substs, - param_env, + self.param_env, callee_body, ) } else { @@ -159,7 +160,7 @@ impl Inliner<'tcx> { // Add callsites from inlined function for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated().skip(start) { if let Some(new_callsite) = - self.get_valid_function_call(bb, bb_data, caller_body, param_env) + self.get_valid_function_call(bb, bb_data, caller_body) { // Don't inline the same function multiple times. if callsite.callee != new_callsite.callee { @@ -190,7 +191,6 @@ impl Inliner<'tcx> { bb: BasicBlock, bb_data: &BasicBlockData<'tcx>, caller_body: &Body<'tcx>, - param_env: ParamEnv<'tcx>, ) -> Option<CallSite<'tcx>> { // Don't inline calls that are in cleanup blocks. if bb_data.is_cleanup { @@ -201,8 +201,9 @@ impl Inliner<'tcx> { let terminator = bb_data.terminator(); if let TerminatorKind::Call { func: ref op, .. } = terminator.kind { if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() { - let instance = - Instance::resolve(self.tcx, param_env, callee_def_id, substs).ok().flatten()?; + let instance = Instance::resolve(self.tcx, self.param_env, callee_def_id, substs) + .ok() + .flatten()?; if let InstanceDef::Virtual(..) = instance.def { return None; @@ -300,9 +301,6 @@ impl Inliner<'tcx> { debug!(" final inline threshold = {}", threshold); // FIXME: Give a bonus to functions with only a single caller - - let param_env = tcx.param_env(callee_body.source.def_id()); - let mut first_block = true; let mut cost = 0; @@ -335,7 +333,7 @@ impl Inliner<'tcx> { // If the place doesn't actually need dropping, treat it like // a regular goto. let ty = place.ty(callee_body, tcx).subst(tcx, callsite.substs).ty; - if ty.needs_drop(tcx, param_env) { + if ty.needs_drop(tcx, self.param_env) { cost += CALL_PENALTY; if let Some(unwind) = unwind { cost += LANDINGPAD_PENALTY; @@ -400,7 +398,7 @@ impl Inliner<'tcx> { let ty = v.ty.subst(tcx, callsite.substs); // Cost of the var is the size in machine-words, if we know // it. - if let Some(size) = type_size_of(tcx, param_env, ty) { + if let Some(size) = type_size_of(tcx, self.param_env, ty) { cost += (size / ptr_size) as usize; } else { cost += UNKNOWN_SIZE_COST; diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index 388fb90651c..babe10a0f14 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -1,26 +1,34 @@ use crate::transform::MirPass; use crate::util::pretty; -use crate::util::spanview::{ - source_range_no_file, statement_kind_name, terminator_kind_name, write_spanview_document, - SpanViewable, TOOLTIP_INDENT, -}; +use crate::util::spanview::{self, SpanViewable}; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; use rustc_middle::hir; +use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ich::StableHashingContext; use rustc_middle::mir; use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Coverage, CoverageInfo, Location, Statement, StatementKind, - TerminatorKind, + AggregateKind, BasicBlock, BasicBlockData, Coverage, CoverageInfo, FakeReadCause, Location, + Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; -use rustc_span::{FileName, Pos, RealFileName, Span, Symbol}; +use rustc_span::source_map::original_sp; +use rustc_span::{ + BytePos, CharPos, FileName, Pos, RealFileName, SourceFile, Span, Symbol, SyntaxContext, +}; + +use std::cmp::Ordering; + +const ID_SEPARATOR: &str = ","; /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen @@ -33,6 +41,21 @@ pub(crate) fn provide(providers: &mut Providers) { providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); } +/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in +/// other words, the number of counter value references injected into the MIR (plus 1 for the +/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected +/// counters have a counter ID from `1..num_counters-1`. +/// +/// `num_expressions` is the number of counter expressions added to the MIR body. +/// +/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend +/// code generate, to lookup counters and expressions by simple u32 indexes. +/// +/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code +/// including injected counters. (It is OK if some counters are optimized out, but those counters +/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the +/// calls may not work; but computing the number of counters or expressions by adding `1` to the +/// highest ID (for a given instrumented function) is valid. struct CoverageVisitor { info: CoverageInfo, } @@ -57,15 +80,6 @@ impl Visitor<'_> for CoverageVisitor { fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { let mir_body = tcx.optimized_mir(def_id); - // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected - // counters, with each counter having a counter ID from `0..num_counters-1`. MIR optimization - // may split and duplicate some BasicBlock sequences. Simply counting the calls may not - // work; but computing the num_counters by adding `1` to the highest counter_id (for a given - // instrumented function) is valid. - // - // `num_expressions` is the number of counter expressions added to the MIR body. Both - // `num_counters` and `num_expressions` are used to initialize new vectors, during backend - // code generate, to lookup counters and expressions by simple u32 indexes. let mut coverage_visitor = CoverageVisitor { info: CoverageInfo { num_counters: 0, num_expressions: 0 } }; @@ -77,25 +91,399 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { // If the InstrumentCoverage pass is called on promoted MIRs, skip them. // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 - if mir_body.source.promoted.is_none() { - Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); + if mir_body.source.promoted.is_some() { + trace!( + "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)", + mir_body.source.def_id() + ); + return; } + + let hir_id = tcx.hir().local_def_id_to_hir_id(mir_body.source.def_id().expect_local()); + let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + + // Only instrument functions, methods, and closures (not constants since they are evaluated + // at compile time by Miri). + // FIXME(#73156): Handle source code coverage in const eval + if !is_fn_like { + trace!( + "InstrumentCoverage skipped for {:?} (not an FnLikeNode)", + mir_body.source.def_id(), + ); + return; + } + // FIXME(richkadel): By comparison, the MIR pass `ConstProp` includes associated constants, + // with functions, methods, and closures. I assume Miri is used for associated constants as + // well. If not, we may need to include them here too. + + trace!("InstrumentCoverage starting for {:?}", mir_body.source.def_id()); + Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); + trace!("InstrumentCoverage starting for {:?}", mir_body.source.def_id()); } } -#[derive(Clone)] -struct CoverageRegion { - pub span: Span, +/// A BasicCoverageBlock (BCB) represents the maximal-length sequence of CFG (MIR) BasicBlocks +/// without conditional branches. +/// +/// The BCB allows coverage analysis to be performed on a simplified projection of the underlying +/// MIR CFG, without altering the original CFG. Note that running the MIR `SimplifyCfg` transform, +/// is not sufficient, and therefore not necessary, since the BCB-based CFG projection is a more +/// aggressive simplification. For example: +/// +/// * The BCB CFG projection ignores (trims) branches not relevant to coverage, such as unwind- +/// related code that is injected by the Rust compiler but has no physical source code to +/// count. This also means a BasicBlock with a `Call` terminator can be merged into its +/// primary successor target block, in the same BCB. +/// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are +/// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as +/// a `Goto`, and merged with its successor into the same BCB. +/// +/// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`. +/// In some cases, a BCB's execution count can be computed by `CounterExpression`. Additional +/// disjoint `CoverageSpan`s in a BCB can also be counted by `CounterExpression` (by adding `ZERO` +/// to the BCB's primary counter or expression). +/// +/// Dominator/dominated relationships (which are fundamental to the coverage analysis algorithm) +/// between two BCBs can be computed using the `mir::Body` `dominators()` with any `BasicBlock` +/// member of each BCB. (For consistency, BCB's use the first `BasicBlock`, also referred to as the +/// `bcb_leader_bb`.) +/// +/// The BCB CFG projection is critical to simplifying the coverage analysis by ensuring graph +/// path-based queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch +/// (control flow) significance. +#[derive(Debug, Clone)] +struct BasicCoverageBlock { pub blocks: Vec<BasicBlock>, } +impl BasicCoverageBlock { + pub fn leader_bb(&self) -> BasicBlock { + self.blocks[0] + } + + pub fn id(&self) -> String { + format!( + "@{}", + self.blocks + .iter() + .map(|bb| bb.index().to_string()) + .collect::<Vec<_>>() + .join(ID_SEPARATOR) + ) + } +} + +struct BasicCoverageBlocks { + vec: IndexVec<BasicBlock, Option<BasicCoverageBlock>>, +} + +impl BasicCoverageBlocks { + pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { + let mut basic_coverage_blocks = + BasicCoverageBlocks { vec: IndexVec::from_elem_n(None, mir_body.basic_blocks().len()) }; + basic_coverage_blocks.extract_from_mir(mir_body); + basic_coverage_blocks + } + + pub fn iter(&self) -> impl Iterator<Item = &BasicCoverageBlock> { + self.vec.iter().filter_map(|option| option.as_ref()) + } + + fn extract_from_mir(&mut self, mir_body: &mir::Body<'tcx>) { + // Traverse the CFG but ignore anything following an `unwind` + let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { + let mut successors = term_kind.successors(); + match &term_kind { + // SwitchInt successors are never unwind, and all of them should be traversed. + + // NOTE: TerminatorKind::FalseEdge targets from SwitchInt don't appear to be + // helpful in identifying unreachable code. I did test the theory, but the following + // changes were not beneficial. (I assumed that replacing some constants with + // non-deterministic variables might effect which blocks were targeted by a + // `FalseEdge` `imaginary_target`. It did not.) + // + // Also note that, if there is a way to identify BasicBlocks that are part of the + // MIR CFG, but not actually reachable, here are some other things to consider: + // + // Injecting unreachable code regions will probably require computing the set + // difference between the basic blocks found without filtering out unreachable + // blocks, and the basic blocks found with the filter; then computing the + // `CoverageSpans` without the filter; and then injecting `Counter`s or + // `CounterExpression`s for blocks that are not unreachable, or injecting + // `Unreachable` code regions otherwise. This seems straightforward, but not + // trivial. + // + // Alternatively, we might instead want to leave the unreachable blocks in + // (bypass the filter here), and inject the counters. This will result in counter + // values of zero (0) for unreachable code (and, notably, the code will be displayed + // with a red background by `llvm-cov show`). + // + // TerminatorKind::SwitchInt { .. } => { + // let some_imaginary_target = successors.clone().find_map(|&successor| { + // let term = mir_body.basic_blocks()[successor].terminator(); + // if let TerminatorKind::FalseEdge { imaginary_target, .. } = term.kind { + // if mir_body.predecessors()[imaginary_target].len() == 1 { + // return Some(imaginary_target); + // } + // } + // None + // }); + // if let Some(imaginary_target) = some_imaginary_target { + // box successors.filter(move |&&successor| successor != imaginary_target) + // } else { + // box successors + // } + // } + // + // Note this also required changing the closure signature for the + // `ShortCurcuitPreorder` to: + // + // F: Fn(&'tcx TerminatorKind<'tcx>) -> Box<dyn Iterator<Item = &BasicBlock> + 'a>, + TerminatorKind::SwitchInt { .. } => successors, + + // For all other kinds, return only the first successor, if any, and ignore unwinds + _ => successors.next().into_iter().chain(&[]), + } + }); + + // Walk the CFG using a Preorder traversal, which starts from `START_BLOCK` and follows + // each block terminator's `successors()`. Coverage spans must map to actual source code, + // so compiler generated blocks and paths can be ignored. To that end the CFG traversal + // intentionally omits unwind paths. + let mut blocks = Vec::new(); + for (bb, data) in cfg_without_unwind { + if let Some(last) = blocks.last() { + let predecessors = &mir_body.predecessors()[bb]; + if predecessors.len() > 1 || !predecessors.contains(last) { + // The `bb` has more than one _incoming_ edge, and should start its own + // `BasicCoverageBlock`. (Note, the `blocks` vector does not yet include `bb`; + // it contains a sequence of one or more sequential blocks with no intermediate + // branches in or out. Save these as a new `BasicCoverageBlock` before starting + // the new one.) + self.add_basic_coverage_block(blocks.split_off(0)); + debug!( + " because {}", + if predecessors.len() > 1 { + "predecessors.len() > 1".to_owned() + } else { + format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) + } + ); + } + } + blocks.push(bb); + + let term = data.terminator(); + + match term.kind { + TerminatorKind::Return { .. } + | TerminatorKind::Abort + | TerminatorKind::Assert { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::SwitchInt { .. } => { + // The `bb` has more than one _outgoing_ edge, or exits the function. Save the + // current sequence of `blocks` gathered to this point, as a new + // `BasicCoverageBlock`. + self.add_basic_coverage_block(blocks.split_off(0)); + debug!(" because term.kind = {:?}", term.kind); + // Note that this condition is based on `TerminatorKind`, even though it + // theoretically boils down to `successors().len() != 1`; that is, either zero + // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but + // since the Coverage graph (the BCB CFG projection) ignores things like unwind + // branches (which exist in the `Terminator`s `successors()` list) checking the + // number of successors won't work. + } + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} + } + } + + if !blocks.is_empty() { + // process any remaining blocks into a final `BasicCoverageBlock` + self.add_basic_coverage_block(blocks.split_off(0)); + debug!(" because the end of the CFG was reached while traversing"); + } + } + + fn add_basic_coverage_block(&mut self, blocks: Vec<BasicBlock>) { + let leader_bb = blocks[0]; + let bcb = BasicCoverageBlock { blocks }; + debug!("adding BCB: {:?}", bcb); + self.vec[leader_bb] = Some(bcb); + } +} + +impl std::ops::Index<BasicBlock> for BasicCoverageBlocks { + type Output = BasicCoverageBlock; + + fn index(&self, index: BasicBlock) -> &Self::Output { + self.vec[index].as_ref().expect("is_some if BasicBlock is a BasicCoverageBlock leader") + } +} + +#[derive(Debug, Copy, Clone)] +enum CoverageStatement { + Statement(BasicBlock, Span, usize), + Terminator(BasicBlock, Span), +} + +impl CoverageStatement { + pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { + match *self { + Self::Statement(bb, span, stmt_index) => { + let stmt = &mir_body.basic_blocks()[bb].statements[stmt_index]; + format!( + "{}: @{}[{}]: {:?}", + spanview::source_range_no_file(tcx, &span), + bb.index(), + stmt_index, + stmt + ) + } + Self::Terminator(bb, span) => { + let term = mir_body.basic_blocks()[bb].terminator(); + format!( + "{}: @{}.{}: {:?}", + spanview::source_range_no_file(tcx, &span), + bb.index(), + term_type(&term.kind), + term.kind + ) + } + } + } + + pub fn span(&self) -> &Span { + match self { + Self::Statement(_, span, _) | Self::Terminator(_, span) => span, + } + } +} + +fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { + match kind { + TerminatorKind::Goto { .. } => "Goto", + TerminatorKind::SwitchInt { .. } => "SwitchInt", + TerminatorKind::Resume => "Resume", + TerminatorKind::Abort => "Abort", + TerminatorKind::Return => "Return", + TerminatorKind::Unreachable => "Unreachable", + TerminatorKind::Drop { .. } => "Drop", + TerminatorKind::DropAndReplace { .. } => "DropAndReplace", + TerminatorKind::Call { .. } => "Call", + TerminatorKind::Assert { .. } => "Assert", + TerminatorKind::Yield { .. } => "Yield", + TerminatorKind::GeneratorDrop => "GeneratorDrop", + TerminatorKind::FalseEdge { .. } => "FalseEdge", + TerminatorKind::FalseUnwind { .. } => "FalseUnwind", + TerminatorKind::InlineAsm { .. } => "InlineAsm", + } +} + +/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that +/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. +/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent +/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the +/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s. +/// +/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that +/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches +/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` +/// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. +#[derive(Debug, Clone)] +struct CoverageSpan { + span: Span, + bcb_leader_bb: BasicBlock, + coverage_statements: Vec<CoverageStatement>, + is_closure: bool, +} + +impl CoverageSpan { + pub fn for_statement( + statement: &Statement<'tcx>, + span: Span, + bcb: &BasicCoverageBlock, + bb: BasicBlock, + stmt_index: usize, + ) -> Self { + let is_closure = match statement.kind { + StatementKind::Assign(box ( + _, + Rvalue::Aggregate(box AggregateKind::Closure(_, _), _), + )) => true, + _ => false, + }; + + Self { + span, + bcb_leader_bb: bcb.leader_bb(), + coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)], + is_closure, + } + } + + pub fn for_terminator(span: Span, bcb: &'a BasicCoverageBlock, bb: BasicBlock) -> Self { + Self { + span, + bcb_leader_bb: bcb.leader_bb(), + coverage_statements: vec![CoverageStatement::Terminator(bb, span)], + is_closure: false, + } + } + + pub fn merge_from(&mut self, mut other: CoverageSpan) { + debug_assert!(self.is_mergeable(&other)); + self.span = self.span.to(other.span); + if other.is_closure { + self.is_closure = true; + } + self.coverage_statements.append(&mut other.coverage_statements); + } + + pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { + self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos); + if let Some(highest_covstmt) = + self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi()) + { + self.span = self.span.with_hi(highest_covstmt.span().hi()); + } + } + + pub fn is_dominated_by( + &self, + other: &CoverageSpan, + dominators: &Dominators<BasicBlock>, + ) -> bool { + debug_assert!(!self.is_in_same_bcb(other)); + dominators.is_dominated_by(self.bcb_leader_bb, other.bcb_leader_bb) + } + + pub fn is_mergeable(&self, other: &Self) -> bool { + self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) + } + + pub fn is_in_same_bcb(&self, other: &Self) -> bool { + self.bcb_leader_bb == other.bcb_leader_bb + } +} + struct Instrumentor<'a, 'tcx> { pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>, + dominators: Option<Dominators<BasicBlock>>, + basic_coverage_blocks: Option<BasicCoverageBlocks>, function_source_hash: Option<u64>, - num_counters: u32, + next_counter_id: u32, num_expressions: u32, } @@ -107,17 +495,19 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { tcx, mir_body, hir_body, + dominators: None, + basic_coverage_blocks: None, function_source_hash: None, - num_counters: 0, + next_counter_id: CounterValueReference::START.as_u32(), num_expressions: 0, } } - /// Counter IDs start from zero and go up. + /// Counter IDs start from one and go up. fn next_counter(&mut self) -> CounterValueReference { - assert!(self.num_counters < u32::MAX - self.num_expressions); - let next = self.num_counters; - self.num_counters += 1; + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = self.next_counter_id; + self.next_counter_id += 1; CounterValueReference::from(next) } @@ -125,12 +515,22 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { /// (add or subtract counts) of both Counter regions and CounterExpression regions. The counter /// expression operand IDs must be unique across both types. fn next_expression(&mut self) -> InjectedExpressionIndex { - assert!(self.num_counters < u32::MAX - self.num_expressions); + assert!(self.next_counter_id < u32::MAX - self.num_expressions); let next = u32::MAX - self.num_expressions; self.num_expressions += 1; InjectedExpressionIndex::from(next) } + fn dominators(&self) -> &Dominators<BasicBlock> { + self.dominators.as_ref().expect("dominators must be initialized before calling") + } + + fn basic_coverage_blocks(&self) -> &BasicCoverageBlocks { + self.basic_coverage_blocks + .as_ref() + .expect("basic_coverage_blocks must be initialized before calling") + } + fn function_source_hash(&mut self) -> u64 { match self.function_source_hash { Some(hash) => hash, @@ -144,86 +544,61 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { fn inject_counters(&mut self) { let tcx = self.tcx; + let source_map = tcx.sess.source_map(); let def_id = self.mir_body.source.def_id(); let mir_body = &self.mir_body; - let body_span = self.hir_body.value.span; - debug!( - "instrumenting {:?}, span: {}", - def_id, - tcx.sess.source_map().span_to_string(body_span) - ); - - if !tcx.sess.opts.debugging_opts.experimental_coverage { - // Coverage at the function level should be accurate. This is the default implementation - // if `-Z experimental-coverage` is *NOT* enabled. - let block = rustc_middle::mir::START_BLOCK; - let counter = self.make_counter(); - self.inject_statement(counter, body_span, block); - return; - } - // FIXME(richkadel): else if `-Z experimental-coverage` *IS* enabled: Efforts are still in - // progress to identify the correct code region spans and associated counters to generate - // accurate Rust coverage reports. - - let block_span = |data: &BasicBlockData<'tcx>| { - // The default span will be the `Terminator` span; but until we have a smarter solution, - // the coverage region also incorporates at least the statements in this BasicBlock as - // well. Extend the span to encompass all, if possible. - // FIXME(richkadel): Assuming the terminator's span is already known to be contained in `body_span`. - let mut span = data.terminator().source_info.span; - // FIXME(richkadel): It's looking unlikely that we should compute a span from MIR - // spans, but if we do keep something like this logic, we will need a smarter way - // to combine `Statement`s and/or `Terminator`s with `Span`s from different - // files. - for statement_span in data.statements.iter().map(|statement| statement.source_info.span) - { - // Only combine Spans from the function's body_span. - if body_span.contains(statement_span) { - span = span.to(statement_span); - } - } - span + let body_span = self.body_span(); + let source_file = source_map.lookup_source_file(body_span.lo()); + let file_name = match &source_file.name { + FileName::Real(RealFileName::Named(path)) => Symbol::intern(&path.to_string_lossy()), + _ => bug!( + "source_file.name should be a RealFileName, but it was: {:?}", + source_file.name + ), }; - // Traverse the CFG but ignore anything following an `unwind` - let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { - let mut successors = term_kind.successors(); - match &term_kind { - // SwitchInt successors are never unwind, and all of them should be traversed - TerminatorKind::SwitchInt { .. } => successors, - // For all other kinds, return only the first successor, if any, and ignore unwinds - _ => successors.next().into_iter().chain(&[]), - } - }); + debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); - let mut coverage_regions = Vec::with_capacity(cfg_without_unwind.size_hint().0); - for (bb, data) in cfg_without_unwind { - if !body_span.contains(data.terminator().source_info.span) { - continue; - } + self.dominators.replace(mir_body.dominators()); + self.basic_coverage_blocks.replace(BasicCoverageBlocks::from_mir(mir_body)); - // FIXME(richkadel): Regions will soon contain multiple blocks. - let mut blocks = Vec::new(); - blocks.push(bb); - let span = block_span(data); - coverage_regions.push(CoverageRegion { span, blocks }); - } + let coverage_spans = self.coverage_spans(); let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) { - Some(self.span_viewables(&coverage_regions)) + Some(self.span_viewables(&coverage_spans)) } else { None }; - // Inject counters for the selected spans - for CoverageRegion { span, blocks } in coverage_regions { - debug!( - "Injecting counter at: {:?}:\n{}\n==========", - span, - tcx.sess.source_map().span_to_snippet(span).expect("Error getting source for span"), - ); - let counter = self.make_counter(); - self.inject_statement(counter, span, blocks[0]); + // Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a + // given BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` + // maps each `bcb_leader_bb` to its `Counter`, when injected. Subsequent `CoverageSpan`s + // for a BCB that already has a `Counter` will inject a `CounterExpression` instead, and + // compute its value by adding `ZERO` to the BCB `Counter` value. + let mut bb_counters = IndexVec::from_elem_n(None, mir_body.basic_blocks().len()); + for CoverageSpan { span, bcb_leader_bb: bb, .. } in coverage_spans { + if let Some(&counter_operand) = bb_counters[bb].as_ref() { + let expression = + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO); + debug!( + "Injecting counter expression {:?} at: {:?}:\n{}\n==========", + expression, + span, + source_map.span_to_snippet(span).expect("Error getting source for span"), + ); + self.inject_statement(file_name, &source_file, expression, span, bb); + } else { + let counter = self.make_counter(); + debug!( + "Injecting counter {:?} at: {:?}:\n{}\n==========", + counter, + span, + source_map.span_to_snippet(span).expect("Error getting source for span"), + ); + let counter_operand = counter.as_operand_id(); + bb_counters[bb] = Some(counter_operand); + self.inject_statement(file_name, &source_file, counter, span, bb); + } } if let Some(span_viewables) = span_viewables { @@ -236,36 +611,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { self.mir_body.source, ) .expect("Unexpected error creating MIR spanview HTML file"); - write_spanview_document(tcx, def_id, span_viewables, &mut file) + let crate_name = tcx.crate_name(def_id.krate); + let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); + let title = format!("{}.{} - Coverage Spans", crate_name, item_name); + spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) .expect("Unexpected IO error dumping coverage spans as HTML"); } - - // FIXME(richkadel): Some regions will be counted by "counter expression". Counter - // expressions are supported, but are not yet generated. When they are, remove this `fake_use` - // block. - let fake_use = false; - if fake_use { - let add = false; - let fake_counter = CoverageKind::Counter { - function_source_hash: self.function_source_hash(), - id: CounterValueReference::from_u32(1), - }; - let fake_expression = CoverageKind::Expression { - id: InjectedExpressionIndex::from(u32::MAX - 1), - lhs: ExpressionOperandId::from_u32(1), - op: Op::Add, - rhs: ExpressionOperandId::from_u32(2), - }; - - let lhs = fake_counter.as_operand_id(); - let op = if add { Op::Add } else { Op::Subtract }; - let rhs = fake_expression.as_operand_id(); - - let block = rustc_middle::mir::START_BLOCK; - - let expression = self.make_expression(lhs, op, rhs); - self.inject_statement(expression, body_span, block); - } } fn make_counter(&mut self) -> CoverageKind { @@ -284,8 +635,15 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { CoverageKind::Expression { id: self.next_expression(), lhs, op, rhs } } - fn inject_statement(&mut self, coverage_kind: CoverageKind, span: Span, block: BasicBlock) { - let code_region = make_code_region(self.tcx, &span); + fn inject_statement( + &mut self, + file_name: Symbol, + source_file: &Lrc<SourceFile>, + coverage_kind: CoverageKind, + span: Span, + block: BasicBlock, + ) { + let code_region = make_code_region(file_name, source_file, span); debug!(" injecting statement {:?} covering {:?}", coverage_kind, code_region); let data = &mut self.mir_body[block]; @@ -297,112 +655,548 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { data.statements.push(statement); } - /// Converts the computed `CoverageRegion`s into `SpanViewable`s. - fn span_viewables(&self, coverage_regions: &Vec<CoverageRegion>) -> Vec<SpanViewable> { + /// Converts the computed `BasicCoverageBlock`s into `SpanViewable`s. + fn span_viewables(&self, coverage_spans: &Vec<CoverageSpan>) -> Vec<SpanViewable> { + let tcx = self.tcx; + let mir_body = &self.mir_body; let mut span_viewables = Vec::new(); - for coverage_region in coverage_regions { - span_viewables.push(SpanViewable { - span: coverage_region.span, - id: format!("{}", coverage_region.blocks[0].index()), - tooltip: self.make_tooltip_text(coverage_region), + for coverage_span in coverage_spans { + let bcb = self.bcb_from_coverage_span(coverage_span); + let CoverageSpan { span, bcb_leader_bb: bb, coverage_statements, .. } = coverage_span; + let id = bcb.id(); + let mut sorted_coverage_statements = coverage_statements.clone(); + sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { + CoverageStatement::Statement(bb, _, index) => (bb, index), + CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), }); + let tooltip = sorted_coverage_statements + .iter() + .map(|covstmt| covstmt.format(tcx, mir_body)) + .collect::<Vec<_>>() + .join("\n"); + span_viewables.push(SpanViewable { bb: *bb, span: *span, id, tooltip }); } span_viewables } - /// A custom tooltip renderer used in a spanview HTML+CSS document used for coverage analysis. - fn make_tooltip_text(&self, coverage_region: &CoverageRegion) -> String { - const INCLUDE_COVERAGE_STATEMENTS: bool = false; - let tcx = self.tcx; - let source_map = tcx.sess.source_map(); - let mut text = Vec::new(); - for (i, &bb) in coverage_region.blocks.iter().enumerate() { - if i > 0 { - text.push("\n".to_owned()); + #[inline(always)] + fn bcb_from_coverage_span(&self, coverage_span: &CoverageSpan) -> &BasicCoverageBlock { + &self.basic_coverage_blocks()[coverage_span.bcb_leader_bb] + } + + #[inline(always)] + fn body_span(&self) -> Span { + self.hir_body.value.span + } + + // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of + // the `BasicBlock`(s) in the given `BasicCoverageBlock`. One `CoverageSpan` is generated for + // each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will + // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple + // `Statement`s and/or `Terminator`s.) + fn extract_spans(&self, bcb: &'a BasicCoverageBlock) -> Vec<CoverageSpan> { + let body_span = self.body_span(); + let mir_basic_blocks = self.mir_body.basic_blocks(); + bcb.blocks + .iter() + .map(|bbref| { + let bb = *bbref; + let data = &mir_basic_blocks[bb]; + data.statements + .iter() + .enumerate() + .filter_map(move |(index, statement)| { + filtered_statement_span(statement, body_span).map(|span| { + CoverageSpan::for_statement(statement, span, bcb, bb, index) + }) + }) + .chain( + filtered_terminator_span(data.terminator(), body_span) + .map(|span| CoverageSpan::for_terminator(span, bcb, bb)), + ) + }) + .flatten() + .collect() + } + + /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be + /// counted. + /// + /// The basic steps are: + /// + /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each + /// `BasicCoverageBlock`. + /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position + /// are sorted with longer spans before shorter spans; and equal spans are sorted + /// (deterministically) based on "dominator" relationship (if any). + /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, + /// if another span or spans are already counting the same code region), or should be merged + /// into a broader combined span (because it represents a contiguous, non-branching, and + /// uninterrupted region of source code). + /// + /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since + /// closures have their own MIR, their `Span` in their enclosing function should be left + /// "uncovered". + /// + /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need + /// to be). + fn coverage_spans(&self) -> Vec<CoverageSpan> { + let mut initial_spans = + Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks().len() * 2); + for bcb in self.basic_coverage_blocks().iter() { + for coverage_span in self.extract_spans(bcb) { + initial_spans.push(coverage_span); } - text.push(format!("{:?}: {}:", bb, &source_map.span_to_string(coverage_region.span))); - let data = &self.mir_body.basic_blocks()[bb]; - for statement in &data.statements { - let statement_string = match statement.kind { - StatementKind::Coverage(box ref coverage) => match coverage.kind { - CoverageKind::Counter { id, .. } => { - if !INCLUDE_COVERAGE_STATEMENTS { - continue; - } - format!("increment counter #{}", id.index()) - } - CoverageKind::Expression { id, lhs, op, rhs } => { - if !INCLUDE_COVERAGE_STATEMENTS { - continue; - } - format!( - "expression #{} = {} {} {}", - id.index(), - lhs.index(), - if op == Op::Add { "+" } else { "-" }, - rhs.index() - ) - } - CoverageKind::Unreachable => { - if !INCLUDE_COVERAGE_STATEMENTS { - continue; - } - String::from("unreachable") - } - }, - _ => format!("{:?}", statement), - }; - let source_range = source_range_no_file(tcx, &statement.source_info.span); - text.push(format!( - "\n{}{}: {}: {}", - TOOLTIP_INDENT, - source_range, - statement_kind_name(statement), - statement_string - )); + } + + if initial_spans.is_empty() { + // This can happen if, for example, the function is unreachable (contains only a + // `BasicBlock`(s) with an `Unreachable` terminator). + return initial_spans; + } + + initial_spans.sort_unstable_by(|a, b| { + if a.span.lo() == b.span.lo() { + if a.span.hi() == b.span.hi() { + if a.is_in_same_bcb(b) { + Some(Ordering::Equal) + } else { + // Sort equal spans by dominator relationship, in reverse order (so + // dominators always come after the dominated equal spans). When later + // comparing two spans in order, the first will either dominate the second, + // or they will have no dominator relationship. + self.dominators().rank_partial_cmp(b.bcb_leader_bb, a.bcb_leader_bb) + } + } else { + // Sort hi() in reverse order so shorter spans are attempted after longer spans. + // This guarantees that, if a `prev` span overlaps, and is not equal to, a `curr` + // span, the prev span either extends further left of the curr span, or they + // start at the same position and the prev span extends further right of the end + // of the curr span. + b.span.hi().partial_cmp(&a.span.hi()) + } + } else { + a.span.lo().partial_cmp(&b.span.lo()) } - let term = data.terminator(); - let source_range = source_range_no_file(tcx, &term.source_info.span); - text.push(format!( - "\n{}{}: {}: {:?}", - TOOLTIP_INDENT, - source_range, - terminator_kind_name(term), - term.kind - )); + .unwrap() + }); + + let refinery = CoverageSpanRefinery::from_sorted_spans(initial_spans, self.dominators()); + refinery.to_refined_spans() + } +} + +struct CoverageSpanRefinery<'a> { + sorted_spans_iter: std::vec::IntoIter<CoverageSpan>, + dominators: &'a Dominators<BasicBlock>, + some_curr: Option<CoverageSpan>, + curr_original_span: Span, + some_prev: Option<CoverageSpan>, + prev_original_span: Span, + pending_dups: Vec<CoverageSpan>, + refined_spans: Vec<CoverageSpan>, +} + +impl<'a> CoverageSpanRefinery<'a> { + fn from_sorted_spans( + sorted_spans: Vec<CoverageSpan>, + dominators: &'a Dominators<BasicBlock>, + ) -> Self { + let refined_spans = Vec::with_capacity(sorted_spans.len()); + let mut sorted_spans_iter = sorted_spans.into_iter(); + let prev = sorted_spans_iter.next().expect("at least one span"); + let prev_original_span = prev.span; + Self { + sorted_spans_iter, + dominators, + refined_spans, + some_curr: None, + curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), + some_prev: Some(prev), + prev_original_span, + pending_dups: Vec::new(), + } + } + + /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and + /// de-duplicated `CoverageSpan`s. + fn to_refined_spans(mut self) -> Vec<CoverageSpan> { + while self.next_coverage_span() { + if self.curr().is_mergeable(self.prev()) { + debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); + let prev = self.take_prev(); + self.curr_mut().merge_from(prev); + // Note that curr.span may now differ from curr_original_span + } else if self.prev_ends_before_curr() { + debug!( + " different bcbs and disjoint spans, so keep curr for next iter, and add \ + prev={:?}", + self.prev() + ); + let prev = self.take_prev(); + self.add_refined_span(prev); + } else if self.prev().is_closure { + // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the + // next iter + debug!( + " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ + prev={:?}", + self.prev() + ); + self.discard_curr(); + } else if self.curr().is_closure { + self.carve_out_span_for_closure(); + } else if self.prev_original_span == self.curr().span { + self.hold_pending_dups_unless_dominated(); + } else { + self.cutoff_prev_at_overlapping_curr(); + } + } + debug!(" AT END, adding last prev={:?}", self.prev()); + let pending_dups = self.pending_dups.split_off(0); + for dup in pending_dups.into_iter() { + debug!(" ...adding at least one pending dup={:?}", dup); + self.add_refined_span(dup); + } + let prev = self.take_prev(); + self.add_refined_span(prev); + + // FIXME(richkadel): Replace some counters with expressions if they can be calculated based + // on branching. (For example, one branch of a SwitchInt can be computed from the counter + // for the CoverageSpan just prior to the SwitchInt minus the sum of the counters of all + // other branches). + + self.to_refined_spans_without_closures() + } + + fn add_refined_span(&mut self, coverage_span: CoverageSpan) { + self.refined_spans.push(coverage_span); + } + + /// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage + /// regions for the current function leave room for the closure's own coverage regions + /// (injected separately, from the closure's own MIR). + fn to_refined_spans_without_closures(mut self) -> Vec<CoverageSpan> { + self.refined_spans.retain(|covspan| !covspan.is_closure); + self.refined_spans + } + + fn curr(&self) -> &CoverageSpan { + self.some_curr + .as_ref() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) + } + + fn curr_mut(&mut self) -> &mut CoverageSpan { + self.some_curr + .as_mut() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) + } + + fn prev(&self) -> &CoverageSpan { + self.some_prev + .as_ref() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + fn prev_mut(&mut self) -> &mut CoverageSpan { + self.some_prev + .as_mut() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + fn take_prev(&mut self) -> CoverageSpan { + self.some_prev.take().unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the + /// `pending_dups` spans), then one of the following two things happened during the previous + /// iteration: + /// * the `span` of prev was modified (by `curr_mut().merge_from(prev)`); or + /// * the `span` of prev advanced past the end of the span of pending_dups + /// (`prev().span.hi() <= curr().span.lo()`) + /// In either case, no more spans will match the span of `pending_dups`, so + /// add the `pending_dups` if they don't overlap `curr`, and clear the list. + fn check_pending_dups(&mut self) { + if let Some(dup) = self.pending_dups.last() { + if dup.span != self.prev().span { + debug!( + " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ + previous iteration, or prev started a new disjoint span" + ); + if dup.span.hi() <= self.curr().span.lo() { + let pending_dups = self.pending_dups.split_off(0); + for dup in pending_dups.into_iter() { + debug!(" ...adding at least one pending={:?}", dup); + self.add_refined_span(dup); + } + } else { + self.pending_dups.clear(); + } + } + } + } + + /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. + fn next_coverage_span(&mut self) -> bool { + if let Some(curr) = self.some_curr.take() { + self.some_prev = Some(curr); + self.prev_original_span = self.curr_original_span; + } + while let Some(curr) = self.sorted_spans_iter.next() { + debug!("FOR curr={:?}", curr); + if self.prev_starts_after_next(&curr) { + debug!( + " prev.span starts after curr.span, so curr will be dropped (skipping past \ + closure?); prev={:?}", + self.prev() + ); + } else { + // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed + // by `self.curr_mut().merge_from(prev)`. + self.curr_original_span = curr.span; + self.some_curr.replace(curr); + self.check_pending_dups(); + return true; + } + } + false + } + + /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the + /// `curr` coverage span. + fn discard_curr(&mut self) { + self.some_curr = None; + } + + /// Returns true if the curr span should be skipped because prev has already advanced beyond the + /// end of curr. This can only happen if a prior iteration updated `prev` to skip past a region + /// of code, such as skipping past a closure. + fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool { + self.prev().span.lo() > next_curr.span.lo() + } + + /// Returns true if the curr span starts past the end of the prev span, which means they don't + /// overlap, so we now know the prev can be added to the refined coverage spans. + fn prev_ends_before_curr(&self) -> bool { + self.prev().span.hi() <= self.curr().span.lo() + } + + /// If `prev`s span extends left of the closure (`curr`), carve out the closure's + /// span from `prev`'s span. (The closure's coverage counters will be injected when + /// processing the closure's own MIR.) Add the portion of the span to the left of the + /// closure; and if the span extends to the right of the closure, update `prev` to + /// that portion of the span. For any `pending_dups`, repeat the same process. + fn carve_out_span_for_closure(&mut self) { + let curr_span = self.curr().span; + let left_cutoff = curr_span.lo(); + let right_cutoff = curr_span.hi(); + let has_pre_closure_span = self.prev().span.lo() < right_cutoff; + let has_post_closure_span = self.prev().span.hi() > right_cutoff; + let mut pending_dups = self.pending_dups.split_off(0); + if has_pre_closure_span { + let mut pre_closure = self.prev().clone(); + pre_closure.span = pre_closure.span.with_hi(left_cutoff); + debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); + if !pending_dups.is_empty() { + for mut dup in pending_dups.iter().cloned() { + dup.span = dup.span.with_hi(left_cutoff); + debug!(" ...and at least one pre_closure dup={:?}", dup); + self.add_refined_span(dup); + } + } + self.add_refined_span(pre_closure); + } + if has_post_closure_span { + // Update prev.span to start after the closure (and discard curr) + self.prev_mut().span = self.prev().span.with_lo(right_cutoff); + self.prev_original_span = self.prev().span; + for dup in pending_dups.iter_mut() { + dup.span = dup.span.with_lo(right_cutoff); + } + self.pending_dups.append(&mut pending_dups); + self.discard_curr(); // since self.prev() was already updated + } else { + pending_dups.clear(); + } + } + + /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if + /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, + /// until their disposition is determined. In this latter case, the `prev` dup is moved into + /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. + fn hold_pending_dups_unless_dominated(&mut self) { + // equal coverage spans are ordered by dominators before dominated (if any) + debug_assert!(!self.prev().is_dominated_by(self.curr(), self.dominators)); + + if self.curr().is_dominated_by(&self.prev(), self.dominators) { + // If one span dominates the other, assocate the span with the dominator only. + // + // For example: + // match somenum { + // x if x < 1 => { ... } + // }... + // The span for the first `x` is referenced by both the pattern block (every + // time it is evaluated) and the arm code (only when matched). The counter + // will be applied only to the dominator block. + // + // The dominator's (`prev`) execution count may be higher than the dominated + // block's execution count, so drop `curr`. + debug!( + " different bcbs but SAME spans, and prev dominates curr. Drop curr and \ + keep prev for next iter. prev={:?}", + self.prev() + ); + self.discard_curr(); + } else { + // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) + // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as + // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. + debug!( + " different bcbs but SAME spans, and neither dominates, so keep curr for \ + next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", + self.prev() + ); + let prev = self.take_prev(); + self.pending_dups.push(prev); + } + } + + /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ + /// statements that end before `curr.lo()` (if any), and add the portion of the + /// combined span for those statements. Any other statements have overlapping spans + /// that can be ignored because `curr` and/or other upcoming statements/spans inside + /// the overlap area will produce their own counters. This disambiguation process + /// avoids injecting multiple counters for overlapping spans, and the potential for + /// double-counting. + fn cutoff_prev_at_overlapping_curr(&mut self) { + debug!( + " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ + if it has statements that end before curr={:?}", + self.prev() + ); + if self.pending_dups.is_empty() { + let curr_span = self.curr().span; + self.prev_mut().cutoff_statements_at(curr_span.lo()); + if self.prev().coverage_statements.is_empty() { + debug!(" ... no non-overlapping statements to add"); + } else { + debug!(" ... adding modified prev={:?}", self.prev()); + let prev = self.take_prev(); + self.add_refined_span(prev); + } + } else { + // with `pending_dups`, `prev` cannot have any statements that don't overlap + self.pending_dups.clear(); } - text.join("") } } +fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option<Span> { + match statement.kind { + // These statements have spans that are often outside the scope of the executed source code + // for their parent `BasicBlock`. + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + // Coverage should not be encountered, but don't inject coverage coverage + | StatementKind::Coverage(_) + // Ignore `Nop`s + | StatementKind::Nop => None, + + // FIXME(richkadel): Look into a possible issue assigning the span to a + // FakeReadCause::ForGuardBinding, in this example: + // match somenum { + // x if x < 1 => { ... } + // }... + // The BasicBlock within the match arm code included one of these statements, but the span + // for it covered the `1` in this source. The actual statements have nothing to do with that + // source span: + // FakeRead(ForGuardBinding, _4); + // where `_4` is: + // _4 = &_1; (at the span for the first `x`) + // and `_1` is the `Place` for `somenum`. + // + // The arm code BasicBlock already has its own assignment for `x` itself, `_3 = 1`, and I've + // decided it's reasonable for that span (even though outside the arm code) to be part of + // the counted coverage of the arm code execution, but I can't justify including the literal + // `1` in the arm code. I'm pretty sure that, if the `FakeRead(ForGuardBinding)` has a + // purpose in codegen, it's probably in the right BasicBlock, but if so, the `Statement`s + // `source_info.span` can't be right. + // + // Consider correcting the span assignment, assuming there is a better solution, and see if + // the following pattern can be removed here: + StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None, + + // Retain spans from all other statements + StatementKind::FakeRead(_, _) // Not including `ForGuardBinding` + | StatementKind::Assign(_) + | StatementKind::SetDiscriminant { .. } + | StatementKind::LlvmInlineAsm(_) + | StatementKind::Retag(_, _) + | StatementKind::AscribeUserType(_, _) => { + Some(source_info_span(&statement.source_info, body_span)) + } + } +} + +fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option<Span> { + match terminator.kind { + // These terminators have spans that don't positively contribute to computing a reasonable + // span of actually executed source code. (For example, SwitchInt terminators extracted from + // an `if condition { block }` has a span that includes the executed block, if true, + // but for coverage, the code region executed, up to *and* through the SwitchInt, + // actually stops before the if's block.) + TerminatorKind::Unreachable // Unreachable blocks are not connected to the CFG + | TerminatorKind::Assert { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Goto { .. } + // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. + | TerminatorKind::FalseEdge { .. } => None, + + // Retain spans from all other terminators + TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Call { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { + Some(source_info_span(&terminator.source_info, body_span)) + } + } +} + +#[inline(always)] +fn source_info_span(source_info: &SourceInfo, body_span: Span) -> Span { + let span = original_sp(source_info.span, body_span).with_ctxt(SyntaxContext::root()); + if body_span.contains(span) { span } else { body_span } +} + /// Convert the Span into its file name, start line and column, and end line and column -fn make_code_region<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> CodeRegion { - let source_map = tcx.sess.source_map(); - let start = source_map.lookup_char_pos(span.lo()); - let end = if span.hi() == span.lo() { - start.clone() +fn make_code_region(file_name: Symbol, source_file: &Lrc<SourceFile>, span: Span) -> CodeRegion { + let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo()); + let (end_line, end_col) = if span.hi() == span.lo() { + let (end_line, mut end_col) = (start_line, start_col); + // Extend an empty span by one character so the region will be counted. + let CharPos(char_pos) = start_col; + if char_pos > 0 { + start_col = CharPos(char_pos - 1); + } else { + end_col = CharPos(char_pos + 1); + } + (end_line, end_col) } else { - let end = source_map.lookup_char_pos(span.hi()); - debug_assert_eq!( - start.file.name, - end.file.name, - "Region start ({:?} -> {:?}) and end ({:?} -> {:?}) don't come from the same source file!", - span.lo(), - start, - span.hi(), - end - ); - end + source_file.lookup_file_pos(span.hi()) }; - match &start.file.name { - FileName::Real(RealFileName::Named(path)) => CodeRegion { - file_name: Symbol::intern(&path.to_string_lossy()), - start_line: start.line as u32, - start_col: start.col.to_u32() + 1, - end_line: end.line as u32, - end_col: end.col.to_u32() + 1, - }, - _ => bug!("start.file.name should be a RealFileName, but it was: {:?}", start.file.name), + CodeRegion { + file_name, + start_line: start_line as u32, + start_col: start_col.to_u32() + 1, + end_line: end_line as u32, + end_col: end_col.to_u32() + 1, } } diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index c00c3b740ed..bd7c25bf250 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -150,26 +150,25 @@ fn dump_matched_mir_node<'tcx, F>( if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview { let _: io::Result<()> = try { - let mut file = - create_dump_file(tcx, "html", pass_num, pass_name, disambiguator, body.source)?; + let file_basename = + dump_file_basename(tcx, pass_num, pass_name, disambiguator, body.source); + let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?; if body.source.def_id().is_local() { - write_mir_fn_spanview(tcx, body, spanview, &mut file)?; + write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?; } }; } } -/// Returns the path to the filename where we should dump a given MIR. -/// Also used by other bits of code (e.g., NLL inference) that dump -/// graphviz data or other things. -fn dump_path( +/// Returns the file basename portion (without extension) of a filename path +/// where we should dump a MIR representation output files. +fn dump_file_basename( tcx: TyCtxt<'_>, - extension: &str, pass_num: Option<&dyn Display>, pass_name: &str, disambiguator: &dyn Display, source: MirSource<'tcx>, -) -> PathBuf { +) -> String { let promotion_id = match source.promoted { Some(id) => format!("-{:?}", id), None => String::new(), @@ -184,9 +183,6 @@ fn dump_path( } }; - let mut file_path = PathBuf::new(); - file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); - let crate_name = tcx.crate_name(source.def_id().krate); let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate(); // All drop shims have the same DefId, so we have to add the type @@ -206,23 +202,46 @@ fn dump_path( _ => String::new(), }; - let file_name = format!( - "{}.{}{}{}{}.{}.{}.{}", - crate_name, - item_name, - shim_disambiguator, - promotion_id, - pass_num, - pass_name, - disambiguator, - extension, - ); + format!( + "{}.{}{}{}{}.{}.{}", + crate_name, item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator, + ) +} + +/// Returns the path to the filename where we should dump a given MIR. +/// Also used by other bits of code (e.g., NLL inference) that dump +/// graphviz data or other things. +fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf { + let mut file_path = PathBuf::new(); + file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); + + let file_name = format!("{}.{}", basename, extension,); file_path.push(&file_name); file_path } +/// Attempts to open the MIR dump file with the given name and extension. +fn create_dump_file_with_basename( + tcx: TyCtxt<'_>, + file_basename: &str, + extension: &str, +) -> io::Result<io::BufWriter<fs::File>> { + let file_path = dump_path(tcx, file_basename, extension); + if let Some(parent) = file_path.parent() { + fs::create_dir_all(parent).map_err(|e| { + io::Error::new( + e.kind(), + format!("IO error creating MIR dump directory: {:?}; {}", parent, e), + ) + })?; + } + Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| { + io::Error::new(e.kind(), format!("IO error creating MIR dump file: {:?}; {}", file_path, e)) + })?)) +} + /// Attempts to open a file where we should dump a given MIR or other /// bit of MIR-related data. Used by `mir-dump`, but also by other /// bits of code (e.g., NLL inference) that dump graphviz data or @@ -235,11 +254,11 @@ pub(crate) fn create_dump_file( disambiguator: &dyn Display, source: MirSource<'tcx>, ) -> io::Result<io::BufWriter<fs::File>> { - let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source); - if let Some(parent) = file_path.parent() { - fs::create_dir_all(parent)?; - } - Ok(io::BufWriter::new(fs::File::create(&file_path)?)) + create_dump_file_with_basename( + tcx, + &dump_file_basename(tcx, pass_num, pass_name, disambiguator, source), + extension, + ) } /// Write out a human-readable textual representation for the given MIR. @@ -652,7 +671,8 @@ pub fn write_allocations<'tcx>( /// Dumps the size and metadata and content of an allocation to the given writer. /// The expectation is that the caller first prints other relevant metadata, so the exact /// format of this function is (*without* leading or trailing newline): -/// ``` +/// +/// ```text /// size: {}, align: {}) { /// <bytes> /// } diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs index fdc724178b6..d3ef8c64565 100644 --- a/compiler/rustc_mir/src/util/spanview.rs +++ b/compiler/rustc_mir/src/util/spanview.rs @@ -16,9 +16,13 @@ const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT B const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">"; const HEADER: &str = r#"<!DOCTYPE html> <html> -<head> - <title>coverage_of_if_else - Code Regions</title> - <style> +<head>"#; +const START_BODY: &str = r#"</head> +<body>"#; +const FOOTER: &str = r#"</body> +</html>"#; + +const STYLE_SECTION: &str = r#"<style> .line { counter-increment: line; } @@ -72,16 +76,12 @@ const HEADER: &str = r#"<!DOCTYPE html> /* requires hover over a span ONLY on its first line */ display: inline-block; } - </style> -</head> -<body>"#; - -const FOOTER: &str = r#" -</body> -</html>"#; +</style>"#; /// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator. +#[derive(Clone, Debug)] pub struct SpanViewable { + pub bb: BasicBlock, pub span: Span, pub id: String, pub tooltip: String, @@ -92,6 +92,7 @@ pub fn write_mir_fn_spanview<'tcx, W>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, spanview: MirSpanview, + title: &str, w: &mut W, ) -> io::Result<()> where @@ -126,16 +127,17 @@ where } } } - write_spanview_document(tcx, def_id, span_viewables, w)?; + write_document(tcx, def_id, span_viewables, title, w)?; Ok(()) } /// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated /// list `SpanViewable`s. -pub fn write_spanview_document<'tcx, W>( +pub fn write_document<'tcx, W>( tcx: TyCtxt<'tcx>, def_id: DefId, mut span_viewables: Vec<SpanViewable>, + title: &str, w: &mut W, ) -> io::Result<()> where @@ -153,6 +155,9 @@ where source_map.span_to_snippet(fn_span).expect("function should have printable source") ); writeln!(w, "{}", HEADER)?; + writeln!(w, "<title>{}</title>", title)?; + writeln!(w, "{}", STYLE_SECTION)?; + writeln!(w, "{}", START_BODY)?; write!( w, r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#, @@ -182,6 +187,7 @@ where end_pos.to_usize(), ordered_viewables.len() ); + let curr_id = &ordered_viewables[0].id; let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps( tcx, from_pos, @@ -204,13 +210,17 @@ where from_pos = next_from_pos; if next_ordered_viewables.len() != ordered_viewables.len() { ordered_viewables = next_ordered_viewables; - alt = !alt; + if let Some(next_ordered_viewable) = ordered_viewables.first() { + if &next_ordered_viewable.id != curr_id { + alt = !alt; + } + } } } if from_pos < end_pos { write_coverage_gap(tcx, from_pos, end_pos, w)?; } - write!(w, r#"</span></div>"#)?; + writeln!(w, r#"</span></div>"#)?; writeln!(w, "{}", FOOTER)?; Ok(()) } @@ -273,7 +283,7 @@ fn statement_span_viewable<'tcx>( } let id = format!("{}[{}]", bb.index(), i); let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None); - Some(SpanViewable { span, id, tooltip }) + Some(SpanViewable { bb, span, id, tooltip }) } fn terminator_span_viewable<'tcx>( @@ -289,7 +299,7 @@ fn terminator_span_viewable<'tcx>( } let id = format!("{}:{}", bb.index(), terminator_kind_name(term)); let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator); - Some(SpanViewable { span, id, tooltip }) + Some(SpanViewable { bb, span, id, tooltip }) } fn block_span_viewable<'tcx>( @@ -304,7 +314,7 @@ fn block_span_viewable<'tcx>( } let id = format!("{}", bb.index()); let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator); - Some(SpanViewable { span, id, tooltip }) + Some(SpanViewable { bb, span, id, tooltip }) } fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span { @@ -456,6 +466,7 @@ where remaining_viewables.len() ); // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`. + let curr_id = &remaining_viewables[0].id; let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps( tcx, from_pos, @@ -480,7 +491,11 @@ where from_pos = next_from_pos; if next_remaining_viewables.len() != remaining_viewables.len() { remaining_viewables = next_remaining_viewables; - subalt = !subalt; + if let Some(next_ordered_viewable) = remaining_viewables.first() { + if &next_ordered_viewable.id != curr_id { + subalt = !subalt; + } + } } } if from_pos <= viewable.span.hi() { @@ -649,8 +664,12 @@ fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span { tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local")); let fn_decl_span = tcx.hir().span(hir_id); let body_span = hir_body(tcx, def_id).value.span; - debug_assert_eq!(fn_decl_span.ctxt(), body_span.ctxt()); - fn_decl_span.to(body_span) + if fn_decl_span.ctxt() == body_span.ctxt() { + fn_decl_span.to(body_span) + } else { + // This probably occurs for functions defined via macros + body_span + } } fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index a7bb2864daf..b71ff6e7557 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -31,7 +31,7 @@ crate fn lit_to_const<'tcx>( (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { - let allocation = Allocation::from_byte_aligned_bytes(data as &Vec<u8>); + let allocation = Allocation::from_byte_aligned_bytes(data as &[u8]); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: data.len() } } diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 6f249f491a6..47d317f9188 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -181,10 +181,9 @@ pub(crate) fn emit_unescape_error( if suggestion_len > 0 { suggestion.push('}'); - let lo = char_span.lo(); - let hi = lo + BytePos(suggestion_len as u32); + let hi = char_span.lo() + BytePos(suggestion_len as u32); diag.span_suggestion( - span.with_lo(lo).with_hi(hi), + span.with_hi(hi), "format of unicode escape sequences uses braces", suggestion, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 8d1b826ea35..8f93bce6e99 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -194,11 +194,14 @@ where // The intent is to treat `impl Trait1 + Trait2` identically to // `dyn Trait1 + Trait2`. Therefore we ignore def-id of the opaque type itself // (it either has no visibility, or its visibility is insignificant, like - // visibilities of type aliases) and recurse into predicates instead to go + // visibilities of type aliases) and recurse into bounds instead to go // through the trait list (default type visitor doesn't visit those traits). // All traits in the list are considered the "primary" part of the type // and are visited by shallow visitors. - if self.visit_predicates(tcx.predicates_of(def_id)) { + if self.visit_predicates(ty::GenericPredicates { + parent: None, + predicates: tcx.explicit_item_bounds(def_id), + }) { return true; } } @@ -1800,6 +1803,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { self } + fn bounds(&mut self) -> &mut Self { + self.visit_predicates(ty::GenericPredicates { + parent: None, + predicates: self.tcx.explicit_item_bounds(self.item_def_id), + }); + self + } + fn ty(&mut self) -> &mut Self { self.visit(self.tcx.type_of(self.item_def_id)); self @@ -1975,7 +1986,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> hir::ItemKind::OpaqueTy(..) => { // `ty()` for opaque types is the underlying type, // it's not a part of interface, so we skip it. - self.check(item.hir_id, item_visibility).generics().predicates(); + self.check(item.hir_id, item_visibility).generics().bounds(); } hir::ItemKind::Trait(.., trait_item_refs) => { self.check(item.hir_id, item_visibility).generics().predicates(); @@ -1987,6 +1998,10 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> trait_item_ref.defaultness, item_visibility, ); + + if let AssocItemKind::Type = trait_item_ref.kind { + self.check(trait_item_ref.id.hir_id, item_visibility).bounds(); + } } } hir::ItemKind::TraitAlias(..) => { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ab96b0333f4..231e315a22f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1756,10 +1756,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } - if debugging_opts.experimental_coverage { - debugging_opts.instrument_coverage = true; - } - if debugging_opts.instrument_coverage { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_error( diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index 3e899e00d11..fef3164de59 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -1882,6 +1882,16 @@ declare_lint! { } declare_lint! { + /// The `invalid_html_tags` lint detects invalid HTML tags. This is a + /// `rustdoc` only lint, see the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags + pub INVALID_HTML_TAGS, + Allow, + "detects invalid HTML tags in doc comments" +} + +declare_lint! { /// The `where_clauses_object_safety` lint detects for [object safety] of /// [where clauses]. /// @@ -2699,6 +2709,7 @@ declare_lint_pass! { INVALID_CODEBLOCK_ATTRIBUTES, MISSING_CRATE_LEVEL_DOCS, MISSING_DOC_CODE_EXAMPLES, + INVALID_HTML_TAGS, PRIVATE_DOC_TESTS, WHERE_CLAUSES_OBJECT_SAFETY, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b705ab6d931..a106007c274 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -895,11 +895,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, all statements)."), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), - experimental_coverage: bool = (false, parse_bool, [TRACKED], - "enable and extend the `-Z instrument-coverage` function-level coverage \ - feature, adding additional experimental (likely inaccurate) counters and \ - code regions (used by `rustc` compiler developers to test new coverage \ - counter placements) (default: no)"), fewer_names: bool = (false, parse_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index aae778217d3..b24ede9c53a 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -159,6 +159,7 @@ impl DefId { DefId { krate: LOCAL_CRATE, index } } + /// Returns whether the item is defined in the crate currently being compiled. #[inline] pub fn is_local(self) -> bool { self.krate == LOCAL_CRATE diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 96a6956a40c..e7cb8cb6e88 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -60,6 +60,8 @@ use md5::Md5; use sha1::Digest; use sha1::Sha1; +use tracing::debug; + #[cfg(test)] mod tests; @@ -1462,6 +1464,88 @@ impl SourceFile { BytePos::from_u32(pos.0 - self.start_pos.0 + diff) } + + /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. + pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { + // The number of extra bytes due to multibyte chars in the `SourceFile`. + let mut total_extra_bytes = 0; + + for mbc in self.multibyte_chars.iter() { + debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos); + if mbc.pos < bpos { + // Every character is at least one byte, so we only + // count the actual extra bytes. + total_extra_bytes += mbc.bytes as u32 - 1; + // We should never see a byte position in the middle of a + // character. + assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32); + } else { + break; + } + } + + assert!(self.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32()); + CharPos(bpos.to_usize() - self.start_pos.to_usize() - total_extra_bytes as usize) + } + + /// Looks up the file's (1-based) line number and (0-based `CharPos`) column offset, for a + /// given `BytePos`. + pub fn lookup_file_pos(&self, pos: BytePos) -> (usize, CharPos) { + let chpos = self.bytepos_to_file_charpos(pos); + match self.lookup_line(pos) { + Some(a) => { + let line = a + 1; // Line numbers start at 1 + let linebpos = self.lines[a]; + let linechpos = self.bytepos_to_file_charpos(linebpos); + let col = chpos - linechpos; + debug!("byte pos {:?} is on the line at byte pos {:?}", pos, linebpos); + debug!("char pos {:?} is on the line at char pos {:?}", chpos, linechpos); + debug!("byte is on line: {}", line); + assert!(chpos >= linechpos); + (line, col) + } + None => (0, chpos), + } + } + + /// Looks up the file's (1-based) line number, (0-based `CharPos`) column offset, and (0-based) + /// column offset when displayed, for a given `BytePos`. + pub fn lookup_file_pos_with_col_display(&self, pos: BytePos) -> (usize, CharPos, usize) { + let (line, col_or_chpos) = self.lookup_file_pos(pos); + if line > 0 { + let col = col_or_chpos; + let linebpos = self.lines[line - 1]; + let col_display = { + let start_width_idx = self + .non_narrow_chars + .binary_search_by_key(&linebpos, |x| x.pos()) + .unwrap_or_else(|x| x); + let end_width_idx = self + .non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let special_chars = end_width_idx - start_width_idx; + let non_narrow: usize = self.non_narrow_chars[start_width_idx..end_width_idx] + .iter() + .map(|x| x.width()) + .sum(); + col.0 - special_chars + non_narrow + }; + (line, col, col_display) + } else { + let chpos = col_or_chpos; + let col_display = { + let end_width_idx = self + .non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let non_narrow: usize = + self.non_narrow_chars[0..end_width_idx].iter().map(|x| x.width()).sum(); + chpos.0 - end_width_idx + non_narrow + }; + (0, chpos, col_display) + } + } } /// Normalizes the source code and records the normalizations. diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 37596b8ef6f..fdb031fd9b3 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -428,58 +428,22 @@ impl SourceMap { } } + /// Return the SourceFile that contains the given `BytePos` + pub fn lookup_source_file(&self, pos: BytePos) -> Lrc<SourceFile> { + let idx = self.lookup_source_file_idx(pos); + (*self.files.borrow().source_files)[idx].clone() + } + /// Looks up source information about a `BytePos`. pub fn lookup_char_pos(&self, pos: BytePos) -> Loc { - let chpos = self.bytepos_to_file_charpos(pos); - match self.lookup_line(pos) { - Ok(SourceFileAndLine { sf: f, line: a }) => { - let line = a + 1; // Line numbers start at 1 - let linebpos = f.lines[a]; - let linechpos = self.bytepos_to_file_charpos(linebpos); - let col = chpos - linechpos; - - let col_display = { - let start_width_idx = f - .non_narrow_chars - .binary_search_by_key(&linebpos, |x| x.pos()) - .unwrap_or_else(|x| x); - let end_width_idx = f - .non_narrow_chars - .binary_search_by_key(&pos, |x| x.pos()) - .unwrap_or_else(|x| x); - let special_chars = end_width_idx - start_width_idx; - let non_narrow: usize = f.non_narrow_chars[start_width_idx..end_width_idx] - .iter() - .map(|x| x.width()) - .sum(); - col.0 - special_chars + non_narrow - }; - debug!("byte pos {:?} is on the line at byte pos {:?}", pos, linebpos); - debug!("char pos {:?} is on the line at char pos {:?}", chpos, linechpos); - debug!("byte is on line: {}", line); - assert!(chpos >= linechpos); - Loc { file: f, line, col, col_display } - } - Err(f) => { - let col_display = { - let end_width_idx = f - .non_narrow_chars - .binary_search_by_key(&pos, |x| x.pos()) - .unwrap_or_else(|x| x); - let non_narrow: usize = - f.non_narrow_chars[0..end_width_idx].iter().map(|x| x.width()).sum(); - chpos.0 - end_width_idx + non_narrow - }; - Loc { file: f, line: 0, col: chpos, col_display } - } - } + let sf = self.lookup_source_file(pos); + let (line, col, col_display) = sf.lookup_file_pos_with_col_display(pos); + Loc { file: sf, line, col, col_display } } // If the corresponding `SourceFile` is empty, does not return a line number. pub fn lookup_line(&self, pos: BytePos) -> Result<SourceFileAndLine, Lrc<SourceFile>> { - let idx = self.lookup_source_file_idx(pos); - - let f = (*self.files.borrow().source_files)[idx].clone(); + let f = self.lookup_source_file(pos); match f.lookup_line(pos) { Some(line) => Ok(SourceFileAndLine { sf: f, line }), @@ -934,27 +898,8 @@ impl SourceMap { /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { let idx = self.lookup_source_file_idx(bpos); - let map = &(*self.files.borrow().source_files)[idx]; - - // The number of extra bytes due to multibyte chars in the `SourceFile`. - let mut total_extra_bytes = 0; - - for mbc in map.multibyte_chars.iter() { - debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos); - if mbc.pos < bpos { - // Every character is at least one byte, so we only - // count the actual extra bytes. - total_extra_bytes += mbc.bytes as u32 - 1; - // We should never see a byte position in the middle of a - // character. - assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32); - } else { - break; - } - } - - assert!(map.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32()); - CharPos(bpos.to_usize() - map.start_pos.to_usize() - total_extra_bytes as usize) + let sf = &(*self.files.borrow().source_files)[idx]; + sf.bytepos_to_file_charpos(bpos) } // Returns the index of the `SourceFile` (in `self.files`) that contains `pos`. diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 24850a8a0d2..d2c9b05c560 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -5,6 +5,7 @@ //! paths etc in all kinds of annoying scenarios. use rustc_hir as hir; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; @@ -44,7 +45,7 @@ impl SymbolNamesTest<'tcx> { tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); } } else if tcx.sess.check_name(attr, DEF_PATH) { - let path = tcx.def_path_str(def_id.to_def_id()); + let path = with_no_trimmed_paths(|| tcx.def_path_str(def_id.to_def_id())); tcx.sess.span_err(attr.span, &format!("def-path({})", path)); } diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs index 0ea99af83a1..bb11ce8ef28 100644 --- a/compiler/rustc_target/src/spec/android_base.rs +++ b/compiler/rustc_target/src/spec/android_base.rs @@ -12,5 +12,6 @@ pub fn opts() -> TargetOptions { base.position_independent_executables = true; base.has_elf_tls = false; base.requires_uwtable = true; + base.crt_static_respected = false; base } diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs index 52892fc3592..7ad972b0692 100644 --- a/compiler/rustc_target/src/spec/linux_base.rs +++ b/compiler/rustc_target/src/spec/linux_base.rs @@ -28,6 +28,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, relro_level: RelroLevel::Full, has_elf_tls: true, + crt_static_respected: true, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index b90e91d2901..16cc3b762f6 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -10,8 +10,6 @@ pub fn opts() -> TargetOptions { // These targets statically link libc by default base.crt_static_default = true; - // These targets allow the user to choose between static and dynamic linking. - base.crt_static_respected = true; base } diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 28697ec4e3b..610c6fd7e35 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -10,7 +10,7 @@ use rustc_infer::infer::free_regions::FreeRegionRelations; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, InferCtxt, InferOk}; use rustc_middle::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config::nightly_options; use rustc_span::Span; @@ -38,13 +38,13 @@ pub struct OpaqueTypeDecl<'tcx> { /// then `substs` would be `['a, T]`. pub substs: SubstsRef<'tcx>, - /// The span of this particular definition of the opaque type. So + /// The span of this particular definition of the opaque type. So /// for example: /// - /// ``` + /// ```ignore (incomplete snippet) /// type Foo = impl Baz; /// fn bar() -> Foo { - /// ^^^ This is the span we are looking for! + /// // ^^^ This is the span we are looking for! /// ``` /// /// In cases where the fn returns `(impl Trait, impl Trait)` or @@ -428,14 +428,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // 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); + let bounds = tcx.explicit_item_bounds(def_id); + debug!("constrain_opaque_type: predicates: {:#?}", bounds); + let bounds: Vec<_> = + bounds.iter().map(|(bound, _)| bound.subst(tcx, opaque_defn.substs)).collect(); 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.into_iter()); + required_region_bounds(tcx, opaque_type, bounds.into_iter()); debug_assert!(!required_region_bounds.is_empty()); for required_region in required_region_bounds { @@ -1112,9 +1113,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { 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 item_bounds = tcx.explicit_item_bounds(def_id); + debug!("instantiate_opaque_types: bounds={:#?}", item_bounds); + let bounds: Vec<_> = + item_bounds.iter().map(|(bound, _)| bound.subst(tcx, substs)).collect(); let param_env = tcx.param_env(def_id); let InferOk { value: bounds, obligations } = @@ -1123,8 +1125,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: bounds={:?}", bounds); - let required_region_bounds = - required_region_bounds(tcx, ty, bounds.predicates.iter().cloned()); + let required_region_bounds = required_region_bounds(tcx, ty, bounds.iter().copied()); debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds); // Make sure that we are in fact defining the *entire* type @@ -1153,7 +1154,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { ); debug!("instantiate_opaque_types: ty_var={:?}", ty_var); - for predicate in &bounds.predicates { + for predicate in &bounds { if let ty::PredicateAtom::Projection(projection) = predicate.skip_binders() { if projection.ty.references_error() { // No point on adding these obligations since there's a type error involved. @@ -1162,14 +1163,14 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { } } - self.obligations.reserve(bounds.predicates.len()); - for predicate in bounds.predicates { + self.obligations.reserve(bounds.len()); + for predicate in bounds { // 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); + let cause = traits::ObligationCause::new(span, self.body_id, traits::MiscObligation); // Require that the predicate holds for the concrete type. debug!("instantiate_opaque_types: predicate={:?}", predicate); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 8586a550230..27751eb554d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -376,7 +376,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { | ty::PredicateAtom::Subtype(_) | ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEquate(..) => { - let (pred, _) = infcx.replace_bound_vars_with_placeholders(binder); + let pred = infcx.replace_bound_vars_with_placeholders(binder); ProcessResult::Changed(mk_pending(vec![ obligation.with(pred.to_predicate(self.selcx.tcx())), ])) @@ -449,6 +449,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { self.selcx.infcx(), obligation.param_env, obligation.cause.body_id, + obligation.recursion_depth + 1, arg, obligation.cause.span, ) { @@ -672,7 +673,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { Ok(Ok(None)) => { *stalled_on = trait_ref_infer_vars( self.selcx, - project_obligation.predicate.to_poly_trait_ref(self.selcx.tcx()), + project_obligation.predicate.to_poly_trait_ref(tcx), ); ProcessResult::Unchanged } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 8fc14cb2997..0e43f1655dd 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -160,6 +160,10 @@ fn object_safety_violations_for_trait( if !spans.is_empty() { violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); } + let spans = bounds_reference_self(tcx, trait_def_id); + if !spans.is_empty() { + violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); + } violations.extend( tcx.associated_items(trait_def_id) @@ -239,51 +243,70 @@ fn predicates_reference_self( } else { tcx.predicates_of(trait_def_id) }; - let self_ty = tcx.types.self_param; - let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); predicates .predicates .iter() - .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) - .filter_map(|(predicate, &sp)| { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(ref data, _) => { - // In the case of a trait predicate, we can skip the "self" type. - if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } - } - ty::PredicateAtom::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<Output=Self::Result> { type Result; }`), - // we just require the user to specify *both* outputs - // in the object type (i.e., `dyn Foo<Output=(), Result=()>`). - // - // This is ALT2 in issue #56288, see that for discussion of the - // possible alternatives. - if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) { - Some(sp) - } else { - None - } - } - ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::TypeOutlives(..) - | ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, - } - }) + .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), *sp)) + .filter_map(|predicate| predicate_references_self(tcx, predicate)) + .collect() +} + +fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { + let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); + tcx.associated_items(trait_def_id) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .flat_map(|item| tcx.explicit_item_bounds(item.def_id)) + .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), *sp)) + .filter_map(|predicate| predicate_references_self(tcx, predicate)) .collect() } +fn predicate_references_self( + tcx: TyCtxt<'tcx>, + (predicate, sp): (ty::Predicate<'tcx>, Span), +) -> Option<Span> { + let self_ty = tcx.types.self_param; + let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); + match predicate.skip_binders() { + ty::PredicateAtom::Trait(ref data, _) => { + // In the case of a trait predicate, we can skip the "self" type. + if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } + } + ty::PredicateAtom::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. + // + // It's also won't be redundant if we allow type-generic associated + // types for trait objects. + // + // Note that we *do* allow projection *outputs* to contain + // `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`), + // we just require the user to specify *both* outputs + // in the object type (i.e., `dyn Foo<Output=(), Result=()>`). + // + // This is ALT2 in issue #56288, see that for discussion of the + // possible alternatives. + if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) { + Some(sp) + } else { + None + } + } + ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + } +} + fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { generics_require_sized_self(tcx, trait_def_id) } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index ef8f7b69b5d..de42aa0e6b7 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1,6 +1,5 @@ //! Code for projecting associated types out of trait references. -use super::elaborate_predicates; use super::specialization_graph; use super::translate_substs; use super::util; @@ -29,7 +28,6 @@ use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_span::symbol::sym; -use rustc_span::DUMMY_SP; pub use rustc_middle::traits::Reveal; @@ -53,13 +51,16 @@ pub enum ProjectionTyError<'tcx> { #[derive(PartialEq, Eq, Debug)] enum ProjectionTyCandidate<'tcx> { - // from a where-clause in the env or object type + /// From a where-clause in the env or object type ParamEnv(ty::PolyProjectionPredicate<'tcx>), - // from the definition of `Trait` when you have something like <<A as Trait>::B as Trait2>::C + /// From the definition of `Trait` when you have something like <<A as Trait>::B as Trait2>::C TraitDef(ty::PolyProjectionPredicate<'tcx>), - // from a "impl" (or a "pseudo-impl" returned by select) + /// Bounds specified on an object type + Object(ty::PolyProjectionPredicate<'tcx>), + + /// From a "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), } @@ -167,7 +168,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( let infcx = selcx.infcx(); infcx.commit_if_ok(|_snapshot| { - let (placeholder_predicate, _) = + let placeholder_predicate = infcx.replace_bound_vars_with_placeholders(&obligation.predicate); let placeholder_obligation = obligation.with(placeholder_predicate); @@ -561,14 +562,6 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( } else { obligations.extend(ty.obligations); } - - obligations.push(get_paranoid_cache_value_obligation( - infcx, - param_env, - projection_ty, - cause, - depth, - )); return Ok(Some(ty.value)); } Err(ProjectionCacheEntry::Error) => { @@ -703,45 +696,6 @@ fn prune_cache_value_obligations<'a, 'tcx>( NormalizedTy { value: result.value, obligations } } -/// Whenever we give back a cache result for a projection like `<T as -/// Trait>::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(infcx.tcx), - } -} - /// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not /// hold. In various error cases, we cannot generate a valid /// normalized projection. Therefore, we create an inference variable @@ -848,12 +802,21 @@ fn project_type<'cx, 'tcx>( assemble_candidates_from_trait_def(selcx, obligation, &obligation_trait_ref, &mut candidates); - assemble_candidates_from_impls(selcx, obligation, &obligation_trait_ref, &mut candidates); + assemble_candidates_from_object_ty(selcx, obligation, &obligation_trait_ref, &mut candidates); + + if let ProjectionTyCandidateSet::Single(ProjectionTyCandidate::Object(_)) = candidates { + // Avoid normalization cycle from selection (see + // `assemble_candidates_from_object_ty`). + // FIXME(lazy_normalization): Lazy normalization should save us from + // having to do special case this. + } else { + 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::Single(candidate) => { + Ok(ProjectedTy::Progress(confirm_candidate(selcx, obligation, candidate))) + } ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress( selcx .tcx() @@ -884,6 +847,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( candidate_set, ProjectionTyCandidate::ParamEnv, obligation.param_env.caller_bounds().iter(), + false, ); } @@ -909,10 +873,8 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( // Check whether the self-type is itself a projection. // If so, extract what we know from the trait and try to come up with a good answer. let bounds = match *obligation_trait_ref.self_ty().kind() { - ty::Projection(ref data) => { - tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) - } - ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), + ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs), + ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, 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. @@ -929,9 +891,57 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( candidate_set, ProjectionTyCandidate::TraitDef, bounds.iter(), + true, ) } +/// In the case of a trait object like +/// `<dyn Iterator<Item = ()> as Iterator>::Item` we can use the existential +/// predicate in the trait object. +/// +/// We don't go through the select candidate for these bounds to avoid cycles: +/// In the above case, `dyn Iterator<Item = ()>: Iterator` would create a +/// nested obligation of `<dyn Iterator<Item = ()> as Iterator>::Item: Sized`, +/// this then has to be normalized without having to prove +/// `dyn Iterator<Item = ()>: Iterator` again. +fn assemble_candidates_from_object_ty<'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_object_ty(..)"); + + let tcx = selcx.tcx(); + + let self_ty = obligation_trait_ref.self_ty(); + let object_ty = selcx.infcx().shallow_resolve(self_ty); + let data = match object_ty.kind() { + ty::Dynamic(data, ..) => data, + ty::Infer(ty::TyVar(_)) => { + // If the self-type is an inference variable, then it MAY wind up + // being an object type, so induce an ambiguity. + candidate_set.mark_ambiguous(); + return; + } + _ => return, + }; + let env_predicates = data + .projection_bounds() + .filter(|bound| bound.item_def_id() == obligation.predicate.item_def_id) + .map(|p| p.with_self_ty(tcx, object_ty).to_predicate(tcx)); + + assemble_candidates_from_predicates( + selcx, + obligation, + obligation_trait_ref, + candidate_set, + ProjectionTyCandidate::Object, + env_predicates, + false, + ); +} + fn assemble_candidates_from_predicates<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -939,6 +949,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( candidate_set: &mut ProjectionTyCandidateSet<'tcx>, ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>, env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>, + potentially_unnormalized_candidates: bool, ) { debug!("assemble_candidates_from_predicates(obligation={:?})", obligation); let infcx = selcx.infcx(); @@ -950,16 +961,12 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( 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() + selcx.match_projection_projections( + obligation, + obligation_trait_ref, + &data, + potentially_unnormalized_candidates, + ) }); debug!( @@ -970,6 +977,15 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( if is_match { candidate_set.push_candidate(ctor(data)); + + if potentially_unnormalized_candidates + && !obligation.predicate.has_infer_types_or_consts() + { + // HACK: Pick the first trait def candidate for a fully + // inferred predicate. This is to allow duplicates that + // differ only in normalization. + return; + } } } } @@ -1003,7 +1019,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( super::ImplSource::Closure(_) | super::ImplSource::Generator(_) | super::ImplSource::FnPointer(_) - | super::ImplSource::Object(_) | super::ImplSource::TraitAlias(_) => { debug!("assemble_candidates_from_impls: impl_source={:?}", impl_source); true @@ -1128,6 +1143,12 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // in `assemble_candidates_from_param_env`. false } + super::ImplSource::Object(_) => { + // Handled by the `Object` projection candidate. See + // `assemble_candidates_from_object_ty` for an explanation of + // why we special case object types. + false + } super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( @@ -1153,19 +1174,22 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( 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); let mut progress = match candidate { ProjectionTyCandidate::ParamEnv(poly_projection) - | ProjectionTyCandidate::TraitDef(poly_projection) => { - confirm_param_env_candidate(selcx, obligation, poly_projection) + | ProjectionTyCandidate::Object(poly_projection) => { + confirm_param_env_candidate(selcx, obligation, poly_projection, false) + } + + ProjectionTyCandidate::TraitDef(poly_projection) => { + confirm_param_env_candidate(selcx, obligation, poly_projection, true) } ProjectionTyCandidate::Select(impl_source) => { - confirm_select_candidate(selcx, obligation, obligation_trait_ref, impl_source) + confirm_select_candidate(selcx, obligation, impl_source) } }; // When checking for cycle during evaluation, we compare predicates with @@ -1182,7 +1206,6 @@ fn confirm_candidate<'cx, 'tcx>( fn confirm_select_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, impl_source: Selection<'tcx>, ) -> Progress<'tcx> { match impl_source { @@ -1193,10 +1216,8 @@ fn confirm_select_candidate<'cx, 'tcx>( super::ImplSource::DiscriminantKind(data) => { confirm_discriminant_kind_candidate(selcx, obligation, data) } - super::ImplSource::Object(_) => { - confirm_object_candidate(selcx, obligation, obligation_trait_ref) - } - super::ImplSource::AutoImpl(..) + super::ImplSource::Object(_) + | super::ImplSource::AutoImpl(..) | super::ImplSource::Param(..) | super::ImplSource::Builtin(..) | super::ImplSource::TraitAlias(..) => @@ -1211,72 +1232,6 @@ fn confirm_select_candidate<'cx, 'tcx>( } } -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(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(selcx.tcx())); - 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(|o| match o.predicate.skip_binders() { - ty::PredicateAtom::Projection(data) - if data.projection_ty.item_def_id == obligation.predicate.item_def_id => - { - Some(ty::Binder::bind(data)) - } - _ => 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>, @@ -1325,7 +1280,7 @@ fn confirm_generator_candidate<'cx, 'tcx>( } }); - confirm_param_env_candidate(selcx, obligation, predicate) + confirm_param_env_candidate(selcx, obligation, predicate, false) .with_addl_obligations(impl_source.nested) .with_addl_obligations(obligations) } @@ -1347,7 +1302,7 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>( ty: self_ty.discriminant_ty(tcx), }; - confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate)) + confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate), false) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( @@ -1422,13 +1377,14 @@ fn confirm_callable_candidate<'cx, 'tcx>( ty: ret_type, }); - confirm_param_env_candidate(selcx, obligation, predicate) + confirm_param_env_candidate(selcx, obligation, predicate, false) } fn confirm_param_env_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, + potentially_unnormalized_candidate: bool, ) -> Progress<'tcx> { let infcx = selcx.infcx(); let cause = &obligation.cause; @@ -1442,8 +1398,28 @@ fn confirm_param_env_candidate<'cx, 'tcx>( let cache_trait_ref = cache_entry.projection_ty.trait_ref(infcx.tcx); let obligation_trait_ref = obligation.predicate.trait_ref(infcx.tcx); + let mut nested_obligations = Vec::new(); + let cache_trait_ref = if potentially_unnormalized_candidate { + ensure_sufficient_stack(|| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &cache_trait_ref, + &mut nested_obligations, + ) + }) + } else { + cache_trait_ref + }; + match infcx.at(cause, param_env).eq(cache_trait_ref, obligation_trait_ref) { - Ok(InferOk { value: _, obligations }) => Progress { ty: cache_entry.ty, obligations }, + Ok(InferOk { value: _, obligations }) => { + nested_obligations.extend(obligations); + assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); + Progress { ty: cache_entry.ty, obligations: nested_obligations } + } Err(e) => { let msg = format!( "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}", @@ -1463,7 +1439,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { let tcx = selcx.tcx(); - let ImplSourceUserDefinedData { impl_def_id, substs, nested } = impl_impl_source; + let ImplSourceUserDefinedData { impl_def_id, substs, mut nested } = impl_impl_source; let assoc_item_id = obligation.predicate.item_def_id; let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); @@ -1496,15 +1472,48 @@ fn confirm_impl_candidate<'cx, 'tcx>( let ty = tcx.type_of(assoc_ty.item.def_id); if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { let err = tcx.ty_error_with_message( - DUMMY_SP, + obligation.cause.span, "impl item and trait item have different parameter counts", ); Progress { ty: err, obligations: nested } } else { + assoc_ty_own_obligations(selcx, obligation, &mut nested); Progress { ty: ty.subst(tcx, substs), obligations: nested } } } +// Get obligations corresponding to the predicates from the where-clause of the +// associated type itself. +// Note: `feature(generic_associated_types)` is required to write such +// predicates, even for non-generic associcated types. +fn assoc_ty_own_obligations<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: &mut Vec<PredicateObligation<'tcx>>, +) { + let tcx = selcx.tcx(); + for predicate in tcx + .predicates_of(obligation.predicate.item_def_id) + .instantiate_own(tcx, obligation.predicate.substs) + .predicates + { + let normalized = normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &predicate, + nested, + ); + nested.push(Obligation::with_depth( + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + normalized, + )); + } +} + /// Locate the definition of an associated type in the specialization hierarchy, /// starting from the given impl. /// diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 9cb5c232646..038ba431c47 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -165,7 +165,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - let needs_infer = stack.obligation.predicate.needs_infer(); + let needs_infer = stack.obligation.predicate.has_infer_types_or_consts(); // If there are STILL multiple candidates, we can further // reduce the list by dropping duplicates -- including @@ -327,8 +327,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); - if result { - candidates.vec.push(ProjectionCandidate); + for predicate_index in result { + candidates.vec.push(ProjectionCandidate(predicate_index)); } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 88b656ce680..96f0bedf6f1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -10,12 +10,13 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; +use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{ToPolyTraitRef, ToPredicate, WithConstness}; use rustc_span::def_id::DefId; -use crate::traits::project::{self, normalize_with_depth}; +use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::select::TraitObligationExt; use crate::traits::util; use crate::traits::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; @@ -68,9 +69,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::AutoImpl(data)) } - ProjectionCandidate => { - self.confirm_projection_candidate(obligation); - Ok(ImplSource::Param(Vec::new())) + ProjectionCandidate(idx) => { + let obligations = self.confirm_projection_candidate(obligation, idx); + Ok(ImplSource::Param(obligations)) } ClosureCandidate => { @@ -116,10 +117,72 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { + fn confirm_projection_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + idx: usize, + ) -> Vec<PredicateObligation<'tcx>> { self.infcx.commit_unconditionally(|_| { - let result = self.match_projection_obligation_against_definition_bounds(obligation); - assert!(result); + let tcx = self.tcx(); + + let trait_predicate = self.infcx.shallow_resolve(obligation.predicate); + let placeholder_trait_predicate = + self.infcx().replace_bound_vars_with_placeholders(&trait_predicate); + let placeholder_self_ty = placeholder_trait_predicate.self_ty(); + let (def_id, substs) = match *placeholder_self_ty.kind() { + ty::Projection(proj) => (proj.item_def_id, proj.substs), + ty::Opaque(def_id, substs) => (def_id, substs), + _ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty), + }; + + let candidate_predicate = tcx.item_bounds(def_id)[idx].subst(tcx, substs); + let candidate = candidate_predicate + .to_opt_poly_trait_ref() + .expect("projection candidate is not a trait predicate"); + let mut obligations = Vec::new(); + let candidate = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &candidate, + &mut obligations, + ); + + obligations.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(placeholder_trait_predicate.trait_ref.to_poly_trait_ref(), candidate) + .map(|InferOk { obligations, .. }| obligations) + .unwrap_or_else(|_| { + bug!( + "Projection bound `{:?}` was applicable to `{:?}` but now is not", + candidate, + obligation + ); + }), + ); + + if let ty::Projection(..) = placeholder_self_ty.kind() { + for predicate in tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates { + let normalized = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &predicate, + &mut obligations, + ); + obligations.push(Obligation::with_depth( + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + normalized, + )); + } + } + + obligations }) } @@ -229,7 +292,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let trait_obligations: Vec<PredicateObligation<'_>> = self.infcx.commit_unconditionally(|_| { let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); - let (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( @@ -307,55 +370,70 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // relying on projections in the impl-trait-ref. // // e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V` - impl_obligations.append(&mut substs.obligations); + substs.obligations.append(&mut impl_obligations); - ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations } + ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: substs.obligations } } fn confirm_object_candidate( &mut self, obligation: &TraitObligation<'tcx>, ) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> { + let tcx = self.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(data, ..) => data - .principal() - .unwrap_or_else(|| { - span_bug!(obligation.cause.span, "object candidate with no principal") - }) - .with_self_ty(self.tcx(), self_ty), + let trait_predicate = + self.infcx.replace_bound_vars_with_placeholders(&obligation.predicate); + let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty()); + let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref); + let data = match self_ty.kind() { + ty::Dynamic(data, ..) => { + self.infcx + .replace_bound_vars_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + data, + ) + .0 + } _ => span_bug!(obligation.cause.span, "object candidate with non-object"), }; + let object_trait_ref = data + .principal() + .unwrap_or_else(|| { + span_bug!(obligation.cause.span, "object candidate with no principal") + }) + .with_self_ty(self.tcx(), self_ty); + 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 + let nonmatching = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref)) + .take_while(|&t| { + match self.infcx.commit_if_ok(|_| { + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation_trait_ref, t) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| ()) + }) { + Ok(obligations) => { + upcast_trait_ref = Some(t); + nested.extend(obligations); + false + } + Err(_) => true, } - Err(_) => true, - } - }); + }); // Additionally, for each of the non-matching predicates that // we pass over, we sum up the set of number of vtable @@ -364,7 +442,73 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum(); } - ImplSourceObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested } + let upcast_trait_ref = upcast_trait_ref.unwrap(); + + // Check supertraits hold. This is so that their associated type bounds + // will be checked in the code below. + for super_trait in tcx + .super_predicates_of(trait_predicate.def_id()) + .instantiate(tcx, trait_predicate.trait_ref.substs) + .predicates + .into_iter() + { + if let ty::PredicateAtom::Trait(..) = super_trait.skip_binders() { + let normalized_super_trait = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &super_trait, + &mut nested, + ); + nested.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env.clone(), + normalized_super_trait, + )); + } + } + + let assoc_types: Vec<_> = tcx + .associated_items(trait_predicate.def_id()) + .in_definition_order() + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.def_id) } else { None }, + ) + .collect(); + + for assoc_type in assoc_types { + if !tcx.generics_of(assoc_type).params.is_empty() { + // FIXME(generic_associated_types) generate placeholders to + // extend the trait substs. + tcx.sess.span_fatal( + obligation.cause.span, + "generic associated types in trait objects are not supported yet", + ); + } + // This maybe belongs in wf, but that can't (doesn't) handle + // higher-ranked things. + // Prevent, e.g., `dyn Iterator<Item = str>`. + for bound in self.tcx().item_bounds(assoc_type) { + let subst_bound = bound.subst(tcx, trait_predicate.trait_ref.substs); + let normalized_bound = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &subst_bound, + &mut nested, + ); + nested.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env.clone(), + normalized_bound, + )); + } + } + + debug!("confirm_object_candidate: nested: {:?}", nested); + ImplSourceObjectData { upcast_trait_ref, vtable_base, nested } } fn confirm_fn_pointer_candidate( @@ -386,8 +530,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let Normalized { value: trait_ref, obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| { + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), @@ -396,12 +540,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }); - self.confirm_poly_trait_refs( + obligations.extend(self.confirm_poly_trait_refs( obligation.cause.clone(), obligation.param_env, obligation.predicate.to_poly_trait_ref(), trait_ref, - )?; + )?); Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested: obligations }) } @@ -413,7 +557,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("confirm_trait_alias_candidate({:?}, {:?})", obligation, alias_def_id); self.infcx.commit_unconditionally(|_| { - let (predicate, _) = + 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; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 114dc79c44f..a142ba58a69 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -9,6 +9,7 @@ use super::coherence::{self, Conflict}; use super::const_evaluatable; use super::project; use super::project::normalize_with_depth_to; +use super::project::ProjectionTyObligation; use super::util; use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; use super::wf; @@ -36,9 +37,8 @@ use rustc_middle::ty::fast_reject; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; -use rustc_middle::ty::{ - self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; +use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::symbol::sym; use std::cell::{Cell, RefCell}; @@ -343,7 +343,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(SelectionError::Overflow) } Err(e) => Err(e), - Ok(candidate) => Ok(Some(candidate)), + Ok(candidate) => { + debug!("select: candidate = {:?}", candidate); + Ok(Some(candidate)) + } } } @@ -413,9 +416,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { predicates: I, ) -> Result<EvaluationResult, OverflowError> where - I: IntoIterator<Item = PredicateObligation<'tcx>>, + I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug, { let mut result = EvaluatedToOk; + debug!("evaluate_predicates_recursively({:?})", predicates); for obligation in predicates { let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); @@ -436,9 +440,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: PredicateObligation<'tcx>, ) -> Result<EvaluationResult, OverflowError> { debug!( - "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", - previous_stack.head(), - obligation + "evaluate_predicate_recursively(obligation={:?}, previous_stack={:?})", + obligation, + previous_stack.head() ); // `previous_stack` stores a `TraitObligation`, while `obligation` is @@ -479,15 +483,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx, obligation.param_env, obligation.cause.body_id, + obligation.recursion_depth + 1, arg, obligation.cause.span, ) { Some(mut obligations) => { self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ) + self.evaluate_predicates_recursively(previous_stack, obligations) } None => Ok(EvaluatedToAmbig), }, @@ -511,10 +513,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match project::poly_project_and_unify_type(self, &project_obligation) { Ok(Ok(Some(mut subobligations))) => { self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); - let result = self.evaluate_predicates_recursively( - previous_stack, - subobligations.into_iter(), - ); + let result = self + .evaluate_predicates_recursively(previous_stack, subobligations); if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate(self, data) { @@ -879,10 +879,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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(), - ), + Ok(selection) => { + debug!("evaluate_candidate: selection = {:?}", selection); + this.evaluate_predicates_recursively( + stack.list(), + selection.nested_obligations().into_iter(), + ) + } Err(..) => Ok(EvaluatedToErr), } })?; @@ -943,10 +946,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// 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`. + /// Additionally, methods like `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. @@ -1156,12 +1158,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.selection_cache.insert(param_env.and(trait_ref), dep_node, candidate); } + /// Matches a predicate against the bounds of its self type. + /// + /// Given an obligation like `<T as Foo>::Bar: Baz` where the self type is + /// a projection, look at the bounds of `T::Bar`, see if we can find a + /// `Baz` bound. We return indexes into the list returned by + /// `tcx.item_bounds` for any applicable bounds. fn match_projection_obligation_against_definition_bounds( &mut self, obligation: &TraitObligation<'tcx>, - ) -> bool { + ) -> smallvec::SmallVec<[usize; 2]> { let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - let (placeholder_trait_predicate, _) = + let placeholder_trait_predicate = self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); debug!( "match_projection_obligation_against_definition_bounds: \ @@ -1170,11 +1178,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let tcx = self.infcx.tcx; - let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { - ty::Projection(ref data) => { - tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) - } - ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), + let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { + ty::Projection(ref data) => (data.item_def_id, data.substs), + ty::Opaque(def_id, substs) => (def_id, substs), _ => { span_bug!( obligation.cause.span, @@ -1184,48 +1190,86 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } }; + let bounds = tcx.item_bounds(def_id).subst(tcx, substs); + + // The bounds returned by `item_bounds` may contain duplicates after + // normalization, so try to deduplicate when possible to avoid + // unnecessary ambiguity. + let mut distinct_normalized_bounds = FxHashSet::default(); - let matching_bound = predicates.iter().find_map(|bound| { - if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() { - let bound = ty::Binder::bind(pred.trait_ref); - if self.infcx.probe(|_| { - self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref) - }) { - return Some(bound); + let matching_bounds = bounds + .iter() + .enumerate() + .filter_map(|(idx, bound)| { + if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() { + let bound = ty::Binder::bind(pred.trait_ref); + if self.infcx.probe(|_| { + match self.match_projection( + obligation, + bound, + placeholder_trait_predicate.trait_ref, + ) { + Ok(None) => true, + Ok(Some(normalized_trait)) + if distinct_normalized_bounds.insert(normalized_trait) => + { + true + } + _ => false, + } + }) { + return Some(idx); + } } - } - None - }); + None + }) + .collect(); debug!( "match_projection_obligation_against_definition_bounds: \ - matching_bound={:?}", - matching_bound + matching_bounds={:?}", + matching_bounds ); - 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); - - assert!(result); - true - } - } + matching_bounds } + /// Equates the trait in `obligation` with trait bound. If the two traits + /// can be equated and the normalized trait bound doesn't contain inference + /// variables or placeholders, the normalized bound is returned. fn match_projection( &mut self, obligation: &TraitObligation<'tcx>, trait_bound: ty::PolyTraitRef<'tcx>, placeholder_trait_ref: ty::TraitRef<'tcx>, - ) -> bool { + ) -> Result<Option<ty::PolyTraitRef<'tcx>>, ()> { debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); + if placeholder_trait_ref.def_id != trait_bound.def_id() { + // Avoid unnecessary normalization + return Err(()); + } + + let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_bound, + ) + }); self.infcx .at(&obligation.cause, obligation.param_env) .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) - .is_ok() + .map(|InferOk { obligations: _, value: () }| { + // This method is called within a probe, so we can't have + // inference variables and placeholders escape. + if !trait_bound.needs_infer() && !trait_bound.has_placeholders() { + Some(trait_bound) + } else { + None + } + }) + .map_err(|_| ()) } fn evaluate_where_clause<'o>( @@ -1235,14 +1279,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result<EvaluationResult, OverflowError> { 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()) - } + Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations), Err(()) => Ok(EvaluatedToErr), } }) } + pub(super) fn match_projection_projections( + &mut self, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + data: &PolyProjectionPredicate<'tcx>, + potentially_unnormalized_candidates: bool, + ) -> bool { + let mut nested_obligations = Vec::new(); + let projection_ty = if potentially_unnormalized_candidates { + ensure_sufficient_stack(|| { + project::normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &data.map_bound_ref(|data| data.projection_ty), + &mut nested_obligations, + ) + }) + } else { + data.map_bound_ref(|data| data.projection_ty) + }; + + // FIXME(generic_associated_types): Compare the whole projections + let data_poly_trait_ref = projection_ty.map_bound(|proj| proj.trait_ref(self.tcx())); + let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation_poly_trait_ref, data_poly_trait_ref) + .map_or(false, |InferOk { obligations, value: () }| { + self.evaluate_predicates_recursively( + TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), + nested_obligations.into_iter().chain(obligations), + ) + .map_or(false, |res| res.may_apply()) + }) + } + /////////////////////////////////////////////////////////////////////////// // WINNOW // @@ -1277,18 +1357,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. - match other.candidate { + match (&other.candidate, &victim.candidate) { + (_, AutoImplCandidate(..)) | (AutoImplCandidate(..), _) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => true, - ParamCandidate(ref cand) => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + (BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate, _) => true, + (_, BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate) => false, + + (ParamCandidate(..), ParamCandidate(..)) => false, + + // 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. + // Arbitrarily give param candidates priority + // over projection and object candidates. + ( + ParamCandidate(ref cand), ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -1296,28 +1385,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | 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" - ); - } - // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + | TraitAliasCandidate(..) + | ObjectCandidate + | ProjectionCandidate(_), + ) => !is_global(cand), + (ObjectCandidate | ProjectionCandidate(_), ParamCandidate(ref cand)) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) + } + ( + ImplCandidate(_) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate(..), + ParamCandidate(ref cand), + ) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) && other.evaluation.must_apply_modulo_regions() + } + + (ProjectionCandidate(i), ProjectionCandidate(j)) => { + // Arbitrarily pick the first candidate for backwards + // compatibility reasons. Don't let this affect inference. + i > j && !needs_infer + } + (ObjectCandidate, ObjectCandidate) => bug!("Duplicate object candidate"), + (ObjectCandidate, ProjectionCandidate(_)) + | (ProjectionCandidate(_), ObjectCandidate) => { + bug!("Have both object and projection candidate") + } + + // Arbitrarily give projection and object candidates priority. + ( + ObjectCandidate | ProjectionCandidate(_), ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -1325,98 +1431,100 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | 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) => { + | TraitAliasCandidate(..), + ) => true, + + ( + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..), + ObjectCandidate | ProjectionCandidate(_), + ) => false, + + (&ImplCandidate(other_def), &ImplCandidate(victim_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); - } - _ => (), + let tcx = self.tcx(); + if tcx.specializes((other_def, victim_def)) { + return true; } - } - - 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, + 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, + }; + } else { + false } } - _ => false, + + // Everything else is ambiguous + ( + ImplCandidate(_) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate(..), + ImplCandidate(_) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate(..), + ) => false, } } @@ -1649,7 +1757,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let ty: ty::Binder<Ty<'tcx>> = ty::Binder::bind(ty); // <----/ self.infcx.commit_unconditionally(|_| { - let (placeholder_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); + let placeholder_ty = self.infcx.replace_bound_vars_with_placeholders(&ty); let Normalized { value: normalized_ty, mut obligations } = ensure_sufficient_stack(|| { project::normalize_with_depth( @@ -1717,7 +1825,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(()); } - let (placeholder_obligation, _) = + let placeholder_obligation = self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 909cd2aa155..d66bfd48206 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -20,6 +20,7 @@ pub fn obligations<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, + recursion_depth: usize, arg: GenericArg<'tcx>, span: Span, ) -> Option<Vec<traits::PredicateObligation<'tcx>>> { @@ -59,7 +60,8 @@ pub fn obligations<'a, 'tcx>( GenericArgKind::Lifetime(..) => return Some(Vec::new()), }; - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None }; wf.compute(arg); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); @@ -80,7 +82,8 @@ pub fn trait_obligations<'a, 'tcx>( span: Span, item: Option<&'tcx hir::Item<'tcx>>, ) -> Vec<traits::PredicateObligation<'tcx>> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item }; + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth: 0, item }; wf.compute_trait_ref(trait_ref, Elaborate::All); wf.normalize() } @@ -92,7 +95,15 @@ pub fn predicate_obligations<'a, 'tcx>( predicate: ty::Predicate<'tcx>, span: Span, ) -> Vec<traits::PredicateObligation<'tcx>> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; + let mut wf = WfPredicates { + infcx, + param_env, + body_id, + span, + out: vec![], + recursion_depth: 0, + item: None, + }; // It's ok to skip the binder here because wf code is prepared for it match predicate.skip_binders() { @@ -142,6 +153,7 @@ struct WfPredicates<'a, 'tcx> { body_id: hir::HirId, span: Span, out: Vec<traits::PredicateObligation<'tcx>>, + recursion_depth: usize, item: Option<&'tcx hir::Item<'tcx>>, } @@ -241,18 +253,27 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) } - fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> { + fn normalize(mut self) -> Vec<traits::PredicateObligation<'tcx>> { 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()); + for mut obligation in self.out { + assert!(!obligation.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); + // Don't normalize the whole obligation, the param env is either + // already normalized, or we're currently normalizing the + // param_env. Either way we should only normalize the predicate. + let normalized_predicate = traits::project::normalize_with_depth_to( + &mut selcx, + param_env, + cause.clone(), + self.recursion_depth, + &obligation.predicate, + &mut obligations, + ); + obligation.predicate = normalized_predicate; + obligations.push(obligation); } obligations } @@ -265,6 +286,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { debug!("compute_trait_ref obligations {:?}", obligations); let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; + let depth = self.recursion_depth; let item = self.item; @@ -286,7 +308,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { &obligation.predicate, tcx.associated_items(trait_ref.def_id).in_definition_order(), ); - traits::Obligation::new(cause, param_env, obligation.predicate) + traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate) }; if let Elaborate::All = elaborate { @@ -315,8 +337,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { new_cause.make_mut().span = self_ty.span; } } - traits::Obligation::new( + traits::Obligation::with_depth( new_cause, + depth, param_env, ty::PredicateAtom::WellFormed(arg).to_predicate(tcx), ) @@ -327,17 +350,51 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// 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(self.infcx.tcx); - let cause = self.cause(traits::ProjectionWf(data)); - self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); - } + // A projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its substs are wf + // + // (*) The predicates of an associated type include the predicates of + // the trait that it's contained in. For example, given + // + // trait A<T>: Clone { + // type X where T: Copy; + // } + // + // The predicates of `<() as A<i32>>::X` are: + // [ + // `(): Sized` + // `(): Clone` + // `(): A<i32>` + // `i32: Sized` + // `i32: Clone` + // `i32: Copy` + // ] + let obligations = self.nominal_obligations(data.item_def_id, data.substs); + self.out.extend(obligations); + + let tcx = self.tcx(); + let cause = self.cause(traits::MiscObligation); + let param_env = self.param_env; + let depth = self.recursion_depth; + + self.out.extend( + data.substs + .iter() + .filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) + .filter(|arg| !arg.has_escaping_bound_vars()) + .map(|arg| { + traits::Obligation::with_depth( + cause.clone(), + depth, + param_env, + ty::PredicateAtom::WellFormed(arg).to_predicate(tcx), + ) + }), + ); } fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { @@ -347,8 +404,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None), substs: self.infcx.tcx.mk_substs_trait(subty, &[]), }; - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, trait_ref.without_const().to_predicate(self.infcx.tcx), )); @@ -359,6 +417,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn compute(&mut self, arg: GenericArg<'tcx>) { let mut walker = arg.walk(); let param_env = self.param_env; + let depth = self.recursion_depth; while let Some(arg) = walker.next() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -378,8 +437,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let predicate = ty::PredicateAtom::ConstEvaluatable(def, substs) .to_predicate(self.tcx()); let cause = self.cause(traits::MiscObligation); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, predicate, )); @@ -394,8 +454,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { val: ty::ConstKind::Infer(resolved), ..*constant }); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, ty::PredicateAtom::WellFormed(resolved_constant.into()) .to_predicate(self.tcx()), @@ -480,8 +541,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // 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( + self.out.push(traits::Obligation::with_depth( cause, + depth, param_env, ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(rty, r)) .to_predicate(self.tcx()), @@ -571,8 +633,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let component_traits = data.auto_traits().chain(data.principal_def_id()); let tcx = self.tcx(); self.out.extend(component_traits.map(|did| { - traits::Obligation::new( + traits::Obligation::with_depth( cause.clone(), + depth, param_env, ty::PredicateAtom::ObjectSafe(did).to_predicate(tcx), ) @@ -597,8 +660,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { if let ty::Infer(ty::TyVar(_)) = ty.kind() { // Not yet resolved, but we've made progress. let cause = self.cause(traits::MiscObligation); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, param_env, ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx()), )); @@ -635,7 +699,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .zip(origins.into_iter().rev()) .map(|((pred, span), origin_def_id)| { let cause = self.cause(traits::BindingObligation(origin_def_id, span)); - traits::Obligation::new(cause, self.param_env, pred) + traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred) }) .filter(|pred| !pred.has_escaping_bound_vars()) .collect() @@ -688,8 +752,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { 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( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, outlives.to_predicate(self.infcx.tcx), )); diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml index 6d495718276..b176b150cd3 100644 --- a/compiler/rustc_traits/Cargo.toml +++ b/compiler/rustc_traits/Cargo.toml @@ -12,9 +12,9 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.29.0" -chalk-solve = "0.29.0" -chalk-engine = "0.29.0" +chalk-ir = "0.31.0" +chalk-solve = "0.31.0" +chalk-engine = "0.31.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_infer = { path = "../rustc_infer" } rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 828ee6dea62..3368c5b7699 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -22,7 +22,6 @@ use rustc_ast::ast; pub struct RustIrDatabase<'tcx> { pub(crate) interner: RustInterner<'tcx>, - pub(crate) restatic_placeholder: ty::Region<'tcx>, pub(crate) reempty_placeholder: ty::Region<'tcx>, } @@ -39,17 +38,27 @@ impl<'tcx> RustIrDatabase<'tcx> { bound_vars: SubstsRef<'tcx>, ) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> { let predicates = self.interner.tcx.predicates_of(def_id).predicates; - let mut regions_substitutor = lowering::RegionsSubstitutor::new( - self.interner.tcx, - self.restatic_placeholder, - self.reempty_placeholder, - ); + let mut regions_substitutor = + lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder); predicates .iter() .map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars)) .map(|wc| wc.fold_with(&mut regions_substitutor)) .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect() } + + fn bounds_for<T>(&self, def_id: DefId, bound_vars: SubstsRef<'tcx>) -> Vec<T> + where + ty::Predicate<'tcx>: LowerInto<'tcx, std::option::Option<T>>, + { + self.interner + .tcx + .explicit_item_bounds(def_id) + .iter() + .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars)) + .filter_map(|bound| LowerInto::<Option<_>>::lower_into(bound, &self.interner)) + .collect() + } } impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'tcx> { @@ -73,10 +82,9 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t } let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); - // FIXME(chalk): this really isn't right I don't think. The functions - // for GATs are a bit hard to figure out. Are these supposed to be where - // clauses or bounds? + let where_clauses = self.where_clauses_for(def_id, bound_vars); + let bounds = self.bounds_for(def_id, bound_vars); Arc::new(chalk_solve::rust_ir::AssociatedTyDatum { trait_id: chalk_ir::TraitId(trait_def_id), @@ -84,7 +92,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t name: (), binders: chalk_ir::Binders::new( binders, - chalk_solve::rust_ir::AssociatedTyDatumBound { bounds: vec![], where_clauses }, + chalk_solve::rust_ir::AssociatedTyDatumBound { bounds, where_clauses }, ), }) } @@ -262,11 +270,8 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl"); let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars); - let mut regions_substitutor = lowering::RegionsSubstitutor::new( - self.interner.tcx, - self.restatic_placeholder, - self.reempty_placeholder, - ); + let mut regions_substitutor = + lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder); let trait_ref = trait_ref.fold_with(&mut regions_substitutor); let where_clauses = self.where_clauses_for(def_id, bound_vars); @@ -304,11 +309,8 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let self_ty = trait_ref.self_ty(); let self_ty = self_ty.subst(self.interner.tcx, bound_vars); - let mut regions_substitutor = lowering::RegionsSubstitutor::new( - self.interner.tcx, - self.restatic_placeholder, - self.reempty_placeholder, - ); + let mut regions_substitutor = + lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder); let self_ty = self_ty.fold_with(&mut regions_substitutor); let lowered_ty = self_ty.lower_into(&self.interner); @@ -442,11 +444,13 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let bound_vars = bound_vars_for_item(self.interner.tcx, opaque_ty_id.0); let binders = binders_for(&self.interner, bound_vars); let where_clauses = self.where_clauses_for(opaque_ty_id.0, bound_vars); + let bounds = self.bounds_for(opaque_ty_id.0, bound_vars); let value = chalk_solve::rust_ir::OpaqueTyDatumBound { - bounds: chalk_ir::Binders::new(binders.clone(), vec![]), + bounds: chalk_ir::Binders::new(binders.clone(), bounds), where_clauses: chalk_ir::Binders::new(binders, where_clauses), }; + Arc::new(chalk_solve::rust_ir::OpaqueTyDatum { opaque_ty_id, bound: chalk_ir::Binders::empty(&self.interner, value), @@ -576,6 +580,20 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let substitution = &substs.as_slice(&self.interner)[0..substs.len(&self.interner) - 3]; chalk_ir::Substitution::from_iter(&self.interner, substitution) } + + fn generator_datum( + &self, + _generator_id: chalk_ir::GeneratorId<RustInterner<'tcx>>, + ) -> Arc<chalk_solve::rust_ir::GeneratorDatum<RustInterner<'tcx>>> { + unimplemented!() + } + + fn generator_witness_datum( + &self, + _generator_id: chalk_ir::GeneratorId<RustInterner<'tcx>>, + ) -> Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<RustInterner<'tcx>>> { + unimplemented!() + } } /// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 1e1841a57f8..391251b6fa5 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -427,13 +427,20 @@ impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty<RustInterner<'tcx>> { chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), }, }, - chalk_ir::TypeName::Array => unimplemented!(), + chalk_ir::TypeName::Array => { + let substs = application_ty.substitution.as_slice(interner); + let ty = substs[0].assert_ty_ref(interner).lower_into(interner); + let c = substs[1].assert_const_ref(interner).lower_into(interner); + ty::Array(ty, interner.tcx.mk_const(c)) + } chalk_ir::TypeName::FnDef(id) => { ty::FnDef(id.0, application_ty.substitution.lower_into(interner)) } chalk_ir::TypeName::Closure(closure) => { ty::Closure(closure.0, application_ty.substitution.lower_into(interner)) } + chalk_ir::TypeName::Generator(_) => unimplemented!(), + chalk_ir::TypeName::GeneratorWitness(_) => unimplemented!(), chalk_ir::TypeName::Never => ty::Never, chalk_ir::TypeName::Tuple(_size) => { ty::Tuple(application_ty.substitution.lower_into(interner)) @@ -483,7 +490,15 @@ impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty<RustInterner<'tcx>> { universe: ty::UniverseIndex::from_usize(placeholder.ui.counter), name: ty::BoundVar::from_usize(placeholder.idx), }), - TyData::Alias(_alias_ty) => unimplemented!(), + chalk_ir::TyData::Alias(alias_ty) => match alias_ty { + chalk_ir::AliasTy::Projection(projection) => ty::Projection(ty::ProjectionTy { + item_def_id: projection.associated_ty_id.0, + substs: projection.substitution.lower_into(interner), + }), + chalk_ir::AliasTy::Opaque(opaque) => { + ty::Opaque(opaque.opaque_ty_id.0, opaque.substitution.lower_into(interner)) + } + }, TyData::Function(_quantified_ty) => unimplemented!(), TyData::BoundVar(_bound) => ty::Bound( ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), @@ -519,8 +534,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t ty::BrEnv => unimplemented!(), }, ReFree(_) => unimplemented!(), - // FIXME(chalk): need to handle ReStatic - ReStatic => unimplemented!(), + ReStatic => chalk_ir::LifetimeData::Static.intern(interner), ReVar(_) => unimplemented!(), RePlaceholder(placeholder_region) => { chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { @@ -550,6 +564,7 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'t name: ty::BoundRegion::BrAnon(p.idx as u32), }) } + chalk_ir::LifetimeData::Static => ty::RegionKind::ReStatic, chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), }; interner.tcx.mk_region(kind) @@ -701,7 +716,16 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<Ru }), ) } - ty::ExistentialPredicate::Projection(_predicate) => unimplemented!(), + ty::ExistentialPredicate::Projection(predicate) => chalk_ir::Binders::new( + chalk_ir::VariableKinds::empty(interner), + chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { + alias: chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id: chalk_ir::AssocTypeId(predicate.item_def_id), + substitution: predicate.substs.lower_into(interner), + }), + ty: predicate.ty.lower_into(interner), + }), + ), ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( chalk_ir::VariableKinds::empty(interner), chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { @@ -728,6 +752,87 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::FnSig<RustInterner<'tcx>>> for ty::Binder<t } } +// We lower into an Option here since there are some predicates which Chalk +// doesn't have a representation for yet (as an `InlineBound`). The `Option` will +// eventually be removed. +impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<RustInterner<'tcx>>>> + for ty::Predicate<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> Option<chalk_solve::rust_ir::QuantifiedInlineBound<RustInterner<'tcx>>> { + match self.bound_atom(interner.tcx).skip_binder() { + ty::PredicateAtom::Trait(predicate, _) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &ty::Binder::bind(predicate)); + + Some(chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::InlineBound::TraitBound( + predicate.trait_ref.lower_into(interner), + ), + )) + } + ty::PredicateAtom::Projection(predicate) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &ty::Binder::bind(predicate)); + + Some(chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::InlineBound::AliasEqBound(predicate.lower_into(interner)), + )) + } + ty::PredicateAtom::TypeOutlives(_predicate) => None, + ty::PredicateAtom::WellFormed(_ty) => None, + + ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + bug!("unexpected predicate {}", &self) + } + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::TraitBound<RustInterner<'tcx>>> + for ty::TraitRef<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_solve::rust_ir::TraitBound<RustInterner<'tcx>> { + chalk_solve::rust_ir::TraitBound { + trait_id: chalk_ir::TraitId(self.def_id), + args_no_self: self.substs[1..].iter().map(|arg| arg.lower_into(interner)).collect(), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>>> + for ty::ProjectionPredicate<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>> { + let trait_ref = self.projection_ty.trait_ref(interner.tcx); + chalk_solve::rust_ir::AliasEqBound { + trait_bound: trait_ref.lower_into(interner), + associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.item_def_id), + parameters: self.projection_ty.substs[trait_ref.substs.len()..] + .iter() + .map(|arg| arg.lower_into(interner)) + .collect(), + value: self.ty.lower_into(interner), + } + } +} + /// To collect bound vars, we have to do two passes. In the first pass, we /// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then /// replace `BrNamed` into `BrAnon`. The two separate passes are important, @@ -1035,17 +1140,12 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { /// Used to substitute specific `Regions`s with placeholders. crate struct RegionsSubstitutor<'tcx> { tcx: TyCtxt<'tcx>, - restatic_placeholder: ty::Region<'tcx>, reempty_placeholder: ty::Region<'tcx>, } impl<'tcx> RegionsSubstitutor<'tcx> { - crate fn new( - tcx: TyCtxt<'tcx>, - restatic_placeholder: ty::Region<'tcx>, - reempty_placeholder: ty::Region<'tcx>, - ) -> Self { - RegionsSubstitutor { tcx, restatic_placeholder, reempty_placeholder } + crate fn new(tcx: TyCtxt<'tcx>, reempty_placeholder: ty::Region<'tcx>) -> Self { + RegionsSubstitutor { tcx, reempty_placeholder } } } @@ -1056,7 +1156,6 @@ impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> { fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { match r { - ty::ReStatic => self.restatic_placeholder, ty::ReEmpty(ui) => { assert_eq!(ui.as_usize(), 0); self.reempty_placeholder diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index 63c5b884357..f174a92274e 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -42,10 +42,6 @@ crate fn evaluate_goal<'tcx>( let mut placeholders_collector = PlaceholdersCollector::new(); obligation.visit_with(&mut placeholders_collector); - let restatic_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { - universe: ty::UniverseIndex::ROOT, - name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder), - })); let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { universe: ty::UniverseIndex::ROOT, name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), @@ -57,8 +53,7 @@ crate fn evaluate_goal<'tcx>( // FIXME(chalk): we really should be substituting these back in the solution let _params: FxHashMap<usize, ParamTy> = params_substitutor.params; - let mut regions_substitutor = - RegionsSubstitutor::new(tcx, restatic_placeholder, reempty_placeholder); + let mut regions_substitutor = RegionsSubstitutor::new(tcx, reempty_placeholder); let obligation = obligation.fold_with(&mut regions_substitutor); let max_universe = obligation.max_universe.index(); @@ -101,7 +96,7 @@ crate fn evaluate_goal<'tcx>( use chalk_solve::Solver; let mut solver = chalk_engine::solve::SLGSolver::new(32, None); - let db = ChalkRustIrDatabase { interner, restatic_placeholder, reempty_placeholder }; + let db = ChalkRustIrDatabase { interner, reempty_placeholder }; let solution = chalk_solve::logging::with_tracing_logs(|| solver.solve(&db, &lowered_goal)); // Ideally, the code to convert *back* to rustc types would live close to diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 79308b032ec..bc5c07fce04 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -61,8 +61,8 @@ fn compute_implied_outlives_bounds<'tcx>( // than the ultimate set. (Note: normally there won't be // unresolved inference variables here anyway, but there might be // during typeck under some circumstances.) - let obligations = - wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, arg, DUMMY_SP).unwrap_or(vec![]); + let obligations = wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP) + .unwrap_or(vec![]); // N.B., all of these predicates *ought* to be easily proven // true. In fact, their correctness is (mostly) implied by diff --git a/compiler/rustc_ty/src/ty.rs b/compiler/rustc_ty/src/ty.rs index c4b6b64339a..1f21d9488a4 100644 --- a/compiler/rustc_ty/src/ty.rs +++ b/compiler/rustc_ty/src/ty.rs @@ -1,11 +1,9 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::svh::Svh; use rustc_hir as hir; -use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -use rustc_infer::traits::util; use rustc_middle::hir::map as hir_map; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{ self, Binder, Predicate, PredicateAtom, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness, }; @@ -492,133 +490,6 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { fn_like.asyncness() } -/// For associated types we allow bounds written on the associated type -/// (`type X: Trait`) to be used as candidates. We also allow the same bounds -/// when desugared as bounds on the trait `where Self::X: Trait`. -/// -/// Note that this filtering is done with the items identity substs to -/// simplify checking that these bounds are met in impls. This means that -/// a bound such as `for<'b> <Self as X<'b>>::U: Clone` can't be used, as in -/// `hr-associated-type-bound-1.rs`. -fn associated_type_projection_predicates( - tcx: TyCtxt<'_>, - assoc_item_def_id: DefId, -) -> &'_ ty::List<ty::Predicate<'_>> { - let generic_trait_bounds = tcx.predicates_of(assoc_item_def_id); - // We include predicates from the trait as well to handle - // `where Self::X: Trait`. - let item_bounds = generic_trait_bounds.instantiate_identity(tcx); - let item_predicates = util::elaborate_predicates(tcx, item_bounds.predicates.into_iter()); - - let assoc_item_ty = ty::ProjectionTy { - item_def_id: assoc_item_def_id, - substs: InternalSubsts::identity_for_item(tcx, assoc_item_def_id), - }; - - let predicates = item_predicates.filter_map(|obligation| { - let pred = obligation.predicate; - match pred.skip_binders() { - ty::PredicateAtom::Trait(tr, _) => { - if let ty::Projection(p) = *tr.self_ty().kind() { - if p == assoc_item_ty { - return Some(pred); - } - } - } - ty::PredicateAtom::Projection(proj) => { - if let ty::Projection(p) = *proj.projection_ty.self_ty().kind() { - if p == assoc_item_ty { - return Some(pred); - } - } - } - ty::PredicateAtom::TypeOutlives(outlives) => { - if let ty::Projection(p) = *outlives.0.kind() { - if p == assoc_item_ty { - return Some(pred); - } - } - } - _ => {} - } - None - }); - - let result = tcx.mk_predicates(predicates); - debug!( - "associated_type_projection_predicates({}) = {:?}", - tcx.def_path_str(assoc_item_def_id), - result - ); - result -} - -/// Opaque types don't have the same issues as associated types: the only -/// predicates on an opaque type (excluding those it inherits from its parent -/// item) should be of the form we're expecting. -fn opaque_type_projection_predicates( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> &'_ ty::List<ty::Predicate<'_>> { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - - let bounds = tcx.predicates_of(def_id); - let predicates = - util::elaborate_predicates(tcx, bounds.predicates.iter().map(|&(pred, _)| pred)); - - let filtered_predicates = predicates.filter_map(|obligation| { - let pred = obligation.predicate; - match pred.skip_binders() { - ty::PredicateAtom::Trait(tr, _) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = *tr.self_ty().kind() { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } - } - ty::PredicateAtom::Projection(proj) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = - *proj.projection_ty.self_ty().kind() - { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } - } - ty::PredicateAtom::TypeOutlives(outlives) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = *outlives.0.kind() { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } else { - // These can come from elaborating other predicates - return None; - } - } - // These can come from elaborating other predicates - ty::PredicateAtom::RegionOutlives(_) => return None, - _ => {} - } - tcx.sess.delay_span_bug( - obligation.cause.span(tcx), - &format!("unexpected predicate {:?} on opaque type", pred), - ); - None - }); - - let result = tcx.mk_predicates(filtered_predicates); - debug!("opaque_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result); - result -} - -fn projection_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List<ty::Predicate<'_>> { - match tcx.def_kind(def_id) { - DefKind::AssocTy => associated_type_projection_predicates(tcx, def_id), - DefKind::OpaqueTy => opaque_type_projection_predicates(tcx, def_id), - k => bug!("projection_predicates called on {}", k.descr(def_id)), - } -} - pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { asyncness, @@ -636,7 +507,6 @@ pub fn provide(providers: &mut ty::query::Providers) { instance_def_size_estimate, issue33140_self_ty, impl_defaultness, - projection_predicates, ..*providers }; } diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs index 63295f5faac..80f39051c58 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_typeck/src/bounds.rs @@ -71,12 +71,8 @@ impl<'tcx> Bounds<'tcx> { self.region_bounds .iter() .map(|&(region_bound, span)| { - // Account for the binder being introduced below; no need to shift `param_ty` - // because, at present at least, it either only refers to early-bound regions, - // or it's a generic associated type that deliberately has escaping bound vars. - let region_bound = ty::fold::shift_region(tcx, region_bound, 1); let outlives = ty::OutlivesPredicate(param_ty, region_bound); - (ty::Binder::bind(outlives).to_predicate(tcx), span) + (ty::Binder::dummy(outlives).to_predicate(tcx), span) }) .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 0647be2dfde..d319ac2cba6 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -1,15 +1,16 @@ use super::coercion::CoerceMany; +use super::compare_method::check_type_bounds; use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; use super::*; use rustc_attr as attr; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemKind, Node}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::RegionVariableOrigin; +use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; @@ -18,6 +19,8 @@ use rustc_session::config::EntryFnType; use rustc_span::symbol::sym; use rustc_span::{self, MultiSpan, Span}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::opaque_types::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCauseCode}; pub fn check_wf_new(tcx: TyCtxt<'_>) { @@ -385,8 +388,13 @@ pub(super) fn check_opaque<'tcx>( origin: &hir::OpaqueTyOrigin, ) { check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - tcx.ensure().type_of(def_id); - check_opaque_for_cycles(tcx, def_id, substs, span, origin); + if tcx.type_of(def_id).references_error() { + return; + } + if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() { + return; + } + check_opaque_meets_bounds(tcx, def_id, substs, span, origin); } /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result @@ -453,8 +461,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( ty: None, }; let prohibit_opaque = tcx - .predicates_of(def_id) - .predicates + .explicit_item_bounds(def_id) .iter() .any(|(predicate, _)| predicate.visit_with(&mut visitor)); debug!( @@ -476,7 +483,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( span, E0760, "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ - a parent scope", + a parent scope", if is_async { "async fn" } else { "impl Trait" }, ); @@ -504,7 +511,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>( substs: SubstsRef<'tcx>, span: Span, origin: &hir::OpaqueTyOrigin, -) { +) -> Result<(), ErrorReported> { if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) { match origin { @@ -514,9 +521,82 @@ pub(super) fn check_opaque_for_cycles<'tcx>( } _ => opaque_type_cycle_error(tcx, def_id, span), } + Err(ErrorReported) + } else { + Ok(()) } } +/// Check that the concrete type behind `impl Trait` actually implements `Trait`. +/// +/// This is mostly checked at the places that specify the opaque type, but we +/// check those cases in the `param_env` of that function, which may have +/// bounds not on this opaque type: +/// +/// type X<T> = impl Clone +/// fn f<T: Clone>(t: T) -> X<T> { +/// t +/// } +/// +/// Without this check the above code is incorrectly accepted: we would ICE if +/// some tried, for example, to clone an `Option<X<&mut ()>>`. +fn check_opaque_meets_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + span: Span, + origin: &hir::OpaqueTyOrigin, +) { + match origin { + // Checked when type checking the function containing them. + hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => return, + // Can have different predicates to their defining use + hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::Misc => {} + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let param_env = tcx.param_env(def_id); + + tcx.infer_ctxt().enter(move |infcx| { + let inh = Inherited::new(infcx, def_id); + let infcx = &inh.infcx; + let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); + + let misc_cause = traits::ObligationCause::misc(span, hir_id); + + let (_, opaque_type_map) = inh.register_infer_ok_obligations( + infcx.instantiate_opaque_types(def_id, hir_id, param_env, &opaque_ty, span), + ); + + for (def_id, opaque_defn) in opaque_type_map { + match infcx + .at(&misc_cause, param_env) + .eq(opaque_defn.concrete_ty, tcx.type_of(def_id).subst(tcx, opaque_defn.substs)) + { + Ok(infer_ok) => inh.register_infer_ok_obligations(infer_ok), + Err(ty_err) => tcx.sess.delay_span_bug( + opaque_defn.definition_span, + &format!( + "could not unify `{}` with revealed type:\n{}", + opaque_defn.concrete_ty, ty_err, + ), + ), + } + } + + // Check that all obligations are satisfied by the implementation's + // version. + if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { + infcx.report_fulfillment_errors(errors, None, false); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let fcx = FnCtxt::new(&inh, param_env, hir_id); + fcx.regionck_item(hir_id, span, &[]); + }); +} + pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { debug!( "check_item_type(it.hir_id={}, it.name={})", @@ -553,9 +633,25 @@ 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::Fn(sig, _) = &item.kind { - let abi = sig.header.abi; - fn_maybe_err(tcx, item.ident.span, abi); + match item.kind { + hir::TraitItemKind::Fn(ref sig, _) => { + let abi = sig.header.abi; + fn_maybe_err(tcx, item.ident.span, abi); + } + hir::TraitItemKind::Type(.., Some(_default)) => { + let item_def_id = tcx.hir().local_def_id(item.hir_id).to_def_id(); + let assoc_item = tcx.associated_item(item_def_id); + let trait_substs = + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + let _: Result<_, rustc_errors::ErrorReported> = check_type_bounds( + tcx, + assoc_item, + assoc_item, + item.span, + ty::TraitRef { def_id: def_id.to_def_id(), substs: trait_substs }, + ); + } + _ => {} } } } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 7aa54e0ebcc..4acc7451a21 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -5,6 +5,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; +use rustc_infer::traits::util; use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -327,7 +328,7 @@ fn compare_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let fcx = FnCtxt::new(&inh, param_env, impl_m_hir_id); - fcx.regionck_item(impl_m_hir_id, impl_m_span, &[]); + fcx.regionck_item(impl_m_hir_id, impl_m_span, trait_sig.inputs_and_output); Ok(()) }) @@ -1052,7 +1053,7 @@ crate fn compare_ty_impl<'tcx>( compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)?; - compare_projection_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref) + check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref) })(); } @@ -1170,20 +1171,13 @@ fn compare_type_predicate_entailment<'tcx>( /// For default associated types the normalization is not possible (the value /// from the impl could be overridden). We also can't normalize generic /// associated types (yet) because they contain bound parameters. -fn compare_projection_bounds<'tcx>( +pub fn check_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, trait_ty: &ty::AssocItem, impl_ty: &ty::AssocItem, impl_ty_span: Span, impl_trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), ErrorReported> { - let have_gats = tcx.features().generic_associated_types; - if impl_ty.defaultness.is_final() && !have_gats { - // For "final", non-generic associate type implementations, we - // don't need this as described above. - return Ok(()); - } - // Given // // impl<A, B> Foo<u32> for (A, B) { @@ -1211,16 +1205,27 @@ fn compare_projection_bounds<'tcx>( // ParamEnv for normalization specifically. let normalize_param_env = { let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>(); - predicates.push( - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { - item_def_id: trait_ty.def_id, - substs: rebased_substs, - }, - ty: impl_ty_value, - }) - .to_predicate(tcx), - ); + match impl_ty_value.kind() { + ty::Projection(proj) + if proj.item_def_id == trait_ty.def_id && proj.substs == rebased_substs => + { + // Don't include this predicate if the projected type is + // exactly the same as the projection. This can occur in + // (somewhat dubious) code like this: + // + // impl<T> X for T where T: X { type Y = <T as X>::Y; } + } + _ => predicates.push( + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + item_def_id: trait_ty.def_id, + substs: rebased_substs, + }, + ty: impl_ty_value, + }) + .to_predicate(tcx), + ), + }; ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing) }; @@ -1231,33 +1236,38 @@ fn compare_projection_bounds<'tcx>( let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); - let cause = ObligationCause::new( - impl_ty_span, - impl_ty_hir_id, - ObligationCauseCode::ItemObligation(trait_ty.def_id), - ); + let mk_cause = |span| { + ObligationCause::new( + impl_ty_span, + impl_ty_hir_id, + ObligationCauseCode::BindingObligation(trait_ty.def_id, span), + ) + }; - let predicates = tcx.projection_predicates(trait_ty.def_id); - debug!("compare_projection_bounds: projection_predicates={:?}", predicates); + let obligations = tcx + .explicit_item_bounds(trait_ty.def_id) + .iter() + .map(|&(bound, span)| { + let concrete_ty_bound = bound.subst(tcx, rebased_substs); + debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); - for predicate in predicates { - let concrete_ty_predicate = predicate.subst(tcx, rebased_substs); - debug!("compare_projection_bounds: concrete predicate = {:?}", concrete_ty_predicate); + traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound) + }) + .collect(); + debug!("check_type_bounds: item_bounds={:?}", obligations); + for mut obligation in util::elaborate_obligations(tcx, obligations) { let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( &mut selcx, normalize_param_env, normalize_cause.clone(), - &concrete_ty_predicate, + &obligation.predicate, ); debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); + obligation.predicate = normalized_predicate; inh.register_predicates(obligations); - inh.register_predicate(traits::Obligation::new( - cause.clone(), - param_env, - normalized_predicate, - )); + inh.register_predicate(obligation); } // Check that all obligations are satisfied by the implementation's @@ -1270,7 +1280,11 @@ fn compare_projection_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id); - fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]); + let implied_bounds = match impl_ty.container { + ty::TraitContainer(_) => vec![], + ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span), + }; + fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &implied_bounds); Ok(()) }) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index af800eab67a..179e383be2e 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1275,7 +1275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Report an error for a struct field expression when there are fields which aren't provided. /// - /// ```ignore (diagnostic) + /// ```text /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` /// --> src/main.rs:8:5 /// | @@ -1327,7 +1327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Report an error for a struct field expression when there are no visible fields. /// - /// ```ignore (diagnostic) + /// ```text /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields /// --> src/main.rs:8:5 /// | diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 3e431a9c000..53bc2069b76 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1381,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to /// inaccessible fields. /// - /// ```ignore (diagnostic) + /// ```text /// error: pattern requires `..` due to inaccessible fields /// --> src/main.rs:10:9 /// | @@ -1431,7 +1431,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a diagnostic reporting a struct pattern which does not mention some fields. /// - /// ```ignore (diagnostic) + /// ```text /// error[E0027]: pattern does not mention field `you_cant_use_this_field` /// --> src/main.rs:15:9 /// | diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 5203f3fa8f1..b4e950ab6e9 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -420,6 +420,9 @@ fn check_associated_item( check_method_receiver(fcx, hir_sig, &item, self_ty); } ty::AssocKind::Type => { + if let ty::AssocItemContainer::TraitContainer(_) = item.container { + check_associated_type_bounds(fcx, item, span) + } if item.defaultness.has_value() { let ty = fcx.tcx.type_of(item.def_id); let ty = fcx.normalize_associated_types_in(span, &ty); @@ -571,7 +574,6 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { for_item(tcx, item).with_fcx(|fcx, _| { check_where_clauses(tcx, fcx, item.span, trait_def_id.to_def_id(), None); - check_associated_type_defaults(fcx, trait_def_id.to_def_id()); vec![] }); @@ -581,96 +583,26 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { /// /// Assuming the defaults are used, check that all predicates (bounds on the /// assoc type and where clauses on the trait) hold. -fn check_associated_type_defaults(fcx: &FnCtxt<'_, '_>, trait_def_id: DefId) { +fn check_associated_type_bounds(fcx: &FnCtxt<'_, '_>, item: &ty::AssocItem, span: Span) { let tcx = fcx.tcx; - let substs = InternalSubsts::identity_for_item(tcx, trait_def_id); - - // For all assoc. types with defaults, build a map from - // `<Self as Trait<...>>::Assoc` to the default type. - let map = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter_map(|item| { - if item.kind == ty::AssocKind::Type && item.defaultness.has_value() { - // `<Self as Trait<...>>::Assoc` - let proj = ty::ProjectionTy { substs, item_def_id: item.def_id }; - let default_ty = tcx.type_of(item.def_id); - debug!("assoc. type default mapping: {} -> {}", proj, default_ty); - Some((proj, default_ty)) - } else { - None - } - }) - .collect::<FxHashMap<_, _>>(); - - /// Replaces projections of associated types with their default types. - /// - /// This does a "shallow substitution", meaning that defaults that refer to - /// other defaulted assoc. types will still refer to the projection - /// afterwards, not to the other default. For example: - /// - /// ```compile_fail - /// trait Tr { - /// type A: Clone = Vec<Self::B>; - /// type B = u8; - /// } - /// ``` - /// - /// This will end up replacing the bound `Self::A: Clone` with - /// `Vec<Self::B>: Clone`, not with `Vec<u8>: Clone`. If we did a deep - /// substitution and ended up with the latter, the trait would be accepted. - /// If an `impl` then replaced `B` with something that isn't `Clone`, - /// suddenly the default for `A` is no longer valid. The shallow - /// substitution forces the trait to add a `B: Clone` bound to be accepted, - /// which means that an `impl` can replace any default without breaking - /// others. - /// - /// Note that this isn't needed for soundness: The defaults would still be - /// checked in any impl that doesn't override them. - struct DefaultNormalizer<'tcx> { - tcx: TyCtxt<'tcx>, - map: FxHashMap<ty::ProjectionTy<'tcx>, Ty<'tcx>>, - } - impl<'tcx> ty::fold::TypeFolder<'tcx> for DefaultNormalizer<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { - self.tcx - } + let bounds = tcx.explicit_item_bounds(item.def_id); - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind() { - ty::Projection(proj_ty) => { - if let Some(default) = self.map.get(&proj_ty) { - default - } else { - t.super_fold_with(self) - } - } - _ => t.super_fold_with(self), - } - } - } - - // Now take all predicates defined on the trait, replace any mention of - // the assoc. types with their default, and prove them. - // We only consider predicates that directly mention the assoc. type. - let mut norm = DefaultNormalizer { tcx, map }; - let predicates = fcx.tcx.predicates_of(trait_def_id); - for &(orig_pred, span) in predicates.predicates.iter() { - let pred = orig_pred.fold_with(&mut norm); - if pred != orig_pred { - // Mentions one of the defaulted assoc. types - debug!("default suitability check: proving predicate: {} -> {}", orig_pred, pred); - let pred = fcx.normalize_associated_types_in(span, &pred); - let cause = traits::ObligationCause::new( - span, - fcx.body_id, - traits::ItemObligation(trait_def_id), - ); - let obligation = traits::Obligation::new(cause, fcx.param_env, pred); + debug!("check_associated_type_bounds: bounds={:?}", bounds); + let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { + let normalized_bound = fcx.normalize_associated_types_in(span, &bound); + traits::wf::predicate_obligations( + fcx, + fcx.param_env, + fcx.body_id, + normalized_bound, + bound_span, + ) + }); - fcx.register_predicate(obligation); - } + for obligation in wf_obligations { + debug!("next obligation cause: {:?}", obligation.cause); + fcx.register_predicate(obligation); } } @@ -1493,7 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> { + pub(super) fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> { match self.tcx.impl_trait_ref(impl_def_id) { Some(ref trait_ref) => { // Trait impl: take implied bounds from all types that diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 1f4f40fca9b..092dae18192 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -50,6 +50,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; +mod item_bounds; mod type_of; struct OnlySelfBounds(bool); @@ -68,12 +69,15 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { opt_const_param_of: type_of::opt_const_param_of, type_of: type_of::type_of, + item_bounds: item_bounds::item_bounds, + explicit_item_bounds: item_bounds::explicit_item_bounds, generics_of, predicates_of, predicates_defined_on, projection_ty_from_predicates, explicit_predicates_of, super_predicates_of, + trait_explicit_predicates_and_bounds, type_param_predicates, trait_def, adt_def, @@ -700,6 +704,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { hir::ItemKind::OpaqueTy(..) => { tcx.ensure().generics_of(def_id); tcx.ensure().predicates_of(def_id); + tcx.ensure().explicit_item_bounds(def_id); } hir::ItemKind::TyAlias(..) | hir::ItemKind::Static(..) @@ -708,8 +713,10 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - if let hir::ItemKind::Fn(..) = it.kind { - tcx.ensure().fn_sig(def_id); + match it.kind { + hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id), + hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id), + _ => (), } } } @@ -730,15 +737,25 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) { tcx.ensure().type_of(def_id); } - hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(_, Some(_)) => { + hir::TraitItemKind::Const(..) => { tcx.ensure().type_of(def_id); - // Account for `const C: _;` and `type T = _;`. + // Account for `const C: _;`. + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_trait_item(trait_item); + placeholder_type_error(tcx, None, &[], visitor.0, false); + } + + hir::TraitItemKind::Type(_, Some(_)) => { + tcx.ensure().item_bounds(def_id); + tcx.ensure().type_of(def_id); + // Account for `type T = _;`. let mut visitor = PlaceholderHirTyCollector::default(); visitor.visit_trait_item(trait_item); placeholder_type_error(tcx, None, &[], visitor.0, false); } hir::TraitItemKind::Type(_, None) => { + tcx.ensure().item_bounds(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = PlaceholderHirTyCollector::default(); @@ -1716,7 +1733,7 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { /// Returns a list of user-specified type predicates for the definition with ID `def_id`. /// N.B., this does not include any implied/inferred constraints. -fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { +fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { use rustc_hir::*; debug!("explicit_predicates_of(def_id={:?})", def_id); @@ -1726,7 +1743,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let mut is_trait = None; let mut is_default_impl_trait = None; - let mut is_trait_associated_type = None; let icx = ItemCtxt::new(tcx, def_id); let constness = icx.default_constness_for_trait_bounds(); @@ -1739,12 +1755,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default(); let ast_generics = match node { - Node::TraitItem(item) => { - if let hir::TraitItemKind::Type(bounds, _) = item.kind { - is_trait_associated_type = Some((bounds, item.span)); - } - &item.generics - } + Node::TraitItem(item) => &item.generics, Node::ImplItem(item) => &item.generics, @@ -1762,44 +1773,38 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat | ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) => generics, - ItemKind::Trait(_, _, ref generics, .., items) => { - is_trait = Some((ty::TraitRef::identity(tcx, def_id), items)); + ItemKind::Trait(_, _, ref generics, ..) => { + is_trait = Some(ty::TraitRef::identity(tcx, def_id)); generics } ItemKind::TraitAlias(ref generics, _) => { - is_trait = Some((ty::TraitRef::identity(tcx, def_id), &[])); + is_trait = Some(ty::TraitRef::identity(tcx, def_id)); generics } ItemKind::OpaqueTy(OpaqueTy { - ref bounds, + bounds: _, impl_trait_fn, ref generics, origin: _, }) => { - let bounds_predicates = ty::print::with_no_queries(|| { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - let opaque_ty = tcx.mk_opaque(def_id, substs); - - // Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`. - let bounds = AstConv::compute_bounds( - &icx, - opaque_ty, - bounds, - SizedByDefault::Yes, - tcx.def_span(def_id), - ); - - bounds.predicates(tcx, opaque_ty) - }); if impl_trait_fn.is_some() { - // opaque types - return ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter(bounds_predicates), - }; + // return-position impl trait + // + // We don't inherit predicates from the parent here: + // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}` + // then the return type is `f::<'static, T>::{{opaque}}`. + // + // If we inherited the predicates of `f` then we would + // require that `T: 'static` to show that the return + // type is well-formed. + // + // The only way to have something with this opaque type + // is from the return type of the containing function, + // which will ensure that the function's predicates + // hold. + return ty::GenericPredicates { parent: None, predicates: &[] }; } else { - // named opaque types - predicates.extend(bounds_predicates); + // type-alias impl trait generics } } @@ -1825,7 +1830,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // and the explicit where-clauses, but to get the full set of predicates // on a trait we need to add in the supertrait bounds and bounds found on // associated types. - if let Some((_trait_ref, _)) = is_trait { + if let Some(_trait_ref) = is_trait { predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned()); } @@ -1992,24 +1997,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat } } - // Add predicates from associated type bounds (`type X: Bound`) - if tcx.features().generic_associated_types { - // New behavior: bounds declared on associate type are predicates of that - // associated type. Not the default because it needs more testing. - if let Some((bounds, span)) = is_trait_associated_type { - let projection_ty = - tcx.mk_projection(def_id, InternalSubsts::identity_for_item(tcx, def_id)); - - predicates.extend(associated_item_bounds(tcx, def_id, bounds, projection_ty, span)) - } - } else if let Some((self_trait_ref, trait_items)) = is_trait { - // Current behavior: bounds declared on associate type are predicates - // of its parent trait. - predicates.extend(trait_items.iter().flat_map(|trait_item_ref| { - trait_associated_item_predicates(tcx, def_id, self_trait_ref, trait_item_ref) - })) - } - if tcx.features().const_evaluatable_checked { predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local())); } @@ -2131,6 +2118,69 @@ fn const_evaluatable_predicates_of<'tcx>( collector.preds } +fn trait_explicit_predicates_and_bounds( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> ty::GenericPredicates<'_> { + assert_eq!(tcx.def_kind(def_id), DefKind::Trait); + gather_explicit_predicates_of(tcx, def_id.to_def_id()) +} + +fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + if let DefKind::Trait = tcx.def_kind(def_id) { + // Remove bounds on associated types from the predicates, they will be + // returned by `explicit_item_bounds`. + let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local()); + let trait_identity_substs = InternalSubsts::identity_for_item(tcx, def_id); + + let is_assoc_item_ty = |ty: Ty<'_>| { + // For a predicate from a where clause to become a bound on an + // associated type: + // * It must use the identity substs of the item. + // * Since any generic parameters on the item are not in scope, + // this means that the item is not a GAT, and its identity + // substs are the same as the trait's. + // * It must be an associated type for this trait (*not* a + // supertrait). + if let ty::Projection(projection) = ty.kind() { + if projection.substs == trait_identity_substs + && tcx.associated_item(projection.item_def_id).container.id() == def_id + { + true + } else { + false + } + } else { + false + } + }; + + let predicates: Vec<_> = predicates_and_bounds + .predicates + .iter() + .copied() + .filter(|(pred, _)| match pred.skip_binders() { + ty::PredicateAtom::Trait(tr, _) => !is_assoc_item_ty(tr.self_ty()), + ty::PredicateAtom::Projection(proj) => { + !is_assoc_item_ty(proj.projection_ty.self_ty()) + } + ty::PredicateAtom::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0), + _ => true, + }) + .collect(); + if predicates.len() == predicates_and_bounds.predicates.len() { + predicates_and_bounds + } else { + ty::GenericPredicates { + parent: predicates_and_bounds.parent, + predicates: tcx.arena.alloc_slice(&predicates), + } + } + } else { + gather_explicit_predicates_of(tcx, def_id) + } +} + fn projection_ty_from_predicates( tcx: TyCtxt<'tcx>, key: ( @@ -2153,55 +2203,6 @@ fn projection_ty_from_predicates( projection_ty } -fn trait_associated_item_predicates( - tcx: TyCtxt<'tcx>, - def_id: DefId, - self_trait_ref: ty::TraitRef<'tcx>, - trait_item_ref: &hir::TraitItemRef, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - let trait_item = tcx.hir().trait_item(trait_item_ref.id); - let item_def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); - let bounds = match trait_item.kind { - hir::TraitItemKind::Type(ref bounds, _) => bounds, - _ => return Vec::new(), - }; - - if !tcx.generics_of(item_def_id).params.is_empty() { - // For GATs the substs provided to the mk_projection call below are - // wrong. We should emit a feature gate error if we get here so skip - // this type. - tcx.sess.delay_span_bug(trait_item.span, "gats used without feature gate"); - return Vec::new(); - } - - let assoc_ty = tcx.mk_projection( - tcx.hir().local_def_id(trait_item.hir_id).to_def_id(), - self_trait_ref.substs, - ); - - associated_item_bounds(tcx, def_id, bounds, assoc_ty, trait_item.span) -} - -fn associated_item_bounds( - tcx: TyCtxt<'tcx>, - def_id: DefId, - bounds: &'tcx [hir::GenericBound<'tcx>], - projection_ty: Ty<'tcx>, - span: Span, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - let bounds = AstConv::compute_bounds( - &ItemCtxt::new(tcx, def_id), - projection_ty, - bounds, - SizedByDefault::Yes, - span, - ); - - let predicates = bounds.predicates(tcx, projection_ty); - - predicates -} - /// Converts a specific `GenericBound` from the AST into a set of /// predicates that apply to the self type. A vector is returned /// because this can be anywhere from zero predicates (`T: ?Sized` adds no diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_typeck/src/collect/item_bounds.rs new file mode 100644 index 00000000000..9c29ceeb593 --- /dev/null +++ b/compiler/rustc_typeck/src/collect/item_bounds.rs @@ -0,0 +1,111 @@ +use super::ItemCtxt; +use crate::astconv::{AstConv, SizedByDefault}; +use rustc_hir as hir; +use rustc_infer::traits::util; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +/// For associated types we include both bounds written on the type +/// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`. +/// +/// Note that this filtering is done with the items identity substs to +/// simplify checking that these bounds are met in impls. This means that +/// a bound such as `for<'b> <Self as X<'b>>::U: Clone` can't be used, as in +/// `hr-associated-type-bound-1.rs`. +fn associated_type_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_item_def_id: DefId, + bounds: &'tcx [hir::GenericBound<'tcx>], + span: Span, +) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + let item_ty = tcx.mk_projection( + assoc_item_def_id, + InternalSubsts::identity_for_item(tcx, assoc_item_def_id), + ); + + let bounds = AstConv::compute_bounds( + &ItemCtxt::new(tcx, assoc_item_def_id), + item_ty, + bounds, + SizedByDefault::Yes, + span, + ); + + let trait_def_id = tcx.associated_item(assoc_item_def_id).container.id(); + let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id.expect_local()); + + let bounds_from_parent = + trait_predicates.predicates.iter().copied().filter(|(pred, _)| match pred.skip_binders() { + ty::PredicateAtom::Trait(tr, _) => tr.self_ty() == item_ty, + ty::PredicateAtom::Projection(proj) => proj.projection_ty.self_ty() == item_ty, + ty::PredicateAtom::TypeOutlives(outlives) => outlives.0 == item_ty, + _ => false, + }); + + let all_bounds = tcx + .arena + .alloc_from_iter(bounds.predicates(tcx, item_ty).into_iter().chain(bounds_from_parent)); + debug!("associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id), all_bounds); + all_bounds +} + +/// Opaque types don't inherit bounds from their parent: for return position +/// impl trait it isn't possible to write a suitable predicate on the +/// containing function and for type-alias impl trait we don't have a backwards +/// compatibility issue. +fn opaque_type_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + opaque_def_id: DefId, + bounds: &'tcx [hir::GenericBound<'tcx>], + span: Span, +) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + let item_ty = + tcx.mk_opaque(opaque_def_id, InternalSubsts::identity_for_item(tcx, opaque_def_id)); + + let bounds = ty::print::with_no_queries(|| { + AstConv::compute_bounds( + &ItemCtxt::new(tcx, opaque_def_id), + item_ty, + bounds, + SizedByDefault::Yes, + span, + ) + }); + + let bounds = bounds.predicates(tcx, item_ty); + debug!("opaque_type_bounds({}) = {:?}", tcx.def_path_str(opaque_def_id), bounds); + + tcx.arena.alloc_slice(&bounds) +} + +pub(super) fn explicit_item_bounds( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> &'_ [(ty::Predicate<'_>, Span)] { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().get(hir_id) { + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Type(bounds, _), + span, + .. + }) => associated_type_bounds(tcx, def_id, bounds, *span), + hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }), + span, + .. + }) => opaque_type_bounds(tcx, def_id, bounds, *span), + _ => bug!("item_bounds called on {:?}", def_id), + } +} + +pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List<ty::Predicate<'_>> { + tcx.mk_predicates( + util::elaborate_predicates( + tcx, + tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound), + ) + .map(|obligation| obligation.predicate), + ) +} diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index 60b9467fca8..4cf3efcf513 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -337,6 +337,7 @@ fn check_predicates<'tcx>( infcx, tcx.param_env(impl1_def_id), tcx.hir().local_def_id_to_hir_id(impl1_def_id), + 0, arg, span, ) { |
