diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 8a930ca59a334..78e9e104bdae8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -5,12 +5,13 @@ use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_index::IndexSlice; use rustc_infer::infer::NllRegionVariableOrigin; +use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir::{ Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::{self, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; use rustc_trait_selection::traits::error_reporting::FindExprBySpan; @@ -290,12 +291,69 @@ impl<'tcx> BorrowExplanation<'tcx> { } } + if let ConstraintCategory::Cast { unsize_to: Some(unsize_ty) } = category { + self.add_object_lifetime_default_note(tcx, err, unsize_ty); + } self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); } _ => {} } } + fn add_object_lifetime_default_note( + &self, + tcx: TyCtxt<'tcx>, + err: &mut Diagnostic, + unsize_ty: Ty<'tcx>, + ) { + if let ty::Adt(def, args) = unsize_ty.kind() { + // We try to elaborate the object lifetime defaults and present those to the user. This should + // make it clear where the region constraint is coming from. + let generics = tcx.generics_of(def.did()); + + let mut has_dyn = false; + let mut failed = false; + + let elaborated_args = std::iter::zip(*args, &generics.params).map(|(arg, param)| { + if let Some(ty::Dynamic(obj, _, ty::DynKind::Dyn)) = arg.as_type().map(Ty::kind) { + let default = tcx.object_lifetime_default(param.def_id); + + let re_static = tcx.lifetimes.re_static; + + let implied_region = match default { + // This is not entirely precise. + ObjectLifetimeDefault::Empty => re_static, + ObjectLifetimeDefault::Ambiguous => { + failed = true; + re_static + } + ObjectLifetimeDefault::Param(param_def_id) => { + let index = generics.param_def_id_to_index[¶m_def_id] as usize; + args.get(index).and_then(|arg| arg.as_region()).unwrap_or_else(|| { + failed = true; + re_static + }) + } + ObjectLifetimeDefault::Static => re_static, + }; + + has_dyn = true; + + Ty::new_dynamic(tcx, obj, implied_region, ty::DynKind::Dyn).into() + } else { + arg + } + }); + let elaborated_ty = Ty::new_adt(tcx, *def, tcx.mk_args_from_iter(elaborated_args)); + + if has_dyn && !failed { + err.note(format!( + "due to object lifetime defaults, `{unsize_ty}` actually means `{elaborated_ty}`" + )); + } + } + } + fn add_lifetime_bound_suggestion_to_diagnostic( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 76c5a06c80db2..da1d1c70f0fad 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -53,7 +53,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::Yield => "yielding this value ", ConstraintCategory::UseAsConst => "using this value as a constant ", ConstraintCategory::UseAsStatic => "using this value as a static ", - ConstraintCategory::Cast => "cast ", + ConstraintCategory::Cast { .. } => "cast ", ConstraintCategory::CallArgument(_) => "argument ", ConstraintCategory::TypeAnnotation => "type annotation ", ConstraintCategory::ClosureBounds => "closure body ", diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5d9fd2647f2cd..bb9ce5daba266 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1934,7 +1934,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -1959,7 +1959,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -1988,7 +1988,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -2013,7 +2013,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_trait_ref( trait_ref, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { + unsize_to: Some(tcx.fold_regions(ty, |r, _| { + if let ty::ReVar(_) = r.kind() { + tcx.lifetimes.re_erased + } else { + r + } + })), + }, ); } @@ -2033,7 +2041,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .iter() .map(|predicate| predicate.with_self_ty(tcx, self_ty)), location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ); let outlives_predicate = tcx.mk_predicate(Binder::dummy( @@ -2044,7 +2052,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_predicate( outlives_predicate, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ); } @@ -2065,7 +2073,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty_from, *ty_to, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -2131,7 +2139,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty_elem, *ty_to, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d9b27fc13182c..4b7de36e7c7bc 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -626,15 +626,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - let coerce_to = match opt_coerce_to { - Some(c) => c, - None => { - // If the loop context is not a `loop { }`, then break with - // a value is illegal, and `opt_coerce_to` will be `None`. - // Return error in that case (#114529). - return Ty::new_misc_error(tcx); - } - }; + // If the loop context is not a `loop { }`, then break with + // a value is illegal, and `opt_coerce_to` will be `None`. + // Set expectation to error in that case and set tainted + // by error (#114529) + let coerce_to = opt_coerce_to.unwrap_or_else(|| { + let guar = tcx.sess.delay_span_bug( + expr.span, + "illegal break with value found but no error reported", + ); + self.set_tainted_by_errors(guar); + Ty::new_error(tcx, guar) + }); // Recurse without `enclosing_breakables` borrowed. e_ty = self.check_expr_with_hint(e, coerce_to); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 2797d07976113..c32c3aa6d2968 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -65,8 +65,15 @@ impl<'tcx> InferCtxt<'tcx> { /// Forks the inference context, creating a new inference context with the same inference /// variables in the same state. This can be used to "branch off" many tests from the same - /// common state. Used in coherence. + /// common state. pub fn fork(&self) -> Self { + self.fork_with_intercrate(self.intercrate) + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state, except possibly changing the intercrate mode. This can be + /// used to "branch off" many tests from the same common state. Used in negative coherence. + pub fn fork_with_intercrate(&self, intercrate: bool) -> Self { Self { tcx: self.tcx, defining_use_anchor: self.defining_use_anchor, @@ -81,7 +88,7 @@ impl<'tcx> InferCtxt<'tcx> { tainted_by_errors: self.tainted_by_errors.clone(), err_count_on_creation: self.err_count_on_creation, universe: self.universe.clone(), - intercrate: self.intercrate, + intercrate, next_trait_solver: self.next_trait_solver, } } diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index d2d99bc0da87d..d3e2d5c764652 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -50,7 +50,7 @@ declare_lint! { Warn, "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, reference: "issue #89460 ", }; } @@ -59,12 +59,13 @@ declare_lint_pass!(DerefIntoDynSupertrait => [DEREF_INTO_DYN_SUPERTRAIT]); impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + let tcx = cx.tcx; // `Deref` is being implemented for `t` if let hir::ItemKind::Impl(impl_) = item.kind && let Some(trait_) = &impl_.of_trait - && let t = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let t = tcx.type_of(item.owner_id).instantiate_identity() && let opt_did @ Some(did) = trait_.trait_def_id() - && opt_did == cx.tcx.lang_items().deref_trait() + && opt_did == tcx.lang_items().deref_trait() // `t` is `dyn t_principal` && let ty::Dynamic(data, _, ty::Dyn) = t.kind() && let Some(t_principal) = data.principal() @@ -73,9 +74,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { && let ty::Dynamic(data, _, ty::Dyn) = target.kind() && let Some(target_principal) = data.principal() // `target_principal` is a supertrait of `t_principal` - && supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self)) - .any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal) + && supertraits(tcx, t_principal.with_self_ty(tcx, tcx.types.trait_object_dummy_self)) + .any(|sup| { + tcx.erase_regions( + sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(tcx, x)), + ) == tcx.erase_regions(target_principal) + }) { + let t = tcx.erase_regions(t); let label = impl_ .items .iter() @@ -83,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { .map(|label| SupertraitAsDerefTargetLabel { label }); cx.emit_spanned_lint( DEREF_INTO_DYN_SUPERTRAIT, - cx.tcx.def_span(item.owner_id.def_id), + tcx.def_span(item.owner_id.def_id), SupertraitAsDerefTarget { t, target_principal, label }, ); } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index d609965f36fe6..b5dd3010d3a8b 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -341,7 +341,11 @@ pub enum ConstraintCategory<'tcx> { UseAsConst, UseAsStatic, TypeAnnotation, - Cast, + Cast { + /// Whether this is an unsizing cast and if yes, this contains the target type. + /// Region variables are erased to ReErased. + unsize_to: Option>, + }, /// A constraint that came from checking the body of a closure. /// diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index aeb2f879152d1..cbba990867e03 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -109,10 +109,12 @@ pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsure, TyCtxtEnsureWithValue // Queries marked with `fatal_cycle` do not need the latter implementation, // as they will raise an fatal error on query cycles instead. rustc_queries! { + /// This exists purely for testing the interactions between delay_span_bug and incremental. query trigger_delay_span_bug(key: DefId) -> () { - desc { "triggering a delay span bug" } + desc { "triggering a delay span bug for testing incremental" } } + /// Collects the list of all tools registered using `#![register_tool]`. query registered_tools(_: ()) -> &'tcx ty::RegisteredTools { arena_cache desc { "compute registered tools for crate" } @@ -286,6 +288,7 @@ rustc_queries! { } } + /// The root query triggering all analysis passes like typeck or borrowck. query analysis(key: ()) -> Result<(), ErrorGuaranteed> { eval_always desc { "running analysis passes on this crate" } @@ -1778,10 +1781,17 @@ rustc_queries! { desc { "calculating the missing lang items in a crate" } separate_provide_extern } + + /// The visible parent map is a map from every item to a visible parent. + /// It prefers the shortest visible path to an item. + /// Used for diagnostics, for example path trimming. + /// The parents are modules, enums or traits. query visible_parent_map(_: ()) -> &'tcx DefIdMap { arena_cache desc { "calculating the visible parent map" } } + /// Collects the "trimmed", shortest accessible paths to all items for diagnostics. + /// See the [provider docs](`rustc_middle::ty::print::trimmed_def_paths`) for more info. query trimmed_def_paths(_: ()) -> &'tcx FxHashMap { arena_cache desc { "calculating trimmed def paths" } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 4af841fcf9a5f..16e7b16a0bf8c 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -3,7 +3,6 @@ use crate::mir; use crate::ty::abstract_const::CastKind; use crate::ty::GenericArgsRef; use crate::ty::{self, visit::TypeVisitableExt as _, List, Ty, TyCtxt}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; @@ -77,28 +76,3 @@ static_assert_size!(Expr<'_>, 24); #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(super::ConstKind<'_>, 32); - -/// An inference variable for a const, for use in const generics. -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] -pub enum InferConst { - /// Infer the value of the const. - Var(ty::ConstVid), - /// Infer the value of the effect. - /// - /// For why this is separate from the `Var` variant above, see the - /// documentation on `EffectVid`. - EffectVar(ty::EffectVid), - /// A fresh const variable. See `infer::freshen` for more details. - Fresh(u32), -} - -impl HashStable for InferConst { - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - match self { - InferConst::Var(_) | InferConst::EffectVar(_) => { - panic!("const variables should not be hashed: {self:?}") - } - InferConst::Fresh(i) => i.hash_stable(hcx, hasher), - } - } -} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ee23c9c48978e..62b0536dabee6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -26,9 +26,9 @@ use crate::traits::solve::{ }; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind, - ImplPolarity, InferTy, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, - Predicate, PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, - TyVid, TypeAndMut, Visibility, + ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, + PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, + TypeAndMut, Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; @@ -95,7 +95,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ParamTy = ParamTy; type BoundTy = ty::BoundTy; type PlaceholderTy = ty::PlaceholderType; - type InferTy = InferTy; type ErrorGuaranteed = ErrorGuaranteed; type BoundExistentialPredicates = &'tcx List>; @@ -103,7 +102,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AllocId = crate::mir::interpret::AllocId; type Const = ty::Const<'tcx>; - type InferConst = ty::InferConst; type AliasConst = ty::UnevaluatedConst<'tcx>; type PlaceholderConst = ty::PlaceholderConst; type ParamConst = ty::ParamConst; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 559bf9fb825de..2436daf62cf2b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -84,9 +84,7 @@ pub use self::closure::{ CapturedPlace, ClosureKind, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList, RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL, }; -pub use self::consts::{ - Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree, -}; +pub use self::consts::{Const, ConstData, ConstInt, Expr, ScalarInt, UnevaluatedConst, ValTree}; pub use self::context::{ tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, }; @@ -98,7 +96,7 @@ pub use self::sty::BoundRegionKind::*; pub use self::sty::{ AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind, CanonicalPolyFnSig, ClauseKind, ClosureArgs, ClosureArgsParts, ConstKind, - ConstVid, CoroutineArgs, CoroutineArgsParts, EarlyParamRegion, EffectVid, ExistentialPredicate, + CoroutineArgs, CoroutineArgsParts, EarlyParamRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, GenSig, InlineConstArgs, InlineConstArgsParts, LateParamRegion, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index ad070dcc9e385..d478677c36793 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3018,7 +3018,8 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N /// The implementation uses similar import discovery logic to that of 'use' suggestions. /// /// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths!`]. -fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap { +// this is pub to be able to intra-doc-link it +pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap { let mut map: FxHashMap = FxHashMap::default(); if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index e223ffd7c5d54..971acda33e233 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -202,34 +202,6 @@ impl<'tcx> DebugWithInfcx> for AliasTy<'tcx> { } } -impl fmt::Debug for ty::InferConst { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - InferConst::Var(var) => write!(f, "{var:?}"), - InferConst::EffectVar(var) => write!(f, "{var:?}"), - InferConst::Fresh(var) => write!(f, "Fresh({var:?})"), - } - } -} -impl<'tcx> DebugWithInfcx> for ty::InferConst { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - use ty::InferConst::*; - match this.infcx.universe_of_ct(*this.data) { - None => write!(f, "{:?}", this.data), - Some(universe) => match *this.data { - Var(vid) => write!(f, "?{}_{}c", vid.index(), universe.index()), - EffectVar(vid) => write!(f, "?{}_{}e", vid.index(), universe.index()), - Fresh(_) => { - unreachable!() - } - }, - } - } -} - impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { WithInfcx::with_no_infcx(self).fmt(f) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index b22ff5c57c305..ea0e179c00de0 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1608,24 +1608,6 @@ impl fmt::Debug for EarlyParamRegion { } } -rustc_index::newtype_index! { - /// A **`const`** **v**ariable **ID**. - #[debug_format = "?{}c"] - pub struct ConstVid {} -} - -rustc_index::newtype_index! { - /// An **effect** **v**ariable **ID**. - /// - /// Handling effect infer variables happens separately from const infer variables - /// because we do not want to reuse any of the const infer machinery. If we try to - /// relate an effect variable with a normal one, we would ICE, which can catch bugs - /// where we are not correctly using the effect var for an effect param. Fallback - /// is also implemented on top of having separate effect and normal const variables. - #[debug_format = "?{}e"] - pub struct EffectVid {} -} - rustc_index::newtype_index! { /// A **region** (lifetime) **v**ariable **ID**. #[derive(HashStable)] diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 13d7ebe1db065..b6d2228d4b8fb 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -110,7 +110,7 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, impl_def_id: DefId, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// If the predicate contained an error, we want to avoid emitting unnecessary trait /// errors but still want to emit errors for other trait goals. We have some special @@ -263,7 +263,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { - return ambig; + return vec![ambig]; } let mut candidates = self.assemble_candidates_via_self_ty(goal, 0); @@ -288,15 +288,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn assemble_self_ty_infer_ambiguity_response>( &mut self, goal: Goal<'tcx, G>, - ) -> Option>> { - goal.predicate.self_ty().is_ty_var().then(|| { - vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result: self - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - .unwrap(), - }] - }) + ) -> Option> { + if goal.predicate.self_ty().is_ty_var() { + debug!("adding self_ty_infer_ambiguity_response"); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let result = self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(); + let mut dummy_probe = self.inspect.new_probe(); + dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); + self.inspect.finish_probe(dummy_probe); + Some(Candidate { source, result }) + } else { + None + } } /// Assemble candidates which apply to the self type. This only looks at candidate which @@ -310,7 +315,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { - return ambig; + return vec![ambig]; } let mut candidates = Vec::new(); @@ -395,8 +400,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { for &impl_def_id in impls_for_type { match G::consider_impl_candidate(self, goal, impl_def_id) { - Ok(result) => candidates - .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } @@ -514,8 +518,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); for &impl_def_id in trait_impls.blanket_impls() { match G::consider_impl_candidate(self, goal, impl_def_id) { - Ok(result) => candidates - .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 6087b91679095..91fd48807a4d8 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -1,5 +1,10 @@ +use crate::solve::assembly::Candidate; + use super::EvalCtxt; -use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult}; +use rustc_middle::traits::{ + query::NoSolution, + solve::{inspect, CandidateSource, QueryResult}, +}; use std::marker::PhantomData; pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { @@ -36,6 +41,23 @@ where } } +pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> { + cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>, + source: CandidateSource, +} + +impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F> +where + F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, +{ + pub(in crate::solve) fn enter( + self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> Result, NoSolution> { + self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result }) + } +} + impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// `probe_kind` is only called when proof tree building is enabled so it can be /// as expensive as necessary to output the desired information. @@ -69,20 +91,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { pub(in crate::solve) fn probe_trait_candidate( &mut self, source: CandidateSource, - ) -> ProbeCtxt< - '_, - 'a, - 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, - QueryResult<'tcx>, - > { - ProbeCtxt { - ecx: self, - probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { - source, - result: *result, + ) -> TraitProbeCtxt<'_, 'a, 'tcx, impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>> + { + TraitProbeCtxt { + cx: ProbeCtxt { + ecx: self, + probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { + source, + result: *result, + }, + _result: PhantomData, }, - _result: PhantomData, + source, } } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs index 7fb550aa3e063..6c29ce492df0e 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs @@ -1,6 +1,6 @@ use crate::traits::{check_args_compatible, specialization_graph}; -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, Candidate}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -154,7 +154,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 0f30b49314b03..a0c065dfa03ba 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,12 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::inspect::ProbeKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::solve::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -38,7 +40,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -63,7 +65,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }, }; - ecx.probe_misc_candidate("impl").enter(|ecx| { + ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 362a734818c1e..e6e05d0adc627 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -25,7 +25,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, Goal}; +use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -397,6 +397,8 @@ fn impl_intersection_has_negative_obligation( ) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); + // N.B. We need to unify impl headers *with* intercrate mode, even if proving negative predicates + // do not need intercrate mode enabled. let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build(); let root_universe = infcx.universe(); assert_eq!(root_universe, ty::UniverseIndex::ROOT); @@ -415,13 +417,6 @@ fn impl_intersection_has_negative_obligation( return false; }; - plug_infer_with_placeholders( - infcx, - root_universe, - (impl1_header.impl_args, impl2_header.impl_args), - ); - let param_env = infcx.resolve_vars_if_possible(param_env); - // FIXME(with_negative_coherence): the infcx has constraints from equating // the impl headers. We should use these constraints as assumptions, not as // requirements, when proving the negated where clauses below. @@ -429,6 +424,13 @@ fn impl_intersection_has_negative_obligation( drop(infcx.take_registered_region_obligations()); drop(infcx.take_and_reset_region_constraints()); + plug_infer_with_placeholders( + infcx, + root_universe, + (impl1_header.impl_args, impl2_header.impl_args), + ); + let param_env = infcx.resolve_vars_if_possible(param_env); + util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env)) } @@ -554,7 +556,11 @@ fn try_prove_negated_where_clause<'tcx>( return false; }; - let ref infcx = root_infcx.fork(); + // N.B. We don't need to use intercrate mode here because we're trying to prove + // the *existence* of a negative goal, not the non-existence of a positive goal. + // Without this, we over-eagerly register coherence ambiguity candidates when + // impl candidates do exist. + let ref infcx = root_infcx.fork_with_intercrate(false); let ocx = ObligationCtxt::new(infcx); ocx.register_obligation(Obligation::new( @@ -1019,6 +1025,28 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { _ => return ControlFlow::Continue(()), }; + // Add ambiguity causes for reservation impls. + for cand in goal.candidates() { + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(def_id), + result: Ok(_), + } = cand.kind() + { + if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { + let value = infcx + .tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(value) = value { + self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }); + } + } + } + } + + // Add ambiguity causes for unknowable goals. let mut ambiguity_cause = None; for cand in goal.candidates() { // FIXME: boiiii, using string comparisions here sure is scuffed. diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index a014a4f38caf5..1cdd51d68dc68 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -22,7 +22,7 @@ pub enum ConstKind { Param(I::ParamConst), /// Infer the value of the const. - Infer(I::InferConst), + Infer(InferConst), /// Bound const variable, used only when preparing a trait query. Bound(DebruijnIndex, I::BoundConst), @@ -65,7 +65,6 @@ const fn const_kind_discriminant(value: &ConstKind) -> usize { impl HashStable for ConstKind where I::ParamConst: HashStable, - I::InferConst: HashStable, I::BoundConst: HashStable, I::PlaceholderConst: HashStable, I::AliasConst: HashStable, @@ -136,3 +135,77 @@ impl DebugWithInfcx for ConstKind { } } } + +rustc_index::newtype_index! { + /// A **`const`** **v**ariable **ID**. + #[debug_format = "?{}c"] + #[gate_rustc_only] + pub struct ConstVid {} +} + +rustc_index::newtype_index! { + /// An **effect** **v**ariable **ID**. + /// + /// Handling effect infer variables happens separately from const infer variables + /// because we do not want to reuse any of the const infer machinery. If we try to + /// relate an effect variable with a normal one, we would ICE, which can catch bugs + /// where we are not correctly using the effect var for an effect param. Fallback + /// is also implemented on top of having separate effect and normal const variables. + #[debug_format = "?{}e"] + #[gate_rustc_only] + pub struct EffectVid {} +} + +/// An inference variable for a const, for use in const generics. +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] +pub enum InferConst { + /// Infer the value of the const. + Var(ConstVid), + /// Infer the value of the effect. + /// + /// For why this is separate from the `Var` variant above, see the + /// documentation on `EffectVid`. + EffectVar(EffectVid), + /// A fresh const variable. See `infer::freshen` for more details. + Fresh(u32), +} + +impl fmt::Debug for InferConst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + InferConst::Var(var) => write!(f, "{var:?}"), + InferConst::EffectVar(var) => write!(f, "{var:?}"), + InferConst::Fresh(var) => write!(f, "Fresh({var:?})"), + } + } +} +impl DebugWithInfcx for InferConst { + fn fmt>( + this: WithInfcx<'_, Infcx, &Self>, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match this.infcx.universe_of_ct(*this.data) { + None => write!(f, "{:?}", this.data), + Some(universe) => match *this.data { + InferConst::Var(vid) => write!(f, "?{}_{}c", vid.index(), universe.index()), + InferConst::EffectVar(vid) => write!(f, "?{}_{}e", vid.index(), universe.index()), + InferConst::Fresh(_) => { + unreachable!() + } + }, + } + } +} + +#[cfg(feature = "nightly")] +impl HashStable for InferConst { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + match self { + InferConst::Var(_) | InferConst::EffectVar(_) => { + panic!("const variables should not be hashed: {self:?}") + } + InferConst::Fresh(i) => i.hash_stable(hcx, hasher), + } + } +} diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs index 4ea3eb3e84f43..db29ec9da8c9e 100644 --- a/compiler/rustc_type_ir/src/debug.rs +++ b/compiler/rustc_type_ir/src/debug.rs @@ -1,4 +1,4 @@ -use crate::{Interner, UniverseIndex}; +use crate::{InferConst, InferTy, Interner, UniverseIndex}; use core::fmt; use std::marker::PhantomData; @@ -6,15 +6,14 @@ use std::marker::PhantomData; pub trait InferCtxtLike { type Interner: Interner; - fn universe_of_ty(&self, ty: ::InferTy) -> Option; + fn universe_of_ty(&self, ty: InferTy) -> Option; fn universe_of_lt( &self, lt: ::InferRegion, ) -> Option; - fn universe_of_ct(&self, ct: ::InferConst) - -> Option; + fn universe_of_ct(&self, ct: InferConst) -> Option; } pub struct NoInfcx(PhantomData); @@ -22,11 +21,11 @@ pub struct NoInfcx(PhantomData); impl InferCtxtLike for NoInfcx { type Interner = I; - fn universe_of_ty(&self, _ty: ::InferTy) -> Option { + fn universe_of_ty(&self, _ty: InferTy) -> Option { None } - fn universe_of_ct(&self, _ct: ::InferConst) -> Option { + fn universe_of_ct(&self, _ct: InferConst) -> Option { None } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index da504c54fddd9..170a791fb54d6 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -27,7 +27,6 @@ pub trait Interner: Sized { type ParamTy: Clone + Debug + Hash + Ord; type BoundTy: Clone + Debug + Hash + Ord; type PlaceholderTy: Clone + Debug + Hash + Ord; - type InferTy: Clone + DebugWithInfcx + Hash + Ord; // Things stored inside of tys type ErrorGuaranteed: Clone + Debug + Hash + Ord; @@ -37,7 +36,6 @@ pub trait Interner: Sized { // Kinds of consts type Const: Clone + DebugWithInfcx + Hash + Ord; - type InferConst: Clone + DebugWithInfcx + Hash + Ord; type AliasConst: Clone + DebugWithInfcx + Hash + Ord; type PlaceholderConst: Clone + Debug + Hash + Ord; type ParamConst: Clone + Debug + Hash + Ord; diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index f31c5069b155d..53446c41f31c5 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -281,7 +281,7 @@ pub enum TyKind { /// correctly deal with higher ranked types. Though unlike placeholders, /// that universe is stored in the `InferCtxt` instead of directly /// inside of the type. - Infer(I::InferTy), + Infer(InferTy), /// A placeholder for a type which could not be computed; this is /// propagated to avoid useless error messages. @@ -491,7 +491,6 @@ where I::BoundTy: HashStable, I::ParamTy: HashStable, I::PlaceholderTy: HashStable, - I::InferTy: HashStable, I::ErrorGuaranteed: HashStable, { #[inline] @@ -922,7 +921,7 @@ impl fmt::Debug for InferTy { } } -impl> DebugWithInfcx for InferTy { +impl DebugWithInfcx for InferTy { fn fmt>( this: WithInfcx<'_, Infcx, &Self>, f: &mut fmt::Formatter<'_>, diff --git a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr index 9f9d4bd8d6c05..b3bf2f924fc3e 100644 --- a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr +++ b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr @@ -77,6 +77,8 @@ LL | reg.register_univ(Box::new(CapturePass::new(®.sess_mut))); | | | immutable borrow occurs here | | cast requires that `reg.sess_mut` is borrowed for `'a` | mutable borrow occurs here + | + = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0502]: cannot borrow `*reg` as mutable because it is also borrowed as immutable --> $DIR/two-phase-surprise-no-conflict.rs:144:5 @@ -119,6 +121,8 @@ LL | reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut))); | | | first mutable borrow occurs here | | cast requires that `reg.sess_mut` is borrowed for `'a` | second mutable borrow occurs here + | + = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time --> $DIR/two-phase-surprise-no-conflict.rs:158:53 diff --git a/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs b/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs index 0e16d12a18114..531977d6dac24 100644 --- a/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs +++ b/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs @@ -1,5 +1,9 @@ // revisions: stock with_negative_coherence + //[with_negative_coherence] known-bug: unknown +// Ideally this would work, but we don't use `&'a T` to imply that `T: 'a` +// which is required for `&'a T: !MyPredicate` to hold. This is similar to the +// test `negative-coherence-placeholder-region-constraints-on-unification.explicit.stderr` #![feature(negative_impls)] #![cfg_attr(with_negative_coherence, feature(with_negative_coherence))] diff --git a/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr b/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr index 097cc4e0fe3e6..6d6e163b2066d 100644 --- a/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr +++ b/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `MyTrait<'_>` for type `&_` - --> $DIR/coherence-negative-outlives-lifetimes.rs:14:1 + --> $DIR/coherence-negative-outlives-lifetimes.rs:18:1 | LL | impl<'a, T: MyPredicate<'a>> MyTrait<'a> for T {} | ---------------------------------------------- first implementation here diff --git a/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr b/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr index 097cc4e0fe3e6..6d6e163b2066d 100644 --- a/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr +++ b/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `MyTrait<'_>` for type `&_` - --> $DIR/coherence-negative-outlives-lifetimes.rs:14:1 + --> $DIR/coherence-negative-outlives-lifetimes.rs:18:1 | LL | impl<'a, T: MyPredicate<'a>> MyTrait<'a> for T {} | ---------------------------------------------- first implementation here diff --git a/tests/ui/coherence/coherence-overlap-with-regions.rs b/tests/ui/coherence/coherence-overlap-with-regions.rs index 9945c8e6cfd7b..32f01f4180103 100644 --- a/tests/ui/coherence/coherence-overlap-with-regions.rs +++ b/tests/ui/coherence/coherence-overlap-with-regions.rs @@ -1,10 +1,4 @@ -// known-bug: unknown - -// This fails because we currently perform negative coherence in coherence mode. -// This means that when looking for a negative predicate, we also assemble a -// coherence-unknowable predicate. Since confirming the negative impl has region -// obligations, we don't prefer the impl over the unknowable predicate -// unconditionally and instead flounder. +// check-pass #![feature(negative_impls)] #![feature(rustc_attrs)] diff --git a/tests/ui/coherence/coherence-overlap-with-regions.stderr b/tests/ui/coherence/coherence-overlap-with-regions.stderr deleted file mode 100644 index fd25f0978bad2..0000000000000 --- a/tests/ui/coherence/coherence-overlap-with-regions.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0119]: conflicting implementations of trait `Bar` for type `&_` - --> $DIR/coherence-overlap-with-regions.rs:20:1 - | -LL | impl Bar for T {} - | ---------------------- first implementation here -LL | impl Bar for &T where T: 'static {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&_` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr b/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr index 4cf50b4f208e3..442934a894927 100644 --- a/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr +++ b/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Bar` for type `&_` - --> $DIR/negative-coherence-considering-regions.rs:22:1 + --> $DIR/negative-coherence-considering-regions.rs:16:1 | LL | impl Bar for T where T: Foo {} | ------------------------------ first implementation here diff --git a/tests/ui/coherence/negative-coherence-considering-regions.rs b/tests/ui/coherence/negative-coherence-considering-regions.rs index 597a597262846..a43ad19eca7c0 100644 --- a/tests/ui/coherence/negative-coherence-considering-regions.rs +++ b/tests/ui/coherence/negative-coherence-considering-regions.rs @@ -1,11 +1,5 @@ // revisions: any_lt static_lt -//[static_lt] known-bug: unknown - -// This fails because we currently perform negative coherence in coherence mode. -// This means that when looking for a negative predicate, we also assemble a -// coherence-unknowable predicate. Since confirming the negative impl has region -// obligations, we don't prefer the impl over the unknowable predicate -// unconditionally and instead flounder. +//[static_lt] check-pass #![feature(negative_impls)] #![feature(with_negative_coherence)] diff --git a/tests/ui/coherence/negative-coherence-considering-regions.static_lt.stderr b/tests/ui/coherence/negative-coherence-considering-regions.static_lt.stderr deleted file mode 100644 index 87e7be2aa44a9..0000000000000 --- a/tests/ui/coherence/negative-coherence-considering-regions.static_lt.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `Bar` for type `&'static _` - --> $DIR/negative-coherence-considering-regions.rs:26:1 - | -LL | impl Bar for T where T: Foo {} - | ------------------------------ first implementation here -... -LL | impl Bar for &'static T {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&'static _` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/dropck/dropck_trait_cycle_checked.stderr b/tests/ui/dropck/dropck_trait_cycle_checked.stderr index 4d4f7b9df1179..63fd07a91633d 100644 --- a/tests/ui/dropck/dropck_trait_cycle_checked.stderr +++ b/tests/ui/dropck/dropck_trait_cycle_checked.stderr @@ -8,6 +8,8 @@ LL | o1.set0(&o2); ... LL | } | - `o2` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o3` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:112:13 @@ -20,6 +22,8 @@ LL | o1.set1(&o3); ... LL | } | - `o3` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o2` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:113:13 @@ -32,6 +36,8 @@ LL | o2.set0(&o2); ... LL | } | - `o2` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o3` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:114:13 @@ -44,6 +50,8 @@ LL | o2.set1(&o3); ... LL | } | - `o3` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o1` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:115:13 @@ -56,6 +64,8 @@ LL | o3.set0(&o1); LL | o3.set1(&o2); LL | } | - `o1` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o2` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:116:13 @@ -67,6 +77,8 @@ LL | o3.set1(&o2); | ^^^ borrowed value does not live long enough LL | } | - `o2` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error: aborting due to 6 previous errors diff --git a/tests/ui/impl-trait/auto-trait-coherence.next.stderr b/tests/ui/impl-trait/auto-trait-coherence.next.stderr index cee359997b4ff..7833ac688babf 100644 --- a/tests/ui/impl-trait/auto-trait-coherence.next.stderr +++ b/tests/ui/impl-trait/auto-trait-coherence.next.stderr @@ -6,8 +6,6 @@ LL | impl AnotherTrait for T {} ... LL | impl AnotherTrait for D { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D` - | - = note: upstream crates may add a new impl of trait `std::marker::Send` for type `OpaqueType` in future versions error: aborting due to previous error diff --git a/tests/ui/never_type/never-from-impl-is-reserved.stderr b/tests/ui/never_type/never-from-impl-is-reserved.current.stderr similarity index 92% rename from tests/ui/never_type/never-from-impl-is-reserved.stderr rename to tests/ui/never_type/never-from-impl-is-reserved.current.stderr index 871c512052821..6c71785de2897 100644 --- a/tests/ui/never_type/never-from-impl-is-reserved.stderr +++ b/tests/ui/never_type/never-from-impl-is-reserved.current.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` - --> $DIR/never-from-impl-is-reserved.rs:10:1 + --> $DIR/never-from-impl-is-reserved.rs:13:1 | LL | impl MyTrait for MyFoo {} | ---------------------- first implementation here diff --git a/tests/ui/never_type/never-from-impl-is-reserved.next.stderr b/tests/ui/never_type/never-from-impl-is-reserved.next.stderr new file mode 100644 index 0000000000000..6c71785de2897 --- /dev/null +++ b/tests/ui/never_type/never-from-impl-is-reserved.next.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` + --> $DIR/never-from-impl-is-reserved.rs:13:1 + | +LL | impl MyTrait for MyFoo {} + | ---------------------- first implementation here +LL | // This will conflict with the first impl if we impl `for T: From`. +LL | impl MyTrait for T where T: From {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFoo` + | + = note: permitting this impl would forbid us from adding `impl From for T` later; see rust-lang/rust#64715 for details + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/never_type/never-from-impl-is-reserved.rs b/tests/ui/never_type/never-from-impl-is-reserved.rs index 9d16015bdc129..f358e045cf019 100644 --- a/tests/ui/never_type/never-from-impl-is-reserved.rs +++ b/tests/ui/never_type/never-from-impl-is-reserved.rs @@ -1,5 +1,8 @@ // check that the `for T: From` impl is reserved +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next-coherence + #![feature(never_type)] pub struct MyFoo; diff --git a/tests/ui/proc-macro/auxiliary/edition-gated-async-move-syntax.rs b/tests/ui/proc-macro/auxiliary/edition-gated-async-move-syntax.rs new file mode 100644 index 0000000000000..ce7e60356f279 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/edition-gated-async-move-syntax.rs @@ -0,0 +1,39 @@ +// force-host +// no-prefer-dynamic + +// Proc macro helper for issue #89699, used by tests/ui/proc-macro/edition-gated-async-move- +// syntax-issue89699.rs, emitting an `async move` closure. This syntax is only available in +// editions 2018 and up, but is used in edition 2015 in the test. + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_attribute] +pub fn foo(_attr: TokenStream, item: TokenStream) -> TokenStream { + let tt = item.into_iter().next().unwrap(); + let sp = tt.span(); + let mut arg = TokenStream::new(); + let mut g = Group::new(Delimiter::Brace, TokenStream::new()); + g.set_span(sp); + arg.extend([ + TokenTree::Ident(Ident::new("async", sp)), + TokenTree::Ident(Ident::new("move", sp)), + TokenTree::Group(g), + ]); + let mut body = TokenStream::new(); + body.extend([ + TokenTree::Ident(Ident::new("async_main", sp)), + TokenTree::Group(Group::new(Delimiter::Parenthesis, arg)), + ]); + + let mut ret = TokenStream::new(); + ret.extend([ + TokenTree::Ident(Ident::new("fn", sp)), + TokenTree::Ident(Ident::new("main", sp)), + TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), + TokenTree::Group(Group::new(Delimiter::Brace, body)), + ]); + ret +} diff --git a/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.rs b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.rs new file mode 100644 index 0000000000000..1a9d4601acafe --- /dev/null +++ b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.rs @@ -0,0 +1,10 @@ +// aux-build:edition-gated-async-move-syntax.rs +// edition: 2015 + +// Non-regression test for issue #89699, where a proc-macro emitting syntax only available in +// edition 2018 and up (`async move`) is used on edition 2015 + +extern crate edition_gated_async_move_syntax; + +#[edition_gated_async_move_syntax::foo] +fn foo() {} //~ ERROR `async move` blocks are only allowed in Rust 2018 or later diff --git a/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.stderr b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.stderr new file mode 100644 index 0000000000000..a0dcc84eef8fa --- /dev/null +++ b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.stderr @@ -0,0 +1,8 @@ +error: `async move` blocks are only allowed in Rust 2018 or later + --> $DIR/edition-gated-async-move-syntax-issue89699.rs:10:1 + | +LL | fn foo() {} + | ^^ + +error: aborting due to previous error + diff --git a/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr b/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr index 4207c2f80b88f..5d5f325e4b473 100644 --- a/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr +++ b/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr @@ -6,9 +6,6 @@ LL | impl Trait for T {} LL | struct LocalTy; LL | impl Trait for ::Assoc {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `::Assoc` - | - = note: downstream crates may implement trait `std::marker::Sized` for type `::Assoc` - = note: downstream crates may implement trait `std::marker::Copy` for type `::Assoc` error: aborting due to previous error diff --git a/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr b/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr index e5a3c3f5cc4b7..393350ea3f12a 100644 --- a/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr +++ b/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr @@ -5,6 +5,8 @@ LL | impl OtherTrait for () {} | ---------------------- first implementation here LL | impl OtherTrait for T {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` + | + = note: this impl is reserved error: aborting due to previous error diff --git a/tests/ui/traits/trait-object-lifetime-default-note.rs b/tests/ui/traits/trait-object-lifetime-default-note.rs new file mode 100644 index 0000000000000..31e3eb4ba4155 --- /dev/null +++ b/tests/ui/traits/trait-object-lifetime-default-note.rs @@ -0,0 +1,16 @@ +trait A {} + +impl A for T {} + +fn main() { + let local = 0; //~ NOTE binding `local` declared here + let r = &local; //~ ERROR `local` does not live long enough + //~| NOTE borrowed value does not live long enough + //~| NOTE due to object lifetime defaults, `Box` actually means `Box<(dyn A + 'static)>` + require_box(Box::new(r)); + //~^ NOTE cast requires that `local` is borrowed for `'static` + + let _ = 0; +} //~ NOTE `local` dropped here while still borrowed + +fn require_box(_a: Box) {} diff --git a/tests/ui/traits/trait-object-lifetime-default-note.stderr b/tests/ui/traits/trait-object-lifetime-default-note.stderr new file mode 100644 index 0000000000000..b6dd67538993c --- /dev/null +++ b/tests/ui/traits/trait-object-lifetime-default-note.stderr @@ -0,0 +1,19 @@ +error[E0597]: `local` does not live long enough + --> $DIR/trait-object-lifetime-default-note.rs:7:13 + | +LL | let local = 0; + | ----- binding `local` declared here +LL | let r = &local; + | ^^^^^^ borrowed value does not live long enough +... +LL | require_box(Box::new(r)); + | ----------- cast requires that `local` is borrowed for `'static` +... +LL | } + | - `local` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box` actually means `Box<(dyn A + 'static)>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs new file mode 100644 index 0000000000000..79fb643eacd01 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs @@ -0,0 +1,36 @@ +#![deny(deref_into_dyn_supertrait)] +#![feature(trait_upcasting)] // remove this and the test compiles + +use std::ops::Deref; + +trait Bar {} +impl Bar for T {} + +trait Foo: Bar { + fn as_dyn_bar_u32<'a>(&self) -> &(dyn Bar + 'a); +} + +impl Foo for () { + fn as_dyn_bar_u32<'a>(&self) -> &(dyn Bar + 'a) { + self + } +} + +impl<'a> Deref for dyn Foo + 'a { + type Target = dyn Bar + 'a; + + fn deref(&self) -> &Self::Target { + self.as_dyn_bar_u32() + } +} + +fn take_dyn(x: &dyn Bar) -> T { + todo!() +} + +fn main() { + let x: &dyn Foo = &(); + let y = take_dyn(x); + let z: u32 = y; + //~^ ERROR mismatched types +} diff --git a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr new file mode 100644 index 0000000000000..cfd6f7f175ff1 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/inference-behavior-change-deref.rs:34:18 + | +LL | let z: u32 = y; + | --- ^ expected `u32`, found `i32` + | | + | expected due to this + | +help: you can convert an `i32` to a `u32` and panic if the converted value doesn't fit + | +LL | let z: u32 = y.try_into().unwrap(); + | ++++++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs new file mode 100644 index 0000000000000..b3f14a5f256e5 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs @@ -0,0 +1,18 @@ +#![deny(deref_into_dyn_supertrait)] + +use std::ops::Deref; + +trait Bar<'a> {} +trait Foo<'a>: Bar<'a> {} + +impl<'a> Deref for dyn Foo<'a> { + //~^ ERROR dyn Foo<'_>` implements `Deref` with supertrait `Bar<'_>` as target + //~| WARN this will change its meaning in a future release! + type Target = dyn Bar<'a>; + + fn deref(&self) -> &Self::Target { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr new file mode 100644 index 0000000000000..250bcabc69820 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr @@ -0,0 +1,19 @@ +error: `dyn Foo<'_>` implements `Deref` with supertrait `Bar<'_>` as target + --> $DIR/migrate-lint-deny-regions.rs:8:1 + | +LL | impl<'a> Deref for dyn Foo<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | type Target = dyn Bar<'a>; + | -------------------------- target type is set here + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #89460 +note: the lint level is defined here + --> $DIR/migrate-lint-deny-regions.rs:1:9 + | +LL | #![deny(deref_into_dyn_supertrait)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs b/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs index d624187561ed7..114d2c249da06 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs @@ -1,16 +1,14 @@ #![deny(deref_into_dyn_supertrait)] -extern crate core; - -use core::ops::Deref; +use std::ops::Deref; // issue 89190 trait A {} trait B: A {} impl<'a> Deref for dyn 'a + B { - //~^ ERROR `(dyn B + 'a)` implements `Deref` with supertrait `A` as target - //~| WARN this was previously accepted by the compiler but is being phased out; + //~^ ERROR `dyn B` implements `Deref` with supertrait `A` as target + //~| WARN this will change its meaning in a future release! type Target = dyn A; fn deref(&self) -> &Self::Target { diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr b/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr index 4533b1163425c..c8b683c23c183 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr @@ -1,5 +1,5 @@ -error: `(dyn B + 'a)` implements `Deref` with supertrait `A` as target - --> $DIR/migrate-lint-deny.rs:11:1 +error: `dyn B` implements `Deref` with supertrait `A` as target + --> $DIR/migrate-lint-deny.rs:9:1 | LL | impl<'a> Deref for dyn 'a + B { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | impl<'a> Deref for dyn 'a + B { LL | type Target = dyn A; | -------------------- target type is set here | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = warning: this will change its meaning in a future release! = note: for more information, see issue #89460 note: the lint level is defined here --> $DIR/migrate-lint-deny.rs:1:9 diff --git a/tests/ui/treat-err-as-bug/delay_span_bug.rs b/tests/ui/treat-err-as-bug/delay_span_bug.rs index 533c8d1ec8f51..f667f8eb99625 100644 --- a/tests/ui/treat-err-as-bug/delay_span_bug.rs +++ b/tests/ui/treat-err-as-bug/delay_span_bug.rs @@ -1,7 +1,7 @@ // compile-flags: -Ztreat-err-as-bug // failure-status: 101 // error-pattern: aborting due to `-Z treat-err-as-bug=1` -// error-pattern: [trigger_delay_span_bug] triggering a delay span bug +// error-pattern: [trigger_delay_span_bug] triggering a delay span bug for testing incremental // normalize-stderr-test "note: .*\n\n" -> "" // normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> "" // rustc-env:RUST_BACKTRACE=0 diff --git a/tests/ui/treat-err-as-bug/delay_span_bug.stderr b/tests/ui/treat-err-as-bug/delay_span_bug.stderr index 22c6175048a63..06a31ae86b258 100644 --- a/tests/ui/treat-err-as-bug/delay_span_bug.stderr +++ b/tests/ui/treat-err-as-bug/delay_span_bug.stderr @@ -7,5 +7,5 @@ LL | fn main() {} error: the compiler unexpectedly panicked. this is a bug. query stack during panic: -#0 [trigger_delay_span_bug] triggering a delay span bug +#0 [trigger_delay_span_bug] triggering a delay span bug for testing incremental end of query stack diff --git a/tests/ui/typeck/issue-114529-illegal-break-with-value.rs b/tests/ui/typeck/issue-114529-illegal-break-with-value.rs index 613d1b6343a34..353a935e37313 100644 --- a/tests/ui/typeck/issue-114529-illegal-break-with-value.rs +++ b/tests/ui/typeck/issue-114529-illegal-break-with-value.rs @@ -17,4 +17,10 @@ fn main() { }; 51 }]; + + while true { + break (|| { //~ ERROR `break` with value from a `while` loop + let local = 9; + }); + } } diff --git a/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr b/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr index 4d6c27bbbd03b..731f234c162ad 100644 --- a/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr +++ b/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr @@ -24,6 +24,21 @@ help: use `break` on its own without a value inside this `while` loop LL | break; | ~~~~~ -error: aborting due to 2 previous errors +error[E0571]: `break` with value from a `while` loop + --> $DIR/issue-114529-illegal-break-with-value.rs:22:9 + | +LL | while true { + | ---------- you can't `break` with a value in a `while` loop +LL | / break (|| { +LL | | let local = 9; +LL | | }); + | |__________^ can only break with a value inside `loop` or breakable block + | +help: use `break` on its own without a value inside this `while` loop + | +LL | break; + | ~~~~~ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0571`.