diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 95ea42b584a3a..103c7ed8ef701 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -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() } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index b1ad22b899e30..5a8b3e30b9fc0 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -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, ); } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 133bbd52b9142..0ba5e61510125 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -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>( diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index c6b497e9b9fc4..0e8ac17fb71b1 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -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); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 416b555db5c5e..e2c967d0b0836 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -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; } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index b6c91d425dff4..6f74ef3ccad6d 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -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. diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index e806e94879d93..267077cdab4e6 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -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; } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index bd1a461b93522..664d3a3a1db84 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -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 diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 8d39fa81165ea..6a4a6a5b0a546 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -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, 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>]) { diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 37af6e79c3ef5..3c57e33f6f7fb 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -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. diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index e8bf299b0378e..25306ebf35679 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -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. diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 682dbab56bc15..89b5e5161a9c2 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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 diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 747ecb036b2a1..5d44092a5f68e 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -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>>, - ) -> 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 diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7563c543d3f19..35323137e2d5a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -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>), ) { 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); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 4066cca8a4bd4..a7a60a19bd37e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -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 diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 72388baa261ea..d5e4b6de581c3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -68,10 +68,6 @@ pub struct FnCtxt<'a, 'tcx> { /// any). pub(super) ret_coercion: Option>>, - /// 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>, @@ -115,6 +111,8 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) enclosing_breakables: RefCell>, pub(super) inh: &'a Inherited<'tcx>, + + pub(super) fallback_has_occurred: Cell, } 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 { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 624443d9594c9..052fdef2fc518 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -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. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 04ecd2757b427..50e2b0f89267d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -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; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 8598369e884b4..c3bcbcc993b7c 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -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![]), diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index e4a76fbd45130..3dbc9b9f3f938 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -91,6 +91,7 @@ pub mod nice_region_error; pub struct TypeErrCtxt<'a, 'tcx> { pub infcx: &'a InferCtxt<'tcx>, pub typeck_results: Option>>, + pub fallback_has_occurred: bool, } impl TypeErrCtxt<'_, '_> { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c2eecd9e87a38..d5be9983ea7c6 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -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 diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 1394993abade9..54bf4d1d6b738 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -819,19 +819,19 @@ fn find_skips_from_snippet( }; fn find_skips(snippet: &str, is_raw: bool) -> Vec { - 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 diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs index 58210c75a3da2..cada28652f98a 100644 --- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs @@ -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() } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 54281f9120547..c7dee4a18ac5d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -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, - fallback_has_occurred: bool, ) -> ErrorGuaranteed; fn report_overflow_error( @@ -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, - 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, - 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, - 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) => { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 5eef54c6330db..82f0440b3078b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -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, + pub subcommands: Vec, + pub message: Option, + pub label: Option, + pub note: Option, + pub parent_label: Option, + pub append_const_msg: Option>, +} + +/// For the `#[rustc_on_unimplemented]` attribute +#[derive(Default)] +pub struct OnUnimplementedNote { + pub message: Option, + pub label: Option, + pub note: Option, + pub parent_label: Option, + /// 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>, +} + +impl<'tcx> OnUnimplementedDirective { + fn parse( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + items: &[NestedMetaItem], + span: Span, + is_root: bool, + ) -> Result { + 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, 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)], + ) -> 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 = + 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 { + 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, + ) -> 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::>(); + 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() + } +} diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index be603c609cb36..b6ded4ce5a396 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -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); } }; } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a33534f5747fa..10e48610e3abb 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -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); } }; diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs deleted file mode 100644 index fb062ea71c4ce..0000000000000 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ /dev/null @@ -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, - pub subcommands: Vec, - pub message: Option, - pub label: Option, - pub note: Option, - pub parent_label: Option, - pub append_const_msg: Option>, -} - -#[derive(Default)] -/// For the `#[rustc_on_unimplemented]` attribute -pub struct OnUnimplementedNote { - pub message: Option, - pub label: Option, - pub note: Option, - pub parent_label: Option, - /// 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>, -} - -impl<'tcx> OnUnimplementedDirective { - fn parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - items: &[NestedMetaItem], - span: Span, - is_root: bool, - ) -> Result { - 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, 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)], - ) -> 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 = - 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 { - 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, - ) -> 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::>(); - 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() - } -} diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs index 38887f29af153..9193c79bee875 100644 --- a/library/core/benches/iter.rs +++ b/library/core/benches/iter.rs @@ -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::() }>() { + 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::() }>() .map(|ary| { let d = u64::from_ne_bytes(ary); diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs index 1e462e3fc3f8c..cf5a7cada93ce 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -5,6 +5,7 @@ #![feature(test)] #![feature(trusted_random_access)] #![feature(iter_array_chunks)] +#![feature(iter_next_chunk)] extern crate test; diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index eae0e1c761866..2090756d7a3ec 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -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; N], - initialized: usize, - } - - impl 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::(); 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; N], + /// The number of items that have been initialized so far. + pub initialized: usize, +} + +impl 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 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] diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index d4fb886101fe7..5e4211058aa6f 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -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(self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + ::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(self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B; +} + +impl SpecFold for ArrayChunks +where + I: Iterator, +{ + #[inline] + default fn fold(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 SpecFold for ArrayChunks +where + I: Iterator + TrustedRandomAccessNoCoerce, +{ + #[inline] + fn fold(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 + } +} diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs index 4e9d89e1e580f..ef4a7e53bdd33 100644 --- a/library/core/tests/iter/adapters/array_chunks.rs +++ b/library/core/tests/iter/adapters/array_chunks.rs @@ -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] diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md index b1854b22a7cd6..9272b9ac9b2d4 100644 --- a/src/doc/rustc/src/linker-plugin-lto.md +++ b/src/doc/rustc/src/linker-plugin-lto.md @@ -131,24 +131,47 @@ able to get around this problem by setting `-Clinker=lld-link` in RUSTFLAGS ## Toolchain Compatibility - @@ -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. diff --git a/src/test/ui/fmt/unicode-escape-spans.rs b/src/test/ui/fmt/unicode-escape-spans.rs new file mode 100644 index 0000000000000..753d91ce58e66 --- /dev/null +++ b/src/test/ui/fmt/unicode-escape-spans.rs @@ -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 +} diff --git a/src/test/ui/fmt/unicode-escape-spans.stderr b/src/test/ui/fmt/unicode-escape-spans.stderr new file mode 100644 index 0000000000000..1d8473f01b822 --- /dev/null +++ b/src/test/ui/fmt/unicode-escape-spans.stderr @@ -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`.