diff options
| author | bors <bors@rust-lang.org> | 2022-02-13 07:04:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-02-13 07:04:56 +0000 |
| commit | 9a60099cc43c8a07abb280be323d1ed9afc27f2c (patch) | |
| tree | 443e36bb4c65e2899f2cbb0e6639a152e826b5d8 /compiler | |
| parent | 3fe229902ecaf1017c931df6ad24cc9d968d8f03 (diff) | |
| parent | 20ea5c50135fd905ad70c38abaa2a3362cf5561a (diff) | |
| download | rust-9a60099cc43c8a07abb280be323d1ed9afc27f2c.tar.gz rust-9a60099cc43c8a07abb280be323d1ed9afc27f2c.zip | |
Auto merge of #93956 - matthiaskrgr:rollup-zfk35hb, r=matthiaskrgr
Rollup of 9 pull requests
Successful merges:
- #89926 (make `Instant::{duration_since, elapsed, sub}` saturating and remove workarounds)
- #90532 (More informative error message for E0015)
- #93810 (Improve chalk integration)
- #93851 (More practical examples for `Option::and_then` & `Result::and_then`)
- #93885 (bootstrap.py: Suggest disabling download-ci-llvm option if url fails to download)
- #93886 (Stabilise inherent_ascii_escape (FCP in #77174))
- #93930 (add link to format_args! when mention it in docs)
- #93936 (Couple of driver cleanups)
- #93944 (Don't relabel to a team if there is already a team label)
Failed merges:
r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
21 files changed, 950 insertions, 593 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7b8b5974fe7..f6d21f879ff 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1,4 +1,5 @@ use either::Either; +use rustc_const_eval::util::{CallDesugaringKind, CallKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -26,7 +27,7 @@ use crate::{ use super::{ explain_borrow::{BorrowExplanation, LaterUseKind}, - FnSelfUseKind, IncludingDowncast, RegionName, RegionNameSource, UseSpans, + IncludingDowncast, RegionName, RegionNameSource, UseSpans, }; #[derive(Debug)] @@ -195,7 +196,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .map(|n| format!("`{}`", n)) .unwrap_or_else(|| "value".to_owned()); match kind { - FnSelfUseKind::FnOnceCall => { + CallKind::FnCall { fn_trait_id, .. } + if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() => + { err.span_label( fn_call_span, &format!( @@ -208,7 +211,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "this value implements `FnOnce`, which causes it to be moved when called", ); } - FnSelfUseKind::Operator { self_arg } => { + CallKind::Operator { self_arg, .. } => { + let self_arg = self_arg.unwrap(); err.span_label( fn_call_span, &format!( @@ -235,12 +239,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - FnSelfUseKind::Normal { - self_arg, - implicit_into_iter, - is_option_or_result, - } => { - if implicit_into_iter { + CallKind::Normal { self_arg, desugaring, is_option_or_result } => { + let self_arg = self_arg.unwrap(); + if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { err.span_label( fn_call_span, &format!( @@ -305,8 +306,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - // Deref::deref takes &self, which cannot cause a move - FnSelfUseKind::DerefCoercion { .. } => unreachable!(), + // Other desugarings takes &self, which cannot cause a move + _ => unreachable!(), } } else { err.span_label( @@ -433,7 +434,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } if let UseSpans::FnSelfUse { - kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty }, + kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. }, .. } = use_spans { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 84acfbf941d..4400fed13b7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1,10 +1,10 @@ //! Borrow checker diagnostics. +use rustc_const_eval::util::call_kind; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItemGroup; use rustc_hir::GeneratorKind; use rustc_middle::mir::{ AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand, @@ -13,7 +13,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::print::Print; use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; -use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span}; +use rustc_span::{symbol::sym, Span}; use rustc_target::abi::VariantIdx; use super::borrow_set::BorrowData; @@ -37,7 +37,7 @@ crate use mutability_errors::AccessKind; crate use outlives_suggestion::OutlivesSuggestionBuilder; crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; crate use region_name::{RegionName, RegionNameSource}; -use rustc_span::symbol::Ident; +crate use rustc_const_eval::util::CallKind; pub(super) struct IncludingDowncast(pub(super) bool); @@ -563,7 +563,7 @@ pub(super) enum UseSpans<'tcx> { fn_call_span: Span, /// The definition span of the method being called fn_span: Span, - kind: FnSelfUseKind<'tcx>, + kind: CallKind<'tcx>, }, /// This access is caused by a `match` or `if let` pattern. PatUse(Span), @@ -571,38 +571,15 @@ pub(super) enum UseSpans<'tcx> { OtherUse(Span), } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum FnSelfUseKind<'tcx> { - /// A normal method call of the form `receiver.foo(a, b, c)` - Normal { - self_arg: Ident, - implicit_into_iter: bool, - /// Whether the self type of the method call has an `.as_ref()` method. - /// Used for better diagnostics. - is_option_or_result: bool, - }, - /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` - FnOnceCall, - /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) - Operator { self_arg: Ident }, - DerefCoercion { - /// The `Span` of the `Target` associated type - /// in the `Deref` impl we are using. - deref_target: Span, - /// The type `T::Deref` we are dereferencing to - deref_target_ty: Ty<'tcx>, - }, -} - impl UseSpans<'_> { pub(super) fn args_or_use(self) -> Span { match self { UseSpans::ClosureUse { args_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -613,9 +590,9 @@ impl UseSpans<'_> { UseSpans::ClosureUse { path_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -626,9 +603,9 @@ impl UseSpans<'_> { UseSpans::ClosureUse { capture_kind_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -904,67 +881,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return normal_ret; }; - let tcx = self.infcx.tcx; - let parent = tcx.parent(method_did); - let is_fn_once = parent == tcx.lang_items().fn_once_trait(); - let is_operator = !from_hir_call - && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); - let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); - let fn_call_span = *fn_span; - - let self_arg = tcx.fn_arg_names(method_did)[0]; - - debug!( - "terminator = {:?} from_hir_call={:?}", - self.body[location.block].terminator, from_hir_call + let kind = call_kind( + self.infcx.tcx, + self.param_env, + method_did, + method_substs, + *fn_span, + *from_hir_call, + Some(self.infcx.tcx.fn_arg_names(method_did)[0]), ); - // Check for a 'special' use of 'self' - - // an FnOnce call, an operator (e.g. `<<`), or a - // deref coercion. - let kind = if is_fn_once { - Some(FnSelfUseKind::FnOnceCall) - } else if is_operator { - Some(FnSelfUseKind::Operator { self_arg }) - } else if is_deref { - let deref_target = - tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { - Instance::resolve(tcx, self.param_env, deref_target, method_substs) - .transpose() - }); - if let Some(Ok(instance)) = deref_target { - let deref_target_ty = instance.ty(tcx, self.param_env); - Some(FnSelfUseKind::DerefCoercion { - deref_target: tcx.def_span(instance.def_id()), - deref_target_ty, - }) - } else { - None - } - } else { - None - }; - - let kind = kind.unwrap_or_else(|| { - // This isn't a 'special' use of `self` - debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); - let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn() - && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop); - let parent_self_ty = parent - .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) - .and_then(|did| match tcx.type_of(did).kind() { - ty::Adt(def, ..) => Some(def.did), - _ => None, - }); - let is_option_or_result = parent_self_ty.map_or(false, |def_id| { - matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) - }); - FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result } - }); - return FnSelfUse { var_span: stmt.source_info.span, - fn_call_span, + fn_call_span: *fn_span, fn_span: self .infcx .tcx diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 2934d921868..b33b779edda 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,3 +1,4 @@ +use rustc_const_eval::util::CallDesugaringKind; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; @@ -8,7 +9,7 @@ use rustc_mir_dataflow::move_paths::{ use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; -use crate::diagnostics::{FnSelfUseKind, UseSpans}; +use crate::diagnostics::{CallKind, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; @@ -410,7 +411,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } else if let Some(UseSpans::FnSelfUse { - kind: FnSelfUseKind::Normal { implicit_into_iter: true, .. }, + kind: + CallKind::Normal { desugaring: Some((CallDesugaringKind::ForLoopIntoIter, _)), .. }, .. }) = use_spans { diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 12a8b8c6d77..095c8f84f41 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, use rustc_middle::ty::{Binder, TraitPredicate, TraitRef}; use rustc_mir_dataflow::{self, Analysis}; use rustc_span::{sym, Span, Symbol}; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::SelectionContext; use std::mem; @@ -293,13 +294,13 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { } /// Emits an error if an expression cannot be evaluated in the current context. - pub fn check_op(&mut self, op: impl NonConstOp) { + pub fn check_op(&mut self, op: impl NonConstOp<'tcx>) { self.check_op_spanned(op, self.span); } /// Emits an error at the given `span` if an expression cannot be evaluated in the current /// context. - pub fn check_op_spanned<O: NonConstOp>(&mut self, op: O, span: Span) { + pub fn check_op_spanned<O: NonConstOp<'tcx>>(&mut self, op: O, span: Span) { let gate = match op.status_in_item(self.ccx) { Status::Allowed => return, @@ -773,7 +774,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_terminator(terminator, location); match &terminator.kind { - TerminatorKind::Call { func, args, .. } => { + TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => { let ConstCx { tcx, body, param_env, .. } = *self.ccx; let caller = self.def_id().to_def_id(); @@ -797,20 +798,24 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if let Some(trait_id) = tcx.trait_of_item(callee) { trace!("attempting to call a trait method"); if !self.tcx.features().const_trait_impl { - self.check_op(ops::FnCallNonConst(Some((callee, substs)))); + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } let trait_ref = TraitRef::from_method(tcx, trait_id, substs); - let obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - Binder::dummy(TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), - ); + let poly_trait_pred = Binder::dummy(TraitPredicate { + trait_ref, + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }); + let obligation = + Obligation::new(ObligationCause::dummy(), param_env, poly_trait_pred); let implsrc = tcx.infer_ctxt().enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); @@ -826,10 +831,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } Ok(Some(ImplSource::UserDefined(data))) => { - if let hir::Constness::NotConst = tcx.impl_constness(data.impl_def_id) { - self.check_op(ops::FnCallNonConst(None)); - return; - } let callee_name = tcx.item_name(callee); if let Some(&did) = tcx .associated_item_def_ids(data.impl_def_id) @@ -841,22 +842,61 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { substs = InternalSubsts::identity_for_item(tcx, did); callee = did; } + + if let hir::Constness::NotConst = tcx.impl_constness(data.impl_def_id) { + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); + return; + } } _ if !tcx.is_const_fn_raw(callee) => { // At this point, it is only legal when the caller is marked with // #[default_method_body_is_const], and the callee is in the same // trait. let callee_trait = tcx.trait_of_item(callee); - if callee_trait.is_some() { - if tcx.has_attr(caller, sym::default_method_body_is_const) { - if tcx.trait_of_item(caller) == callee_trait { - nonconst_call_permission = true; - } - } + if callee_trait.is_some() + && tcx.has_attr(caller, sym::default_method_body_is_const) + && callee_trait == tcx.trait_of_item(caller) + // Can only call methods when it's `<Self as TheTrait>::f`. + && tcx.types.self_param == substs.type_at(0) + { + nonconst_call_permission = true; } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst(None)); + let obligation = Obligation::new( + ObligationCause::dummy_with_span(*fn_span), + param_env, + tcx.mk_predicate( + poly_trait_pred.map_bound(ty::PredicateKind::Trait), + ), + ); + + // improve diagnostics by showing what failed. Our requirements are stricter this time + // as we are going to error again anyways. + tcx.infer_ctxt().enter(|infcx| { + if let Err(e) = implsrc { + infcx.report_selection_error( + obligation.clone(), + &obligation, + &e, + false, + ); + } + }); + + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } } @@ -925,7 +965,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst(None)); + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 24c4a4915e5..519b4c02b61 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -3,14 +3,22 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; +use rustc_middle::mir; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::{mir, ty::AssocKind}; +use rustc_middle::ty::{ + suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, TraitPredicate, Ty, +}; +use rustc_middle::ty::{Binder, BoundConstness, ImplPolarity, TraitRef}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::{symbol::Ident, Span, Symbol}; -use rustc_span::{BytePos, Pos}; +use rustc_span::{BytePos, Pos, Span, Symbol}; +use rustc_trait_selection::traits::SelectionContext; use super::ConstCx; +use crate::util::{call_kind, CallDesugaringKind, CallKind}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Status { @@ -29,9 +37,9 @@ pub enum DiagnosticImportance { } /// An operation that is not *always* allowed in a const context. -pub trait NonConstOp: std::fmt::Debug { +pub trait NonConstOp<'tcx>: std::fmt::Debug { /// Returns an enum indicating whether this operation is allowed within the given item. - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Forbidden } @@ -39,13 +47,13 @@ pub trait NonConstOp: std::fmt::Debug { DiagnosticImportance::Primary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; } #[derive(Debug)] pub struct FloatingPointOp; -impl NonConstOp for FloatingPointOp { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for FloatingPointOp { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() == hir::ConstContext::ConstFn { Status::Unstable(sym::const_fn_floating_point_arithmetic) } else { @@ -53,7 +61,7 @@ impl NonConstOp for FloatingPointOp { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_floating_point_arithmetic, @@ -66,77 +74,229 @@ impl NonConstOp for FloatingPointOp { /// A function call where the callee is a pointer. #[derive(Debug)] pub struct FnCallIndirect; -impl NonConstOp for FnCallIndirect { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn") } } /// A function call where the callee is not marked as `const`. -#[derive(Debug)] -pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>); -impl<'a> NonConstOp for FnCallNonConst<'a> { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - let mut err = struct_span_err!( - ccx.tcx.sess, - span, - E0015, - "calls in {}s are limited to constant functions, \ - tuple structs and tuple variants", - ccx.const_kind(), - ); +#[derive(Debug, Clone, Copy)] +pub struct FnCallNonConst<'tcx> { + pub caller: DefId, + pub callee: DefId, + pub substs: SubstsRef<'tcx>, + pub span: Span, + pub from_hir_call: bool, +} - if let FnCallNonConst(Some((callee, substs))) = *self { - if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() { - if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind( - ccx.tcx, - Ident::with_dummy_span(sym::eq), - AssocKind::Fn, - trait_def_id, - ) { - if callee == eq_item.def_id && substs.len() == 2 { - match (substs[0].unpack(), substs[1].unpack()) { - (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) - if self_ty == rhs_ty - && self_ty.is_ref() - && self_ty.peel_refs().is_primitive() => - { - let mut num_refs = 0; - let mut tmp_ty = self_ty; - while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { - num_refs += 1; - tmp_ty = inner_ty; - } - let deref = "*".repeat(num_refs); - - if let Ok(call_str) = - ccx.tcx.sess.source_map().span_to_snippet(span) - { - if let Some(eq_idx) = call_str.find("==") { - if let Some(rhs_idx) = call_str[(eq_idx + 2)..] - .find(|c: char| !c.is_whitespace()) - { - let rhs_pos = span.lo() - + BytePos::from_usize(eq_idx + 2 + rhs_idx); - let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); - err.multipart_suggestion( - "consider dereferencing here", - vec![ - (span.shrink_to_lo(), deref.clone()), - (rhs_span, deref), - ], - Applicability::MachineApplicable, - ); - } +impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> { + let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self; + let ConstCx { tcx, param_env, .. } = *ccx; + + let diag_trait = |mut err, self_ty: Ty<'_>, trait_id| { + let trait_ref = TraitRef::from_method(tcx, trait_id, substs); + + match self_ty.kind() { + Param(param_ty) => { + debug!(?param_ty); + if let Some(generics) = caller + .as_local() + .map(|id| tcx.hir().local_def_id_to_hir_id(id)) + .map(|id| tcx.hir().get(id)) + .as_ref() + .and_then(|node| node.generics()) + { + let constraint = with_no_trimmed_paths(|| { + format!("~const {}", trait_ref.print_only_trait_path()) + }); + suggest_constraining_type_param( + tcx, + generics, + &mut err, + ¶m_ty.name.as_str(), + &constraint, + None, + ); + } + } + Adt(..) => { + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + Binder::dummy(TraitPredicate { + trait_ref, + constness: BoundConstness::NotConst, + polarity: ImplPolarity::Positive, + }), + ); + + let implsrc = tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + selcx.select(&obligation) + }); + + if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { + let span = + tcx.sess.source_map().guess_head_span(tcx.def_span(data.impl_def_id)); + err.span_note(span, "impl defined here, but it is not `const`"); + } + } + _ => {} + } + + err + }; + + let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None); + + debug!(?call_kind); + + let mut err = match call_kind { + CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { + macro_rules! error { + ($fmt:literal) => { + struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind()) + }; + } + + let err = match kind { + CallDesugaringKind::ForLoopIntoIter => { + error!("cannot convert `{}` into an iterator in {}s") + } + CallDesugaringKind::QuestionBranch => { + error!("`?` cannot determine the branch of `{}` in {}s") + } + CallDesugaringKind::QuestionFromResidual => { + error!("`?` cannot convert from residual of `{}` in {}s") + } + CallDesugaringKind::TryBlockFromOutput => { + error!("`try` block cannot convert `{}` to the result in {}s") + } + }; + + diag_trait(err, self_ty, kind.trait_def_id(tcx)) + } + CallKind::FnCall { fn_trait_id, self_ty } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot call non-const closure in {}s", + ccx.const_kind(), + ); + + match self_ty.kind() { + FnDef(def_id, ..) => { + let span = tcx.sess.source_map().guess_head_span(tcx.def_span(*def_id)); + if ccx.tcx.is_const_fn_raw(*def_id) { + span_bug!(span, "calling const FnDef errored when it shouldn't"); + } + + err.span_note(span, "function defined here, but it is not `const`"); + } + FnPtr(..) => { + err.note(&format!( + "function pointers need an RFC before allowed to be called in {}s", + ccx.const_kind() + )); + } + Closure(..) => { + err.note(&format!( + "closures need an RFC before allowed to be called in {}s", + ccx.const_kind() + )); + } + _ => {} + } + + diag_trait(err, self_ty, fn_trait_id) + } + CallKind::Operator { trait_id, self_ty, .. } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot call non-const operator in {}s", + ccx.const_kind() + ); + + if Some(trait_id) == ccx.tcx.lang_items().eq_trait() { + match (substs[0].unpack(), substs[1].unpack()) { + (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) + if self_ty == rhs_ty + && self_ty.is_ref() + && self_ty.peel_refs().is_primitive() => + { + let mut num_refs = 0; + let mut tmp_ty = self_ty; + while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { + num_refs += 1; + tmp_ty = inner_ty; + } + let deref = "*".repeat(num_refs); + + if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) { + if let Some(eq_idx) = call_str.find("==") { + if let Some(rhs_idx) = + call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) + { + let rhs_pos = + span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); + err.multipart_suggestion( + "consider dereferencing here", + vec![ + (span.shrink_to_lo(), deref.clone()), + (rhs_span, deref), + ], + Applicability::MachineApplicable, + ); } } } - _ => {} } + _ => {} } } + + diag_trait(err, self_ty, trait_id) } - } + CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot perform deref coercion on `{}` in {}s", + self_ty, + ccx.const_kind() + ); + + err.note(&format!("attempting to deref into `{}`", deref_target_ty)); + + // Check first whether the source is accessible (issue #87060) + if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { + err.span_note(deref_target, "deref defined here"); + } + + diag_trait(err, self_ty, tcx.lang_items().deref_trait().unwrap()) + } + _ => struct_span_err!( + ccx.tcx.sess, + span, + E0015, + "cannot call non-const fn `{}` in {}s", + ccx.tcx.def_path_str_with_substs(callee, substs), + ccx.const_kind(), + ), + }; + + err.note(&format!( + "calls in {}s are limited to constant functions, \ + tuple structs and tuple variants", + ccx.const_kind(), + )); err } @@ -148,8 +308,8 @@ impl<'a> NonConstOp for FnCallNonConst<'a> { #[derive(Debug)] pub struct FnCallUnstable(pub DefId, pub Option<Symbol>); -impl NonConstOp for FnCallUnstable { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let FnCallUnstable(def_id, feature) = *self; let mut err = ccx.tcx.sess.struct_span_err( @@ -174,8 +334,8 @@ impl NonConstOp for FnCallUnstable { #[derive(Debug)] pub struct FnPtrCast; -impl NonConstOp for FnPtrCast { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for FnPtrCast { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -183,7 +343,7 @@ impl NonConstOp for FnPtrCast { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_fn_ptr_basics, @@ -195,8 +355,8 @@ impl NonConstOp for FnPtrCast { #[derive(Debug)] pub struct Generator(pub hir::GeneratorKind); -impl NonConstOp for Generator { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for Generator { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { Status::Unstable(sym::const_async_blocks) } else { @@ -204,7 +364,7 @@ impl NonConstOp for Generator { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind()); if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg) @@ -216,8 +376,8 @@ impl NonConstOp for Generator { #[derive(Debug)] pub struct HeapAllocation; -impl NonConstOp for HeapAllocation { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for HeapAllocation { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -240,8 +400,8 @@ impl NonConstOp for HeapAllocation { #[derive(Debug)] pub struct InlineAsm; -impl NonConstOp for InlineAsm { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for InlineAsm { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( ccx.tcx.sess, span, @@ -256,8 +416,8 @@ impl NonConstOp for InlineAsm { pub struct LiveDrop { pub dropped_at: Option<Span>, } -impl NonConstOp for LiveDrop { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for LiveDrop { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -276,8 +436,8 @@ impl NonConstOp for LiveDrop { /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to /// the final value of the constant. pub struct TransientCellBorrow; -impl NonConstOp for TransientCellBorrow { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_refs_to_cell) } fn importance(&self) -> DiagnosticImportance { @@ -285,7 +445,7 @@ impl NonConstOp for TransientCellBorrow { // not additionally emit a feature gate error if activating the feature gate won't work. DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_refs_to_cell, @@ -300,8 +460,8 @@ impl NonConstOp for TransientCellBorrow { /// the final value of the constant, and thus we cannot allow this (for now). We may allow /// it in the future for static items. pub struct CellBorrow; -impl NonConstOp for CellBorrow { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for CellBorrow { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -337,8 +497,8 @@ impl NonConstOp for CellBorrow { /// static or const items. pub struct MutBorrow(pub hir::BorrowKind); -impl NonConstOp for MutBorrow { - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for MutBorrow { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Forbidden } @@ -348,7 +508,7 @@ impl NonConstOp for MutBorrow { DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let raw = match self.0 { hir::BorrowKind::Raw => "raw ", hir::BorrowKind::Ref => "", @@ -382,12 +542,12 @@ impl NonConstOp for MutBorrow { #[derive(Debug)] pub struct TransientMutBorrow(pub hir::BorrowKind); -impl NonConstOp for TransientMutBorrow { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let raw = match self.0 { hir::BorrowKind::Raw => "raw ", hir::BorrowKind::Ref => "", @@ -404,8 +564,8 @@ impl NonConstOp for TransientMutBorrow { #[derive(Debug)] pub struct MutDeref; -impl NonConstOp for MutDeref { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for MutDeref { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } @@ -414,7 +574,7 @@ impl NonConstOp for MutDeref { DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -427,8 +587,8 @@ impl NonConstOp for MutDeref { /// A call to a `panic()` lang item where the first argument is _not_ a `&str`. #[derive(Debug)] pub struct PanicNonStr; -impl NonConstOp for PanicNonStr { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for PanicNonStr { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { ccx.tcx.sess.struct_span_err( span, "argument to `panic!()` in a const context must have type `&str`", @@ -441,8 +601,8 @@ impl NonConstOp for PanicNonStr { /// allocation base addresses that are not known at compile-time. #[derive(Debug)] pub struct RawPtrComparison; -impl NonConstOp for RawPtrComparison { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = ccx .tcx .sess @@ -457,12 +617,12 @@ impl NonConstOp for RawPtrComparison { #[derive(Debug)] pub struct RawMutPtrDeref; -impl NonConstOp for RawMutPtrDeref { +impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { Status::Unstable(sym::const_mut_refs) } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -477,8 +637,8 @@ impl NonConstOp for RawMutPtrDeref { /// allocation base addresses that are not known at compile-time. #[derive(Debug)] pub struct RawPtrToIntCast; -impl NonConstOp for RawPtrToIntCast { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = ccx .tcx .sess @@ -494,8 +654,8 @@ impl NonConstOp for RawPtrToIntCast { /// An access to a (non-thread-local) `static`. #[derive(Debug)] pub struct StaticAccess; -impl NonConstOp for StaticAccess { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for StaticAccess { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if let hir::ConstContext::Static(_) = ccx.const_kind() { Status::Allowed } else { @@ -503,7 +663,7 @@ impl NonConstOp for StaticAccess { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -528,8 +688,8 @@ impl NonConstOp for StaticAccess { /// An access to a thread-local `static`. #[derive(Debug)] pub struct ThreadLocalAccess; -impl NonConstOp for ThreadLocalAccess { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( ccx.tcx.sess, span, @@ -546,8 +706,8 @@ pub mod ty { #[derive(Debug)] pub struct MutRef(pub mir::LocalKind); - impl NonConstOp for MutRef { - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + impl<'tcx> NonConstOp<'tcx> for MutRef { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } @@ -560,11 +720,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -576,7 +732,7 @@ pub mod ty { #[derive(Debug)] pub struct FnPtr(pub mir::LocalKind); - impl NonConstOp for FnPtr { + impl<'tcx> NonConstOp<'tcx> for FnPtr { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -586,7 +742,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -594,11 +750,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_fn_ptr_basics, @@ -610,16 +762,12 @@ pub mod ty { #[derive(Debug)] pub struct ImplTrait; - impl NonConstOp for ImplTrait { + impl<'tcx> NonConstOp<'tcx> for ImplTrait { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { Status::Unstable(sym::const_impl_trait) } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_impl_trait, @@ -631,7 +779,7 @@ pub mod ty { #[derive(Debug)] pub struct TraitBound(pub mir::LocalKind); - impl NonConstOp for TraitBound { + impl<'tcx> NonConstOp<'tcx> for TraitBound { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -641,7 +789,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -649,11 +797,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_trait_bound, @@ -674,7 +818,7 @@ pub mod ty { #[derive(Debug)] pub struct DynTrait(pub mir::LocalKind); - impl NonConstOp for DynTrait { + impl<'tcx> NonConstOp<'tcx> for DynTrait { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -684,7 +828,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -692,11 +836,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_trait_bound, @@ -718,16 +858,12 @@ pub mod ty { /// A trait bound with the `?const Trait` opt-out #[derive(Debug)] pub struct TraitBoundNotConst; - impl NonConstOp for TraitBoundNotConst { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + impl<'tcx> NonConstOp<'tcx> for TraitBoundNotConst { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_trait_bound_opt_out) } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_trait_bound_opt_out, diff --git a/compiler/rustc_const_eval/src/util/call_kind.rs b/compiler/rustc_const_eval/src/util/call_kind.rs new file mode 100644 index 00000000000..11bb9508a1f --- /dev/null +++ b/compiler/rustc_const_eval/src/util/call_kind.rs @@ -0,0 +1,143 @@ +//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`, +//! as well as errors when attempting to call a non-const function in a const +//! context. + +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItemGroup; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, DesugaringKind, Span}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallDesugaringKind { + /// for _ in x {} calls x.into_iter() + ForLoopIntoIter, + /// x? calls x.branch() + QuestionBranch, + /// x? calls type_of(x)::from_residual() + QuestionFromResidual, + /// try { ..; x } calls type_of(x)::from_output(x) + TryBlockFromOutput, +} + +impl CallDesugaringKind { + pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { + match self { + Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), + Self::QuestionBranch | Self::TryBlockFromOutput => { + tcx.lang_items().try_trait().unwrap() + } + Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallKind<'tcx> { + /// A normal method call of the form `receiver.foo(a, b, c)` + Normal { + self_arg: Option<Ident>, + desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>, + /// Whether the self type of the method call has an `.as_ref()` method. + /// Used for better diagnostics. + is_option_or_result: bool, + }, + /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)` + FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> }, + /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) + Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> }, + DerefCoercion { + /// The `Span` of the `Target` associated type + /// in the `Deref` impl we are using. + deref_target: Span, + /// The type `T::Deref` we are dereferencing to + deref_target_ty: Ty<'tcx>, + self_ty: Ty<'tcx>, + }, +} + +pub fn call_kind<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + method_did: DefId, + method_substs: SubstsRef<'tcx>, + fn_call_span: Span, + from_hir_call: bool, + self_arg: Option<Ident>, +) -> CallKind<'tcx> { + let parent = tcx.opt_associated_item(method_did).and_then(|assoc| match assoc.container { + AssocItemContainer::ImplContainer(impl_did) => tcx.trait_id_of_impl(impl_did), + AssocItemContainer::TraitContainer(trait_did) => Some(trait_did), + }); + + let fn_call = parent + .and_then(|p| tcx.lang_items().group(LangItemGroup::Fn).iter().find(|did| **did == p)); + + let operator = (!from_hir_call) + .then(|| parent) + .flatten() + .and_then(|p| tcx.lang_items().group(LangItemGroup::Op).iter().find(|did| **did == p)); + + let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); + + // Check for a 'special' use of 'self' - + // an FnOnce call, an operator (e.g. `<<`), or a + // deref coercion. + let kind = if let Some(&trait_id) = fn_call { + Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) }) + } else if let Some(&trait_id) = operator { + Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) }) + } else if is_deref { + let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { + Instance::resolve(tcx, param_env, deref_target, method_substs).transpose() + }); + if let Some(Ok(instance)) = deref_target { + let deref_target_ty = instance.ty(tcx, param_env); + Some(CallKind::DerefCoercion { + deref_target: tcx.def_span(instance.def_id()), + deref_target_ty, + self_ty: method_substs.type_at(0), + }) + } else { + None + } + } else { + None + }; + + kind.unwrap_or_else(|| { + // This isn't a 'special' use of `self` + debug!(?method_did, ?fn_call_span); + let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn() + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) + { + Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0))) + } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { + if Some(method_did) == tcx.lang_items().branch_fn() { + Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0))) + } else if Some(method_did) == tcx.lang_items().from_residual_fn() { + Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0))) + } else { + None + } + } else if Some(method_did) == tcx.lang_items().from_output_fn() + && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock) + { + Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0))) + } else { + None + }; + let parent_self_ty = tcx + .parent(method_did) + .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) + .and_then(|did| match tcx.type_of(did).kind() { + ty::Adt(def, ..) => Some(def.did), + _ => None, + }); + let is_option_or_result = parent_self_ty.map_or(false, |def_id| { + matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) + }); + CallKind::Normal { self_arg, desugaring, is_option_or_result } + }) +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 4a406f8bfd0..a1876bed83e 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -1,8 +1,10 @@ pub mod aggregate; mod alignment; +mod call_kind; pub mod collect_writes; mod find_self_call; pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; +pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind}; pub use self::find_self_call::find_self_call; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index ca4e7b5142e..7eeae66d709 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -263,7 +263,7 @@ fn run_compiler( describe_lints(compiler.session(), &lint_store, registered_lints); return; } - let should_stop = RustcDefaultCalls::print_crate_info( + let should_stop = print_crate_info( &***compiler.codegen_backend(), compiler.session(), None, @@ -292,7 +292,7 @@ fn run_compiler( interface::run_compiler(config, |compiler| { let sess = compiler.session(); - let should_stop = RustcDefaultCalls::print_crate_info( + let should_stop = print_crate_info( &***compiler.codegen_backend(), sess, Some(compiler.input()), @@ -301,13 +301,9 @@ fn run_compiler( compiler.temps_dir(), ) .and_then(|| { - RustcDefaultCalls::list_metadata( - sess, - &*compiler.codegen_backend().metadata_loader(), - compiler.input(), - ) + list_metadata(sess, &*compiler.codegen_backend().metadata_loader(), compiler.input()) }) - .and_then(|| RustcDefaultCalls::try_process_rlink(sess, compiler)); + .and_then(|| try_process_rlink(sess, compiler)); if should_stop == Compilation::Stop { return sess.compile_status(); @@ -512,10 +508,6 @@ impl Compilation { } } -/// CompilerCalls instance for a regular rustc build. -#[derive(Copy, Clone)] -pub struct RustcDefaultCalls; - fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { let upper_cased_code = code.to_ascii_uppercase(); let normalised = if upper_cased_code.starts_with('E') { @@ -588,162 +580,157 @@ fn show_content_with_pager(content: &str) { } } -impl RustcDefaultCalls { - pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { - if sess.opts.debugging_opts.link_only { - if let Input::File(file) = compiler.input() { - // FIXME: #![crate_type] and #![crate_name] support not implemented yet - sess.init_crate_types(collect_crate_types(sess, &[])); - let outputs = compiler.build_output_filenames(sess, &[]); - let rlink_data = fs::read(file).unwrap_or_else(|err| { - sess.fatal(&format!("failed to read rlink file: {}", err)); - }); - let mut decoder = rustc_serialize::opaque::Decoder::new(&rlink_data, 0); - let codegen_results: CodegenResults = - rustc_serialize::Decodable::decode(&mut decoder); - let result = compiler.codegen_backend().link(sess, codegen_results, &outputs); - abort_on_err(result, sess); - } else { - sess.fatal("rlink must be a file") - } - Compilation::Stop +pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { + if sess.opts.debugging_opts.link_only { + if let Input::File(file) = compiler.input() { + // FIXME: #![crate_type] and #![crate_name] support not implemented yet + sess.init_crate_types(collect_crate_types(sess, &[])); + let outputs = compiler.build_output_filenames(sess, &[]); + let rlink_data = fs::read(file).unwrap_or_else(|err| { + sess.fatal(&format!("failed to read rlink file: {}", err)); + }); + let mut decoder = rustc_serialize::opaque::Decoder::new(&rlink_data, 0); + let codegen_results: CodegenResults = rustc_serialize::Decodable::decode(&mut decoder); + let result = compiler.codegen_backend().link(sess, codegen_results, &outputs); + abort_on_err(result, sess); } else { - Compilation::Continue + sess.fatal("rlink must be a file") } + Compilation::Stop + } else { + Compilation::Continue } +} - pub fn list_metadata( - sess: &Session, - metadata_loader: &dyn MetadataLoader, - input: &Input, - ) -> Compilation { - if sess.opts.debugging_opts.ls { - match *input { - Input::File(ref ifile) => { - let path = &(*ifile); - let mut v = Vec::new(); - locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v) - .unwrap(); - println!("{}", String::from_utf8(v).unwrap()); - } - Input::Str { .. } => { - early_error(ErrorOutputType::default(), "cannot list metadata for stdin"); - } +pub fn list_metadata( + sess: &Session, + metadata_loader: &dyn MetadataLoader, + input: &Input, +) -> Compilation { + if sess.opts.debugging_opts.ls { + match *input { + Input::File(ref ifile) => { + let path = &(*ifile); + let mut v = Vec::new(); + locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap(); + println!("{}", String::from_utf8(v).unwrap()); + } + Input::Str { .. } => { + early_error(ErrorOutputType::default(), "cannot list metadata for stdin"); } - return Compilation::Stop; } - - Compilation::Continue + return Compilation::Stop; } - fn print_crate_info( - codegen_backend: &dyn CodegenBackend, - sess: &Session, - input: Option<&Input>, - odir: &Option<PathBuf>, - ofile: &Option<PathBuf>, - temps_dir: &Option<PathBuf>, - ) -> Compilation { - use rustc_session::config::PrintRequest::*; - // NativeStaticLibs and LinkArgs are special - printed during linking - // (empty iterator returns true) - if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) { - return Compilation::Continue; - } + Compilation::Continue +} - let attrs = match input { - None => None, - Some(input) => { - let result = parse_crate_attrs(sess, input); - match result { - Ok(attrs) => Some(attrs), - Err(mut parse_error) => { - parse_error.emit(); - return Compilation::Stop; - } +fn print_crate_info( + codegen_backend: &dyn CodegenBackend, + sess: &Session, + input: Option<&Input>, + odir: &Option<PathBuf>, + ofile: &Option<PathBuf>, + temps_dir: &Option<PathBuf>, +) -> Compilation { + use rustc_session::config::PrintRequest::*; + // NativeStaticLibs and LinkArgs are special - printed during linking + // (empty iterator returns true) + if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) { + return Compilation::Continue; + } + + let attrs = match input { + None => None, + Some(input) => { + let result = parse_crate_attrs(sess, input); + match result { + Ok(attrs) => Some(attrs), + Err(mut parse_error) => { + parse_error.emit(); + return Compilation::Stop; } } - }; - for req in &sess.opts.prints { - match *req { - TargetList => { - let mut targets = - rustc_target::spec::TARGETS.iter().copied().collect::<Vec<_>>(); - targets.sort_unstable(); - println!("{}", targets.join("\n")); - } - Sysroot => println!("{}", sess.sysroot.display()), - TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()), - TargetSpec => println!("{}", sess.target.to_json().pretty()), - FileNames | CrateName => { - let input = input.unwrap_or_else(|| { - early_error(ErrorOutputType::default(), "no input file provided") - }); - let attrs = attrs.as_ref().unwrap(); - let t_outputs = rustc_interface::util::build_output_filenames( - input, odir, ofile, temps_dir, attrs, sess, - ); - let id = rustc_session::output::find_crate_name(sess, attrs, input); - if *req == PrintRequest::CrateName { - println!("{}", id); - continue; - } - let crate_types = collect_crate_types(sess, attrs); - for &style in &crate_types { - let fname = - rustc_session::output::filename_for_input(sess, style, &id, &t_outputs); - println!("{}", fname.file_name().unwrap().to_string_lossy()); - } + } + }; + for req in &sess.opts.prints { + match *req { + TargetList => { + let mut targets = rustc_target::spec::TARGETS.iter().copied().collect::<Vec<_>>(); + targets.sort_unstable(); + println!("{}", targets.join("\n")); + } + Sysroot => println!("{}", sess.sysroot.display()), + TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()), + TargetSpec => println!("{}", sess.target.to_json().pretty()), + FileNames | CrateName => { + let input = input.unwrap_or_else(|| { + early_error(ErrorOutputType::default(), "no input file provided") + }); + let attrs = attrs.as_ref().unwrap(); + let t_outputs = rustc_interface::util::build_output_filenames( + input, odir, ofile, temps_dir, attrs, sess, + ); + let id = rustc_session::output::find_crate_name(sess, attrs, input); + if *req == PrintRequest::CrateName { + println!("{}", id); + continue; } - Cfg => { - let mut cfgs = sess - .parse_sess - .config - .iter() - .filter_map(|&(name, value)| { - // Note that crt-static is a specially recognized cfg - // directive that's printed out here as part of - // rust-lang/rust#37406, but in general the - // `target_feature` cfg is gated under - // rust-lang/rust#29717. For now this is just - // specifically allowing the crt-static cfg and that's - // it, this is intended to get into Cargo and then go - // through to build scripts. - if (name != sym::target_feature || value != Some(sym::crt_dash_static)) - && !sess.is_nightly_build() - && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some() - { - return None; - } - - if let Some(value) = value { - Some(format!("{}=\"{}\"", name, value)) - } else { - Some(name.to_string()) - } - }) - .collect::<Vec<String>>(); - - cfgs.sort(); - for cfg in cfgs { - println!("{}", cfg); - } + let crate_types = collect_crate_types(sess, attrs); + for &style in &crate_types { + let fname = + rustc_session::output::filename_for_input(sess, style, &id, &t_outputs); + println!("{}", fname.file_name().unwrap().to_string_lossy()); } - RelocationModels - | CodeModels - | TlsModels - | TargetCPUs - | StackProtectorStrategies - | TargetFeatures => { - codegen_backend.print(*req, sess); + } + Cfg => { + let mut cfgs = sess + .parse_sess + .config + .iter() + .filter_map(|&(name, value)| { + // Note that crt-static is a specially recognized cfg + // directive that's printed out here as part of + // rust-lang/rust#37406, but in general the + // `target_feature` cfg is gated under + // rust-lang/rust#29717. For now this is just + // specifically allowing the crt-static cfg and that's + // it, this is intended to get into Cargo and then go + // through to build scripts. + if (name != sym::target_feature || value != Some(sym::crt_dash_static)) + && !sess.is_nightly_build() + && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some() + { + return None; + } + + if let Some(value) = value { + Some(format!("{}=\"{}\"", name, value)) + } else { + Some(name.to_string()) + } + }) + .collect::<Vec<String>>(); + + cfgs.sort(); + for cfg in cfgs { + println!("{}", cfg); } - // Any output here interferes with Cargo's parsing of other printed output - NativeStaticLibs => {} - LinkArgs => {} } + RelocationModels + | CodeModels + | TlsModels + | TargetCPUs + | StackProtectorStrategies + | TargetFeatures => { + codegen_backend.print(*req, sess); + } + // Any output here interferes with Cargo's parsing of other printed output + NativeStaticLibs => {} + LinkArgs => {} } - Compilation::Stop } + Compilation::Stop } /// Prints version information diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 603971a6a91..b299e71c9c4 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -21,9 +21,10 @@ use std::lazy::SyncLazy; pub enum LangItemGroup { Op, + Fn, } -const NUM_GROUPS: usize = 1; +const NUM_GROUPS: usize = 2; macro_rules! expand_group { () => { @@ -98,11 +99,12 @@ macro_rules! language_item_table { /// Construct an empty collection of lang items and no missing ones. pub fn new() -> Self { fn init_none(_: LangItem) -> Option<DefId> { None } + const EMPTY: Vec<DefId> = Vec::new(); Self { items: vec![$(init_none(LangItem::$variant)),*], missing: Vec::new(), - groups: [vec![]; NUM_GROUPS], + groups: [EMPTY; NUM_GROUPS], } } @@ -251,9 +253,9 @@ language_item_table! { DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; - Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); - FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); - FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + Fn(Fn), kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); + FnMut(Fn), sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnce(Fn), sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; @@ -264,8 +266,8 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; - PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); - PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + PartialEq(Op), sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); + PartialOrd(Op), sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 9d40b3cba29..bd5892dba38 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -49,6 +49,31 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state) } + /// Like [Self::canonicalize_query], but preserves distinct universes. For + /// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and + /// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1` + /// in `U2`. + /// + /// This is used for Chalk integration. + pub fn canonicalize_query_preserving_universes<V>( + &self, + value: V, + query_state: &mut OriginalQueryValues<'tcx>, + ) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); + + Canonicalizer::canonicalize( + value, + self, + self.tcx, + &CanonicalizeAllFreeRegionsPreservingUniverses, + query_state, + ) + } + /// Canonicalizes a query *response* `V`. When we canonicalize a /// query response, we only canonicalize unbound inference /// variables, and we leave other free regions alone. So, @@ -133,7 +158,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// maximally general query. But if we are canonicalizing a *query /// response*, then we don't typically replace free regions, as they /// must have been introduced from other parts of the system. -trait CanonicalizeRegionMode { +trait CanonicalizeMode { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, @@ -141,11 +166,14 @@ trait CanonicalizeRegionMode { ) -> ty::Region<'tcx>; fn any(&self) -> bool; + + // Do we preserve universe of variables. + fn preserve_universes(&self) -> bool; } struct CanonicalizeQueryResponse; -impl CanonicalizeRegionMode for CanonicalizeQueryResponse { +impl CanonicalizeMode for CanonicalizeQueryResponse { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, @@ -198,11 +226,15 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { fn any(&self) -> bool { false } + + fn preserve_universes(&self) -> bool { + true + } } struct CanonicalizeUserTypeAnnotation; -impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { +impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, @@ -221,11 +253,15 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { fn any(&self) -> bool { false } + + fn preserve_universes(&self) -> bool { + false + } } struct CanonicalizeAllFreeRegions; -impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { +impl CanonicalizeMode for CanonicalizeAllFreeRegions { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, @@ -237,11 +273,39 @@ impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { fn any(&self) -> bool { true } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeAllFreeRegionsPreservingUniverses; + +impl CanonicalizeMode for CanonicalizeAllFreeRegionsPreservingUniverses { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + let universe = canonicalizer.infcx.universe_of_region(r); + canonicalizer.canonical_var_for_region( + CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) }, + r, + ) + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + true + } } struct CanonicalizeFreeRegionsOtherThanStatic; -impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic { +impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, @@ -257,6 +321,10 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic { fn any(&self) -> bool { true } + + fn preserve_universes(&self) -> bool { + false + } } struct Canonicalizer<'cx, 'tcx> { @@ -267,7 +335,7 @@ struct Canonicalizer<'cx, 'tcx> { // Note that indices is only used once `var_values` is big enough to be // heap-allocated. indices: FxHashMap<GenericArg<'tcx>, BoundVar>, - canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode, + canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, binder_index: ty::DebruijnIndex, @@ -311,7 +379,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { vid, r ); let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid)); - self.canonicalize_region_mode.canonicalize_free_region(self, r) + self.canonicalize_mode.canonicalize_free_region(self, r) } ty::ReStatic @@ -319,7 +387,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { | ty::ReFree(_) | ty::ReEmpty(_) | ty::RePlaceholder(..) - | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r), + | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), } } @@ -337,8 +405,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `TyVar(vid)` is unresolved, track its universe index in the canonicalized // result. Err(mut ui) => { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } self.canonicalize_ty_var( CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), @@ -422,8 +492,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `ConstVar(vid)` is unresolved, track its universe index in the // canonicalized result Err(mut ui) => { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } return self.canonicalize_const_var( CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty) }, ct, @@ -462,7 +534,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { value: V, infcx: &InferCtxt<'_, 'tcx>, tcx: TyCtxt<'tcx>, - canonicalize_region_mode: &dyn CanonicalizeRegionMode, + canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'tcx>, ) -> Canonicalized<'tcx, V> where @@ -493,7 +565,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { let mut canonicalizer = Canonicalizer { infcx, tcx, - canonicalize_region_mode, + canonicalize_mode: canonicalize_region_mode, needs_canonical_flags, variables: SmallVec::new(), query_state, @@ -504,10 +576,11 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { // Once we have canonicalized `out_value`, it should not // contain anything that ties it to this inference context - // anymore, so it should live in the global arena. - debug_assert!(!out_value.needs_infer()); + // anymore. + debug_assert!(!out_value.needs_infer() && !out_value.has_placeholders()); - let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables); + let canonical_variables = + tcx.intern_canonical_var_infos(&canonicalizer.universe_canonicalized_variables()); let max_universe = canonical_variables .iter() @@ -527,6 +600,19 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { let var_values = &mut query_state.var_values; + let universe = info.universe(); + if universe != ty::UniverseIndex::ROOT { + assert!(self.canonicalize_mode.preserve_universes()); + + // Insert universe into the universe map. To preserve the order of the + // universes in the value being canonicalized, we don't update the + // universe in `info` until we have finished canonicalizing. + match query_state.universe_map.binary_search(&universe) { + Err(idx) => query_state.universe_map.insert(idx, universe), + Ok(_) => {} + } + } + // This code is hot. `variables` and `var_values` are usually small // (fewer than 8 elements ~95% of the time). They are SmallVec's to // avoid allocations in those cases. We also don't use `indices` to @@ -569,6 +655,61 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } } + /// Replaces the universe indexes used in `var_values` with their index in + /// `query_state.universe_map`. This minimizes the maximum universe used in + /// the canonicalized value. + fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> { + if self.query_state.universe_map.len() == 1 { + return self.variables; + } + + let reverse_universe_map: FxHashMap<ty::UniverseIndex, ty::UniverseIndex> = self + .query_state + .universe_map + .iter() + .enumerate() + .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx))) + .collect(); + + self.variables + .iter() + .map(|v| CanonicalVarInfo { + kind: match v.kind { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + return *v; + } + CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + } + CanonicalVarKind::Region(u) => { + CanonicalVarKind::Region(reverse_universe_map[&u]) + } + CanonicalVarKind::Const(u, t) => { + CanonicalVarKind::Const(reverse_universe_map[&u], t) + } + CanonicalVarKind::PlaceholderTy(placeholder) => { + CanonicalVarKind::PlaceholderTy(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderRegion(placeholder) => { + CanonicalVarKind::PlaceholderRegion(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderConst(placeholder) => { + CanonicalVarKind::PlaceholderConst(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + }, + }) + .collect() + } + /// Shorthand helper that creates a canonical region variable for /// `r` (always in the root universe). The reason that we always /// put these variables into the root universe is because this diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 3c7908fae79..a18e2d1d638 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -4,7 +4,7 @@ //! `rustc_data_structures::AtomicRef` type, which allows us to setup a global //! static which can then be set in this file at program startup. //! -//! See `SPAN_DEBUG` for an example of how to set things up. +//! See `SPAN_TRACK` for an example of how to set things up. //! //! The functions in this file should fall back to the default set in their //! origin crate when the `TyCtxt` is not present in TLS. @@ -13,18 +13,6 @@ use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS}; use rustc_middle::ty::tls; use std::fmt; -/// This is a callback from `rustc_ast` as it cannot access the implicit state -/// in `rustc_middle` otherwise. -fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - tls::with_opt(|tcx| { - if let Some(tcx) = tcx { - rustc_span::debug_with_source_map(span, f, tcx.sess.source_map()) - } else { - rustc_span::default_span_debug(span, f) - } - }) -} - fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { tls::with_opt(|tcx| { if let Some(tcx) = tcx { @@ -65,7 +53,6 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> /// Sets up the callbacks in prior crates which we want to refer to the /// TyCtxt in. pub fn setup_callbacks() { - rustc_span::SPAN_DEBUG.swap(&(span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_))); rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); TRACK_DIAGNOSTICS.swap(&(track_diagnostic as fn(&_))); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 237aef1cf23..8bd24487b78 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -186,6 +186,8 @@ pub struct Config { } pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R) -> R { + crate::callbacks::setup_callbacks(); + let registry = &config.registry; let (mut sess, codegen_backend) = util::create_session( config.opts, @@ -238,7 +240,7 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R pub fn run_compiler<R: Send>(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { tracing::trace!("run_compiler"); let stderr = config.stderr.take(); - util::setup_callbacks_and_run_in_thread_pool_with_globals( + util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.debugging_opts.threads, &stderr, diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index eebeabbd452..dcad3036cc2 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -15,6 +15,7 @@ mod proc_macro_decls; mod queries; pub mod util; +pub use callbacks::setup_callbacks; pub use interface::{run_compiler, Config}; pub use passes::{DEFAULT_EXTERN_QUERY_PROVIDERS, DEFAULT_QUERY_PROVIDERS}; pub use queries::Queries; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index f74cadfebac..d206f2644e0 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -128,7 +128,7 @@ fn scoped_thread<F: FnOnce() -> R + Send, R: Send>(cfg: thread::Builder, f: F) - } #[cfg(not(parallel_compiler))] -pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( +pub fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( edition: Edition, _threads: usize, stderr: &Option<Arc<Mutex<Vec<u8>>>>, @@ -140,8 +140,6 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Se cfg = cfg.stack_size(size); } - crate::callbacks::setup_callbacks(); - let main_handler = move || { rustc_span::create_session_globals_then(edition, || { io::set_output_capture(stderr.clone()); @@ -176,14 +174,12 @@ unsafe fn handle_deadlock() { } #[cfg(parallel_compiler)] -pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( +pub fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( edition: Edition, threads: usize, stderr: &Option<Arc<Mutex<Vec<u8>>>>, f: F, ) -> R { - crate::callbacks::setup_callbacks(); - let mut config = rayon::ThreadPoolBuilder::new() .thread_name(|_| "rustc".to_string()) .acquire_thread_handler(jobserver::acquire_thread) diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 28217aeab13..4efe3640dfa 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -64,9 +64,9 @@ pub struct CanonicalVarValues<'tcx> { /// result. #[derive(Clone, Debug)] pub struct OriginalQueryValues<'tcx> { - /// Map from the universes that appear in the query to the - /// universes in the caller context. For the time being, we only - /// ever put ROOT values into the query, so this map is very + /// Map from the universes that appear in the query to the universes in the + /// caller context. For all queries except `evaluate_goal` (used by Chalk), + /// we only ever put ROOT values into the query, so this map is very /// simple. pub universe_map: SmallVec<[ty::UniverseIndex; 4]>, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 823a927fd8c..3ce9f852c3d 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1013,37 +1013,25 @@ pub fn with_source_map<T, F: FnOnce() -> T>(source_map: Lrc<SourceMap>, f: F) -> f() } -pub fn debug_with_source_map( - span: Span, - f: &mut fmt::Formatter<'_>, - source_map: &SourceMap, -) -> fmt::Result { - write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(span), span.ctxt()) -} - -pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - with_session_globals(|session_globals| { - if let Some(source_map) = &*session_globals.source_map.borrow() { - debug_with_source_map(span, f, source_map) - } else { - f.debug_struct("Span") - .field("lo", &span.lo()) - .field("hi", &span.hi()) - .field("ctxt", &span.ctxt()) - .finish() - } - }) -} - impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(*self, f) + with_session_globals(|session_globals| { + if let Some(source_map) = &*session_globals.source_map.borrow() { + write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(*self), self.ctxt()) + } else { + f.debug_struct("Span") + .field("lo", &self.lo()) + .field("hi", &self.hi()) + .field("ctxt", &self.ctxt()) + .finish() + } + }) } } impl fmt::Debug for SpanData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(Span::new(self.lo, self.hi, self.ctxt, self.parent), f) + fmt::Debug::fmt(&Span::new(self.lo, self.hi, self.ctxt, self.parent), f) } } @@ -2003,8 +1991,6 @@ pub struct FileLines { pub lines: Vec<LineInfo>, } -pub static SPAN_DEBUG: AtomicRef<fn(Span, &mut fmt::Formatter<'_>) -> fmt::Result> = - AtomicRef::new(&(default_span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); pub static SPAN_TRACK: AtomicRef<fn(LocalDefId)> = AtomicRef::new(&((|_| {}) as fn(_))); // _____________________________________________________________________________ diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cab1d4e21c9..2e451502263 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -184,6 +184,7 @@ symbols! { Formatter, From, FromIterator, + FromResidual, Future, FxHashMap, FxHashSet, diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 34fc4ca8fea..93c2f202545 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -8,7 +8,7 @@ use crate::traits::{ PredicateObligation, SelectionError, TraitEngine, }; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; pub struct FulfillmentContext<'tcx> { obligations: FxIndexSet<PredicateObligation<'tcx>>, @@ -91,7 +91,12 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { let environment = obligation.param_env.caller_bounds(); let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate }; let mut orig_values = OriginalQueryValues::default(); - let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values); + if goal.references_error() { + continue; + } + + let canonical_goal = + infcx.canonicalize_query_preserving_universes(goal, &mut orig_values); match infcx.tcx.evaluate_goal(canonical_goal) { Ok(response) => { diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 3f51442277f..0170ab223b0 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -20,11 +20,10 @@ use rustc_span::symbol::sym; use std::fmt; use std::sync::Arc; -use crate::chalk::lowering::{self, LowerInto}; +use crate::chalk::lowering::LowerInto; pub struct RustIrDatabase<'tcx> { pub(crate) interner: RustInterner<'tcx>, - pub(crate) reempty_placeholder: ty::Region<'tcx>, } impl fmt::Debug for RustIrDatabase<'_> { @@ -40,12 +39,9 @@ impl<'tcx> RustIrDatabase<'tcx> { bound_vars: SubstsRef<'tcx>, ) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> { let predicates = self.interner.tcx.predicates_defined_on(def_id).predicates; - 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() @@ -287,9 +283,6 @@ 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.reempty_placeholder); - let trait_ref = trait_ref.fold_with(&mut regions_substitutor); let where_clauses = self.where_clauses_for(def_id, bound_vars); @@ -335,9 +328,6 @@ 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.reempty_placeholder); - let self_ty = self_ty.fold_with(&mut regions_substitutor); let lowered_ty = self_ty.lower_into(self.interner); parameters[0].assert_ty_ref(self.interner).could_match( @@ -556,11 +546,11 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t Fn => lang_items.fn_trait(), FnMut => lang_items.fn_mut_trait(), FnOnce => lang_items.fn_once_trait(), + Generator => lang_items.gen_trait(), Unsize => lang_items.unsize_trait(), Unpin => lang_items.unpin_trait(), CoerceUnsized => lang_items.coerce_unsized_trait(), DiscriminantKind => lang_items.discriminant_kind_trait(), - Generator => lang_items.generator_return(), }; def_id.map(chalk_ir::TraitId) } @@ -684,28 +674,18 @@ impl<'tcx> chalk_ir::UnificationDatabase<RustInterner<'tcx>> for RustIrDatabase< let variances = self.interner.tcx.variances_of(def_id.0); chalk_ir::Variances::from_iter( self.interner, - variances.iter().map(|v| match v { - ty::Variance::Invariant => chalk_ir::Variance::Invariant, - ty::Variance::Covariant => chalk_ir::Variance::Covariant, - ty::Variance::Contravariant => chalk_ir::Variance::Contravariant, - ty::Variance::Bivariant => unimplemented!(), - }), + variances.iter().map(|v| v.lower_into(self.interner)), ) } fn adt_variance( &self, - def_id: chalk_ir::AdtId<RustInterner<'tcx>>, + adt_id: chalk_ir::AdtId<RustInterner<'tcx>>, ) -> chalk_ir::Variances<RustInterner<'tcx>> { - let variances = self.interner.tcx.variances_of(def_id.0.did); + let variances = self.interner.tcx.variances_of(adt_id.0.did); chalk_ir::Variances::from_iter( self.interner, - variances.iter().map(|v| match v { - ty::Variance::Invariant => chalk_ir::Variance::Invariant, - ty::Variance::Covariant => chalk_ir::Variance::Covariant, - ty::Variance::Contravariant => chalk_ir::Variance::Contravariant, - ty::Variance::Bivariant => unimplemented!(), - }), + variances.iter().map(|v| v.lower_into(self.interner)), ) } } diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 67d0ba39667..6f143c1c607 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -188,12 +188,18 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(t)), ), + ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => { + chalk_ir::GoalData::SubtypeGoal(chalk_ir::SubtypeGoal { + a: a.lower_into(interner), + b: b.lower_into(interner), + }) + } + // FIXME(chalk): other predicates // // We can defer this, but ultimately we'll want to express // some of these in terms of chalk operations. ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) => { @@ -464,9 +470,11 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t }) .intern(interner) } - ReEmpty(_) => unimplemented!(), - // FIXME(chalk): need to handle ReErased - ReErased => unimplemented!(), + ReEmpty(ui) => { + chalk_ir::LifetimeData::Empty(chalk_ir::UniverseIndex { counter: ui.index() }) + .intern(interner) + } + ReErased => chalk_ir::LifetimeData::Erased.intern(interner), } } } @@ -488,12 +496,12 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'t name: ty::BoundRegionKind::BrAnon(p.idx as u32), }) } - chalk_ir::LifetimeData::Static => ty::RegionKind::ReStatic, - chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), + chalk_ir::LifetimeData::Static => return interner.tcx.lifetimes.re_static, chalk_ir::LifetimeData::Empty(ui) => { - ty::RegionKind::ReEmpty(ty::UniverseIndex::from_usize(ui.counter)) + ty::ReEmpty(ty::UniverseIndex::from_usize(ui.counter)) } - chalk_ir::LifetimeData::Erased => ty::RegionKind::ReErased, + chalk_ir::LifetimeData::Erased => return interner.tcx.lifetimes.re_erased, + chalk_ir::LifetimeData::Phantom(void, _) => match *void {}, }; interner.tcx.mk_region(kind) } @@ -788,6 +796,16 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::Polarity> for ty::ImplPolarity } } } +impl<'tcx> LowerInto<'tcx, chalk_ir::Variance> for ty::Variance { + fn lower_into(self, _interner: RustInterner<'tcx>) -> chalk_ir::Variance { + match self { + ty::Variance::Covariant => chalk_ir::Variance::Covariant, + ty::Variance::Invariant => chalk_ir::Variance::Invariant, + ty::Variance::Contravariant => chalk_ir::Variance::Contravariant, + ty::Variance::Bivariant => unimplemented!(), + } + } +} impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>>> for ty::ProjectionPredicate<'tcx> @@ -1016,10 +1034,6 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { - // FIXME(chalk): currently we convert params to placeholders starting at - // index `0`. To support placeholders, we'll actually need to do a - // first pass to collect placeholders. Then we can insert params after. - ty::Placeholder(_) => unimplemented!(), ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { Some(idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::from_usize(0), @@ -1035,15 +1049,15 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { })) } }, - _ => t.super_fold_with(self), } } fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { match r { - // FIXME(chalk) - jackh726 - this currently isn't hit in any tests. - // This covers any region variables in a goal, right? + // FIXME(chalk) - jackh726 - this currently isn't hit in any tests, + // since canonicalization will already change these to canonical + // variables (ty::ReLateBound). ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { Some(idx) => { let br = ty::BoundRegion { @@ -1066,6 +1080,39 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { } } +crate struct ReverseParamsSubstitutor<'tcx> { + tcx: TyCtxt<'tcx>, + params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>, +} + +impl<'tcx> ReverseParamsSubstitutor<'tcx> { + crate fn new( + tcx: TyCtxt<'tcx>, + params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>, + ) -> Self { + Self { tcx, params } + } +} + +impl<'tcx> TypeFolder<'tcx> for ReverseParamsSubstitutor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, name }) => { + match self.params.get(&name.as_usize()) { + Some(param) => self.tcx.mk_ty(ty::Param(*param)), + None => t, + } + } + + _ => t.super_fold_with(self), + } + } +} + /// Used to collect `Placeholder`s. crate struct PlaceholdersCollector { universe_index: ty::UniverseIndex, @@ -1110,32 +1157,3 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { r.super_visit_with(self) } } - -/// Used to substitute specific `Regions`s with placeholders. -crate struct RegionsSubstitutor<'tcx> { - tcx: TyCtxt<'tcx>, - reempty_placeholder: ty::Region<'tcx>, -} - -impl<'tcx> RegionsSubstitutor<'tcx> { - crate fn new(tcx: TyCtxt<'tcx>, reempty_placeholder: ty::Region<'tcx>) -> Self { - RegionsSubstitutor { tcx, reempty_placeholder } - } -} - -impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - match r { - ty::ReEmpty(ui) => { - assert_eq!(ui.as_usize(), 0); - self.reempty_placeholder - } - - _ => r.super_fold_with(self), - } - } -} diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index 09bfdabf473..3c2a266dab9 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -22,9 +22,8 @@ use rustc_infer::infer::canonical::{ use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal}; use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase; -use crate::chalk::lowering::{ - LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor, -}; +use crate::chalk::lowering::LowerInto; +use crate::chalk::lowering::{ParamsSubstitutor, PlaceholdersCollector, ReverseParamsSubstitutor}; use chalk_solve::Solution; @@ -42,19 +41,10 @@ crate fn evaluate_goal<'tcx>( let mut placeholders_collector = PlaceholdersCollector::new(); obligation.visit_with(&mut placeholders_collector); - let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { - universe: ty::UniverseIndex::ROOT, - name: ty::BoundRegionKind::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), - })); - let mut params_substitutor = ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder); let obligation = obligation.fold_with(&mut params_substitutor); - // 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, reempty_placeholder); - let obligation = obligation.fold_with(&mut regions_substitutor); + let params: FxHashMap<usize, ParamTy> = params_substitutor.params; let max_universe = obligation.max_universe.index(); @@ -96,7 +86,8 @@ crate fn evaluate_goal<'tcx>( use chalk_solve::Solver; let mut solver = chalk_engine::solve::SLGSolver::new(32, None); - let db = ChalkRustIrDatabase { interner, reempty_placeholder }; + let db = ChalkRustIrDatabase { interner }; + debug!(?lowered_goal); let solution = solver.solve(&db, &lowered_goal); debug!(?obligation, ?solution, "evaluate goal"); @@ -110,8 +101,9 @@ crate fn evaluate_goal<'tcx>( use rustc_middle::infer::canonical::CanonicalVarInfo; let mut var_values: IndexVec<BoundVar, GenericArg<'tcx>> = IndexVec::new(); + let mut reverse_param_substitutor = ReverseParamsSubstitutor::new(tcx, params); subst.as_slice(interner).iter().for_each(|p| { - var_values.push(p.lower_into(interner)); + var_values.push(p.lower_into(interner).fold_with(&mut reverse_param_substitutor)); }); let variables: Vec<_> = binders .iter(interner) |
