mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-16 22:16:53 +00:00
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:
commit
57d3c58ed6
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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>(
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>]) {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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![]),
|
||||
|
@ -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<'_, '_> {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#![feature(test)]
|
||||
#![feature(trusted_random_access)]
|
||||
#![feature(iter_array_chunks)]
|
||||
#![feature(iter_next_chunk)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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.
|
||||
|
19
src/test/ui/fmt/unicode-escape-spans.rs
Normal file
19
src/test/ui/fmt/unicode-escape-spans.rs
Normal 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
|
||||
}
|
63
src/test/ui/fmt/unicode-escape-spans.stderr
Normal file
63
src/test/ui/fmt/unicode-escape-spans.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user