Auto merge of #104138 - Dylan-DPC:rollup-m3ojpjg, r=Dylan-DPC

Rollup of 7 pull requests

Successful merges:

 - #103446 (Specialize `iter::ArrayChunks::fold` for TrustedRandomAccess iterators)
 - #103651 (Fix `rustc_parse_format` spans following escaped utf-8 multibyte chars)
 - #103865 (Move `fallback_has_occurred` state tracking to `FnCtxt`)
 - #103955 (Update linker-plugin-lto.md to contain up to Rust 1.65)
 - #103987 (Remove `in_tail_expr` from FnCtxt)
 - #104067 (fix debuginfo for windows_gnullvm_base.rs)
 - #104094 (fully move `on_unimplemented` to `error_reporting`)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-11-08 06:49:52 +00:00
commit 57d3c58ed6
36 changed files with 840 additions and 623 deletions

View file

@ -299,7 +299,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
if errors.is_empty() {
definition_ty
} else {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
self.tcx.ty_error()
}
}

View file

@ -765,7 +765,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
}
}
@ -831,7 +831,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
obligation.clone(),
&obligation,
&e,
false,
);
}

View file

@ -26,6 +26,7 @@ use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVE
use rustc_span::symbol::sym;
use rustc_span::{self, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCtxt};
@ -471,7 +472,7 @@ fn check_opaque_meets_bounds<'tcx>(
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
}
match origin {
// Checked when type checking the function containing them.
@ -655,7 +656,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
// an error would be reported if this fails.
let _ = traits::OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id());
let _ = OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id());
}
pub(super) fn check_specialization_validity<'tcx>(

View file

@ -405,7 +405,7 @@ fn compare_predicate_entailment<'tcx>(
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return Err(reported);
}
@ -538,7 +538,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
// RPITs.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return Err(reported);
}
@ -1431,7 +1431,7 @@ pub(crate) fn raw_compare_const_impl<'tcx>(
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false));
return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None));
}
// FIXME return `ErrorReported` if region obligations error?
@ -1549,7 +1549,7 @@ fn compare_type_predicate_entailment<'tcx>(
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return Err(reported);
}
@ -1769,7 +1769,7 @@ pub fn check_type_bounds<'tcx>(
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return Err(reported);
}

View file

@ -105,7 +105,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
f(&mut wfcx);
let errors = wfcx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return;
}

View file

@ -321,7 +321,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
}),
);
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
}
// Finally, resolve all regions.
@ -561,7 +561,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, source, &[target.into()]);
let errors = traits::fully_solve_obligation(&infcx, predicate);
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
}
// Finally, resolve all regions.

View file

@ -155,7 +155,7 @@ fn get_impl_substs<'tcx>(
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return None;
}

View file

@ -173,7 +173,7 @@ fn require_same_types<'tcx>(
match &errors[..] {
[] => true,
errors => {
infcx.err_ctxt().report_fulfillment_errors(errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(errors, None);
false
}
}
@ -336,7 +336,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
ocx.register_bound(cause, param_env, norm_return_ty, term_did);
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
error = true;
}
// now we can take the return type of the given main function

View file

