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()
}
}

View File

@ -1,3 +1,4 @@
use core::borrow::Borrow;
use core::iter::*;
use core::mem;
use core::num::Wrapping;
@ -403,13 +404,31 @@ fn bench_trusted_random_access_adapters(b: &mut Bencher) {
/// Exercises the iter::Copied specialization for slice::Iter
#[bench]
fn bench_copied_array_chunks(b: &mut Bencher) {
fn bench_copied_chunks(b: &mut Bencher) {
let v = vec![1u8; 1024];
b.iter(|| {
let mut iter = black_box(&v).iter().copied();
let mut acc = Wrapping(0);
// This uses a while-let loop to side-step the TRA specialization in ArrayChunks
while let Ok(chunk) = iter.next_chunk::<{ mem::size_of::<u64>() }>() {
let d = u64::from_ne_bytes(chunk);
acc += Wrapping(d.rotate_left(7).wrapping_add(1));
}
acc
})
}
/// Exercises the TrustedRandomAccess specialization in ArrayChunks
#[bench]
fn bench_trusted_random_access_chunks(b: &mut Bencher) {
let v = vec![1u8; 1024];
b.iter(|| {
black_box(&v)
.iter()
.copied()
// this shows that we're not relying on the slice::Iter specialization in Copied
.map(|b| *b.borrow())
.array_chunks::<{ mem::size_of::<u64>() }>()
.map(|ary| {
let d = u64::from_ne_bytes(ary);

View File

@ -5,6 +5,7 @@
#![feature(test)]
#![feature(trusted_random_access)]
#![feature(iter_array_chunks)]
#![feature(iter_next_chunk)]
extern crate test;

View File

@ -865,24 +865,6 @@ where
return Ok(Try::from_output(unsafe { mem::zeroed() }));
}
struct Guard<'a, T, const N: usize> {
array_mut: &'a mut [MaybeUninit<T>; N],
initialized: usize,
}
impl<T, const N: usize> Drop for Guard<'_, T, N> {
fn drop(&mut self) {
debug_assert!(self.initialized <= N);
// SAFETY: this slice will contain only initialized objects.
unsafe {
crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
&mut self.array_mut.get_unchecked_mut(..self.initialized),
));
}
}
}
let mut array = MaybeUninit::uninit_array::<N>();
let mut guard = Guard { array_mut: &mut array, initialized: 0 };
@ -896,13 +878,11 @@ where
ControlFlow::Continue(elem) => elem,
};
// SAFETY: `guard.initialized` starts at 0, is increased by one in the
// loop and the loop is aborted once it reaches N (which is
// `array.len()`).
// SAFETY: `guard.initialized` starts at 0, which means push can be called
// at most N times, which this loop does.
unsafe {
guard.array_mut.get_unchecked_mut(guard.initialized).write(item);
guard.push_unchecked(item);
}
guard.initialized += 1;
}
None => {
let alive = 0..guard.initialized;
@ -920,6 +900,55 @@ where
Ok(Try::from_output(output))
}
/// Panic guard for incremental initialization of arrays.
///
/// Disarm the guard with `mem::forget` once the array has been initialized.
///
/// # Safety
///
/// All write accesses to this structure are unsafe and must maintain a correct
/// count of `initialized` elements.
///
/// To minimize indirection fields are still pub but callers should at least use
/// `push_unchecked` to signal that something unsafe is going on.
pub(crate) struct Guard<'a, T, const N: usize> {
/// The array to be initialized.
pub array_mut: &'a mut [MaybeUninit<T>; N],
/// The number of items that have been initialized so far.
pub initialized: usize,
}
impl<T, const N: usize> Guard<'_, T, N> {
/// Adds an item to the array and updates the initialized item counter.
///
/// # Safety
///
/// No more than N elements must be initialized.
#[inline]
pub unsafe fn push_unchecked(&mut self, item: T) {
// SAFETY: If `initialized` was correct before and the caller does not
// invoke this method more than N times then writes will be in-bounds
// and slots will not be initialized more than once.
unsafe {
self.array_mut.get_unchecked_mut(self.initialized).write(item);
self.initialized = self.initialized.unchecked_add(1);
}
}
}
impl<T, const N: usize> Drop for Guard<'_, T, N> {
fn drop(&mut self) {
debug_assert!(self.initialized <= N);
// SAFETY: this slice will contain only initialized objects.
unsafe {
crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
&mut self.array_mut.get_unchecked_mut(..self.initialized),
));
}
}
}
/// Returns the next chunk of `N` items from the iterator or errors with an
/// iterator over the remainder. Used for `Iterator::next_chunk`.
#[inline]

