diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index ce4c21ff8d86e..af779152ed675 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -239,26 +239,21 @@ fn compare_method_predicate_entailment<'tcx>( let hybrid_preds = hybrid_preds.into_iter().map(Unnormalized::skip_norm_wip); let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id); let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds)); - // FIXME(-Zhigher-ranked-assumptions): The `hybrid_preds` + // NOTE(-Zhigher-ranked-assumptions): The `hybrid_preds` // should be well-formed. However, using them may result in // region errors as we currently don't track placeholder // assumptions. // - // To avoid being backwards incompatible with the old solver, - // we also eagerly normalize the where-bounds in the new solver - // here while ignoring region constraints. This means we can then - // use where-bounds whose normalization results in placeholder - // errors further down without getting any errors. + // We eagerly normalize the where-clauses here while ignoring + // region constraints. This means we can then use where-bounds + // whose normalization results in placeholder errors further + // down without getting any errors. // - // It should be sound to do so as the only region errors here + // This should be sound to do so as the only region errors here // should be due to missing implied bounds. // // cc trait-system-refactor-initiative/issues/166. - let param_env = if tcx.next_trait_solver_globally() { - traits::deeply_normalize_param_env_ignoring_regions(tcx, param_env, normalize_cause) - } else { - traits::normalize_param_env_or_error(tcx, param_env, normalize_cause) - }; + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); debug!(caller_bounds=?param_env.caller_bounds()); let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 95e67b135e411..3268acd786b9b 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -23,6 +23,7 @@ use rustc_trait_selection::traits; use tracing::{debug, instrument, trace}; use super::{CoroutineTypes, Expectation, FnCtxt, check_fn}; +use crate::fn_ctxt::UseSubtyping; /// What signature do we *expect* the closure to have from context? #[derive(Debug, Clone, TypeFoldable, TypeVisitable)] @@ -317,7 +318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates( Ty::new_var(self.tcx, self.root_var(vid)), closure_kind, - self.obligations_for_self_ty(vid) + self.obligations_for_self_ty(vid, UseSubtyping::No) .into_iter() .map(|obl| (obl.predicate, obl.cause.span)), ), @@ -587,7 +588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. let mut return_ty = None; - for bound in self.obligations_for_self_ty(return_vid) { + for bound in self.obligations_for_self_ty(return_vid, UseSubtyping::No) { if let Some(ret_projection) = bound.predicate.as_projection_clause() && let Some(ret_projection) = ret_projection.no_bound_vars() && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput) @@ -987,9 +988,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output_ty = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).into_iter().find_map(|obligation| { - get_future_output(obligation.predicate, obligation.cause.span) - })? + self.obligations_for_self_ty(ret_vid, UseSubtyping::No).into_iter().find_map( + |obligation| get_future_output(obligation.predicate, obligation.cause.span), + )? } ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }) => { return Some(Ty::new_error_with_message( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 9a471d3ea007b..37e198d51a831 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -755,14 +755,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { let sized_did = self.tcx.lang_items().sized_trait(); - self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { - match obligation.predicate.kind().skip_binder() { + + // NB: `T: Sized` implies that all subtypes and all supertypes of `T` are also sized, + // so it's valid to use subtyping here. (subtyping has to preserve layout and + // `T <: U => &T <: &U`, so subtyping can't change sizedness) + self.obligations_for_self_ty(self_ty, super::UseSubtyping::Yes).into_iter().any( + |obligation| match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { Some(data.def_id()) == sized_did } _ => false, - } - }) + }, + ) } pub(crate) fn err_args(&self, len: usize, guar: ErrorGuaranteed) -> Vec> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 04b095dc8c92c..cfaa60231379e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -13,19 +13,38 @@ use tracing::{debug, instrument, trace}; use crate::FnCtxt; +/// Whatever to use subtyping or not when inspecting obligations +#[derive(Debug, Copy, Clone)] +pub(crate) enum UseSubtyping { + /// Do **not** use subtyping. [`FnCtxt::obligations_for_self_ty`] will only return obligations + /// where the self type is known to be equal to the provided vid. + No, + + /// Use subtyping. [`FnCtxt::obligations_for_self_ty`] will return obligations + /// where the self type is related to the provided vid via subtyping. + /// + /// Using this requires extra care, as traits holding for a subtype or a supertype, does not + /// necessarily imply that they hold for the respective supertype or subtype. + Yes, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a list of all obligations whose self type has been unified /// with the unconstrained type `self_ty`. #[instrument(skip(self), level = "debug")] - pub(crate) fn obligations_for_self_ty(&self, self_ty: ty::TyVid) -> PredicateObligations<'tcx> { + pub(crate) fn obligations_for_self_ty( + &self, + self_ty: ty::TyVid, + subtyping: UseSubtyping, + ) -> PredicateObligations<'tcx> { if self.next_trait_solver() { - self.obligations_for_self_ty_next(self_ty) + self.obligations_for_self_ty_next(self_ty, subtyping) } else { - let ty_var_root = self.root_var(self_ty); let mut obligations = self.fulfillment_cx.borrow().pending_obligations(); trace!("pending_obligations = {:#?}", obligations); - obligations - .retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root)); + obligations.retain(|obligation| { + self.predicate_has_self_ty(obligation.predicate, self_ty, subtyping) + }); obligations } } @@ -35,14 +54,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, predicate: ty::Predicate<'tcx>, expected_vid: ty::TyVid, + subtyping: UseSubtyping, ) -> bool { match predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - self.type_matches_expected_vid(data.self_ty(), expected_vid) + self.type_matches_expected_vid(data.self_ty(), expected_vid, subtyping) } ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { if data.projection_term.kind.is_trait_projection() { - self.type_matches_expected_vid(data.self_ty(), expected_vid) + self.type_matches_expected_vid(data.self_ty(), expected_vid, subtyping) } else { false } @@ -64,14 +84,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self), ret)] - fn type_matches_expected_vid(&self, ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { + fn type_matches_expected_vid( + &self, + ty: Ty<'tcx>, + expected_vid: ty::TyVid, + subtyping: UseSubtyping, + ) -> bool { let ty = self.shallow_resolve(ty); debug!(?ty); match *ty.kind() { - ty::Infer(ty::TyVar(found_vid)) => { - self.root_var(expected_vid) == self.root_var(found_vid) - } + ty::Infer(ty::TyVar(found_vid)) => match subtyping { + UseSubtyping::No => self.root_var(expected_vid) == self.root_var(found_vid), + UseSubtyping::Yes => { + self.sub_unification_table_root_var(expected_vid) + == self.sub_unification_table_root_var(found_vid) + } + }, _ => false, } } @@ -79,6 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn obligations_for_self_ty_next( &self, self_ty: ty::TyVid, + subtyping: UseSubtyping, ) -> PredicateObligations<'tcx> { // We only look at obligations which may reference the self type. // This lookup uses the `sub_root` instead of the inference variable @@ -93,6 +123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .pending_obligations_potentially_referencing_sub_root(&self.infcx, sub_root_var); debug!(?obligations); + let mut obligations_for_self_ty = PredicateObligations::new(); for obligation in obligations { let mut visitor = NestedObligationsForSelfTy { @@ -100,6 +131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, obligations_for_self_ty: &mut obligations_for_self_ty, root_cause: &obligation.cause, + subtyping, }; let goal = obligation.as_goal(); @@ -182,6 +214,7 @@ struct NestedObligationsForSelfTy<'a, 'tcx> { self_ty: ty::TyVid, root_cause: &'a ObligationCause<'tcx>, obligations_for_self_ty: &'a mut PredicateObligations<'tcx>, + subtyping: UseSubtyping, } impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { @@ -209,7 +242,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { .orig_values() .iter() .filter_map(|arg| arg.as_type()) - .any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty)) + .any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty, self.subtyping)) { debug!(goal = ?inspect_goal.goal(), "goal does not mention self type"); return; @@ -217,7 +250,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); - if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty, self.subtyping) { self.obligations_for_self_ty.push(traits::Obligation::new( tcx, self.root_cause.clone(), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index c4a1b56320ff1..6d2e36e081a5e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -8,6 +8,7 @@ mod suggestions; use std::cell::{Cell, RefCell}; use std::ops::Deref; +pub(crate) use inspect_obligations::UseSubtyping; use rustc_errors::DiagCtxtHandle; use rustc_hir::attrs::{DivergingBlockBehavior, DivergingFallbackBehavior}; use rustc_hir::def_id::{DefId, LocalDefId}; diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 4c56bf0923c39..21ba07428d9ec 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -246,13 +246,11 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { } /// Returns the "root" variable of `vid` in the `sub_unification_table` - /// equivalence table. All type variables that have been are related via + /// equivalence table. All type variables that have been related via /// equality or subtyping will yield the same root variable (per the /// union-find algorithm), so `sub_unification_table_root_var(a) - /// == sub_unification_table_root_var(b)` implies that: - /// ```text - /// exists X. (a <: X || X <: a) && (b <: X || X <: b) - /// ``` + /// == sub_unification_table_root_var(b)` implies that `a` and `b` are + /// transitively related via subtyping. pub(crate) fn sub_unification_table_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.sub_unification_table().find(vid).vid } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0c850b69ca7f1..c0336cd57603c 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4660,3 +4660,33 @@ pub(crate) struct UseRegularStructSuggestion { #[suggestion_part(code = "")] pub semicolon: Option, } +#[derive(Diagnostic)] +#[diag("expected type parameter, found path `{$path}`")] +pub(crate) struct FoundPathInGenerics { + #[primary_span] + pub span: Span, + pub path: String, +} +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to bind a type parameter to a trait", + applicability = "maybe-incorrect", + code = "T: " +)] + +pub(crate) struct SuggestBindTypeParameter { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "alternatively, you might have meant to introduce type parameter", + applicability = "maybe-incorrect", + code = "{parameters}" +)] +pub(crate) struct SuggestIntroduceTypeParameter { + #[primary_span] + pub span: Span, + pub parameters: String, +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7040fc60e4f1a..28e2841b9f1fe 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, - Block, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, + Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind, MgcaDisambiguation, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, }; use rustc_ast_pretty::pprust; @@ -31,14 +31,16 @@ use crate::errors::{ AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound, - ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, - GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition, - InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, - MisspelledKw, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, - SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, - SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, - TernaryOperatorSuggestion, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, FoundPathInGenerics, + GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, + HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait, + IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, MisspelledKw, + PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, + StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, + SuggEscapeIdentifier, SuggRemoveComma, SuggestBindTypeParameter, SuggestIntroduceTypeParameter, + TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::exp; use crate::parser::FnContext; @@ -3164,4 +3166,48 @@ impl<'a> Parser<'a> { } Ok(()) } + pub(super) fn maybe_type_in_generic_parameter(&mut self, origin_error: Diag<'a>) -> Diag<'a> { + if !self.may_recover() { + return origin_error; + } + self.with_recovery(super::Recovery::Forbidden, |snapshot| { + snapshot.bump(); + let lo = snapshot.token.span.shrink_to_lo(); + + let ty = match snapshot.parse_ty() { + Ok(t) => t, + Err(err) => { + err.cancel(); + return origin_error; + } + }; + let TyKind::Path(_, path) = ty.kind else { + return origin_error; + }; + let Some(GenericArgs::AngleBracketed(AngleBracketedArgs { span: _, ref args })) = + path.segments[0].args + else { + return origin_error; + }; + + let path_span = path.span; + let mut new_error = snapshot.dcx().create_err(FoundPathInGenerics { + span: path_span, + path: snapshot.span_to_snippet(path_span).unwrap(), + }); + new_error.subdiagnostic(SuggestBindTypeParameter { span: lo }); + origin_error.cancel(); + + let params = args + .iter() + .map(|arg| snapshot.span_to_snippet(arg.span()).unwrap()) + .collect::>() + .join(", "); + new_error.subdiagnostic(SuggestIntroduceTypeParameter { + span: path_span, + parameters: params, + }); + new_error + }) + } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 16fed9c6c273b..627711cb2f822 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -662,11 +662,20 @@ impl<'a> Parser<'a> { let constness = self.parse_constness(Case::Sensitive); let safety = self.parse_safety(Case::Sensitive); self.expect_keyword(exp!(Impl))?; - + let mut generics_snapshot = None; // First, parse generic parameters if necessary. let mut generics = if self.choose_generics_over_qpath(0) { self.parse_generics()? } else { + // We might be mistakenly trying to use a generic type as a generic parameter. + // impl> Trait for Y { ... } + if self.look_ahead(0, |t| t == &token::Lt) + && self.look_ahead(1, |t| t.is_ident()) + && self.look_ahead(2, |t| t == &token::Lt) + { + generics_snapshot = Some(self.create_snapshot_for_diagnostic()); + } + let mut generics = Generics::default(); // impl A for B {} // /\ this is where `generics.span` should point when there are no type params. @@ -698,9 +707,13 @@ impl<'a> Parser<'a> { for_span: span.to(self.token.span), })); } else { - self.parse_ty_with_generics_recovery(&generics)? + self.parse_ty_with_generics_recovery(&generics).map_err(|e| { + let Some(mut snapshot) = generics_snapshot else { + return e; + }; + snapshot.maybe_type_in_generic_parameter(e) + })? }; - // If `for` is missing we try to recover. let has_for = self.eat_keyword(exp!(For)); let missing_for_span = self.prev_token.span.between(self.token.span); diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index f9b5f9af951e5..79d11e54303f3 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -139,7 +139,7 @@ pub struct BasicBlock { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Terminator { pub kind: TerminatorKind, - pub span: Span, + pub source_info: SourceInfo, } impl Terminator { @@ -468,7 +468,7 @@ pub enum NonDivergingIntrinsic { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Statement { pub kind: StatementKind, - pub span: Span, + pub source_info: SourceInfo, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 5a3afc9937351..ae69f14faddd8 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -139,9 +139,9 @@ macro_rules! make_mir_visitor { fn super_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { let BasicBlock { statements, terminator } = bb; for stmt in statements { - self.visit_statement(stmt, Location(stmt.span)); + self.visit_statement(stmt, Location(stmt.source_info.span)); } - self.visit_terminator(terminator, Location(terminator.span)); + self.visit_terminator(terminator, Location(terminator.source_info.span)); } fn super_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { @@ -159,8 +159,8 @@ macro_rules! make_mir_visitor { } fn super_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { - let Statement { kind, span } = stmt; - self.visit_span(span); + let Statement { kind, source_info } = stmt; + self.visit_span(&$($mutability)? source_info.span); match kind { StatementKind::Assign(place, rvalue) => { self.visit_place(place, PlaceContext::MUTATING, location); @@ -199,8 +199,8 @@ macro_rules! make_mir_visitor { } fn super_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { - let Terminator { kind, span } = term; - self.visit_span(span); + let Terminator { kind, source_info } = term; + self.visit_span(&$($mutability)? source_info.span); match kind { TerminatorKind::Goto { .. } | TerminatorKind::Resume @@ -540,7 +540,7 @@ impl Location { pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location { let bb = &body.blocks[*bb_idx]; let stmt = &bb.statements[stmt_idx]; - Location(stmt.span) + Location(stmt.source_info.span) } /// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given @@ -548,7 +548,7 @@ pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location { let bb = &body.blocks[*bb_idx]; let terminator = &bb.terminator; - Location(terminator.span) + Location(terminator.source_info.span) } /// Reference to a place used to represent a partial projection. diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index d126304af8cde..c98a87d67458f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -74,7 +74,7 @@ impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> { ) -> Self::T { Statement { kind: self.kind.stable(tables, cx), - span: self.source_info.span.stable(tables, cx), + source_info: self.source_info.stable(tables, cx), } } } @@ -695,7 +695,7 @@ impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> { use crate::mir::Terminator; Terminator { kind: self.kind.stable(tables, cx), - span: self.source_info.span.stable(tables, cx), + source_info: self.source_info.stable(tables, cx), } } } diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4977e6c1217d8..611bf5950d27d 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -153,6 +153,7 @@ type ImpliedFeatures = &'static [&'static str]; static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("aclass", Unstable(sym::arm_target_feature), &[]), + ("acquire-release", Unstable(sym::arm_target_feature), &[]), ("aes", Unstable(sym::arm_target_feature), &["neon"]), ( "atomics-32", @@ -171,6 +172,8 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("fpregs", Unstable(sym::arm_target_feature), &[]), ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), ("mclass", Unstable(sym::arm_target_feature), &[]), + ("mve", Unstable(sym::arm_target_feature), &["v8.1m.main", "dsp", "fpregs"]), + ("mve.fp", Unstable(sym::arm_target_feature), &["mve"]), ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), ("rclass", Unstable(sym::arm_target_feature), &[]), ("sha2", Unstable(sym::arm_target_feature), &["neon"]), @@ -188,9 +191,13 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("v5te", Unstable(sym::arm_target_feature), &[]), ("v6", Unstable(sym::arm_target_feature), &["v5te"]), ("v6k", Unstable(sym::arm_target_feature), &["v6"]), - ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v6m", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "v8m", "thumb2"]), ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("v8.1m.main", Unstable(sym::arm_target_feature), &["v8m.main"]), + ("v8m", Unstable(sym::arm_target_feature), &["v6m"]), + ("v8m.main", Unstable(sym::arm_target_feature), &["v7"]), ("vfp2", Unstable(sym::arm_target_feature), &[]), ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), @@ -1073,9 +1080,8 @@ const X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static const AARCH64_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")]; -// We might want to add "helium" too. const ARM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = - &[(128, "neon")]; + &[(128, "neon"), (128, "mve")]; const AMDGPU_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[(1024, "")]; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 139aa6bd77a87..9f5cfb39b9718 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4479,7 +4479,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::OpaqueReturnType(expr_info) => { let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { - let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); let expr = tcx.hir_expect_expr(hir_id); (expr_ty, expr) } else if let Some(body_id) = tcx.hir_node_by_def_id(body_id).body_id() @@ -4494,15 +4493,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() && self.can_eq(param_env, pred.self_ty(), expr_ty) { - let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); (expr_ty, expr) } else { return; }; + let expr_ty_string = tcx.short_string(expr_ty, err.long_ty_path()); + if expr_ty.is_never() + && let span = expr.span.source_callsite() + && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) + && span != expr.span + { + err.span_suggestion( + span, + "`!` can be coerced to any type; consider casting it to a concrete type that implements the trait", + format!("{snippet} as /* Type */"), + Applicability::HasPlaceholders, + ); + } err.span_label( expr.span, with_forced_trimmed_paths!(format!( - "return type was inferred to be `{expr_ty}` here", + "return type was inferred to be `{expr_ty_string}` here", )), ); suggest_remove_deref(err, &expr); diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a98d06351f258..3abc79bd21304 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -30,7 +30,6 @@ use rustc_errors::ErrorGuaranteed; pub use rustc_infer::traits::*; use rustc_macros::TypeVisitable; use rustc_middle::query::Providers; -use rustc_middle::span_bug; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{ self, Clause, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeFolder, @@ -257,12 +256,6 @@ fn do_normalize_predicates<'tcx>( elaborated_env: ty::ParamEnv<'tcx>, predicates: Vec>, ) -> Result>, ErrorGuaranteed> { - // Even if we move back to eager normalization elsewhere, - // param env normalization remains lazy in the next solver. - if tcx.next_trait_solver_globally() { - return Ok(predicates); - } - // FIXME. We should really... do something with these region // obligations. But this call just continues the older // behavior (i.e., doesn't cause any new bugs), and it would @@ -294,17 +287,20 @@ fn do_normalize_predicates<'tcx>( // We can use the `elaborated_env` here; the region code only // cares about declarations like `'a: 'b`. + // // FIXME: It's very weird that we ignore region obligations but apparently // still need to use `resolve_regions` as we need the resolved regions in // the normalized predicates. - let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); - if !errors.is_empty() { - tcx.dcx().span_delayed_bug( - span, - format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), - ); - } - + // + // FIXME(-Zhigher-ranked-assumptions): We're ignoring region errors for now. + // There're placeholder constraints `leaking` out. This is a hack to work around + // the fact that we don't support placeholder assumptions right now and is necessary + // for `compare_method_predicate_entailment`. We should remove this once we + // have proper support for implied bounds on binders. + // + // This is required by trait-system-refactor-initiative#166. The new solver encounters + // this more frequently as we entirely ignore outlives predicates with the old solver. + let _errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); match infcx.fully_resolve(predicates) { Ok(predicates) => Ok(predicates), Err(fixup_err) => { @@ -481,69 +477,6 @@ pub fn normalize_param_env_or_error<'tcx>( ty::ParamEnv::new(tcx.mk_clauses(&predicates)) } -/// Deeply normalize the param env using the next solver ignoring -/// region errors. -/// -/// FIXME(-Zhigher-ranked-assumptions): this is a hack to work around -/// the fact that we don't support placeholder assumptions right now -/// and is necessary for `compare_method_predicate_entailment`, see the -/// use of this function for more info. We should remove this once we -/// have proper support for implied bounds on binders. -#[instrument(level = "debug", skip(tcx))] -pub fn deeply_normalize_param_env_ignoring_regions<'tcx>( - tcx: TyCtxt<'tcx>, - unnormalized_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, -) -> ty::ParamEnv<'tcx> { - let predicates: Vec<_> = - util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect(); - - debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - - let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates)); - if !elaborated_env.has_aliases() { - return elaborated_env; - } - - let span = cause.span; - let infcx = tcx - .infer_ctxt() - .with_next_trait_solver(true) - .ignoring_regions() - .build(TypingMode::non_body_analysis()); - let predicates = match crate::solve::deeply_normalize::<_, FulfillmentError<'tcx>>( - infcx.at(&cause, elaborated_env), - Unnormalized::new_wip(predicates), - ) { - Ok(predicates) => predicates, - Err(errors) => { - infcx.err_ctxt().report_fulfillment_errors(errors); - // An unnormalized env is better than nothing. - debug!("normalize_param_env_or_error: errored resolving predicates"); - return elaborated_env; - } - }; - - debug!("do_normalize_predicates: normalized predicates = {:?}", predicates); - // FIXME(-Zhigher-ranked-assumptions): We're ignoring region errors for now. - // There're placeholder constraints `leaking` out. - // See the fixme in the enclosing function's docs for more. - let _errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); - - let predicates = match infcx.fully_resolve(predicates) { - Ok(predicates) => predicates, - Err(fixup_err) => { - span_bug!( - span, - "inference variables in normalized parameter environment: {}", - fixup_err - ) - } - }; - debug!("normalize_param_env_or_error: final predicates={:?}", predicates); - ty::ParamEnv::new(tcx.mk_clauses(&predicates)) -} - #[derive(Debug)] pub enum EvaluateConstErr { /// The constant being evaluated was either a generic parameter or inference variable, *or*, diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 6f392bba93ef4..732842bc65df7 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,5 +1,7 @@ use crate::boxed::Box; -use crate::io::SizeHint; +use crate::io::{self, Seek, SeekFrom, SizeHint}; +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +use crate::sync::Arc; // ============================================================================= // Forwarding implementations @@ -18,5 +20,66 @@ impl SizeHint for Box { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for Box { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } +} + // ============================================================================= // In-memory buffer implementations + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Seek for Arc +where + for<'a> &'a S: Seek, + S: crate::io::IoHandle, +{ + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (&**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (&**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (&**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (&**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (&**self).seek_relative(offset) + } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 86dc8c685a985..44a1d1b71d164 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -13,9 +13,9 @@ pub use core::io::const_error; pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ - Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, - repeat, sink, + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, + Sink, Take, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, take}; +pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, stream_len_default, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 33fbcd8e32a6e..01b4e6f938616 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -155,6 +155,7 @@ #![feature(ptr_metadata)] #![feature(raw_os_error_ty)] #![feature(rev_into_inner)] +#![feature(seek_stream_len)] #![feature(set_ptr_value)] #![feature(share_trait)] #![feature(sized_type_properties)] diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index abd80aef20bd3..de1adf9c9ec7c 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -1049,8 +1049,8 @@ impl AsciiChar { /// before using this function. /// /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// [pct]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_06_05 /// /// # Examples /// diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 8517df4da2afc..d0114c30a6b3c 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -2354,8 +2354,8 @@ impl char { /// before using this function. /// /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// [pct]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_06_05 /// /// # Examples /// diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 24d3bfc67b988..c104b426f48df 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,3 +1,5 @@ +use crate::io::{self, ErrorKind, SeekFrom}; + /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. /// @@ -21,7 +23,7 @@ /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html /// [`Write`]: ../../std/io/trait.Write.html -/// [`Seek`]: ../../std/io/trait.Seek.html +/// [`Seek`]: crate::io::Seek /// [Vec]: ../../alloc/vec/struct.Vec.html /// /// ```no_run @@ -291,3 +293,38 @@ where self.pos = other.pos; } } + +#[stable(feature = "rust1", since = "1.0.0")] +impl io::Seek for Cursor +where + T: AsRef<[u8]>, +{ + fn seek(&mut self, style: SeekFrom) -> io::Result { + let (base_pos, offset) = match style { + SeekFrom::Start(n) => { + self.set_position(n); + return Ok(n); + } + SeekFrom::End(n) => (self.get_ref().as_ref().len() as u64, n), + SeekFrom::Current(n) => (self.position(), n), + }; + match base_pos.checked_add_signed(offset) { + Some(n) => { + self.set_position(n); + Ok(n) + } + None => Err(io::const_error!( + ErrorKind::InvalidInput, + "invalid seek to a negative or overflowing position", + )), + } + } + + fn stream_len(&mut self) -> io::Result { + Ok(self.get_ref().as_ref().len() as u64) + } + + fn stream_position(&mut self) -> io::Result { + Ok(self.position()) + } +} diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 2d15307daf153..4214a5f87e3ef 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -80,7 +80,7 @@ pub type Result = result::Result; // FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html /// [Write]: ../../std/io/trait.Write.html -/// [Seek]: ../../std/io/trait.Seek.html +/// [Seek]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] #[rustc_has_incoherent_inherent_impls] pub struct Error { diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs index cc222c56121e2..e8c716f060119 100644 --- a/library/core/src/io/impls.rs +++ b/library/core/src/io/impls.rs @@ -1,4 +1,4 @@ -use crate::io::SizeHint; +use crate::io::{self, Seek, SeekFrom, SizeHint}; // ============================================================================= // Forwarding implementations @@ -17,6 +17,34 @@ impl SizeHint for &mut T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for &mut S { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } +} + // ============================================================================= // In-memory buffer implementations diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 95fcc2d6dc19b..1c27e42229bb7 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -5,6 +5,7 @@ mod cursor; mod error; mod impls; mod io_slice; +mod seek; mod size_hint; mod util; @@ -21,12 +22,14 @@ pub use self::{ cursor::Cursor, error::{Error, ErrorKind, Result}, io_slice::{IoSlice, IoSliceMut}, + seek::{Seek, SeekFrom}, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ error::{Custom, CustomOwner, OsFunctions}, + seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, }; @@ -46,7 +49,7 @@ pub use self::{ /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html /// [`Write`]: ../../std/io/trait.Write.html -/// [`Seek`]: ../../std/io/trait.Seek.html +/// [`Seek`]: crate::io::Seek #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub trait IoHandle {} diff --git a/library/core/src/io/seek.rs b/library/core/src/io/seek.rs new file mode 100644 index 0000000000000..4c242c761dfe6 --- /dev/null +++ b/library/core/src/io/seek.rs @@ -0,0 +1,221 @@ +use crate::io::Result; + +/// The `Seek` trait provides a cursor which can be moved within a stream of +/// bytes. +/// +/// The stream typically has a fixed size, allowing seeking relative to either +/// end or the current offset. +/// +/// # Examples +/// +/// `File`s implement `Seek`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// use std::io::SeekFrom; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// +/// // move the cursor 42 bytes from the start of the file +/// f.seek(SeekFrom::Start(42))?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] +pub trait Seek { + /// Seek to an offset, in bytes, in a stream. + /// + /// A seek beyond the end of a stream is allowed, but behavior is defined + /// by the implementation. + /// + /// If the seek operation completed successfully, + /// this method returns the new position from the start of the stream. + /// That position can be used later with [`SeekFrom::Start`]. + /// + /// # Errors + /// + /// Seeking can fail, for example because it might involve flushing a buffer. + /// + /// Seeking to a negative offset is considered an error. + #[stable(feature = "rust1", since = "1.0.0")] + fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + /// + /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. + /// + /// # Errors + /// + /// Rewinding can fail, for example because it might involve flushing a buffer. + /// + /// # Example + /// + /// ```no_run + /// use std::io::{Read, Seek, Write}; + /// use std::fs::OpenOptions; + /// + /// let mut f = OpenOptions::new() + /// .write(true) + /// .read(true) + /// .create(true) + /// .open("foo.txt")?; + /// + /// let hello = "Hello!\n"; + /// write!(f, "{hello}")?; + /// f.rewind()?; + /// + /// let mut buf = String::new(); + /// f.read_to_string(&mut buf)?; + /// assert_eq!(&buf, hello); + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "seek_rewind", since = "1.55.0")] + fn rewind(&mut self) -> Result<()> { + self.seek(SeekFrom::Start(0))?; + Ok(()) + } + + /// Returns the length of this stream (in bytes). + /// + /// The default implementation uses up to three seek operations. If this + /// method returns successfully, the seek position is unchanged (i.e. the + /// position before calling this method is the same as afterwards). + /// However, if this method returns an error, the seek position is + /// unspecified. + /// + /// If you need to obtain the length of *many* streams and you don't care + /// about the seek position afterwards, you can reduce the number of seek + /// operations by simply calling `seek(SeekFrom::End(0))` and using its + /// return value (it is also the stream length). + /// + /// Note that length of a stream can change over time (for example, when + /// data is appended to a file). So calling this method multiple times does + /// not necessarily return the same length each time. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_stream_len)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// let len = f.stream_len()?; + /// println!("The file is currently {len} bytes long"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_stream_len", issue = "59359")] + fn stream_len(&mut self) -> Result { + stream_len_default(self) + } + + /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + /// + /// # Example + /// + /// ```no_run + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "seek_convenience", since = "1.51.0")] + fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)) + } + + /// Seeks relative to the current position. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but + /// doesn't return the new position which can allow some implementations + /// such as `BufReader` to perform more efficient seeks. + /// + /// # Example + /// + /// ```no_run + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// f.seek_relative(10)?; + /// assert_eq!(f.stream_position()?, 10); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "seek_seek_relative", since = "1.80.0")] + fn seek_relative(&mut self, offset: i64) -> Result<()> { + self.seek(SeekFrom::Current(offset))?; + Ok(()) + } +} + +/// The default implementation of [`Seek::stream_len`]. +/// This may be desirable in `libstd` where the default implementation is desirable, +/// but additional work needs to be done before or after. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn stream_len_default(self_: &mut T) -> Result { + let old_pos = self_.stream_position()?; + let len = self_.seek(SeekFrom::End(0))?; + + // Avoid seeking a third time when we were already at the end of the + // stream. The branch is usually way cheaper than a seek operation. + if old_pos != len { + self_.seek(SeekFrom::Start(old_pos))?; + } + + Ok(len) +} + +/// Enumeration of possible methods to seek within an I/O object. +/// +/// It is used by the [`Seek`] trait. +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] +pub enum SeekFrom { + /// Sets the offset to the provided number of bytes. + #[stable(feature = "rust1", since = "1.0.0")] + Start(#[stable(feature = "rust1", since = "1.0.0")] u64), + + /// Sets the offset to the size of this object plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + End(#[stable(feature = "rust1", since = "1.0.0")] i64), + + /// Sets the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + Current(#[stable(feature = "rust1", since = "1.0.0")] i64), +} diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index a40318bb85e08..de37690436c80 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,4 +1,4 @@ -use crate::io::SizeHint; +use crate::io::{ErrorKind, Result, Seek, SeekFrom, SizeHint}; use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty @@ -23,6 +23,24 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_seek", since = "1.51.0")] +impl Seek for Empty { + #[inline] + fn seek(&mut self, _pos: SeekFrom) -> Result { + Ok(0) + } + + #[inline] + fn stream_len(&mut self) -> Result { + Ok(0) + } + + #[inline] + fn stream_position(&mut self) -> Result { + Ok(0) + } +} + /// Creates a value that is always at EOF for reads, and ignores all data written. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] @@ -464,6 +482,49 @@ impl Take { } } +#[stable(feature = "seek_io_take", since = "1.89.0")] +impl Seek for Take { + fn seek(&mut self, pos: SeekFrom) -> Result { + let new_position = match pos { + SeekFrom::Start(v) => Some(v), + SeekFrom::Current(v) => self.position().checked_add_signed(v), + SeekFrom::End(v) => self.len.checked_add_signed(v), + }; + let new_position = match new_position { + Some(v) if v <= self.len => v, + _ => return Err(ErrorKind::InvalidInput.into()), + }; + while new_position != self.position() { + if let Some(offset) = new_position.checked_signed_diff(self.position()) { + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + break; + } + let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + } + Ok(new_position) + } + + fn stream_len(&mut self) -> Result { + Ok(self.len) + } + + fn stream_position(&mut self) -> Result { + Ok(self.position()) + } + + fn seek_relative(&mut self, offset: i64) -> Result<()> { + if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { + return Err(ErrorKind::InvalidInput.into()); + } + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + Ok(()) + } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] #[must_use] diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 002c56083659c..59dfe6bd8d9b7 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1125,8 +1125,8 @@ impl u8 { /// before using this function. /// /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// [pct]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_06_05 /// /// # Examples /// diff --git a/library/core/src/range.rs b/library/core/src/range.rs index fb7a51a779f6d..07f423edcfea1 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -402,7 +402,8 @@ const impl From> for RangeInclusive { /// /// # Panics /// - /// Panics if the legacy range iterator has been exhausted. + /// If the legacy range iterator has been exhausted, + /// this function will either panic or return an empty range. /// /// # Examples /// @@ -419,19 +420,23 @@ const impl From> for RangeInclusive { /// assert_eq!((empty.start, empty.last), (0, 0)); /// ``` /// - /// ```should_panic + /// ``` + /// # // This test requires unwinding to work. + /// # // Disable it when unwinding isn't available. + /// # #[cfg(panic = "unwind")] + /// # fn main() { /// use core::range::legacy; /// use core::range::RangeInclusive; + /// use std::panic::catch_unwind; /// /// let mut exhausted: legacy::RangeInclusive = 0..=0; /// exhausted.next(); - /// # if exhausted.is_empty() { - /// # // assert!s don't work correctly in `should_panic` doctests since you - /// # // can't assert the panic message. Skip the rest of the test instead, - /// # // so that the expected panic doesn't happen and the test fails. - /// assert!(exhausted.is_empty()); - /// let _ = RangeInclusive::from(exhausted); // this panics + /// let result = catch_unwind(|| RangeInclusive::from(exhausted)); + /// // The `from` call either panicked or returned an empty range. + /// assert!(result.is_err() || result.is_ok_and(|range| range.is_empty())); /// # } + /// # #[cfg(not(panic = "unwind"))] + /// # fn main() {} /// ``` #[inline] fn from(value: legacy::RangeInclusive) -> Self { diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 53b60c1ae6703..5399789b4242f 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -7,42 +7,7 @@ pub use core::io::Cursor; use crate::alloc::Allocator; use crate::cmp; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl io::Seek for Cursor -where - T: AsRef<[u8]>, -{ - fn seek(&mut self, style: SeekFrom) -> io::Result { - let (base_pos, offset) = match style { - SeekFrom::Start(n) => { - self.set_position(n); - return Ok(n); - } - SeekFrom::End(n) => (self.get_ref().as_ref().len() as u64, n), - SeekFrom::Current(n) => (self.position(), n), - }; - match base_pos.checked_add_signed(offset) { - Some(n) => { - self.set_position(n); - Ok(n) - } - None => Err(io::const_error!( - ErrorKind::InvalidInput, - "invalid seek to a negative or overflowing position", - )), - } - } - - fn stream_len(&mut self) -> io::Result { - Ok(self.get_ref().as_ref().len() as u64) - } - - fn stream_position(&mut self) -> io::Result { - Ok(self.position()) - } -} +use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor @@ -137,6 +102,54 @@ where } } +/// Trait used to allow indirect implementation of `Write` for `Cursor`. +/// Since [`Cursor`] is not a foundational type, it is not possible to implement +/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a +/// downstream crate (e.g., `liballoc` or `libstd`). +/// +/// Methods are identical in purpose and meaning to their `Write` namesakes. +trait WriteThroughCursor: Sized { + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; + fn is_write_vectored(this: &Cursor) -> bool; + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; + fn flush(this: &mut Cursor) -> io::Result<()>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteThroughCursor::write(self, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + WriteThroughCursor::write_vectored(self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + WriteThroughCursor::is_write_vectored(self) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + WriteThroughCursor::write_all(self, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + WriteThroughCursor::write_all_vectored(self, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + WriteThroughCursor::flush(self) + } +} + // Non-resizing write implementation #[inline] fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { @@ -348,117 +361,117 @@ impl Write for Cursor<&mut [u8]> { } #[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl Write for Cursor<&mut Vec> +impl WriteThroughCursor for &mut Vec where A: Allocator, { - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf) } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf)?; Ok(()) } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs)?; Ok(()) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor> +impl WriteThroughCursor for Vec where A: Allocator, { - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf) } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf)?; Ok(()) } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs)?; Ok(()) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } #[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl Write for Cursor> +impl WriteThroughCursor for Box<[u8], A> where A: Allocator, { #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); slice_write(pos, inner, buf) } #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); slice_write_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); slice_write_all(pos, inner, buf) } #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); slice_write_all_vectored(pos, inner, bufs) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 1dd1859e9a3d8..08bb34376b075 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -3,7 +3,7 @@ mod tests; use crate::alloc::Allocator; use crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Write}; use crate::sync::Arc; use crate::{cmp, fmt, mem, str}; @@ -90,33 +90,6 @@ impl Write for &mut W { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for &mut S { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -229,33 +202,6 @@ impl Write for Box { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for Box { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -804,34 +750,3 @@ where (&**self).write_fmt(fmt) } } -#[stable(feature = "io_traits_arc", since = "1.73.0")] -impl Seek for Arc -where - for<'a> &'a S: Seek, - S: crate::io::IoHandle, -{ - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (&**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (&**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (&**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (&**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (&**self).seek_relative(offset) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b06ec9af7239d..0f2d9238c37ad 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -299,7 +299,6 @@ mod tests; use core::slice::memchr; -pub(crate) use alloc_crate::io::IoHandle; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -311,8 +310,9 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, }; +pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -1762,225 +1762,6 @@ pub trait Write { } } -/// The `Seek` trait provides a cursor which can be moved within a stream of -/// bytes. -/// -/// The stream typically has a fixed size, allowing seeking relative to either -/// end or the current offset. -/// -/// # Examples -/// -/// [`File`]s implement `Seek`: -/// -/// [`File`]: crate::fs::File -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// use std::fs::File; -/// use std::io::SeekFrom; -/// -/// fn main() -> io::Result<()> { -/// let mut f = File::open("foo.txt")?; -/// -/// // move the cursor 42 bytes from the start of the file -/// f.seek(SeekFrom::Start(42))?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] -pub trait Seek { - /// Seek to an offset, in bytes, in a stream. - /// - /// A seek beyond the end of a stream is allowed, but behavior is defined - /// by the implementation. - /// - /// If the seek operation completed successfully, - /// this method returns the new position from the start of the stream. - /// That position can be used later with [`SeekFrom::Start`]. - /// - /// # Errors - /// - /// Seeking can fail, for example because it might involve flushing a buffer. - /// - /// Seeking to a negative offset is considered an error. - #[stable(feature = "rust1", since = "1.0.0")] - fn seek(&mut self, pos: SeekFrom) -> Result; - - /// Rewind to the beginning of a stream. - /// - /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. - /// - /// # Errors - /// - /// Rewinding can fail, for example because it might involve flushing a buffer. - /// - /// # Example - /// - /// ```no_run - /// use std::io::{Read, Seek, Write}; - /// use std::fs::OpenOptions; - /// - /// let mut f = OpenOptions::new() - /// .write(true) - /// .read(true) - /// .create(true) - /// .open("foo.txt")?; - /// - /// let hello = "Hello!\n"; - /// write!(f, "{hello}")?; - /// f.rewind()?; - /// - /// let mut buf = String::new(); - /// f.read_to_string(&mut buf)?; - /// assert_eq!(&buf, hello); - /// # std::io::Result::Ok(()) - /// ``` - #[stable(feature = "seek_rewind", since = "1.55.0")] - fn rewind(&mut self) -> Result<()> { - self.seek(SeekFrom::Start(0))?; - Ok(()) - } - - /// Returns the length of this stream (in bytes). - /// - /// The default implementation uses up to three seek operations. If this - /// method returns successfully, the seek position is unchanged (i.e. the - /// position before calling this method is the same as afterwards). - /// However, if this method returns an error, the seek position is - /// unspecified. - /// - /// If you need to obtain the length of *many* streams and you don't care - /// about the seek position afterwards, you can reduce the number of seek - /// operations by simply calling `seek(SeekFrom::End(0))` and using its - /// return value (it is also the stream length). - /// - /// Note that length of a stream can change over time (for example, when - /// data is appended to a file). So calling this method multiple times does - /// not necessarily return the same length each time. - /// - /// # Example - /// - /// ```no_run - /// #![feature(seek_stream_len)] - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// - /// let len = f.stream_len()?; - /// println!("The file is currently {len} bytes long"); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "seek_stream_len", issue = "59359")] - fn stream_len(&mut self) -> Result { - stream_len_default(self) - } - - /// Returns the current seek position from the start of the stream. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(0))`. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, BufRead, BufReader, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = BufReader::new(File::open("foo.txt")?); - /// - /// let before = f.stream_position()?; - /// f.read_line(&mut String::new())?; - /// let after = f.stream_position()?; - /// - /// println!("The first line was {} bytes long", after - before); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "seek_convenience", since = "1.51.0")] - fn stream_position(&mut self) -> Result { - self.seek(SeekFrom::Current(0)) - } - - /// Seeks relative to the current position. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but - /// doesn't return the new position which can allow some implementations - /// such as [`BufReader`] to perform more efficient seeks. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// f.seek_relative(10)?; - /// assert_eq!(f.stream_position()?, 10); - /// Ok(()) - /// } - /// ``` - /// - /// [`BufReader`]: crate::io::BufReader - #[stable(feature = "seek_seek_relative", since = "1.80.0")] - fn seek_relative(&mut self, offset: i64) -> Result<()> { - self.seek(SeekFrom::Current(offset))?; - Ok(()) - } -} - -pub(crate) fn stream_len_default(self_: &mut T) -> Result { - let old_pos = self_.stream_position()?; - let len = self_.seek(SeekFrom::End(0))?; - - // Avoid seeking a third time when we were already at the end of the - // stream. The branch is usually way cheaper than a seek operation. - if old_pos != len { - self_.seek(SeekFrom::Start(old_pos))?; - } - - Ok(len) -} - -/// Enumeration of possible methods to seek within an I/O object. -/// -/// It is used by the [`Seek`] trait. -#[derive(Copy, PartialEq, Eq, Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] -pub enum SeekFrom { - /// Sets the offset to the provided number of bytes. - #[stable(feature = "rust1", since = "1.0.0")] - Start(#[stable(feature = "rust1", since = "1.0.0")] u64), - - /// Sets the offset to the size of this object plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - End(#[stable(feature = "rust1", since = "1.0.0")] i64), - - /// Sets the offset to the current position plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - Current(#[stable(feature = "rust1", since = "1.0.0")] i64), -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { @@ -2632,49 +2413,6 @@ impl BufRead for Take { } } -#[stable(feature = "seek_io_take", since = "1.89.0")] -impl Seek for Take { - fn seek(&mut self, pos: SeekFrom) -> Result { - let new_position = match pos { - SeekFrom::Start(v) => Some(v), - SeekFrom::Current(v) => self.position().checked_add_signed(v), - SeekFrom::End(v) => self.len.checked_add_signed(v), - }; - let new_position = match new_position { - Some(v) if v <= self.len => v, - _ => return Err(ErrorKind::InvalidInput.into()), - }; - while new_position != self.position() { - if let Some(offset) = new_position.checked_signed_diff(self.position()) { - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - break; - } - let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - } - Ok(new_position) - } - - fn stream_len(&mut self) -> Result { - Ok(self.len) - } - - fn stream_position(&mut self) -> Result { - Ok(self.position()) - } - - fn seek_relative(&mut self, offset: i64) -> Result<()> { - if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { - return Err(ErrorKind::InvalidInput.into()); - } - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - Ok(()) - } -} - /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index fbb69a11c6b92..ce2137f9567c7 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -5,8 +5,7 @@ mod tests; use crate::fmt; use crate::io::{ - self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Seek, SeekFrom, Sink, - Write, + self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Sink, Write, }; #[stable(feature = "rust1", since = "1.0.0")] @@ -84,24 +83,6 @@ impl BufRead for Empty { } } -#[stable(feature = "empty_seek", since = "1.51.0")] -impl Seek for Empty { - #[inline] - fn seek(&mut self, _pos: SeekFrom) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - Ok(0) - } -} - #[stable(feature = "empty_write", since = "1.73.0")] impl Write for Empty { #[inline] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index afa30c8c00f27..bc1ba00636407 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -374,6 +374,7 @@ #![feature(random)] #![feature(raw_os_error_ty)] #![feature(seek_io_take_position)] +#![feature(seek_stream_len)] #![feature(share_trait)] #![feature(slice_internals)] #![feature(slice_ptr_get)] diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 703113783f198..c226a0dc2403f 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -199,7 +199,7 @@ impl Drop for OwnedFd { unsafe { // Note that errors are ignored when closing a file descriptor. According to POSIX 2024, // we can and indeed should retry `close` on `EINTR` - // (https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/close.html), + // (https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html), // but it is not clear yet how well widely-used implementations are conforming with this // mandate since older versions of POSIX left the state of the FD after an `EINTR` // unspecified. Ignoring errors is "fine" because some of the major Unices (in diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index dcec7b2a1077c..3a35aa3006d75 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -102,7 +102,7 @@ pub impl(self) trait CommandExt { /// locations might not appear where intended. /// /// [POSIX fork() specification]: - /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html + /// https://pubs.opengroup.org/onlinepubs/9799919799/functions/fork.html /// [`std::env`]: mod@crate::env /// [`Error::new`]: ../../../io/struct.Error.html#method.new /// [`Error::other`]: ../../../io/struct.Error.html#method.other diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 2687a12919701..b3e2c2bd6c6a3 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -4137,7 +4137,7 @@ impl Error for NormalizeError {} /// Note that this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior -/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 +/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap04.html#tag_04_16 /// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew /// [cygwin-path]: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html #[stable(feature = "absolute_path", since = "1.79.0")] diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 8fee56348c071..7283e8f710c55 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1926,7 +1926,7 @@ impl fmt::Debug for File { // Format in octal, followed by the mode format used in `ls -l`. // // References: -// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html +// https://pubs.opengroup.org/onlinepubs/9799919799/utilities/ls.html // https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html // https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html // diff --git a/library/std/src/sys/pal/unix/conf.rs b/library/std/src/sys/pal/unix/conf.rs index 4c00379a88439..6d42e890fd45b 100644 --- a/library/std/src/sys/pal/unix/conf.rs +++ b/library/std/src/sys/pal/unix/conf.rs @@ -11,7 +11,7 @@ pub fn page_size() -> usize { /// `_CS_PATH` or `_CS_V[67]_ENV` in the future). /// /// [posix_confstr]: -/// https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html +/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/confstr.html #[cfg(target_vendor = "apple")] pub fn confstr( key: crate::ffi::c_int, diff --git a/library/std/src/sys/pal/unix/sync/mutex.rs b/library/std/src/sys/pal/unix/sync/mutex.rs index 557e70af94ba7..145bfb1e22413 100644 --- a/library/std/src/sys/pal/unix/sync/mutex.rs +++ b/library/std/src/sys/pal/unix/sync/mutex.rs @@ -25,7 +25,7 @@ impl Mutex { // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you // try to re-lock it from the same thread when you already hold a lock - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // (https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_mutex_init.html). // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index b49c39a9253fa..5a8c0c77745e0 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -20,8 +20,8 @@ pub const HAS_PREFIXES: bool = false; pub(crate) fn absolute(path: &Path) -> io::Result { // This is mostly a wrapper around collecting `Path::components`, with // exceptions made where this conflicts with the POSIX specification. - // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017 - // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + // See 4.16 Pathname Resolution, IEEE Std 1003.1-2024 + // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap04.html#tag_04_16 // Get the components, skipping the redundant leading "." component if it exists. let mut components = path.strip_prefix(".").unwrap_or(path).components(); diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 55906ed2854f2..dc11f44574a5f 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -1101,7 +1101,7 @@ impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html. If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. match NonZero::try_from(self.0) { diff --git a/library/std/src/sys/process/unix/unsupported/wait_status.rs b/library/std/src/sys/process/unix/unsupported/wait_status.rs index f348d557e4b7e..ac54824875813 100644 --- a/library/std/src/sys/process/unix/unsupported/wait_status.rs +++ b/library/std/src/sys/process/unix/unsupported/wait_status.rs @@ -46,7 +46,7 @@ impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html. If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. match NonZero::try_from(self.wait_status) { diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index bea057ceed1b0..33e7f6a9a0fea 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -221,7 +221,7 @@ impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html. If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. match NonZero::try_from(self.0) { diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index b3ab1c7c4485b..c9258f9558de8 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -2,7 +2,7 @@ **Tier: 3** -Support for the [QNX®][qnx.com] [QNX Software Development Platform (SDP)], version 7.0, 7.1 and 8.0. +Support for the [QNX®](https://qnx.com) [QNX Software Development Platform (SDP)], version 7.0, 7.1 and 8.0. [QNX Software Development Platform (SDP)]: https://qnx.software/en/software/products-and-solutions/qnx-software-development-platform diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a12e95d210967..abf017174b547 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -31,7 +31,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::{debug, info, instrument, trace}; use crate::clean::utils::find_nearest_parent_module; -use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType}; +use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType, reexport_chain}; use crate::core::DocContext; use crate::html::markdown::{MarkdownLink, MarkdownLinkRange, markdown_links}; use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}; @@ -1148,10 +1148,19 @@ impl LinkCollector<'_, '_> { // `use` statement, we need to use the `def_id` of the `use` statement, not the // inlined item. // - let item_id = if let Some(inline_stmt_id) = item.inline_stmt_id - && find_attr!(tcx, inline_stmt_id, Deprecated { span, ..} if span == depr_span) - { - inline_stmt_id.to_def_id() + let item_id = if let Some(inline_stmt_id) = item.inline_stmt_id { + let target_def_id = item.item_id.expect_def_id(); + reexport_chain(tcx, inline_stmt_id, target_def_id) + .iter() + .flat_map(|reexport| reexport.id()) + .find(|&reexport_def_id| { + find_attr!( + tcx, + reexport_def_id, + Deprecated { span, .. } if span == depr_span + ) + }) + .unwrap_or(target_def_id) } else { item.item_id.expect_def_id() }; diff --git a/tests/crashes/136661.rs b/tests/crashes/136661.rs deleted file mode 100644 index 76161a566f4c7..0000000000000 --- a/tests/crashes/136661.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ known-bug: #136661 - -#![allow(unused)] - -trait Supertrait {} - -trait Other { - fn method(&self) {} -} - -impl WithAssoc for &'static () { - type As = (); -} - -trait WithAssoc { - type As; -} - -trait Trait: Supertrait { - fn method(&self) {} -} - -fn hrtb Trait<&'a ()>>() {} - -pub fn main() {} diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-from-inlined-reexport-chain.rs b/tests/rustdoc-ui/intra-doc/deprecated-note-from-inlined-reexport-chain.rs new file mode 100644 index 0000000000000..d2e527fcb29f3 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-from-inlined-reexport-chain.rs @@ -0,0 +1,18 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/158745. +// This checks that rustdoc resolves intra-doc links in deprecation notes using +// the re-export that carries the attribute, even when that re-export is later +// inlined through another re-export. + +#![deny(rustdoc::broken_intra_doc_links)] + +mod bar { + pub struct A; +} + +#[deprecated(note = "[std::io::ErrorKind::NotFound]")] +pub use bar::A; + +#[doc(inline)] +pub use A as X; diff --git a/tests/ui-fulldeps/rustc_public/check_transform.rs b/tests/ui-fulldeps/rustc_public/check_transform.rs index 17b9612db3067..a87e45380074e 100644 --- a/tests/ui-fulldeps/rustc_public/check_transform.rs +++ b/tests/ui-fulldeps/rustc_public/check_transform.rs @@ -95,7 +95,7 @@ fn change_panic_msg(mut body: Body, new_msg: &str) -> Body { let new_const = MirConst::from_str(new_msg); args[0] = Operand::Constant(ConstOperand { const_: new_const, - span: bb.terminator.span, + span: bb.terminator.source_info.span, user_ty: None, }); } diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index bcc3abf4ff267..98ba1de498bbe 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -14,6 +14,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `7e10` `a` `aclass` +`acquire-release` `addsubiw` `adx` `aes` @@ -223,6 +224,8 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `mul32high` `multivalue` `mutable-globals` +`mve` +`mve.fp` `neon` `nnp-assist` `nontrapping-fptoint` @@ -366,6 +369,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `v68` `v69` `v6k` +`v6m` `v6t2` `v7` `v71` @@ -374,6 +378,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `v79` `v8` `v8.1a` +`v8.1m.main` `v8.2a` `v8.3a` `v8.4a` @@ -382,6 +387,8 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `v8.7a` `v8.8a` `v8.9a` +`v8m` +`v8m.main` `v8plus` `v9` `v9.1a` diff --git a/tests/ui/impl-trait/return-never-type.rs b/tests/ui/impl-trait/return-never-type.rs new file mode 100644 index 0000000000000..97ad2756dce71 --- /dev/null +++ b/tests/ui/impl-trait/return-never-type.rs @@ -0,0 +1,14 @@ +//@ edition:2024 + +#![feature(never_type)] + +use std::ops::Add; + +fn foo() -> impl Add { + //~^ ERROR cannot add `u32` to `!` + //~| HELP the trait `Add` is not implemented for `!` + unimplemented!() + //~^ HELP `!` can be coerced to any type; consider casting it to a concrete type that implements the trait +} + +fn main() {} diff --git a/tests/ui/impl-trait/return-never-type.stderr b/tests/ui/impl-trait/return-never-type.stderr new file mode 100644 index 0000000000000..dff06d7f41c98 --- /dev/null +++ b/tests/ui/impl-trait/return-never-type.stderr @@ -0,0 +1,18 @@ +error[E0277]: cannot add `u32` to `!` + --> $DIR/return-never-type.rs:7:13 + | +LL | fn foo() -> impl Add { + | ^^^^^^^^^^^^^ no implementation for `! + u32` +... +LL | unimplemented!() + | ---------------- return type was inferred to be `!` here + | + = help: the trait `Add` is not implemented for `!` +help: `!` can be coerced to any type; consider casting it to a concrete type that implements the trait + | +LL | unimplemented!() as /* Type */ + | +++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion.next.stderr b/tests/ui/impl-trait/unsized_coercion.next.stderr deleted file mode 100644 index f5ca850336fbc..0000000000000 --- a/tests/ui/impl-trait/unsized_coercion.next.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/unsized_coercion.rs:12:15 - | -LL | fn hello() -> Box { - | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/unsized_coercion.rs:15:17 - | -LL | let x = hello(); - | ^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion.rs b/tests/ui/impl-trait/unsized_coercion.rs index 3f5ec3de89f31..f77f2198be0ef 100644 --- a/tests/ui/impl-trait/unsized_coercion.rs +++ b/tests/ui/impl-trait/unsized_coercion.rs @@ -3,17 +3,15 @@ //@ revisions: next old //@[next] compile-flags: -Znext-solver -//@[old] check-pass +//@ check-pass trait Trait {} impl Trait for u32 {} fn hello() -> Box { -//[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time if true { let x = hello(); - //[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time let y: Box = x; } Box::new(1u32) diff --git a/tests/ui/parser/bare-type-in-impl-parameter.rs b/tests/ui/parser/bare-type-in-impl-parameter.rs new file mode 100644 index 0000000000000..7e71d4d2f283a --- /dev/null +++ b/tests/ui/parser/bare-type-in-impl-parameter.rs @@ -0,0 +1 @@ +impl> Z for X {} //~ ERROR: expected type parameter, found path `Y` diff --git a/tests/ui/parser/bare-type-in-impl-parameter.stderr b/tests/ui/parser/bare-type-in-impl-parameter.stderr new file mode 100644 index 0000000000000..87b65793eec9b --- /dev/null +++ b/tests/ui/parser/bare-type-in-impl-parameter.stderr @@ -0,0 +1,18 @@ +error: expected type parameter, found path `Y` + --> $DIR/bare-type-in-impl-parameter.rs:1:6 + | +LL | impl> Z for X {} + | ^^^^^^^^^^ + | +help: you might have meant to bind a type parameter to a trait + | +LL | impl> Z for X {} + | ++ +help: alternatively, you might have meant to introduce type parameter + | +LL - impl> Z for X {} +LL + impl Z for X {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/process/process-spawn-failure.rs b/tests/ui/process/process-spawn-failure.rs index ac2c34bc7836d..b3f0071670bc6 100644 --- a/tests/ui/process/process-spawn-failure.rs +++ b/tests/ui/process/process-spawn-failure.rs @@ -38,7 +38,7 @@ fn find_zombies() { extern crate libc; let my_pid = unsafe { libc::getpid() }; - // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html + // https://pubs.opengroup.org/onlinepubs/9799919799/utilities/ps.html let ps_cmd_output = Command::new("ps").args(&["-A", "-o", "pid,ppid,args"]).output().unwrap(); let ps_output = String::from_utf8_lossy(&ps_cmd_output.stdout); // On AIX, the PPID is not always present, such as when a process is blocked diff --git a/tests/ui/sized/infer_var_subtyping.rs b/tests/ui/sized/infer_var_subtyping.rs new file mode 100644 index 0000000000000..2535a71aac619 --- /dev/null +++ b/tests/ui/sized/infer_var_subtyping.rs @@ -0,0 +1,19 @@ +// Regression test for failure #4 in . +// +// This checks that when we are checking the sized-ness of an inference variable +// (here coming from the never-to-any coercion) we consider subtyping. That is, +// `?0` is sized if `(?0 <: ?1 || ?0 :> ?1) && ?1: Sized`). +// +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(never_type)] + +fn blah(e: !) { + let source = Box::new(e); + let _: Box = source; +} + +fn main() {} diff --git a/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs b/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs index 2bd8529df3991..16c0f80b1c8dd 100644 --- a/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs +++ b/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs @@ -16,7 +16,7 @@ where } impl<'a, T> Foo for &T -//~^ ERROR: conflicting implementations of trait `Foo` for type `&_` +//~^ ERROR: cycle detected when computing normalized predicates of `` where Self::Item: Baz, { diff --git a/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr b/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr index cae27b4b31097..4c2dc5e8e71fa 100644 --- a/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr +++ b/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr @@ -1,17 +1,22 @@ -error[E0119]: conflicting implementations of trait `Foo` for type `&_` +error[E0391]: cycle detected when computing normalized predicates of `` --> $DIR/next-solver-region-resolution.rs:18:1 | -LL | / impl<'a, T> Foo for &'a T -LL | | where -LL | | Self::Item: 'a, - | |___________________- first implementation here -... LL | / impl<'a, T> Foo for &T LL | | LL | | where LL | | Self::Item: Baz, - | |____________________^ conflicting implementation for `&_` + | |____________________^ + | + = note: ...which immediately requires computing normalized predicates of `` again +note: cycle used when computing whether impls specialize one another + --> $DIR/next-solver-region-resolution.rs:12:1 + | +LL | / impl<'a, T> Foo for &'a T +LL | | where +LL | | Self::Item: 'a, + | |___________________^ + = note: for more information, see and error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr index 1e450571ed9da..147b248c33da8 100644 --- a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr @@ -18,8 +18,9 @@ LL | fn accept0(_: Container<{ T::make() }>) {} = note: ...which requires building an abstract representation for `accept0::{constant#0}`... = note: ...which requires building THIR for `accept0::{constant#0}`... = note: ...which requires type-checking `accept0::{constant#0}`... + = note: ...which requires computing normalized predicates of `accept0::{constant#0}`... = note: ...which again requires evaluating type-level constant, completing the cycle - = note: cycle used when checking that `accept0` is well-formed + = note: cycle used when computing normalized predicates of `accept0` = note: for more information, see and error[E0391]: cycle detected when checking if `accept1::{constant#0}` is a trivial const @@ -32,6 +33,7 @@ LL | const fn accept1(_: Container<{ T::make() }>) {} = note: ...which requires building an abstract representation for `accept1::{constant#0}`... = note: ...which requires building THIR for `accept1::{constant#0}`... = note: ...which requires type-checking `accept1::{constant#0}`... + = note: ...which requires computing normalized predicates of `accept1::{constant#0}`... = note: ...which requires evaluating type-level constant... = note: ...which requires const-evaluating + checking `accept1::{constant#0}`... = note: ...which again requires checking if `accept1::{constant#0}` is a trivial const, completing the cycle diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs index 432b13d161afb..7c3985f30cae9 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs @@ -21,6 +21,7 @@ trait Foo { impl Foo for () { type Item = String where String: Copy; //~^ ERROR overflow evaluating the requirement `String: Copy` + //~| ERROR: overflow evaluating the requirement `<() as Foo>::Item == _` [E0275] } fn main() { diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index b20534f5dc584..d82c5016eeaca 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -1,3 +1,9 @@ +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` + --> $DIR/alias-bound-unsound.rs:22:5 + | +LL | type Item = String where String: Copy; + | ^^^^^^^^^ + error[E0275]: overflow evaluating the requirement `String: Copy` --> $DIR/alias-bound-unsound.rs:22:38 | @@ -13,17 +19,17 @@ LL | type Item: Copy | ^^^^ this trait's associated type doesn't have the requirement `String: Copy` error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == String` - --> $DIR/alias-bound-unsound.rs:28:22 + --> $DIR/alias-bound-unsound.rs:29:22 | LL | let _ = identity(<() as Foo>::copy_me(&x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:28:43 + --> $DIR/alias-bound-unsound.rs:29:43 | LL | let _ = identity(<() as Foo>::copy_me(&x)); | ^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs index ffbbecaf89570..83e300b077428 100644 --- a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs @@ -38,6 +38,7 @@ fn impls_bound() { // - normalize `>::Assoc` // - via blanket impl, requires where-clause `Foo: Bound` -> cycle fn generic() +//~^ ERROR the trait bound `Foo: Bound` is not satisfied where >::Assoc: Bound, //~^ ERROR the trait bound `Foo: Bound` is not satisfied diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr index f679b94a92377..e18265190278a 100644 --- a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr @@ -1,5 +1,32 @@ error[E0277]: the trait bound `Foo: Bound` is not satisfied - --> $DIR/normalizes-to-is-not-productive.rs:42:31 + --> $DIR/normalizes-to-is-not-productive.rs:40:1 + | +LL | / fn generic() +LL | | +LL | | where +LL | | >::Assoc: Bound, + | |____________________________________^ unsatisfied trait bound + | +help: the trait `Bound` is not implemented for `Foo` + --> $DIR/normalizes-to-is-not-productive.rs:18:1 + | +LL | struct Foo; + | ^^^^^^^^^^ +help: the trait `Bound` is implemented for `u32` + --> $DIR/normalizes-to-is-not-productive.rs:11:1 + | +LL | impl Bound for u32 { + | ^^^^^^^^^^^^^^^^^^ +note: required for `Foo` to implement `Trait` + --> $DIR/normalizes-to-is-not-productive.rs:23:19 + | +LL | impl Trait for T { + | ----- ^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:43:31 | LL | >::Assoc: Bound, | ^^^^^ unsatisfied trait bound @@ -30,7 +57,7 @@ LL | | } | |_^ required by this bound in `Bound` error[E0277]: the trait bound `Foo: Bound` is not satisfied - --> $DIR/normalizes-to-is-not-productive.rs:47:19 + --> $DIR/normalizes-to-is-not-productive.rs:48:19 | LL | impls_bound::(); | ^^^ unsatisfied trait bound @@ -51,6 +78,6 @@ note: required by a bound in `impls_bound` LL | fn impls_bound() { | ^^^^^ required by this bound in `impls_bound` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs index 914773c82196a..4d088073c3ba6 100644 --- a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs +++ b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs @@ -14,7 +14,7 @@ fn foo() where T: for<'a> Proj<'a, Assoc = for<'b> fn(>::Assoc)>, (): Trait<>::Assoc> - //~^ ERROR: overflow evaluating the requirement `(): Trait<>::Assoc>` + //~^ ERROR: the trait bound `(): Trait fn(>::Assoc))>` is not satisfied { } diff --git a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr index e2ee83cfadbef..1408890184a90 100644 --- a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr +++ b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr @@ -1,11 +1,14 @@ -error[E0275]: overflow evaluating the requirement `(): Trait<>::Assoc>` +error[E0277]: the trait bound `(): Trait fn(>::Assoc))>` is not satisfied --> $DIR/find-param-recursion-issue-152716.rs:16:9 | LL | (): Trait<>::Assoc> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait fn(>::Assoc))>` is not implemented for `()` | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`find_param_recursion_issue_152716`) +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | (): Trait<>::Assoc>, (): Trait fn(>::Assoc))> + | +++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr index d3effe2eb0389..553a2157074f3 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr @@ -35,6 +35,16 @@ LL | trait A { LL | fn f() | ^ this trait's associated function doesn't have the requirement `_: A` +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc == _` + --> $DIR/normalize-param-env-2.rs:22:5 + | +LL | / fn f() +LL | | where +LL | | Self::Assoc: A, + | |__________________________^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` --> $DIR/normalize-param-env-2.rs:24:22 | @@ -82,7 +92,7 @@ LL | where LL | Self::Assoc: A, | ^^^^ required by this bound in `A::f` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0275, E0283. For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr index 47d38365e970e..aea394c6ec58a 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr @@ -1,3 +1,11 @@ +error[E0275]: overflow evaluating the requirement `::Assoc == _` + --> $DIR/normalize-param-env-4.rs:17:1 + | +LL | / fn foo() +LL | | where +LL | | ::Assoc: Trait, + | |_______________________________^ + error[E0275]: overflow evaluating the requirement `::Assoc: Trait` --> $DIR/normalize-param-env-4.rs:19:26 | @@ -22,6 +30,6 @@ note: required by a bound in `impls_trait` LL | fn impls_trait() {} | ^^^^^ required by this bound in `impls_trait` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/normalize/param_env-region-constraints-ambiguity.rs b/tests/ui/traits/next-solver/normalize/param_env-region-constraints-ambiguity.rs new file mode 100644 index 0000000000000..08a7148ca5e88 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/param_env-region-constraints-ambiguity.rs @@ -0,0 +1,101 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#265. Different where-clauses +// normalizing to the same bound caused ambiguity errors if we lazily normalized +// where-clauses when using them to prove a goal. +// +// We avoid these errors by eagerly normalizing the `param_env`. + +mod one { + trait Trait { + type Assoc<'a> + where + Self: 'a; + } + + trait Bound<'a> {} + fn impls_bound<'a, T: Bound<'a>>() {} + fn foo<'a, T: 'a>() + where + T: Trait = T> + Bound<'a>, + T::Assoc<'a>: Bound<'a>, + { + impls_bound::<'_, T>(); + } +} + +mod two { + trait Trait { + type Assoc<'a> + where + Self: 'a; + } + + trait Bound {} + fn impls_bound() {} + fn foo<'a, T: 'a>() + where + T: Trait = T> + Bound, + T::Assoc<'a>: Bound, + { + impls_bound::(); + } +} + +// Minimization of tokio-par-util. +mod three { + trait Trait1 { + type Assoc1; + } + + trait Trait2 { + type Assoc2; + } + + struct Indir(T); + impl Trait2 for Indir + where + T: Trait1, + T::Assoc1: Trait2 + 'static, + { + type Assoc2 = ::Assoc2; + } + + struct WrapperTwo(T, U); + + impl Trait1 for WrapperTwo + where + T: Trait1, + T::Assoc1: Trait2 + 'static, + // additional region constraint in this candidate so they + // can't be merged. + U: Trait1 as Trait2>::Assoc2>, + U: Trait1::Assoc2>, + { + type Assoc1 = i32; + } +} + +// Minimization of `qazer` +mod four { + trait Value { + type SelfType<'a> + where + Self: 'a; + } + + trait Repository {} + struct RedbRepo(From, Into); + + impl Repository for RedbRepo + where + for<'a> From: Value = From> + Clone + 'static, + for<'a> ::SelfType<'a>: Clone, + { + } +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs index ed52d05b39c99..8dc27c0da605a 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs @@ -13,8 +13,7 @@ fn needs_bar() {} fn test::Assoc2> + Foo2::Assoc1>>() { needs_bar::(); - //~^ ERROR: the trait bound `::Assoc2: Bar` is not satisfied - //~| ERROR: the size for values of type `::Assoc2` cannot be known at compilation time + //~^ ERROR: the trait bound `::Assoc1: Bar` is not satisfied } fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr index 40291ce0cfb86..6f5111a6193ca 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `::Assoc2: Bar` is not satisfied +error[E0277]: the trait bound `::Assoc1: Bar` is not satisfied --> $DIR/recursive-self-normalization-2.rs:15:17 | LL | needs_bar::(); - | ^^^^^^^^^ the trait `Bar` is not implemented for `::Assoc2` + | ^^^^^^^^^ the trait `Bar` is not implemented for `::Assoc1` | note: required by a bound in `needs_bar` --> $DIR/recursive-self-normalization-2.rs:12:17 @@ -11,30 +11,9 @@ LL | fn needs_bar() {} | ^^^ required by this bound in `needs_bar` help: consider further restricting the associated type | -LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc2: Bar { +LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc1: Bar { | ++++++++++++++++++++++++++++++ -error[E0277]: the size for values of type `::Assoc2` cannot be known at compilation time - --> $DIR/recursive-self-normalization-2.rs:15:17 - | -LL | needs_bar::(); - | ^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `::Assoc2` -note: required by an implicit `Sized` bound in `needs_bar` - --> $DIR/recursive-self-normalization-2.rs:12:14 - | -LL | fn needs_bar() {} - | ^ required by the implicit `Sized` requirement on this type parameter in `needs_bar` -help: consider further restricting the associated type - | -LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc2: Sized { - | ++++++++++++++++++++++++++++++++ -help: consider relaxing the implicit `Sized` restriction - | -LL | fn needs_bar() {} - | ++++++++ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-1.rs b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-1.rs new file mode 100644 index 0000000000000..7fcb164d3a95d --- /dev/null +++ b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-1.rs @@ -0,0 +1,41 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// In `fn hrtb` normalizing the elaborated where-clause +// `T: for<'a> Supertrait<<&'a () as WithAssoc>::As>` results in +// a region error as `&'a ()` is not equal to `&'static ()`. +// +// This happens even though the where-clauses themselves are well-formed +// as we never have to prove `&'a (): WithAssoc` as `'a` is a bound variable. +// +// I don't think this can cause unsoundness and has already been accepted in the +// old solver as we didn't register TypeOutlives constraints if `ignoring_regions` +// is set. The new solver always registers outlives constraints, so this then +// caused an error there. We need to ignore these region errors with the new solver +// due to trait-system-refactor-initiative#166. + +#![allow(unused)] + +trait Supertrait {} + +trait Other { + fn method(&self) {} +} + +impl WithAssoc for T { + type As = T; +} + +trait WithAssoc { + type As; +} + +trait Trait: Supertrait { + fn method(&self) {} +} + +fn hrtb Trait<&'a ()>>() {} + +pub fn main() {} diff --git a/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-2.rs b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-2.rs new file mode 100644 index 0000000000000..9339518aae1a0 --- /dev/null +++ b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-2.rs @@ -0,0 +1,39 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// In `fn hrtb` normalizing the elaborated where-clause +// `T: for<'a> Supertrait<<&'a () as WithAssoc>::As>` results in +// a region error as `&'a ()` is not `'static`. +// +// This happens even though the where-clauses themselves are well-formed +// as we never have to prove `&'a (): WithAssoc` as `'a` is a bound variable. +// +// Unlike `normalize-param-env-missing-implied-bound-1.rs` this snippet caused +// an ICE with the old trait solver, as we did register region constraints from +// relating types. This ICE was tracked in #136661. + +#![allow(unused)] + +trait Supertrait {} + +trait Other { + fn method(&self) {} +} + +impl WithAssoc for &'static () { + type As = (); +} + +trait WithAssoc { + type As; +} + +trait Trait: Supertrait { + fn method(&self) {} +} + +fn hrtb Trait<&'a ()>>() {} + +pub fn main() {}