@ -1,6 +1,6 @@
use crate::coercion::{AsCoercionSite, CoerceMany};
use crate::{Diverges, Expectation, FnCtxt, Needs};
use rustc_errors::{Applicability, MultiSpan};
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir::{self as hir, ExprKind};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::traits::Obligation;
@ -137,55 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(&arm.body),
arm_ty,
Some(&mut |err| {
let Some(ret) = self
.tcx
.hir()
.find_by_def_id(self.body_id.owner.def_id)
.and_then(|owner| owner.fn_decl())
.map(|decl| decl.output.span())
else { return; };
let Expectation::IsLast(stmt) = orig_expected else {
return
};
let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
Some(ret_coercion) if self.in_tail_expr => {
let ret_ty = ret_coercion.borrow().expected_ty();
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
self.can_coerce(arm_ty, ret_ty)
&& prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty))
// The match arms need to unify for the case of `impl Trait`.
&& !matches!(ret_ty.kind(), ty::Opaque(..))
}
_ => false,
};
if !can_coerce_to_return_ty {
return;
}
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
let mut ret_span: MultiSpan = semi_span.into();
ret_span.push_span_label(
expr.span,
"this could be implicitly returned but it is a statement, not a \
tail expression",
);
ret_span
.push_span_label(ret, "the `match` arms can conform to this return type");
ret_span.push_span_label(
semi_span,
"the `match` is a statement because of this semicolon, consider \
removing it",
);
err.span_note(
ret_span,
"you might have meant to return the `match` expression",
);
err.tool_only_span_suggestion(
semi_span,
"remove this semicolon",
"",
Applicability::MaybeIncorrect,
);
self.suggest_removing_semicolon_for_coerce(
err,
expr,
orig_expected,
arm_ty,
prior_arm,
)
}),
false,
);
@ -219,6 +177,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
coercion.complete(self)
}
fn suggest_removing_semicolon_for_coerce(
&self,
diag: &mut Diagnostic,
expr: &hir::Expr<'tcx>,
expectation: Expectation<'tcx>,
arm_ty: Ty<'tcx>,
prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
) {
let hir = self.tcx.hir();
// First, check that we're actually in the tail of a function.
let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, _), .. }) =
hir.get(self.body_id) else { return; };
let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. })
= block.innermost_block().stmts.last() else { return; };
if last_expr.hir_id != expr.hir_id {
return;
}
// Next, make sure that we have no type expectation.
let Some(ret) = hir
.find_by_def_id(self.body_id.owner.def_id)
.and_then(|owner| owner.fn_decl())
.map(|decl| decl.output.span()) else { return; };
let Expectation::IsLast(stmt) = expectation else {
return;
};
let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
Some(ret_coercion) => {
let ret_ty = ret_coercion.borrow().expected_ty();
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
self.can_coerce(arm_ty, ret_ty)
&& prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty))
// The match arms need to unify for the case of `impl Trait`.
&& !matches!(ret_ty.kind(), ty::Opaque(..))
}
_ => false,
};
if !can_coerce_to_return_ty {
return;
}
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
let mut ret_span: MultiSpan = semi_span.into();
ret_span.push_span_label(
expr.span,
"this could be implicitly returned but it is a statement, not a \
tail expression",
);
ret_span.push_span_label(ret, "the `match` arms can conform to this return type");
ret_span.push_span_label(
semi_span,
"the `match` is a statement because of this semicolon, consider \
removing it",
);
diag.span_note(ret_span, "you might have meant to return the `match` expression");
diag.tool_only_span_suggestion(
semi_span,
"remove this semicolon",
"",
Applicability::MaybeIncorrect,
);
}
/// When the previously checked expression (the scrutinee) diverges,
/// warn the user about the match arms being unreachable.
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {

View file

@ -100,7 +100,6 @@ pub(super) fn check_fn<'a, 'tcx>(
inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
fcx.in_tail_expr = true;
if let ty::Dynamic(..) = declared_ret_ty.kind() {
// FIXME: We need to verify that the return type is `Sized` after the return expression has
// been evaluated so that we have types available for all the nodes being returned, but that
@ -119,7 +118,6 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
fcx.check_return_expr(&body.value, false);
}
fcx.in_tail_expr = false;
// We insert the deferred_generator_interiors entry after visiting the body.
// This ensures that all nested generators appear before the entry of this generator.

View file

@ -705,12 +705,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// Object safety violations or miscellaneous.
Err(err) => {
self.err_ctxt().report_selection_error(
obligation.clone(),
&obligation,
&err,
false,
);
self.err_ctxt().report_selection_error(obligation.clone(), &obligation, &err);
// Treat this like an obligation and follow through
// with the unsizing - the lack of a coercion should
// be silent, as it causes a type mismatch later.

View file

@ -843,7 +843,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
// Point any obligations that were registered due to opaque type
// inference at the return expression.
self.select_obligations_where_possible(false, |errors| {
self.select_obligations_where_possible(|errors| {
self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty);
});
}
@ -2738,7 +2738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some((index_ty, element_ty)) => {
// two-phase not needed because index_ty is never mutable
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
self.select_obligations_where_possible(false, |errors| {
self.select_obligations_where_possible(|errors| {
self.point_at_index_if_possible(errors, idx.span)
});
element_ty

View file

@ -7,16 +7,16 @@ use rustc_data_structures::{
use rustc_middle::ty::{self, Ty};
impl<'tcx> FnCtxt<'_, 'tcx> {
/// Performs type inference fallback, returning true if any fallback
/// occurs.
pub(super) fn type_inference_fallback(&self) -> bool {
/// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
/// if fallback has occurred.
pub(super) fn type_inference_fallback(&self) {
debug!(
"type-inference-fallback start obligations: {:#?}",
self.fulfillment_cx.borrow_mut().pending_obligations()
);
// All type checking constraints were added, try to fallback unsolved variables.
self.select_obligations_where_possible(false, |_| {});
self.select_obligations_where_possible(|_| {});
debug!(
"type-inference-fallback post selection obligations: {:#?}",
@ -26,18 +26,17 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
// Check if we have any unsolved variables. If not, no need for fallback.
let unsolved_variables = self.unsolved_variables();
if unsolved_variables.is_empty() {
return false;
return;
}
let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
let mut fallback_has_occurred = false;
// We do fallback in two passes, to try to generate
// better error messages.
// The first time, we do *not* replace opaque types.
for ty in unsolved_variables {
debug!("unsolved_variable = {:?}", ty);
fallback_has_occurred |= self.fallback_if_possible(ty, &diverging_fallback);
self.fallback_if_possible(ty, &diverging_fallback);
}
// We now see if we can make progress. This might cause us to
@ -63,9 +62,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
// If we had tried to fallback the opaque inference variable to `MyType`,
// we will generate a confusing type-check error that does not explicitly
// refer to opaque types.
self.select_obligations_where_possible(fallback_has_occurred, |_| {});
fallback_has_occurred
self.select_obligations_where_possible(|_| {});
}
// Tries to apply a fallback to `ty` if it is an unsolved variable.
@ -81,12 +78,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
// Fallback becomes very dubious if we have encountered
// type-checking errors. In that case, fallback to Error.
//
// The return value indicates whether fallback has occurred.
// Sets `FnCtxt::fallback_has_occurred` if fallback is performed
// during this call.
fn fallback_if_possible(
&self,
ty: Ty<'tcx>,
diverging_fallback: &FxHashMap<Ty<'tcx>, Ty<'tcx>>,
) -> bool {
) {
// Careful: we do NOT shallow-resolve `ty`. We know that `ty`
// is an unsolved variable, and we determine its fallback
// based solely on how it was created, not what other type
@ -111,7 +109,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
_ => match diverging_fallback.get(&ty) {
Some(&fallback_ty) => fallback_ty,
None => return false,
None => return,
},
};
debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
@ -122,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
.map(|origin| origin.span)
.unwrap_or(rustc_span::DUMMY_SP);
self.demand_eqtype(span, ty, fallback);
true
self.fallback_has_occurred.set(true);
}
/// The "diverging fallback" system is rather complicated. This is

View file

@ -106,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// possible. This can help substantially when there are
// indirect dependencies that don't seem worth tracking
// precisely.
self.select_obligations_where_possible(false, mutate_fulfillment_errors);
self.select_obligations_where_possible(mutate_fulfillment_errors);
self.resolve_vars_if_possible(ty)
}
@ -600,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
let mut generators = self.deferred_generator_interiors.borrow_mut();
for (body_id, interior, kind) in generators.drain(..) {
self.select_obligations_where_possible(false, |_| {});
self.select_obligations_where_possible(|_| {});
crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
}
}
@ -611,25 +611,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !errors.is_empty() {
self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false);
self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id);
}
}
/// Select as many obligations as we can at present.
pub(in super::super) fn select_obligations_where_possible(
&self,
fallback_has_occurred: bool,
mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
) {
let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
if !result.is_empty() {
mutate_fulfillment_errors(&mut result);
self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
self.err_ctxt().report_fulfillment_errors(
&result,
self.inh.body_id,
fallback_has_occurred,
);
self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id);
}
}

View file

@ -345,7 +345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// an "opportunistic" trait resolution of any trait bounds on
// the call. This helps coercions.
if check_closures {
self.select_obligations_where_possible(false, |_| {})
self.select_obligations_where_possible(|_| {})
}
// Check each argument, to satisfy the input it was provided for

View file

@ -68,10 +68,6 @@ pub struct FnCtxt<'a, 'tcx> {
/// any).
pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
/// Used exclusively to reduce cost of advanced evaluation used for
/// more helpful diagnostics.
pub(super) in_tail_expr: bool,
/// First span of a return site that we find. Used in error messages.
pub(super) ret_coercion_span: Cell<Option<Span>>,
@ -115,6 +111,8 @@ pub struct FnCtxt<'a, 'tcx> {
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
pub(super) inh: &'a Inherited<'tcx>,
pub(super) fallback_has_occurred: Cell<bool>,
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@ -128,7 +126,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
param_env,
err_count_on_creation: inh.tcx.sess.err_count(),
ret_coercion: None,
in_tail_expr: false,
ret_coercion_span: Cell::new(None),
resume_yield_tys: None,
ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
@ -138,6 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
by_id: Default::default(),
}),
inh,
fallback_has_occurred: Cell::new(false),
}
}
@ -159,7 +157,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
///
/// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
TypeErrCtxt { infcx: &self.infcx, typeck_results: Some(self.typeck_results.borrow()) }
TypeErrCtxt {
infcx: &self.infcx,
typeck_results: Some(self.typeck_results.borrow()),
fallback_has_occurred: self.fallback_has_occurred.get(),
}
}
pub fn errors_reported_since_creation(&self) -> bool {

View file

@ -316,12 +316,12 @@ fn typeck_with_fallback<'tcx>(
fcx
};
let fallback_has_occurred = fcx.type_inference_fallback();
fcx.type_inference_fallback();
// Even though coercion casts provide type hints, we check casts after fallback for
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
fcx.check_casts();
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
fcx.select_obligations_where_possible(|_| {});
// Closure and generator analysis may run after fallback
// because they don't constrain other type variables.