View File

@ -1,6 +1,8 @@
use crate::array;
use crate::iter::{ByRefSized, FusedIterator, Iterator};
use crate::ops::{ControlFlow, Try};
use crate::const_closure::ConstFnMutClosure;
use crate::iter::{ByRefSized, FusedIterator, Iterator, TrustedRandomAccessNoCoerce};
use crate::mem::{self, MaybeUninit};
use crate::ops::{ControlFlow, NeverShortCircuit, Try};
/// An iterator over `N` elements of the iterator at a time.
///
@ -82,7 +84,13 @@ where
}
}
impl_fold_via_try_fold! { fold -> try_fold }
fn fold<B, F>(self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
<Self as SpecFold>::fold(self, init, f)
}
}
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
@ -168,3 +176,64 @@ where
self.iter.len() < N
}
}
trait SpecFold: Iterator {
fn fold<B, F>(self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B;
}
impl<I, const N: usize> SpecFold for ArrayChunks<I, N>
where
I: Iterator,
{
#[inline]
default fn fold<B, F>(mut self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
let fold = ConstFnMutClosure::new(&mut f, NeverShortCircuit::wrap_mut_2_imp);
self.try_fold(init, fold).0
}
}
impl<I, const N: usize> SpecFold for ArrayChunks<I, N>
where
I: Iterator + TrustedRandomAccessNoCoerce,
{
#[inline]
fn fold<B, F>(mut self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
let mut accum = init;
let inner_len = self.iter.size();
let mut i = 0;
// Use a while loop because (0..len).step_by(N) doesn't optimize well.
while inner_len - i >= N {
let mut chunk = MaybeUninit::uninit_array();
let mut guard = array::Guard { array_mut: &mut chunk, initialized: 0 };
while guard.initialized < N {
// SAFETY: The method consumes the iterator and the loop condition ensures that
// all accesses are in bounds and only happen once.
unsafe {
let idx = i + guard.initialized;
guard.push_unchecked(self.iter.__iterator_get_unchecked(idx));
}
}
mem::forget(guard);
// SAFETY: The loop above initialized all elements
let chunk = unsafe { MaybeUninit::array_assume_init(chunk) };
accum = f(accum, chunk);
i += N;
}
// unlike try_fold this method does not need to take care of the remainder
// since `self` will be dropped
accum
}
}

View File

@ -139,7 +139,8 @@ fn test_iterator_array_chunks_fold() {
let result =
(0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().fold(0, |acc, _item| acc + 1);
assert_eq!(result, 3);
assert_eq!(count.get(), 10);
// fold impls may or may not process the remainder
assert!(count.get() <= 10 && count.get() >= 9);
}
#[test]

View File

@ -131,24 +131,47 @@ able to get around this problem by setting `-Clinker=lld-link` in RUSTFLAGS
## Toolchain Compatibility
<!-- NOTE: to update the below table, you can use this shell script:
<!-- NOTE: to update the below table, you can use this Python script:
```sh
rustup toolchain install --profile minimal nightly
MINOR_VERSION=$(rustc +nightly --version | cut -d . -f 2)
LOWER_BOUND=61
```python
from collections import defaultdict
import subprocess
llvm_version() {
toolchain="$1"
printf "Rust $toolchain | Clang "
rustc +"$toolchain" -Vv | grep LLVM | cut -d ':' -f 2 | tr -d ' '
}
def minor_version(version):
return int(version.split('.')[1])
for version in `seq $LOWER_BOUND $((MINOR_VERSION - 2))`; do
toolchain=1.$version.0
rustup toolchain install --no-self-update --profile minimal $toolchain >/dev/null 2>&1
llvm_version $toolchain
done
INSTALL_TOOLCHAIN = ["rustup", "toolchain", "install", "--profile", "minimal"]
subprocess.run(INSTALL_TOOLCHAIN + ["nightly"])
LOWER_BOUND = 65
NIGHTLY_VERSION = minor_version(subprocess.run(
["rustc", "+nightly", "--version"],
capture_output=True,
text=True).stdout)
def llvm_version(toolchain):
version_text = subprocess.run(
["rustc", "+{}".format(toolchain), "-Vv"],
capture_output=True,
text=True).stdout
return int(version_text.split("LLVM")[1].split(':')[1].split('.')[0])
version_map = defaultdict(lambda: [])
for version in range(LOWER_BOUND, NIGHTLY_VERSION - 1):
toolchain = "1.{}.0".format(version)
subprocess.run(
INSTALL_TOOLCHAIN + ["--no-self-update", toolchain],
capture_output=True)
version_map[llvm_version(toolchain)].append(version)
print("| Rust Version | Clang Version |")
print("|--------------|---------------|")
for clang, rust in sorted(version_map.items()):
if len(rust) > 1:
rust_range = "1.{} - 1.{}".format(rust[0], rust[-1])
else:
rust_range = "1.{} ".format(rust[0])
print("| {} | {} |".format(rust_range, clang))
```
-->
@ -166,32 +189,13 @@ The following table shows known good combinations of toolchain versions.
| Rust Version | Clang Version |
|--------------|---------------|
| Rust 1.34 | Clang 8 |
| Rust 1.35 | Clang 8 |
| Rust 1.36 | Clang 8 |
| Rust 1.37 | Clang 8 |
| Rust 1.38 | Clang 9 |
| Rust 1.39 | Clang 9 |
| Rust 1.40 | Clang 9 |
| Rust 1.41 | Clang 9 |
| Rust 1.42 | Clang 9 |
| Rust 1.43 | Clang 9 |
| Rust 1.44 | Clang 9 |
| Rust 1.45 | Clang 10 |
| Rust 1.46 | Clang 10 |
| Rust 1.47 | Clang 11 |
| Rust 1.48 | Clang 11 |
| Rust 1.49 | Clang 11 |
| Rust 1.50 | Clang 11 |
| Rust 1.51 | Clang 11 |
| Rust 1.52 | Clang 12 |
| Rust 1.53 | Clang 12 |
| Rust 1.54 | Clang 12 |
| Rust 1.55 | Clang 12 |
| Rust 1.56 | Clang 13 |
| Rust 1.57 | Clang 13 |
| Rust 1.58 | Clang 13 |
| Rust 1.59 | Clang 13 |
| Rust 1.60 | Clang 14 |
| 1.34 - 1.37 | 8 |
| 1.38 - 1.44 | 9 |
| 1.45 - 1.46 | 10 |
| 1.47 - 1.51 | 11 |
| 1.52 - 1.55 | 12 |
| 1.56 - 1.59 | 13 |
| 1.60 - 1.64 | 14 |
| 1.65 | 15 |
Note that the compatibility policy for this feature might change in the future.

View File

@ -0,0 +1,19 @@
fn main() {
// 1 byte in UTF-8
format!("\u{000041}{a}"); //~ ERROR cannot find value
format!("\u{0041}{a}"); //~ ERROR cannot find value
format!("\u{41}{a}"); //~ ERROR cannot find value
format!("\u{0}{a}"); //~ ERROR cannot find value
// 2 bytes
format!("\u{0df}{a}"); //~ ERROR cannot find value
format!("\u{df}{a}"); //~ ERROR cannot find value
// 3 bytes
format!("\u{00211d}{a}"); //~ ERROR cannot find value
format!("\u{211d}{a}"); //~ ERROR cannot find value
// 4 bytes
format!("\u{1f4a3}{a}"); //~ ERROR cannot find value
format!("\u{10ffff}{a}"); //~ ERROR cannot find value
}

View File

@ -0,0 +1,63 @@
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:3:25
|
LL | format!("\u{000041}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:4:23
|
LL | format!("\u{0041}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:5:21
|
LL | format!("\u{41}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:6:20
|
LL | format!("\u{0}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:9:22
|
LL | format!("\u{0df}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:10:21
|
LL | format!("\u{df}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:13:25
|
LL | format!("\u{00211d}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:14:23
|
LL | format!("\u{211d}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:17:24
|
LL | format!("\u{1f4a3}{a}");
| ^ not found in this scope
error[E0425]: cannot find value `a` in this scope
--> $DIR/unicode-escape-spans.rs:18:25
|
LL | format!("\u{10ffff}{a}");
| ^ not found in this scope
error: aborting due to 10 previous errors
For more information about this error, try `rustc --explain E0425`.