View file

@ -23,10 +23,11 @@ use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol;
use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote,
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
};
use std::cmp::Ordering;

View file

@ -772,7 +772,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match (method, trait_did) {
(Some(ok), _) => {
let method = self.register_infer_ok_obligations(ok);
self.select_obligations_where_possible(false, |_| {});
self.select_obligations_where_possible(|_| {});
Ok(method)
}
(None, None) => Err(vec![]),

View file

@ -91,6 +91,7 @@ pub mod nice_region_error;
pub struct TypeErrCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>,
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
pub fallback_has_occurred: bool,
}
impl TypeErrCtxt<'_, '_> {

View file

@ -677,9 +677,9 @@ pub struct CombinedSnapshot<'tcx> {
impl<'tcx> InferCtxt<'tcx> {
/// Creates a `TypeErrCtxt` for emitting various inference errors.
/// During typeck, use `FnCtxt::infer_err` instead.
/// During typeck, use `FnCtxt::err_ctxt` instead.
pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
TypeErrCtxt { infcx: self, typeck_results: None }
TypeErrCtxt { infcx: self, typeck_results: None, fallback_has_occurred: false }
}
/// calls `tcx.try_unify_abstract_consts` after

View file

@ -819,19 +819,19 @@ fn find_skips_from_snippet(
};
fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
let mut s = snippet.char_indices().peekable();
let mut s = snippet.char_indices();
let mut skips = vec![];
while let Some((pos, c)) = s.next() {
match (c, s.peek()) {
match (c, s.clone().next()) {
// skip whitespace and empty lines ending in '\\'
('\\', Some((next_pos, '\n'))) if !is_raw => {
skips.push(pos);
skips.push(*next_pos);
skips.push(next_pos);
let _ = s.next();
while let Some((pos, c)) = s.peek() {
while let Some((pos, c)) = s.clone().next() {
if matches!(c, ' ' | '\n' | '\t') {
skips.push(*pos);
skips.push(pos);
let _ = s.next();
} else {
break;
@ -839,7 +839,7 @@ fn find_skips_from_snippet(
}
}
('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => {
skips.push(*next_pos);
skips.push(next_pos);
let _ = s.next();
}
('\\', Some((_, 'x'))) if !is_raw => {
@ -858,19 +858,30 @@ fn find_skips_from_snippet(
}
if let Some((next_pos, next_c)) = s.next() {
if next_c == '{' {
skips.push(next_pos);
let mut i = 0; // consume up to 6 hexanumeric chars + closing `}`
while let (Some((next_pos, c)), true) = (s.next(), i < 7) {
if c.is_digit(16) {
skips.push(next_pos);
} else if c == '}' {
skips.push(next_pos);
break;
} else {
break;
}
i += 1;
// consume up to 6 hexanumeric chars
let digits_len =
s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count();
let len_utf8 = s
.as_str()
.get(..digits_len)
.and_then(|digits| u32::from_str_radix(digits, 16).ok())
.and_then(char::from_u32)
.map_or(1, char::len_utf8);
// Skip the digits, for chars that encode to more than 1 utf-8 byte
// exclude as many digits as it is greater than 1 byte
//
// So for a 3 byte character, exclude 2 digits
let required_skips =
digits_len.saturating_sub(len_utf8.saturating_sub(1));
// skip '{' and '}' also
for pos in (next_pos..).take(required_skips + 2) {
skips.push(pos)
}
s.nth(digits_len);
} else if next_c.is_digit(16) {
skips.push(next_pos);
// We suggest adding `{` and `}` when appropriate, accept it here as if

View file

@ -1,4 +1,5 @@
use crate::spec::{cvs, Cc, LinkerFlavor, Lld, TargetOptions};
use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions};
use std::borrow::Cow;
pub fn opts() -> TargetOptions {
// We cannot use `-nodefaultlibs` because compiler-rt has to be passed
@ -36,7 +37,10 @@ pub fn opts() -> TargetOptions {
eh_frame_header: false,
no_default_libraries: false,
has_thread_local: true,
// FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to
// output DWO, despite using DWARF, doesn't use ELF..
debuginfo_kind: DebuginfoKind::Pdb,
supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
..Default::default()
}
}

View file

@ -3,14 +3,17 @@ pub mod suggestions;
use super::{
FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective,
OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation,
SelectionContext, SelectionError, TraitNotObjectSafe,
Obligation, ObligationCause, ObligationCauseCode, OutputTypeParameterMismatch, Overflow,
PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{self, InferCtxt, TyCtxtInferExt};
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::query::normalize::AtExt as _;
use crate::traits::specialize::to_pretty_impl_header;
use on_unimplemented::OnUnimplementedNote;
use on_unimplemented::TypeErrCtxtExt as _;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@ -40,11 +43,6 @@ use rustc_span::{ExpnKind, Span, DUMMY_SP};
use std::fmt;
use std::iter;
use std::ops::ControlFlow;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::query::normalize::AtExt as _;
use crate::traits::specialize::to_pretty_impl_header;
use on_unimplemented::TypeErrCtxtExt as _;
use suggestions::TypeErrCtxtExt as _;
pub use rustc_infer::traits::error_reporting::*;
@ -101,7 +99,6 @@ pub trait TypeErrCtxtExt<'tcx> {
&self,
errors: &[FulfillmentError<'tcx>],
body_id: Option<hir::BodyId>,
fallback_has_occurred: bool,
) -> ErrorGuaranteed;
fn report_overflow_error<T>(
@ -124,7 +121,6 @@ pub trait TypeErrCtxtExt<'tcx> {
obligation: PredicateObligation<'tcx>,
root_obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
fallback_has_occurred: bool,
);
}
@ -375,7 +371,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&self,
errors: &[FulfillmentError<'tcx>],
body_id: Option<hir::BodyId>,
fallback_has_occurred: bool,
) -> ErrorGuaranteed {
#[derive(Debug)]
struct ErrorDescriptor<'tcx> {
@ -452,7 +447,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
for (error, suppressed) in iter::zip(errors, is_suppressed) {
if !suppressed {
self.report_fulfillment_error(error, body_id, fallback_has_occurred);
self.report_fulfillment_error(error, body_id);
}
}
@ -534,7 +529,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
mut obligation: PredicateObligation<'tcx>,
root_obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
fallback_has_occurred: bool,
) {
self.set_tainted_by_errors();
let tcx = self.tcx;
@ -1015,7 +1009,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// variable that used to fallback to `()` now falling back to `!`. Issue a
// note informing about the change in behaviour.
if trait_predicate.skip_binder().self_ty().is_never()
&& fallback_has_occurred
&& self.fallback_has_occurred
{
let predicate = trait_predicate.map_bound(|mut trait_pred| {
trait_pred.trait_ref.substs = self.tcx.mk_substs_trait(
@ -1381,7 +1375,6 @@ trait InferCtxtPrivExt<'tcx> {
&self,
error: &FulfillmentError<'tcx>,
body_id: Option<hir::BodyId>,
fallback_has_occurred: bool,
);
fn report_projection_error(
@ -1531,7 +1524,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&self,
error: &FulfillmentError<'tcx>,
body_id: Option<hir::BodyId>,
fallback_has_occurred: bool,
) {
match error.code {
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
@ -1539,7 +1531,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
error.obligation.clone(),
&error.root_obligation,
selection_error,
fallback_has_occurred,
);
}
FulfillmentErrorCode::CodeProjectionError(ref e) => {

View file

@ -1,14 +1,22 @@
use super::{
ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation,
};
use super::{ObligationCauseCode, PredicateObligation};
use crate::infer::error_reporting::TypeErrCtxt;
use rustc_ast::{MetaItem, NestedMetaItem};
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{struct_span_err, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::SubstsRef;
use rustc_middle::ty::{self, GenericParamDefKind};
use rustc_span::symbol::sym;
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use std::iter;
use crate::errors::{
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
};
use super::InferCtxtPrivExt;
pub trait TypeErrCtxtExt<'tcx> {
@ -276,3 +284,383 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
}
#[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString(Symbol);
#[derive(Debug)]
pub struct OnUnimplementedDirective {
pub condition: Option<MetaItem>,
pub subcommands: Vec<OnUnimplementedDirective>,
pub message: Option<OnUnimplementedFormatString>,
pub label: Option<OnUnimplementedFormatString>,
pub note: Option<OnUnimplementedFormatString>,
pub parent_label: Option<OnUnimplementedFormatString>,
pub append_const_msg: Option<Option<Symbol>>,
}
/// For the `#[rustc_on_unimplemented]` attribute
#[derive(Default)]
pub struct OnUnimplementedNote {
pub message: Option<String>,
pub label: Option<String>,
pub note: Option<String>,
pub parent_label: Option<String>,
/// Append a message for `~const Trait` errors. `None` means not requested and
/// should fallback to a generic message, `Some(None)` suggests using the default
/// appended message, `Some(Some(s))` suggests use the `s` message instead of the
/// default one..
pub append_const_msg: Option<Option<Symbol>>,
}
impl<'tcx> OnUnimplementedDirective {
fn parse(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
items: &[NestedMetaItem],
span: Span,
is_root: bool,
) -> Result<Self, ErrorGuaranteed> {
let mut errored = None;
let mut item_iter = items.iter();
let parse_value = |value_str| {
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some)
};
let condition = if is_root {
None
} else {
let cond = item_iter
.next()
.ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))?
.meta_item()
.ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
if let Some(value) = cfg.value && let Err(guar) = parse_value(value) {
errored = Some(guar);
}
true
});
Some(cond.clone())
};
let mut message = None;
let mut label = None;
let mut note = None;
let mut parent_label = None;
let mut subcommands = vec![];
let mut append_const_msg = None;
for item in item_iter {
if item.has_name(sym::message) && message.is_none() {
if let Some(message_) = item.value_str() {
message = parse_value(message_)?;
continue;
}
} else if item.has_name(sym::label) && label.is_none() {
if let Some(label_) = item.value_str() {
label = parse_value(label_)?;
continue;
}
} else if item.has_name(sym::note) && note.is_none() {
if let Some(note_) = item.value_str() {
note = parse_value(note_)?;
continue;
}
} else if item.has_name(sym::parent_label) && parent_label.is_none() {
if let Some(parent_label_) = item.value_str() {
parent_label = parse_value(parent_label_)?;
continue;
}
} else if item.has_name(sym::on)
&& is_root
&& message.is_none()
&& label.is_none()
&& note.is_none()
{
if let Some(items) = item.meta_item_list() {
match Self::parse(tcx, item_def_id, &items, item.span(), false) {
Ok(subcommand) => subcommands.push(subcommand),
Err(reported) => errored = Some(reported),
};
continue;
}
} else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() {
if let Some(msg) = item.value_str() {
append_const_msg = Some(Some(msg));
continue;
} else if item.is_word() {
append_const_msg = Some(None);
continue;
}
}
// nothing found
tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
}
if let Some(reported) = errored {
Err(reported)
} else {
Ok(OnUnimplementedDirective {
condition,
subcommands,
message,
label,
note,
parent_label,
append_const_msg,
})
}
}
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else {
return Ok(None);
};
let result = if let Some(items) = attr.meta_item_list() {
Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some)
} else if let Some(value) = attr.value_str() {
Ok(Some(OnUnimplementedDirective {
condition: None,
message: None,
subcommands: vec![],
label: Some(OnUnimplementedFormatString::try_parse(
tcx,
item_def_id,
value,
attr.span,
)?),
note: None,
parent_label: None,
append_const_msg: None,
}))
} else {
let reported =
tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
return Err(reported);
};
debug!("of_item({:?}) = {:?}", item_def_id, result);
result
}
pub fn evaluate(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &[(Symbol, Option<String>)],
) -> OnUnimplementedNote {
let mut message = None;
let mut label = None;
let mut note = None;
let mut parent_label = None;
let mut append_const_msg = None;
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
let options_map: FxHashMap<Symbol, String> =
options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect();
for command in self.subcommands.iter().chain(Some(self)).rev() {
if let Some(ref condition) = command.condition && !attr::eval_condition(
condition,
&tcx.sess.parse_sess,
Some(tcx.features()),
&mut |cfg| {
let value = cfg.value.map(|v| {
OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
});
options.contains(&(cfg.name, value))
},
) {
debug!("evaluate: skipping {:?} due to condition", command);
continue;
}
debug!("evaluate: {:?} succeeded", command);
if let Some(ref message_) = command.message {
message = Some(message_.clone());
}
if let Some(ref label_) = command.label {
label = Some(label_.clone());
}
if let Some(ref note_) = command.note {
note = Some(note_.clone());
}
if let Some(ref parent_label_) = command.parent_label {
parent_label = Some(parent_label_.clone());
}
append_const_msg = command.append_const_msg;
}
OnUnimplementedNote {
label: label.map(|l| l.format(tcx, trait_ref, &options_map)),
message: message.map(|m| m.format(tcx, trait_ref, &options_map)),
note: note.map(|n| n.format(tcx, trait_ref, &options_map)),
parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)),
append_const_msg,
}
}
}
impl<'tcx> OnUnimplementedFormatString {
fn try_parse(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
from: Symbol,
err_sp: Span,
) -> Result<Self, ErrorGuaranteed> {
let result = OnUnimplementedFormatString(from);
result.verify(tcx, item_def_id, err_sp)?;
Ok(result)
}
fn verify(
&self,
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
span: Span,
) -> Result<(), ErrorGuaranteed> {
let trait_def_id = if tcx.is_trait(item_def_id) {
item_def_id
} else {
tcx.trait_id_of_impl(item_def_id)
.expect("expected `on_unimplemented` to correspond to a trait")
};
let trait_name = tcx.item_name(trait_def_id);
let generics = tcx.generics_of(item_def_id);
let s = self.0.as_str();
let parser = Parser::new(s, None, None, false, ParseMode::Format);
let mut result = Ok(());
for token in parser {
match token {
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => {
match Symbol::intern(s) {
// `{Self}` is allowed
kw::SelfUpper => (),
// `{ThisTraitsName}` is allowed
s if s == trait_name => (),
// `{from_method}` is allowed
sym::from_method => (),
// `{from_desugaring}` is allowed
sym::from_desugaring => (),
// `{ItemContext}` is allowed
sym::ItemContext => (),
// `{integral}` and `{integer}` and `{float}` are allowed
sym::integral | sym::integer_ | sym::float => (),
// So is `{A}` if A is a type parameter
s => match generics.params.iter().find(|param| param.name == s) {
Some(_) => (),
None => {
let reported = struct_span_err!(
tcx.sess,
span,
E0230,
"there is no parameter `{}` on {}",
s,
if trait_def_id == item_def_id {
format!("trait `{}`", trait_name)
} else {
"impl".to_string()
}
)
.emit();
result = Err(reported);
}
},
}
}
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
let reported = struct_span_err!(
tcx.sess,
span,
E0231,
"only named substitution parameters are allowed"
)
.emit();
result = Err(reported);
}
},
}
}
result
}
pub fn format(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &FxHashMap<Symbol, String>,
) -> String {
let name = tcx.item_name(trait_ref.def_id);
let trait_str = tcx.def_path_str(trait_ref.def_id);
let generics = tcx.generics_of(trait_ref.def_id);
let generic_map = generics
.params
.iter()
.filter_map(|param| {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
trait_ref.substs[param.index as usize].to_string()
}
GenericParamDefKind::Lifetime => return None,
};
let name = param.name;
Some((name, value))
})
.collect::<FxHashMap<Symbol, String>>();
let empty_string = String::new();
let s = self.0.as_str();
let parser = Parser::new(s, None, None, false, ParseMode::Format);
let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
parser
.map(|p| match p {
Piece::String(s) => s,
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => {
let s = Symbol::intern(s);
match generic_map.get(&s) {
Some(val) => val,
None if s == name => &trait_str,
None => {
if let Some(val) = options.get(&s) {
val
} else if s == sym::from_desugaring || s == sym::from_method {
// don't break messages using these two arguments incorrectly
&empty_string
} else if s == sym::ItemContext {
&item_context
} else if s == sym::integral {
"{integral}"
} else if s == sym::integer_ {
"{integer}"
} else if s == sym::float {
"{float}"
} else {
bug!(
"broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}",
self.0,
trait_ref,
s
)
}
}
}
}
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
},
})
.collect()
}
}

View file

@ -70,7 +70,7 @@ pub fn can_type_implement_copy<'tcx>(
}
}
Err(errors) => {
infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
}
};
}

View file

@ -12,7 +12,6 @@ pub mod error_reporting;
mod fulfill;
pub mod misc;
mod object_safety;
mod on_unimplemented;
pub mod outlives_bounds;
mod project;
pub mod query;
@ -58,7 +57,6 @@ pub use self::object_safety::astconv_object_safety_violations;
pub use self::object_safety::is_vtable_safe_method;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::ObjectSafetyViolation;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::project::{normalize, normalize_projection_type, normalize_to};
pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
@ -214,7 +212,7 @@ fn do_normalize_predicates<'tcx>(
let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) {
Ok(predicates) => predicates,
Err(errors) => {
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return Err(reported);
}
};

View file

@ -1,393 +0,0 @@
use rustc_ast::{MetaItem, NestedMetaItem};
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{struct_span_err, ErrorGuaranteed};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use crate::errors::{
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
};
#[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString(Symbol);
#[derive(Debug)]
pub struct OnUnimplementedDirective {
pub condition: Option<MetaItem>,
pub subcommands: Vec<OnUnimplementedDirective>,
pub message: Option<OnUnimplementedFormatString>,
pub label: Option<OnUnimplementedFormatString>,
pub note: Option<OnUnimplementedFormatString>,
pub parent_label: Option<OnUnimplementedFormatString>,
pub append_const_msg: Option<Option<Symbol>>,
}
#[derive(Default)]
/// For the `#[rustc_on_unimplemented]` attribute
pub struct OnUnimplementedNote {
pub message: Option<String>,
pub label: Option<String>,
pub note: Option<String>,
pub parent_label: Option<String>,
/// Append a message for `~const Trait` errors. `None` means not requested and
/// should fallback to a generic message, `Some(None)` suggests using the default
/// appended message, `Some(Some(s))` suggests use the `s` message instead of the
/// default one..
pub append_const_msg: Option<Option<Symbol>>,
}
impl<'tcx> OnUnimplementedDirective {
fn parse(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
items: &[NestedMetaItem],
span: Span,
is_root: bool,
) -> Result<Self, ErrorGuaranteed> {
let mut errored = None;
let mut item_iter = items.iter();
let parse_value = |value_str| {
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some)
};
let condition = if is_root {
None
} else {
let cond = item_iter
.next()
.ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))?
.meta_item()
.ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
if let Some(value) = cfg.value && let Err(guar) = parse_value(value) {
errored = Some(guar);
}
true
});
Some(cond.clone())
};
let mut message = None;
let mut label = None;
let mut note = None;
let mut parent_label = None;
let mut subcommands = vec![];
let mut append_const_msg = None;
for item in item_iter {
if item.has_name(sym::message) && message.is_none() {
if let Some(message_) = item.value_str() {
message = parse_value(message_)?;
continue;
}
} else if item.has_name(sym::label) && label.is_none() {
if let Some(label_) = item.value_str() {
label = parse_value(label_)?;
continue;
}
} else if item.has_name(sym::note) && note.is_none() {
if let Some(note_) = item.value_str() {
note = parse_value(note_)?;
continue;
}
} else if item.has_name(sym::parent_label) && parent_label.is_none() {
if let Some(parent_label_) = item.value_str() {
parent_label = parse_value(parent_label_)?;
continue;
}
} else if item.has_name(sym::on)
&& is_root
&& message.is_none()
&& label.is_none()
&& note.is_none()
{
if let Some(items) = item.meta_item_list() {
match Self::parse(tcx, item_def_id, &items, item.span(), false) {
Ok(subcommand) => subcommands.push(subcommand),
Err(reported) => errored = Some(reported),
};
continue;
}
} else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() {
if let Some(msg) = item.value_str() {
append_const_msg = Some(Some(msg));
continue;
} else if item.is_word() {
append_const_msg = Some(None);
continue;
}
}
// nothing found
tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
}
if let Some(reported) = errored {
Err(reported)
} else {
Ok(OnUnimplementedDirective {
condition,
subcommands,
message,
label,
note,
parent_label,
append_const_msg,
})
}
}
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else {
return Ok(None);
};
let result = if let Some(items) = attr.meta_item_list() {
Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some)
} else if let Some(value) = attr.value_str() {
Ok(Some(OnUnimplementedDirective {
condition: None,
message: None,
subcommands: vec![],
label: Some(OnUnimplementedFormatString::try_parse(
tcx,
item_def_id,
value,
attr.span,
)?),
note: None,
parent_label: None,
append_const_msg: None,
}))
} else {
let reported =
tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
return Err(reported);
};
debug!("of_item({:?}) = {:?}", item_def_id, result);
result
}
pub fn evaluate(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &[(Symbol, Option<String>)],
) -> OnUnimplementedNote {
let mut message = None;
let mut label = None;
let mut note = None;
let mut parent_label = None;
let mut append_const_msg = None;
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
let options_map: FxHashMap<Symbol, String> =
options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect();
for command in self.subcommands.iter().chain(Some(self)).rev() {
if let Some(ref condition) = command.condition && !attr::eval_condition(
condition,
&tcx.sess.parse_sess,
Some(tcx.features()),
&mut |cfg| {
let value = cfg.value.map(|v| {
OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
});
options.contains(&(cfg.name, value))
},
) {
debug!("evaluate: skipping {:?} due to condition", command);
continue;
}
debug!("evaluate: {:?} succeeded", command);
if let Some(ref message_) = command.message {
message = Some(message_.clone());
}
if let Some(ref label_) = command.label {
label = Some(label_.clone());
}
if let Some(ref note_) = command.note {
note = Some(note_.clone());
}
if let Some(ref parent_label_) = command.parent_label {
parent_label = Some(parent_label_.clone());
}
append_const_msg = command.append_const_msg;
}
OnUnimplementedNote {
label: label.map(|l| l.format(tcx, trait_ref, &options_map)),
message: message.map(|m| m.format(tcx, trait_ref, &options_map)),
note: note.map(|n| n.format(tcx, trait_ref, &options_map)),
parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)),
append_const_msg,
}
}
}
impl<'tcx> OnUnimplementedFormatString {
fn try_parse(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
from: Symbol,
err_sp: Span,
) -> Result<Self, ErrorGuaranteed> {
let result = OnUnimplementedFormatString(from);
result.verify(tcx, item_def_id, err_sp)?;
Ok(result)
}
fn verify(
&self,
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
span: Span,
) -> Result<(), ErrorGuaranteed> {
let trait_def_id = if tcx.is_trait(item_def_id) {
item_def_id
} else {
tcx.trait_id_of_impl(item_def_id)
.expect("expected `on_unimplemented` to correspond to a trait")
};
let trait_name = tcx.item_name(trait_def_id);
let generics = tcx.generics_of(item_def_id);
let s = self.0.as_str();
let parser = Parser::new(s, None, None, false, ParseMode::Format);
let mut result = Ok(());
for token in parser {
match token {
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => {
match Symbol::intern(s) {
// `{Self}` is allowed
kw::SelfUpper => (),
// `{ThisTraitsName}` is allowed
s if s == trait_name => (),
// `{from_method}` is allowed
sym::from_method => (),
// `{from_desugaring}` is allowed
sym::from_desugaring => (),
// `{ItemContext}` is allowed
sym::ItemContext => (),
// `{integral}` and `{integer}` and `{float}` are allowed
sym::integral | sym::integer_ | sym::float => (),
// So is `{A}` if A is a type parameter
s => match generics.params.iter().find(|param| param.name == s) {
Some(_) => (),
None => {
let reported = struct_span_err!(
tcx.sess,
span,
E0230,
"there is no parameter `{}` on {}",
s,
if trait_def_id == item_def_id {
format!("trait `{}`", trait_name)
} else {
"impl".to_string()
}
)
.emit();
result = Err(reported);
}
},
}
}
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
let reported = struct_span_err!(
tcx.sess,
span,
E0231,
"only named substitution parameters are allowed"
)
.emit();
result = Err(reported);
}
},
}
}
result
}
pub fn format(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &FxHashMap<Symbol, String>,
) -> String {
let name = tcx.item_name(trait_ref.def_id);
let trait_str = tcx.def_path_str(trait_ref.def_id);
let generics = tcx.generics_of(trait_ref.def_id);
let generic_map = generics
.params
.iter()
.filter_map(|param| {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
trait_ref.substs[param.index as usize].to_string()
}
GenericParamDefKind::Lifetime => return None,
};
let name = param.name;
Some((name, value))
})
.collect::<FxHashMap<Symbol, String>>();
let empty_string = String::new();
let s = self.0.as_str();
let parser = Parser::new(s, None, None, false, ParseMode::Format);
let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
parser
.map(|p| match p {
Piece::String(s) => s,
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => {
let s = Symbol::intern(s);
match generic_map.get(&s) {
Some(val) => val,
None if s == name => &trait_str,
None => {
if let Some(val) = options.get(&s) {
val
} else if s == sym::from_desugaring || s == sym::from_method {
// don't break messages using these two arguments incorrectly
&empty_string
} else if s == sym::ItemContext {
&item_context
} else if s == sym::integral {
"{integral}"
} else if s == sym::integer_ {
"{integer}"
} else if s == sym::float {
"{float}"
} else {
bug!(
"broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}",
self.0,
trait_ref,
s
)
}
}
}
}
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
},
})
.collect()
}
}