diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 6850104f1cda6..c86d6366e0753 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -4096,6 +4096,7 @@ pub struct TestBinderConstraints { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct TestBinderBody { + pub span: Span, pub foralls: ThinVec, pub exists: ThinVec, pub constraints: Vec, diff --git a/compiler/rustc_ast_lowering/src/delegation/mod.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs index b2c0e1ffa81b1..eb980d31ce1ef 100644 --- a/compiler/rustc_ast_lowering/src/delegation/mod.rs +++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs @@ -51,7 +51,7 @@ use rustc_hir::{self as hir, FnDeclFlags, QPath}; use rustc_middle::ty::Asyncness; use rustc_span::def_id::DefId; use rustc_span::symbol::kw; -use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol}; use crate::delegation::generics::{GenericsGenerationResults, GenericsPosition}; use crate::delegation::resolution::resolver::DelegationResolver; @@ -480,7 +480,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc_slice(&[hir::ExprField { hir_id: self.next_id(), is_shorthand: false, - ident: Ident::new(sym::integer(0), span), + ident: Ident::new(kw::Underscore, span), expr: self.arena.alloc(call), span, }]), diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 14ce5ffc23314..abeafe2580171 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -2015,7 +2015,8 @@ impl<'hir> LoweringContext<'_, 'hir> { .iter() .map(|predicate| self.lower_where_predicate(predicate, &[], &mut dedup_map)), ); - hir::TestBinderBody { foralls, exists, constraints, predicates } + let span = self.lower_span(body.span); + hir::TestBinderBody { foralls, exists, constraints, predicates, span } } fn lower_test_binder_forall( diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 6def9c6dfb98a..70595316845bc 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -1333,3 +1333,11 @@ pub(crate) struct AbiCustomCannotBeCold { #[label("`extern \"custom\"` because of this")] pub abi_span: Span, } + +#[derive(Diagnostic)] +#[diag("showing the spans of the {$kind}")] +pub(crate) struct ShowSpan { + #[primary_span] + pub span: Span, + pub kind: &'static str, +} diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 918a623b41752..5742d5a9e1a73 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -12,3 +12,4 @@ pub mod ast_validation; mod diagnostics; pub mod feature_gate; +pub mod show_spans; diff --git a/compiler/rustc_ast_passes/src/show_spans.rs b/compiler/rustc_ast_passes/src/show_spans.rs new file mode 100644 index 0000000000000..b28535653b0ed --- /dev/null +++ b/compiler/rustc_ast_passes/src/show_spans.rs @@ -0,0 +1,67 @@ +use rustc_ast::visit::{self, Visitor}; +use rustc_ast::{self as ast, ItemKind}; +use rustc_attr_ir::{Attribute, AttributeKind, RustcShowSpansKind}; +use rustc_attr_parsing::AttributeParser; +use rustc_session::Session; +use rustc_span::sym; + +use crate::diagnostics; + +struct ShowSpans<'a> { + sess: &'a Session, + expr: bool, + ty: bool, + path: bool, +} + +impl<'a> Visitor<'a> for ShowSpans<'a> { + fn visit_item(&mut self, item: &'a ast::Item) { + let saved = (self.expr, self.ty, self.path); + (self.expr, self.ty, self.path) = (false, false, false); + + if let ItemKind::Fn(_) = &item.kind + && let Some(Attribute::Parsed(AttributeKind::RustcShowSpans(kinds))) = + AttributeParser::parse_limited_sym(self.sess, &item.attrs, &[sym::rustc_show_spans]) + { + for kind in kinds { + match kind { + RustcShowSpansKind::Expr => self.expr = true, + RustcShowSpansKind::Ty => self.ty = true, + RustcShowSpansKind::Path => self.path = true, + } + } + } + visit::walk_item(self, item); + + // Recover the state. Needed if we are looking into nested items. + (self.expr, self.ty, self.path) = saved; + } + + // We override it as to not report attribute paths as paths. + fn visit_attribute(&mut self, _: &'a ast::Attribute) {} + + fn visit_expr(&mut self, e: &'a ast::Expr) { + if self.expr { + self.sess.dcx().emit_note(diagnostics::ShowSpan { span: e.span, kind: "expr" }); + } + visit::walk_expr(self, e); + } + + fn visit_ty(&mut self, t: &'a ast::Ty) { + if self.ty { + self.sess.dcx().emit_note(diagnostics::ShowSpan { span: t.span, kind: "ty" }); + } + visit::walk_ty(self, t); + } + + fn visit_path(&mut self, p: &'a ast::Path) { + if self.path { + self.sess.dcx().emit_note(diagnostics::ShowSpan { span: p.span, kind: "path" }); + } + visit::walk_path(self, p); + } +} + +pub fn check_crate(sess: &Session, krate: &ast::Crate) { + visit::walk_crate(&mut ShowSpans { sess, expr: false, ty: false, path: false }, krate); +} diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index aa1bb2b5fae5e..6ae4eb1d4b388 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -577,6 +577,13 @@ impl rustc_serialize::Encodable for DocAttribute } } +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] +pub enum RustcShowSpansKind { + Expr, + Ty, + Path, +} + #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub enum RustcDumpLayoutKind { Align, @@ -1426,6 +1433,9 @@ pub enum AttributeKind { /// Represents `#[rustc_should_not_be_called_on_const_items]` RustcShouldNotBeCalledOnConstItems, + /// Represents `#[rustc_show_spans]` + RustcShowSpans(ThinVec), + /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`. RustcSimdMonomorphizeLaneLimit(Limit), diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index 5779bfb6c8624..ac49e4c17ffa8 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -189,6 +189,7 @@ impl AttributeKind { RustcRegions => No, RustcScalableVector { .. } => Yes, RustcShouldNotBeCalledOnConstItems => Yes, + RustcShowSpans(_) => No, RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate RustcSkipDuringMethodDispatch { .. } => No, RustcSpecializationTrait => No, @@ -405,6 +406,7 @@ impl AttributeKind { RustcRegions => false, RustcScalableVector { .. } => false, RustcShouldNotBeCalledOnConstItems => false, + RustcShowSpans(_) => false, RustcSimdMonomorphizeLaneLimit(..) => false, RustcSkipDuringMethodDispatch { .. } => false, RustcSpecializationTrait => false, diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 99c7e933b07b4..07ce47b8858e2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -1,5 +1,5 @@ use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; -use rustc_attr_ir::{AttributeKind, RustcDumpLayoutKind}; +use rustc_attr_ir::{AttributeKind, RustcDumpLayoutKind, RustcShowSpansKind}; use rustc_feature::AttributeStability; use rustc_span::{Span, Symbol, sym}; @@ -301,3 +301,44 @@ impl NoArgsAttributeParser for RustcDumpVtableParser { const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDumpVtable; } + +pub(crate) struct RustcShowSpans; + +impl SingleAttributeParser for RustcShowSpans { + const PATH: &[Symbol] = &[sym::rustc_show_spans]; + + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + + const TEMPLATE: AttributeTemplate = template!(List: &["expr", "ty", "path"]); + const STABILITY: AttributeStability = unstable!(rustc_attrs); + + fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { + let Some(items) = cx.expect_list(args, cx.attr_span) else { + return None; + }; + + let mut result = ThinVec::new(); + for item in items.mixed() { + let Some(arg) = item.meta_item_no_args() else { + cx.adcx().expected_not_literal(item.span()); + continue; + }; + let Some(ident) = arg.ident() else { + cx.adcx().expected_identifier(arg.span()); + return None; + }; + let kind = match ident.name { + sym::expr => RustcShowSpansKind::Expr, + sym::ty => RustcShowSpansKind::Ty, + sym::path => RustcShowSpansKind::Path, + _ => { + cx.adcx() + .expected_specific_argument(ident.span, &[sym::expr, sym::ty, sym::path]); + continue; + } + }; + result.push(kind); + } + Some(AttributeKind::RustcShowSpans(result)) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 067c372cc527a..f5e1868364355 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -252,6 +252,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 85e94a6d88d6f..4b2c1ff13160b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -385,6 +385,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ // Internal attributes, Testing: // ========================================================================== + sym::rustc_show_spans, sym::rustc_effective_visibility, sym::rustc_dump_inferred_outlives, sym::rustc_capture_analysis, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3801fb2f5fa7c..17de5f003b27a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -770,7 +770,7 @@ declare_features! ( (incomplete, unsafe_fields, "1.85.0", Some(132922)), /// Allows const generic parameters to be defined with types that /// are not `Sized`, e.g. `fn foo() {`. - (incomplete, unsized_const_params, "1.82.0", Some(95174)), + (incomplete, unsized_const_params, "1.82.0", Some(128028)), /// Allows unsized fn parameters. (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ff7f08dda5510..4b27781086b02 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4530,6 +4530,7 @@ impl FnHeader { #[derive(Debug, Clone, Copy, StableHash)] pub struct TestBinderBody<'hir> { + pub span: Span, pub foralls: &'hir [TestBinderForall<'hir>], pub exists: &'hir [TestBinderExists<'hir>], /// Constraints to be inserted directly into constraint storage to be proven diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 2f934d7ef728d..0fa40d0409854 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1600,7 +1600,7 @@ pub fn walk_test_binder_body<'v, V: Visitor<'v>>( visitor: &mut V, body: &'v TestBinderBody<'v>, ) -> V::Result { - let TestBinderBody { foralls, exists, constraints, predicates } = body; + let TestBinderBody { span: _, foralls, exists, constraints, predicates } = body; walk_list!(visitor, visit_test_binder_forall, *foralls); walk_list!(visitor, visit_test_binder_exists, *exists); try_visit!(visitor.visit_test_binder_constraint(&constraints)); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2074213c167b2..1eaa8545c1b6d 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2380,7 +2380,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn check_test_binder_body(&self, body: TestBinderBody<'tcx>) { - let TestBinderBody { foralls, exists, constraints, predicates } = body; + let TestBinderBody { span, foralls, exists, constraints, predicates } = body; if !predicates.is_empty() { for (predicate, span) in predicates { let cause = traits::ObligationCause::misc(span, self.body_def_id); @@ -2400,6 +2400,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { Ok(()) => constraints, Err(_guar) => ty::region_constraint::RegionConstraint::new_true(), }; + if constraints.is_false() { + self.tcx().dcx().span_err(span, "top level false constraints are not supported"); + return; + } self.infcx.register_solver_region_constraint(constraints); @@ -2479,6 +2483,13 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { u, ) .with_spans(forall.span); + if constraint.is_false() { + self.tcx().dcx().span_err(forall.span, "forall evaluated to false"); + self.infcx.overwrite_solver_region_constraint( + ty::region_constraint::RegionConstraint::new_true(), + ); + return; + } if let Some(assert_on_exit) = &forall.assert_on_exit { self.check_test_binder_region_constraints(forall.span, assert_on_exit, &constraint); } @@ -2798,6 +2809,7 @@ struct RedundantLifetimeArgsLint<'tcx> { #[derive(Clone, Debug, TypeFoldable, TypeVisitable)] pub(crate) struct TestBinderBody<'tcx> { + pub span: Span, pub foralls: Vec>, pub exists: Vec>, /// Constraints to be inserted directly into constraint storage to be proven diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 1965ade38204e..7f181ef2e7e04 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -328,7 +328,7 @@ impl<'tcx> ItemCtxt<'tcx> { &self, item: &hir::TestBinderBody<'tcx>, ) -> TestBinderBody<'tcx> { - let hir::TestBinderBody { foralls, exists, constraints, predicates } = item; + let hir::TestBinderBody { span, foralls, exists, constraints, predicates } = item; let foralls = foralls.iter().map(|forall| self.lower_test_binder_forall(forall)).collect(); let exists = exists.iter().map(|exists| self.lower_test_binder_exists(exists)).collect(); let constraints = self.lower_test_binder_constraint(&constraints); @@ -337,7 +337,7 @@ impl<'tcx> ItemCtxt<'tcx> { clauses_of::where_predicate_clauses(self, predicate, &mut clauses); } let predicates = clauses.into_iter().map(|(c, span)| (c.kind(), span)).collect(); - TestBinderBody { foralls, exists, constraints, predicates } + TestBinderBody { span: *span, foralls, exists, constraints, predicates } } #[instrument(level = "debug", skip(self), ret)] diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d9d7c2603b880..8baec2f1e7ae3 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1910,10 +1910,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let adt_kind = adt.adt_kind(); + let get_field_ident = |field: &ty::FieldDef| field.ident(tcx).normalize_to_macros_2_0(); let mut remaining_fields = variant .fields .iter_enumerated() - .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field))) + .map(|(i, field)| (get_field_ident(field), (i, field))) .collect::>(); let mut seen_fields = FxHashMap::default(); @@ -1931,41 +1932,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check each field. for (idx, field) in hir_fields.iter().enumerate() { - let ident = tcx.adjust_ident(field.ident, variant.def_id); - let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { - seen_fields.insert(ident, field.span); - self.write_field_index(field.hir_id, i); - - // We don't look at stability attributes on - // struct-like enums (yet...), but it's definitely not - // a bug to have constructed one. - if adt_kind != AdtKind::Enum { - tcx.check_stability(v_field.did, Some(field.hir_id), field.span, None); + let ident = if field.ident.name == kw::Underscore { + // We are processing delegation `Self` mapping field, we expect that it is a struct + // with one field, so if it is not true we emit an error as we can't infer + // what field to use to initialize the struct. + // The `_` is a keyword so if user uses it during struct initialization as a field name + // it will be error anyway. + if let [v_field] = &variant.fields[..] { + Ok(get_field_ident(v_field)) + } else { + Err(tcx.dcx().span_delayed_bug( + field.ident.span, + format!( + "can not infer the delegation `Self` mapping field for {:?}", + self.body_def_id + ), + )) } - - self.field_ty(field.span, v_field, args) } else { - error_happened = true; - let guar = if let Some(prev_span) = seen_fields.get(&ident) { - self.dcx().emit_err(FieldMultiplySpecifiedInInitializer { - span: field.ident.span, - prev_span: *prev_span, - ident, - }) - } else { - self.report_unknown_field( - adt_ty, - variant, - expr, - field, - hir_fields, - adt.variant_descr(), - ) - }; - - Ty::new_error(tcx, guar) + Ok(tcx.adjust_ident(field.ident, variant.def_id)) }; + let field_type = ident + .and_then(|ident| { + if let Some((i, v_field)) = remaining_fields.remove(&ident) { + seen_fields.insert(ident, field.span); + self.write_field_index(field.hir_id, i); + + // We don't look at stability attributes on + // struct-like enums (yet...), but it's definitely not + // a bug to have constructed one. + if adt_kind != AdtKind::Enum { + tcx.check_stability(v_field.did, Some(field.hir_id), field.span, None); + } + + Ok(self.field_ty(field.span, v_field, args)) + } else { + error_happened = true; + let guar = if let Some(prev_span) = seen_fields.get(&ident) { + self.dcx().emit_err(FieldMultiplySpecifiedInInitializer { + span: field.ident.span, + prev_span: *prev_span, + ident, + }) + } else { + self.report_unknown_field( + adt_ty, + variant, + expr, + field, + hir_fields, + adt.variant_descr(), + ) + }; + + Err(guar) + } + }) + .unwrap_or_else(|err| Ty::new_error(tcx, err)); + // Check that the expected field type is WF. Otherwise, we emit no use-site error // in the case of coercions for non-WF fields, which leads to incorrect error // tainting. See issue #126272. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index e00928761a4f7..f28a99744aa56 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -17,11 +17,11 @@ use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, ResolvedStructPath}; use rustc_index::IndexVec; use rustc_infer::infer::{ - BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace, relate, + BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TypeTrace, relate, }; use rustc_infer::traits::WellFormedLoc; use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; @@ -293,22 +293,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_input_tys .into_iter() .zip(formal_input_tys) - // if the expected input type is structurally equal to the formal input type, - // i.e. we've only changed some inference variables around, keep the formal - // input ty as the expected input ty. Usually fudging helps because it gains - // information from a callsite of a function. However, Fudging also sometimes - // loses information, when the original, formal, input type had constraints on it, - // and fudging replaces all inference variables with fresh ones, those constraints - // are discarded. This check makes sure we only keep fudging output if structural - // changes were made to the type. If all that was changed were some typevars, - // we go back to the unfudged formal input type. .map(|(expected_input_ty, formal_input_ty)| { - if same_type_modulo_vars(tcx, expected_input_ty, *formal_input_ty) { - // if they're the same, fall back to the formal input type - *formal_input_ty - } else { - expected_input_ty - } + only_keep_structural_changes( + &self.infcx, + *formal_input_ty, + expected_input_ty, + ) }) .collect() })) @@ -3624,50 +3614,63 @@ enum SuggestionText { DidYouMean, } -fn same_type_modulo_vars<'tcx>(tcx: TyCtxt<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - struct SameModuloVars<'tcx> { - tcx: TyCtxt<'tcx>, +/// Postprocessing step of fudging: after fudging, any changes from the formal input type to the +/// expected input type are only kept, if they result in structural changes. If the only change is +/// one inference variable changing to another (fresh) inference variable, the formal one is kept, +/// not the fresh one. This avoids cases in which those fresh inference variables are *less* +/// constrained than the original formal ones, and making them fresh has no benefit. +fn only_keep_structural_changes<'tcx>( + infcx: &InferCtxt<'tcx>, + formal: Ty<'tcx>, + expected: Ty<'tcx>, +) -> Ty<'tcx> { + struct OnlyKeepStructuralChanges<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, } - impl<'tcx> TypeRelation> for SameModuloVars<'tcx> { + impl<'tcx> TypeRelation> for OnlyKeepStructuralChanges<'_, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { - self.tcx + self.infcx.tcx } fn relate_ty_args( &mut self, - a_ty: Ty<'tcx>, - _b_ty: Ty<'tcx>, - _ty_def_id: DefId, - a_args: ty::GenericArgsRef<'tcx>, - b_args: ty::GenericArgsRef<'tcx>, - _mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + formal_ty: Ty<'tcx>, + _expected_ty: Ty<'tcx>, + _def_id: DefId, + formal_args: ty::GenericArgsRef<'tcx>, + expected_args: ty::GenericArgsRef<'tcx>, + mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, ) -> RelateResult<'tcx, Ty<'tcx>> { - relate::relate_args_invariantly(self, a_args, b_args)?; - Ok(a_ty) + let new_args = relate::relate_args_invariantly(self, formal_args, expected_args)?; + + if new_args == formal_args { Ok(formal_ty) } else { Ok(mk(new_args)) } } fn relate_with_variance>>( &mut self, _variance: ty::Variance, _info: ty::VarianceDiagInfo>, - a: T, - b: T, + formal: T, + expected: T, ) -> RelateResult<'tcx, T> { - self.relate(a, b) + self.relate(formal, expected) } - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - if a == b { - return Ok(a); + fn tys(&mut self, formal: Ty<'tcx>, expected: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if formal == expected { + return Ok(formal); } - match (a.kind(), b.kind()) { + match (formal.kind(), expected.kind()) { + // If both are an infer var, keep the formal one. (&ty::Infer(ty::InferTy::TyVar(_)), &ty::Infer(ty::InferTy::TyVar(_))) | (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Infer(ty::InferTy::FloatVar(_))) - | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) => Ok(a), - (&ty::Infer(_), _) | (_, &ty::Infer(_)) => Err(TypeError::Mismatch), + | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) => { + Ok(formal) + } + (&ty::Infer(_), _) | (_, &ty::Infer(_)) => Ok(expected), (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.cx(), guar)), - _ => relate::structurally_relate_tys(self, a, b), + _ => relate::structurally_relate_tys(self, formal, expected), } } @@ -3681,42 +3684,47 @@ fn same_type_modulo_vars<'tcx>(tcx: TyCtxt<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> b fn consts( &mut self, - mut a: ty::Const<'tcx>, - mut b: ty::Const<'tcx>, + mut formal: ty::Const<'tcx>, + mut expected: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { - if a == b { - return Ok(a); + if formal == expected { + return Ok(formal); } // Avoid ICEs when in gce, and `structurally_relate_consts` // turns a non-infer const into an infer const - if self.tcx.features().generic_const_exprs() { - a = self.tcx.expand_abstract_consts(a); - b = self.tcx.expand_abstract_consts(b); + if self.cx().features().generic_const_exprs() { + formal = self.cx().expand_abstract_consts(formal); + expected = self.cx().expand_abstract_consts(expected); } - match (a.kind(), b.kind()) { - (ty::ConstKind::Infer(_), ty::ConstKind::Infer(_)) => return Ok(a), - (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - return Err(TypeError::ConstMismatch(ExpectedFound::new(a, b))); - } + match (formal.kind(), expected.kind()) { + // If both are an infer var, keep the formal one. + (ty::ConstKind::Infer(_), ty::ConstKind::Infer(_)) => return Ok(formal), + (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => return Ok(formal), + _ => {} } - relate::structurally_relate_consts(self, a, b) + relate::structurally_relate_consts(self, formal, expected) } fn binders( &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, + formal: ty::Binder<'tcx, T>, + expected: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate>, { - Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) + Ok(formal.rebind(self.relate(formal.skip_binder(), expected.skip_binder())?)) } } - SameModuloVars { tcx }.relate(a, b).is_ok() + // If it errors for some reason, keep the expected type, not the formal one, keeping the fudging. + let res = OnlyKeepStructuralChanges { infcx }.relate(formal, expected).unwrap_or(expected); + tracing::debug!( + "formal: {formal:?} and expected {expected:?} => only keeping structural changes creates {res:?}" + ); + res } diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index c96144d849944..da9162629ec80 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -190,19 +190,24 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .filter_map(ty::GenericArg::as_term) .find(|term| term.is_infer()) .unwrap_or_else(|| hidden_type.ty.into()); - self.err_ctxt() - .emit_inference_failure_err( - self.body_def_id, - hidden_type.span, - infer_var, - TypeAnnotationNeeded::E0282, - false, - ) - // The shared E0282 label only says "cannot infer type", which gives no - // hint that the ambiguity is in an opaque's hidden type rather than an - // ordinary local inference failure. - .with_note("cannot infer type of hidden type of opaque") - .emit_err() + let mut err = self.err_ctxt().emit_inference_failure_err( + self.body_def_id, + hidden_type.span, + infer_var, + TypeAnnotationNeeded::E0282, + false, + ); + // The shared E0282 label only says "cannot infer type", which gives no + // hint that the ambiguity is in an opaque's hidden type rather than an + // ordinary local inference failure. + let msg = "cannot infer type of hidden type of opaque"; + let opaque_span = self.tcx.def_span(def_id); + if err.span.primary_span().is_some_and(|sp| opaque_span.contains(sp)) { + err.note(msg); + } else { + err.span_note(opaque_span, msg); + } + err.emit_err() } } }; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 679de1e75613f..f0990d075e396 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -266,6 +266,10 @@ fn configure_and_expand( ) }); + if features.rustc_attrs() { + rustc_ast_passes::show_spans::check_crate(sess, &krate); + } + let crate_types = tcx.crate_types(); let is_executable_crate = crate_types.contains(&CrateType::Executable); let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 8b575033d0df6..5167737ac001f 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -1212,10 +1212,6 @@ pub(crate) struct QueryUntracked { pub method: Symbol, } -#[derive(Diagnostic)] -#[diag("use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`")] -pub(crate) struct SpanUseEqCtxtDiag; - #[derive(Diagnostic)] #[diag("using `{$fn_name}` on a string literal")] #[help("consider adding the symbol to `compiler/rustc_span/src/symbol.rs`")] diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index bce726311e51f..03622e253c90a 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -20,8 +20,8 @@ use crate::diagnostics::{ AttributeKindInFindAttr, BadOptAccessDiag, DefaultHashTypesDiag, ImplicitSysrootCrateImportDiag, LintPassByHand, MissingGenericTypeVisitableDeriveDiag, NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, - RustcMustMatchExhaustivelyNotExhaustive, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, - TyQualified, TykindDiag, TykindKind, TypeIrDirectUse, TypeIrInherentUsage, TypeIrTraitUsage, + RustcMustMatchExhaustivelyNotExhaustive, SymbolInternStringLiteralDiag, TyQualified, + TykindDiag, TykindKind, TypeIrDirectUse, TypeIrInherentUsage, TypeIrTraitUsage, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -570,41 +570,6 @@ impl LateLintPass<'_> for BadOptAccess { } } -declare_tool_lint! { - pub rustc::SPAN_USE_EQ_CTXT, - Allow, - "forbid uses of `==` with `Span::ctxt`, suggest `Span::eq_ctxt` instead", - report_in_external_macro: true -} - -declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]); - -impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { - if let hir::ExprKind::Binary( - hir::BinOp { node: hir::BinOpKind::Eq | hir::BinOpKind::Ne, .. }, - lhs, - rhs, - ) = expr.kind - { - if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) { - cx.emit_span_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag); - } - } - } -} - -fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - match &expr.kind { - hir::ExprKind::MethodCall(..) => cx - .typeck_results() - .type_dependent_def_id(expr.hir_id) - .is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)), - - _ => false, - } -} - declare_tool_lint! { /// The `symbol_intern_string_literal` lint detects `Symbol::intern` or `Ident::from_str_and_span` being called on a string literal pub rustc::SYMBOL_INTERN_STRING_LITERAL, diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 88d99449ba879..94f6183740954 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -301,7 +301,6 @@ late_lint_methods!( MissingGenericTypeVisitableDerive: MissingGenericTypeVisitableDerive, QueryStability: QueryStability, RustcMustMatchExhaustively: RustcMustMatchExhaustively, - SpanUseEqCtxt: SpanUseEqCtxt, SymbolInternStringLiteral: SymbolInternStringLiteral, TyTyKind: TyTyKind, TypeIr: TypeIr, @@ -759,8 +758,6 @@ fn register_internals(store: &mut LintStore) { LintId::of(BAD_OPT_ACCESS), // Late pass: DisallowedPassByRef LintId::of(DISALLOWED_PASS_BY_REF), - // Late pass: SpanUseEqCtxt - LintId::of(SPAN_USE_EQ_CTXT), // Late pass: SymbolInternStringLiteral // Note: this one is not included in rustc::internal because rustc_driver crates // outside the compiler can't/shouldn't add preinterned symbols. For rustc itself, diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 54d91eca01327..a942d6b060dab 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -1712,6 +1712,15 @@ pub(crate) struct GenericParamsWithoutAngleBrackets { pub sugg: GenericParamsWithoutAngleBracketsSugg, } +#[derive(Diagnostic)] +#[diag("use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments")] +pub(crate) struct MissingTurbofishForLifetime { + #[primary_span] + pub span: Span, + #[suggestion("add `::`", style = "verbose", code = "::", applicability = "machine-applicable")] + pub suggest_turbofish: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( "surround the type parameters with angle brackets", diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 705aac9cb17a6..d734a70399b11 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1144,6 +1144,13 @@ impl<'a> Parser<'a> { } } + pub(super) fn recover_missing_turbofish_for_lifetimes(&self, lt_span: Span) { + self.dcx().emit_err(crate::diagnostics::MissingTurbofishForLifetime { + span: lt_span, + suggest_turbofish: lt_span.shrink_to_lo(), + }); + } + /// When writing a turbofish with multiple type parameters missing the leading `::`, we will /// encounter a parse error when encountering the first `,`. pub(super) fn check_mistyped_turbofish_with_multiple_type_params( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d04d4c9818c5b..5ad0aae9a916a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -410,12 +410,6 @@ impl<'a> Parser<'a> { pre_span, )); } - token::Ident(..) - if this.token.is_keyword(kw::Move) - && this.look_ahead(1, |t| *t == token::OpenParen) => - { - make_it!(this, attrs, |this, _| this.parse_expr_move(lo)) - } token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => { make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) } @@ -447,16 +441,6 @@ impl<'a> Parser<'a> { self.parse_expr_unary(lo, UnOp::Not) } - fn parse_expr_move(&mut self, move_kw: Span) -> PResult<'a, (Span, ExprKind)> { - self.bump(); - self.psess.gated_spans.gate(sym::move_expr, move_kw); - self.expect(exp!(OpenParen))?; - let expr = self.parse_expr()?; - self.expect(exp!(CloseParen))?; - let span = move_kw.to(self.prev_token.span); - Ok((span, ExprKind::Move(expr, move_kw))) - } - fn is_mistaken_not_ident_negation(&self) -> bool { let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind { // These tokens can start an expression after `!`, but @@ -1335,6 +1319,10 @@ impl<'a> Parser<'a> { this.parse_expr_builtin() } else if this.check_path() { this.parse_expr_path_start() + } else if this.token.is_keyword(kw::Move) + && this.look_ahead(1, |t| *t == token::OpenParen) + { + this.parse_expr_move() } else if this.check_keyword(exp!(Move)) || this.check_keyword(exp!(Use)) || this.check_keyword(exp!(Static)) @@ -2216,6 +2204,17 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None))) } + fn parse_expr_move(&mut self) -> PResult<'a, Box> { + let move_kw = self.token.span; + self.expect_keyword(exp!(Move))?; + self.expect(exp!(OpenParen))?; + let expr = self.parse_expr()?; + self.expect(exp!(CloseParen))?; + let span = move_kw.to(self.prev_token.span); + self.psess.gated_spans.gate(sym::move_expr, span); + Ok(self.mk_expr(span, ExprKind::Move(expr, move_kw))) + } + /// Parses a closure expression (e.g., `move |args| expr`). fn parse_expr_closure(&mut self) -> PResult<'a, Box> { let lo = self.token.span; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index ee0f317f637d7..2011f8646346e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2731,6 +2731,7 @@ impl<'a> Parser<'a> { let mut exists = ThinVec::new(); let mut constraints = Vec::new(); let mut predicates = Vec::new(); + let mut span = self.token.span; self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |this| { match this.token.ident() { Some((Ident { name: sym::predicates, .. }, IdentKind::Normal)) => { @@ -2749,7 +2750,8 @@ impl<'a> Parser<'a> { } Ok(()) })?; - Ok(TestBinderBody { foralls, exists, constraints, predicates }) + span = span.to(self.prev_token.span); + Ok(TestBinderBody { foralls, exists, constraints, predicates, span }) } pub fn parse_test_binder_forall(&mut self) -> PResult<'a, TestBinderForall> { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 59d8bf932d430..8dfe95a849449 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -303,9 +303,20 @@ impl<'a> Parser<'a> { is_args_start(&this.token) }; + // A lifetime followed by `>` or `,` can never start a valid expression, so `<` here + // must be the start of generic arguments with a missing `::`. + let is_missing_turbofish_for_lifetime = |this: &mut Self| { + this.token == token::Lt + && this.may_recover() + && style == PathStyle::Expr + && this.look_ahead(1, |t| t.is_lifetime()) + && this.look_ahead(2, |t| matches!(t.kind, token::Gt | token::Comma)) + }; + Ok( if style == PathStyle::Type && check_args_start(self) || style != PathStyle::Mod && self.check_path_sep_and_look_ahead(is_args_start) + || is_missing_turbofish_for_lifetime(self) { // We use `style == PathStyle::Expr` to check if this is in a recursion or not. If // it isn't, then we reset the unmatched angle bracket count as we're about to start @@ -314,12 +325,18 @@ impl<'a> Parser<'a> { self.unmatched_angle_bracket_count = 0; } + let missing_turbofish = + style == PathStyle::Expr && !self.check_path_sep_and_look_ahead(is_args_start); + // Generic arguments are found - `<`, `(`, `::<` or `::(`. // First, eat `::` if it exists. let _ = self.eat_path_sep(); let lo = self.token.span; let args = if self.eat_lt() { + if missing_turbofish { + self.recover_missing_turbofish_for_lifetimes(lo); + } // `<'a, T, A = U>` let args = self.parse_angle_args_with_leading_angle_bracket_recovery( style, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 64710a3e68f11..70b77967cfcba 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -381,6 +381,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcRegions => (), AttributeKind::RustcScalableVector { .. } => (), AttributeKind::RustcShouldNotBeCalledOnConstItems => (), + AttributeKind::RustcShowSpans(_) => (), AttributeKind::RustcSimdMonomorphizeLaneLimit(..) => (), AttributeKind::RustcSkipDuringMethodDispatch { .. } => (), diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 16a2ee9f22eaf..926efb9ccd7c0 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -35,7 +35,7 @@ use rustc_middle::ty::{ self, AssocContainer, Const, GenericParamDefKind, PredicateProxy, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; -use rustc_span::{Ident, Span, Symbol, bug, span_bug, sym}; +use rustc_span::{Ident, Span, Symbol, bug, kw, span_bug, sym}; use tracing::debug; //////////////////////////////////////////////////////////////////////////////// @@ -1077,7 +1077,14 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { for field in fields { let index = self.typeck_results().field_index(field.hir_id); if self.check_field(field.ident.span, adt, &variant.fields[index]) { - failed_fields.push((field.ident.name, field.ident.span, true)); + // kw::Underscore is a `Self` mapping delegation field which is + // generated by compiler, see delegation AST -> HIR lowering code + // and `check_expr_struct_fields` for details. + let name = match field.ident.name { + kw::Underscore => variant.fields[index].name, + name => name, + }; + failed_fields.push((name, field.ident.span, true)); } } self.emit_unreachable_field_error(failed_fields, adt, None, qpath.span()); diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index f1400c83e4295..18a02666cfc4a 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -206,21 +206,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .iter() .map(|(import, import_error)| { if let Some(ModuleOrUniformRoot::Module(module_data)) = import.imported_module.get() - && let ModuleKind::Def(DefKind::Mod, def_id, _, name) = module_data.kind + && let ModuleKind::Def(DefKind::Mod, def_id, ..) = module_data.kind { - let Some(directive) = self.on_unknown_data(def_id) else { + let Some((this, directive)) = self.on_unknown_data(def_id) else { return CustomDiagnostic::default(); }; - let this = if let Some(name) = name { - name.to_string() - } else if let Some(crate_name) = &self.tcx.sess.opts.crate_name { - crate_name.to_string() - } else { - "".to_string() - }; let unresolved = import_error.segment.map(|s| s.name).unwrap_or(kw::Underscore); - let args = FormatArgs { this, unresolved: unresolved.to_string(), .. }; + let args = FormatArgs { + this: this.to_string(), + unresolved: unresolved.to_string(), + .. + }; directive.eval(None, &args) } else { @@ -3867,12 +3864,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - /// Gets the `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. - pub(crate) fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> { - match def_id.as_local() { + /// Gets the name and `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. + pub(crate) fn on_unknown_data(&self, def_id: DefId) -> Option<(Symbol, &Directive)> { + let directive = match def_id.as_local() { Some(local) => Some(self.on_unknown_data.get(&local)?.directive.as_ref()), - None => find_attr!(self.tcx, def_id, OnUnknown{ directive } => directive)?.as_deref(), - } + None => find_attr!(self.tcx, def_id, OnUnknown { directive } => directive)?.as_deref(), + }?; + + let name = + self.expect_module(def_id).name().unwrap_or_else(|| self.tcx.crate_name(def_id.krate)); + + Some((name, directive)) } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 821c16ceee3e5..83e5fbef9bcb9 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -558,9 +558,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }; let mut notes = Vec::new(); if let Some(module_def_id) = module - && let Some(directive) = self.r.on_unknown_data(module_def_id) + && let Some((this, directive)) = self.r.on_unknown_data(module_def_id) { - let args = FormatArgs { unresolved: item_ident.to_string(), this: mod_str, .. }; + let args = + FormatArgs { unresolved: item_ident.to_string(), this: this.to_string(), .. }; let CustomDiagnostic { message, label, diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index f4cba4384d589..b036e2de05b02 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -479,7 +479,6 @@ impl Span { /// This function is used as a fast path when decoding the full `SpanData` is not necessary. /// It's a cut-down version of `data_untracked` and doesn't require taking the interner lock. - #[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")] #[inline] pub fn ctxt(self) -> SyntaxContext { let ctxt32 = match_span_kind! { @@ -492,12 +491,8 @@ impl Span { SyntaxContext::from_u32(ctxt32) } - // Allow `span_use_eq_ctxt` because this is `eq_ctxt`! - // - // FIXME(nnethercote): context equality used to be much more complex, but the span - // representation changed and now simple equality is fine. This method and the - // `span_use_eq_ctxt` lint and the `SpanCtxt` diagnostic item can be removed. - #[allow(rustc::span_use_eq_ctxt)] + // With the pre-#163857 span representation this method was more complex and required. Now it's + // trivial but it hasn't been removed simply to avoid churn. #[inline] pub fn eq_ctxt(self, other: Span) -> bool { self.ctxt() == other.ctxt() diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2fab2cbbe7a20..963c549ea5ee1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -330,7 +330,6 @@ symbols! { SliceIndex, Some, Source, - SpanCtxt, StdinLock, Str, String, @@ -1917,6 +1916,7 @@ symbols! { rustc_regions, rustc_scalable_vector, rustc_should_not_be_called_on_const_items, + rustc_show_spans, rustc_simd_monomorphize_lane_limit, rustc_skip_during_method_dispatch, rustc_specialization_trait, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 22d6b6027e00a..1c5718473a06b 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -53,20 +53,26 @@ //! //! # Memory layout //! -//! For non-zero-sized values, a [`Box`] will use the [`Global`] allocator for its allocation. It is -//! valid to convert both ways between a [`Box`] and a raw pointer allocated with the [`Global`] -//! allocator, given that the [`Layout`] used with the allocator is correct for the type and the raw -//! pointer points to a valid value of the right type. More precisely, a `value: *mut T` that has -//! been allocated with the [`Global`] allocator with `Layout::for_value(&*value)` may be converted -//! into a box using [`Box::::from_raw(value)`]. Conversely, the memory backing a `value: *mut T` -//! obtained from [`Box::::into_raw`] may be deallocated using the [`Global`] allocator with -//! [`Layout::for_value(&*value)`]. +//! For non-zero-sized values, a [`Box`] will use its allocator of type `A` for its allocation. +//! It is valid to convert both ways between a `Box` and a raw pointer allocated with `A`, so long as: +//! - the size of the pointee, as reported by [`size_of_val`], is non-zero, +//! - the [`Layout::for_value`] of the pointee [*fits*](Allocator#memory-fitting) the memory block, and +//! - the pointee is a valid instance of `T` (for the given metadata of the pointer). //! -//! For zero-sized values, the `Box` pointer has to be non-null and sufficiently aligned. The -//! recommended way to build a Box to a ZST if `Box::new` cannot be used is to use -//! [`ptr::NonNull::dangling`]. +//! For zero-sized values, a [`Box`] may use any sufficiently aligned non-null pointer. +//! While it is sound to convert a zero-sized allocation from `A` into a `Box`, as +//! long as it is backed by a valid value of `T`, doing so may lead to memory leaks, as +//! the destructor of [`Box`] will not call [`deallocate`](Allocator::deallocate) +//! on zero-sized values. It is recommended to use [`ptr::dangling`] instead. //! -//! On top of these basic layout requirements, a `Box` must point to a valid value of `T`. +//! Likewise, manually deallocating the raw pointer acquired from a `Box` is only sound +//! if the size of the pointee is non-zero, as zero-sized values may be represented by +//! a pointer that is not [*currently allocated*] by that allocator. It is recommended to +//! pair [`Box::into_raw_with_allocator`] with [`Box::from_raw_in`], rather than +//! doing this. +//! +//! Note that even the [`Global`] allocator might not use dangling pointers for +//! zero-sized allocations, even though *most* implementations do this. //! //! So long as `T: Sized`, a `Box` is guaranteed to be represented //! as a single pointer and is also ABI-compatible with C pointers @@ -172,6 +178,7 @@ //! and it's best to avoid relying on this edition-dependent behavior if you wish to preserve //! compatibility with future versions of the compiler. //! +//! [*currently allocated*]: Allocator#currently-allocated-memory //! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 //! [ucg#326]: https://github.com/rust-lang/unsafe-code-guidelines/issues/326 //! [dereferencing]: core::ops::Deref @@ -343,12 +350,15 @@ impl Box { /// Allocates memory on the heap and then places `x` into it. /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// /// ``` /// let five = Box::new(5); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] @@ -435,6 +445,7 @@ impl Box { /// returning an error if the allocation fails /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -444,6 +455,8 @@ impl Box { /// let five = Box::try_new(5)?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn try_new(x: T) -> Result { @@ -502,6 +515,7 @@ impl Box { /// Allocates memory in the given allocator then places `x` into it. /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -510,6 +524,8 @@ impl Box { /// /// let five = Box::new_in(5, System); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[stable(feature = "allocator_api", since = "1.100.0")] #[must_use] @@ -525,6 +541,7 @@ impl Box { /// returning an error if the allocation fails /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -536,6 +553,8 @@ impl Box { /// let five = Box::try_new_in(5, System)?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn try_new_in(x: T, alloc: A) -> Result { @@ -842,6 +861,7 @@ impl Box { /// Allocates memory on the heap then clones `src` into it. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -850,6 +870,8 @@ impl Box { /// /// let hello: Box = Box::clone_from_ref("hello"); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[unstable(feature = "clone_from_ref", issue = "149075")] #[must_use] @@ -861,6 +883,7 @@ impl Box { /// Allocates memory on the heap then clones `src` into it, returning an error if allocation fails. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -870,6 +893,8 @@ impl Box { /// let hello: Box = Box::try_clone_from_ref("hello")?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "clone_from_ref", issue = "149075")] //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] @@ -882,6 +907,7 @@ impl Box { /// Allocates memory in the given allocator then clones `src` into it. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -892,6 +918,8 @@ impl Box { /// /// let hello: Box = Box::clone_from_ref_in("hello", System); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[unstable(feature = "clone_from_ref", issue = "149075")] //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] @@ -908,6 +936,7 @@ impl Box { /// Allocates memory in the given allocator then clones `src` into it, returning an error if allocation fails. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -919,6 +948,8 @@ impl Box { /// let hello: Box = Box::try_clone_from_ref_in("hello", System)?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "clone_from_ref", issue = "149075")] //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] @@ -1368,9 +1399,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by the global allocator. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// [`Global`]. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned non-null pointer, e.g. [`ptr::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1420,9 +1458,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same `NonNull` pointer. /// - /// The non-null pointer must point to a block of memory allocated by the global allocator. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// [`Global`]. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned pointer, e.g. [`NonNull::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1471,6 +1516,11 @@ impl Box { /// [`Box::from_raw`] function, allowing the `Box` destructor to perform /// the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_raw(b)` instead of `b.into_raw()`. This /// is so that there is no conflict with a method on the inner type. @@ -1506,6 +1556,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "box_raw", since = "1.4.0")] #[inline] @@ -1533,6 +1584,11 @@ impl Box { /// [`Box::from_non_null`] function, allowing the `Box` destructor to /// perform the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_non_null(b)` instead of `b.into_non_null()`. /// This is so that there is no conflict with a method on the inner type. @@ -1567,6 +1623,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "box_vec_non_null", since = "1.99.0")] #[inline] @@ -1585,9 +1642,8 @@ impl Box { /// /// After calling this function, the raw pointer is owned by the /// resulting `Box`. Specifically, the `Box` destructor will call - /// the destructor of `T` and free the allocated memory. For this - /// to be safe, the memory must have been allocated in accordance - /// with the [memory layout] used by `Box` . + /// the destructor of `T` and free the allocated memory, except + /// in the cases described in the [memory layout] section. /// /// # Safety /// @@ -1595,9 +1651,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by `alloc`. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// `alloc`. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned non-null pointer, e.g. [`ptr::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1641,9 +1704,8 @@ impl Box { /// /// After calling this function, the `NonNull` pointer is owned by /// the resulting `Box`. Specifically, the `Box` destructor will call - /// the destructor of `T` and free the allocated memory. For this - /// to be safe, the memory must have been allocated in accordance - /// with the [memory layout] used by `Box` . + /// the destructor of `T` and free the allocated memory, except + /// in the cases described in the [memory layout] section. /// /// # Safety /// @@ -1651,9 +1713,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The non-null pointer must point to a block of memory allocated by `alloc`. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// `alloc`. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned pointer, e.g. [`NonNull::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1702,6 +1771,11 @@ impl Box { /// [`Box::from_raw_in`] function, allowing the `Box` destructor to perform /// the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_raw_with_allocator(b)` instead of `b.into_raw_with_allocator()`. This /// is so that there is no conflict with a method on the inner type. @@ -1732,6 +1806,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "allocator_api", since = "1.100.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] @@ -1761,6 +1836,11 @@ impl Box { /// [`Box::from_non_null_in`] function, allowing the `Box` destructor to /// perform the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_non_null_with_allocator(b)` instead of /// `b.into_non_null_with_allocator()`. This is so that there is no @@ -1790,6 +1870,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "allocator_api", since = "1.100.0")] #[inline] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 03c07b15712bb..1d864b061a825 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -940,6 +940,10 @@ impl String { /// into a `String` with the [`from_raw_parts`] function, allowing /// the destructor to perform the cleanup. /// + /// If `capacity == 0`, then `pointer` may be dangling. Otherwise, + /// it will always be valid for reads and writes over `capacity` + /// bytes, and point to the start of the string. + /// /// [`from_raw_parts`]: String::from_raw_parts /// /// # Examples diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 1a151e4eebb33..d51eb95e9eeec 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -51,25 +51,50 @@ //! //! # Memory layout //! -//! When the type is non-zero-sized and the capacity is nonzero, [`Vec`] uses the [`Global`] -//! allocator for its allocation. It is valid to convert both ways between such a [`Vec`] and a raw -//! pointer allocated with the [`Global`] allocator, provided that the [`Layout`] used with the -//! allocator is correct for a sequence of `capacity` elements of the type, and the first `len` -//! values pointed to by the raw pointer are valid. More precisely, a `ptr: *mut T` that has been -//! allocated with the [`Global`] allocator with [`Layout::array::(capacity)`][Layout::array] may -//! be converted into a vec using -//! [`Vec::::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts). Conversely, the memory -//! backing a `value: *mut T` obtained from [`Vec::::as_mut_ptr`] may be deallocated using the -//! [`Global`] allocator with the same layout. +//! A [`Vec`] conceptually consists of a raw pointer `ptr` to the start of the allocation, a +//! length `len`, a capacity `cap`, and an allocator `alloc`. +//! While the exact fields and their order are not specified, a [`Vec`] can be converted to and +//! from these parts using [`Vec::into_raw_parts_with_allocator`], [`Vec::from_raw_parts_in`], +//! [`Vec::into_raw_parts`], and [`Vec::from_raw_parts`], as well as their [`NonNull`]-based +//! versions. //! -//! For zero-sized types (ZSTs), or when the capacity is zero, the `Vec` pointer must be non-null -//! and sufficiently aligned. The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be -//! used is to use [`ptr::NonNull::dangling`]. +//! The parts of a [`Vec`] have the following invariants: //! +//! * If [`size_of::()`](size_of) and `cap` are non-zero, then: +//! - `ptr` must point to the start of a memory block [*currently allocated*] via `alloc`. +//! - [`Layout::array::(cap)`] must [*fit*] the memory block of `ptr`. In particular, +//! * `T` must have the same alignment as the [`Layout`] `ptr` was allocated with, and +//! * `size_of::() * cap` must be at least the size of the [`Layout`] `ptr` was +//! allocated with, and at most the size of the allocation returned the allocator. +//! - `size_of::() * cap` must be at most [`isize::MAX`]. +//! See the safety documentation of [`pointer::offset`]. +//! * If [`size_of::()`](size_of) or `cap` is zero, then `ptr` must be any sufficiently aligned non-null pointer. +//! +//! NOTE: `ptr` will not be passed to the allocator for deallocation by the destructor of [`Vec`] +//! in this case. Thus, it is unwise to use a pointer acquired from the allocator for this, as +//! overallocated memory may end up being leaked. Use [`ptr::dangling`] instead. +//! * The following must always be satisfied: +//! - `len` needs to be less than or equal to `cap`. +//! - At `ptr`, there are at least `len` valid values of type `T`, laid out contiguously +//! (like an array). These values are considered owned by the [`Vec`]. +//! +//! It is sound to create a [`Vec`] from parts that satisfy these invariants. +//! +//! Conversely, the memory backing `ptr` obtained from [`Vec::as_mut_ptr`] or +//! [`Vec::into_raw_parts_with_allocator`]-like methods may be deallocated +//! using the allocator, with layout [`Layout::array::(cap)`], provided +//! that [`size_of::()`](size_of) or `cap` is non-zero. +//! To automatically handle the case where the allocation is zero-sized, +//! it is recommended to use [`Vec::from_raw_parts_in`], instead of manually +//! deallocating. +//! +//! [*fit*]: crate::alloc::Allocator#memory-fitting +//! [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory +//! [`allocate`]: crate::alloc::Allocator::allocate //! [`push`]: Vec::push //! [`ptr::NonNull::dangling`]: NonNull::dangling //! [`Layout`]: crate::alloc::Layout -//! [Layout::array]: crate::alloc::Layout::array +//! [`Layout::array::(cap)`]: crate::alloc::Layout::array #![stable(feature = "rust1", since = "1.0.0")] @@ -303,35 +328,13 @@ mod sve_retain; /// /// # Guarantees /// +/// The following section is to be read as an addendum of the +/// [memory layout](self#memory-layout) section. +/// /// Due to its incredibly fundamental nature, `Vec` makes a lot of guarantees /// about its design. This ensures that it's as low-overhead as possible in /// the general case, and can be correctly manipulated in primitive ways -/// by unsafe code. Note that these guarantees refer to an unqualified `Vec`. -/// If additional type parameters are added (e.g., to support custom allocators), -/// overriding their defaults may change the behavior. -/// -/// Most fundamentally, `Vec` is and always will be a (pointer, capacity, length) -/// triplet. No more, no less. The order of these fields is completely -/// unspecified, and you should use the appropriate methods to modify these. -/// The pointer will never be null, so this type is null-pointer-optimized. -/// -/// However, the pointer might not actually point to allocated memory. In particular, -/// if you construct a `Vec` with capacity 0 via [`Vec::new`], [`vec![]`][`vec!`], -/// [`Vec::with_capacity(0)`][`Vec::with_capacity`], or by calling [`shrink_to_fit`] -/// on an empty Vec, it will not allocate memory. Similarly, if you store zero-sized -/// types inside a `Vec`, it will not allocate space for them. *Note that in this case -/// the `Vec` might not report a [`capacity`] of 0*. `Vec` will allocate if and only -/// if [size_of::\]\() * [capacity]\() > 0. In general, `Vec`'s allocation -/// details are very subtle --- if you intend to allocate memory using a `Vec` -/// and use it for something else (either to pass to unsafe code, or to build your -/// own memory-backed collection), be sure to deallocate this memory by using -/// `from_raw_parts` to recover the `Vec` and then dropping it. -/// -/// If a `Vec` *has* allocated memory, then the memory it points to is on the heap -/// (as defined by the allocator Rust is configured to use by default), and its -/// pointer points to [`len`] initialized, contiguous elements in order (what -/// you would see if you coerced it to a slice), followed by [capacity] - [len] -/// logically uninitialized, contiguous elements. +/// by unsafe code. /// /// A vector containing the elements `'a'` and `'b'` with capacity 4 can be /// visualized as below. The top part is the `Vec` struct, it contains a @@ -469,7 +472,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -479,11 +482,12 @@ impl Vec { /// If it is important to know the exact allocated capacity of a `Vec`, /// always use the [`capacity`] method after construction. /// - /// For `Vec` where `T` is a zero-sized type, there will be no allocation - /// and the capacity will always be `usize::MAX`. + /// For `Vec` where `T` is a zero-sized type, the vector + /// [will not allocate], and the capacity will always be `usize::MAX`. /// /// [Capacity and reallocation]: #capacity-and-reallocation /// [`capacity`]: Vec::capacity + /// [will not allocate]: self#memory-layout /// /// # Panics /// @@ -529,12 +533,14 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// # Errors /// /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, /// or if the allocator reports allocation failure. + /// + /// [will not allocate]: self#memory-layout #[inline] #[unstable(feature = "try_with_capacity", issue = "91913")] pub fn try_with_capacity(capacity: usize) -> Result { @@ -545,34 +551,10 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * If `T` is not a zero-sized type and the capacity is nonzero, `ptr` must have - /// been allocated using the global allocator, such as via the [`alloc::alloc`] - /// function. If `T` is a zero-sized type or the capacity is zero, `ptr` need - /// only be non-null and aligned. - /// * `T` needs to have the same alignment as what `ptr` was allocated with, - /// if the pointer is required to be allocated. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes), if - /// nonzero, needs to be the same size as the pointer was allocated with. - /// (Because similar to alignment, [`dealloc`] must be called with the same - /// layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to be the capacity that the pointer was allocated with, - /// if the pointer is required to be allocated. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section, + /// with allocator [`Global`]. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is normally **not** safe /// to build a `Vec` from a pointer to a C `char` array with length /// `size_t`, doing so is only safe if the array was initially allocated by @@ -591,8 +573,7 @@ impl Vec { /// function. /// /// [`String`]: crate::string::String - /// [`alloc::alloc`]: crate::alloc::alloc - /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -652,29 +633,10 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` must have been allocated using the global allocator, such as via - /// the [`alloc::alloc`] function. - /// * `T` needs to have the same alignment as what `ptr` was allocated with. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs - /// to be the same size as the pointer was allocated with. (Because similar to - /// alignment, [`dealloc`] must be called with the same layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to be the capacity that the pointer was allocated with. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section, + /// with allocator [`Global`]. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is normally **not** safe /// to build a `Vec` from a pointer to a C `char` array with length /// `size_t`, doing so is only safe if the array was initially allocated by @@ -693,8 +655,7 @@ impl Vec { /// function. /// /// [`String`]: crate::string::String - /// [`alloc::alloc`]: crate::alloc::alloc - /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -917,7 +878,7 @@ const impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -927,11 +888,12 @@ const impl Vec { /// If it is important to know the exact allocated capacity of a `Vec`, /// always use the [`capacity`] method after construction. /// - /// For `Vec` where `T` is a zero-sized type, there will be no allocation - /// and the capacity will always be `usize::MAX`. + /// For `Vec` where `T` is a zero-sized type, the vector + /// [will not allocate], and the capacity will always be `usize::MAX`. /// /// [Capacity and reallocation]: #capacity-and-reallocation /// [`capacity`]: Vec::capacity + /// [will not allocate]: self#memory-layout /// /// # Panics /// @@ -1069,12 +1031,14 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// # Errors /// /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, /// or if the allocator reports allocation failure. + /// + /// [will not allocate]: self#memory-layout #[inline] #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] // #[unstable(feature = "try_with_capacity", issue = "91913")] @@ -1087,28 +1051,9 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` must be [*currently allocated*] via the given allocator `alloc`. - /// * `T` needs to have the same alignment as what `ptr` was allocated with. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs - /// to be the same size as the pointer was allocated with. (Because similar to - /// alignment, [`dealloc`] must be called with the same layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to [*fit*] the layout size that the pointer was allocated with. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is **not** safe /// to build a `Vec` from a pointer to a C `char` array with length `size_t`. /// It's also not safe to build one from a `Vec` and its length, because @@ -1123,9 +1068,8 @@ impl Vec { /// function. /// /// [`String`]: crate::string::String - /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc - /// [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory - /// [*fit*]: crate::alloc::Allocator#memory-fitting + /// [`allocate`]: crate::alloc::Allocator::allocate + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -1201,28 +1145,9 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` must be [*currently allocated*] via the given allocator `alloc`. - /// * `T` needs to have the same alignment as what `ptr` was allocated with. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs - /// to be the same size as the pointer was allocated with. (Because similar to - /// alignment, [`dealloc`] must be called with the same layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to [*fit*] the layout size that the pointer was allocated with. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is **not** safe /// to build a `Vec` from a pointer to a C `char` array with length `size_t`. /// It's also not safe to build one from a `Vec` and its length, because @@ -1238,8 +1163,7 @@ impl Vec { /// /// [`String`]: crate::string::String /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc - /// [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory - /// [*fit*]: crate::alloc::Allocator#memory-fitting + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -1899,7 +1823,7 @@ impl Vec { } /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer - /// valid for zero sized reads if the vector didn't allocate. + /// valid for zero sized reads if the vector [didn't allocate]. /// /// The caller must ensure that the vector outlives the pointer this /// function returns, or else it will end up dangling. @@ -1951,6 +1875,7 @@ impl Vec { /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null + /// [didn't allocate]: self#memory-layout #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_never_returns_null_ptr] @@ -1963,7 +1888,7 @@ impl Vec { } /// Returns a raw mutable pointer to the vector's buffer, or a dangling - /// raw pointer valid for zero sized reads if the vector didn't allocate. + /// raw pointer valid for zero sized reads if the vector [didn't allocate]. /// /// The caller must ensure that the vector outlives the pointer this /// function returns, or else it will end up dangling. @@ -2035,6 +1960,7 @@ impl Vec { /// [`as_non_null`]: Vec::as_non_null /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc /// [`ManuallyDrop`]: core::mem::ManuallyDrop + /// [didn't allocate]: self#memory-layout #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_never_returns_null_ptr] @@ -2047,7 +1973,7 @@ impl Vec { } /// Returns a `NonNull` pointer to the vector's buffer, or a dangling - /// `NonNull` pointer valid for zero sized reads if the vector didn't allocate. + /// `NonNull` pointer valid for zero sized reads if the vector [didn't allocate]. /// /// The caller must ensure that the vector outlives the pointer this /// function returns, or else it will end up dangling. @@ -2102,6 +2028,7 @@ impl Vec { /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null + /// [didn't allocate]: self#memory-layout #[unstable(feature = "vec_as_non_null", issue = "157843")] #[rustc_const_unstable(feature = "vec_as_non_null", issue = "157843")] #[rustc_as_ptr] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 530af0c3af656..aeabacb0d6364 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1135,7 +1135,7 @@ marker_impls! { } marker_impls! { - #[unstable(feature = "unsized_const_params", issue = "95174")] + #[unstable(feature = "unsized_const_params", issue = "128028")] #[unstable_feature_bound(unsized_const_params)] ConstParamTy_ for str, diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 1171f1c05b25c..c668e6216fd4b 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2257,10 +2257,9 @@ impl Metadata { FileType(self.0.file_type()) } - /// Returns `true` if this metadata is for a directory. The - /// result is mutually exclusive to the result of - /// [`Metadata::is_file`], and will be false for symlink metadata - /// obtained from [`symlink_metadata`]. + /// Returns `true` if this metadata is for a directory. The result is mutually exclusive to the + /// result of [`Metadata::is_file`] and [`Metadata::is_symlink`] (i.e., at most one of these + /// three methods returns `true`). /// /// # Examples /// @@ -2280,10 +2279,9 @@ impl Metadata { self.file_type().is_dir() } - /// Returns `true` if this metadata is for a regular file. The - /// result is mutually exclusive to the result of - /// [`Metadata::is_dir`], and will be false for symlink metadata - /// obtained from [`symlink_metadata`]. + /// Returns `true` if this metadata is for a regular file. The result is mutually exclusive to + /// the result of [`Metadata::is_dir`] and [`Metadata::is_symlink`] (i.e., at most one of these + /// three methods returns `true`). /// /// When the goal is simply to read from (or write to) the source, the most /// reliable way to test the source can be read (or written to) is to open @@ -2970,6 +2968,9 @@ impl AsInner for DirEntry { /// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files. /// Note that, this [may change in the future][changes]. /// +/// On Windows, symbolic links to directories cannot currently be removed with this function. +/// Use [`remove_dir`] for that purpose. +/// /// [changes]: io#platform-specific-behavior /// /// # Errors @@ -3528,6 +3529,9 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// and the `RemoveDirectory` function on Windows. /// Note that, this [may change in the future][changes]. /// +/// On Windows, this can also be used to remove symbolic links to directories. +/// On Unix, [`remove_file`] must be used for that purpose. +/// /// [changes]: io#platform-specific-behavior /// /// # Errors diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 7fb976d5ef3da..605ad25270a63 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -852,102 +852,175 @@ fn file_test_windows_fileext_trait_case_1() { #[test] #[cfg(windows)] fn file_test_windows_fileext_trait_case_2() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; // Test when seek_read(), seek_write() return Ok(0) struct MockFile { expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); Ok(0) } fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); Ok(0) } } - { - let mock_file = MockFile { expected_offset: 0 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), - io::ErrorKind::UnexpectedEof - ); - assert_eq!(mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); - } - - { - let mock_file = MockFile { expected_offset: 420 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), - io::ErrorKind::UnexpectedEof - ); - assert_eq!( - mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), - io::ErrorKind::WriteZero - ); + let mut buf = [0; 256]; + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_read_exact(&mut buf, offset).unwrap_err().kind(), + ErrorKind::UnexpectedEof + ); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_write_all(&buf, offset).unwrap_err().kind(), + ErrorKind::WriteZero + ); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } } #[test] #[cfg(windows)] fn file_test_windows_fileext_trait_case_3() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; - // Test that Err other than io::ErrorKind::Interrupted are propagated up. + // Test that Err are propagated up. struct MockFile { expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); - Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) + Err(io::Error::new(ErrorKind::PermissionDenied, "seek_read")) } fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); - Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) + Err(io::Error::new(ErrorKind::ConnectionRefused, "seek_write")) } } - { - let mock_file = MockFile { expected_offset: 0 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), - io::ErrorKind::PermissionDenied - ); - assert_eq!( - mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), - io::ErrorKind::ConnectionRefused - ); + let mut buf = [0; 256]; + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_read_exact(&mut buf, offset).unwrap_err().kind(), + ErrorKind::PermissionDenied + ); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_write_all(&buf, offset).unwrap_err().kind(), + ErrorKind::ConnectionRefused + ); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } +} - { - let mock_file = MockFile { expected_offset: 420 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), - io::ErrorKind::PermissionDenied - ); - assert_eq!( - mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), - io::ErrorKind::ConnectionRefused - ); +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_4() { + use crate::cell::RefCell; + use crate::os::windows::fs::FileExt; + + // Test that Err ErrorKind::Interrupted are propagated up (unlike Unix versions) + struct MockFile { + expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } + } + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(ErrorKind::Interrupted, "seek_read")) + } + + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(ErrorKind::Interrupted, "seek_write")) + } + } + + let mut buf = [0; 256]; + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_read_exact(&mut buf, offset).unwrap_err().kind(), + ErrorKind::Interrupted + ); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_write_all(&buf, offset).unwrap_err().kind(), + ErrorKind::Interrupted + ); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } - // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) } #[test] #[cfg(windows)] -fn file_test_windows_fileext_trait_case_4() { +fn file_test_windows_fileext_trait_case_5() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; const MSG: &[u8] = @@ -957,10 +1030,19 @@ fn file_test_windows_fileext_trait_case_4() { // seek_write(), respectively. struct MockFile { expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); assert_eq!(buf.len(), MSG.len()); assert_eq!(buf, &[0; MSG.len()]); @@ -969,6 +1051,7 @@ fn file_test_windows_fileext_trait_case_4() { } fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); assert_eq!(buf.len(), MSG.len()); assert_eq!(buf, MSG); @@ -976,26 +1059,28 @@ fn file_test_windows_fileext_trait_case_4() { } } - { - let mock_file = MockFile { expected_offset: 0 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 0)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 0)); - } - - { - let mock_file = MockFile { expected_offset: 420 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 420)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 420)); + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, offset)); + assert_eq!(&buf, MSG); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + check!(mock_file.seek_write_all(MSG, offset)); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } } #[test] #[cfg(windows)] -fn file_test_windows_fileext_trait_case_5() { +fn file_test_windows_fileext_trait_case_6() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; const MSG: &[u8] = @@ -1004,36 +1089,47 @@ fn file_test_windows_fileext_trait_case_5() { // Test pathological case where seek_read(), seek_write() only do 1 byte per call, return Ok(1) struct MockFile { base_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(base_offset: u64) -> Self { + Self { base_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; let offset = (offset - self.base_offset) as usize; buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); Ok(1) } fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; let offset = (offset - self.base_offset) as usize; assert_eq!(buf[0..1], MSG[offset..offset + 1]); Ok(1) } } - { - let mock_file = MockFile { base_offset: 0 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 0)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 0)); - } - - { - let mock_file = MockFile { base_offset: 420 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 420)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 420)); + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, offset)); + assert_eq!(&buf, MSG); + assert_eq!(*mock_file.read_calls.borrow(), MSG.len()); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + check!(mock_file.seek_write_all(MSG, offset)); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), MSG.len()); + } } } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 0848f97e69d32..527671442fb72 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -95,27 +95,6 @@ const fn stderr_raw() -> StderrRaw { StderrRaw(stdio::Stderr::new()) } -#[cfg(windows)] -impl StdoutRaw { - /// Starts a new lock session: stream state cached per lock session (the - /// handle and its console mode) is re-queried at the next write, so that - /// changing the process stdio handles (e.g. with `SetStdHandle`) between - /// lock sessions keeps working. - #[inline] - fn refresh(&mut self) { - self.0.refresh(); - } -} - -#[cfg(windows)] -impl StderrRaw { - /// See `StdoutRaw::refresh`. - #[inline] - fn refresh(&mut self) { - self.0.refresh(); - } -} - impl Read for StdinRaw { fn read(&mut self, buf: &mut [u8]) -> io::Result { handle_ebadf(self.0.read(buf), || Ok(0)) @@ -790,19 +769,7 @@ impl Stdout { // Locks this handle with 'static lifetime. This depends on the // implementation detail that the underlying `ReentrantMutex` is // static. - let lock = StdoutLock { inner: self.inner.lock() }; - // A new lock session begins, so let Windows re-query cached stream - // state at the next write; other platforms cache nothing, and skip - // this entirely so their `lock()` is unchanged. Skipped also if the - // cell is already borrowed (a nested lock during an ongoing write); - // such a lock is part of the outer session anyway. - #[cfg(windows)] - { - if let Ok(mut w) = lock.inner.try_borrow_mut() { - w.get_mut().refresh(); - } - } - lock + StdoutLock { inner: self.inner.lock() } } } @@ -1034,15 +1001,7 @@ impl Stderr { // Locks this handle with 'static lifetime. This depends on the // implementation detail that the underlying `ReentrantMutex` is // static. - let lock = StderrLock { inner: self.inner.lock() }; - // See `Stdout::lock`: a new lock session begins (Windows only). - #[cfg(windows)] - { - if let Ok(mut w) = lock.inner.try_borrow_mut() { - w.refresh(); - } - } - lock + StderrLock { inner: self.inner.lock() } } } diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index c10766470e873..4c2506ffae66c 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -69,9 +69,6 @@ pub trait FileExt { /// /// # Errors /// - /// If this function encounters an error of the kind - /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. /// /// If this function encounters an "end of file" before completely filling /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. @@ -113,7 +110,6 @@ pub trait FileExt { buf = &mut buf[n..]; offset += n as u64; } - Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } @@ -203,7 +199,6 @@ pub trait FileExt { let prev_written = buf.written(); match self.seek_read_buf(buf.reborrow(), offset) { Ok(()) => {} - Err(e) if e.is_interrupted() => {} Err(e) => return Err(e), } let n = buf.written() - prev_written; @@ -264,8 +259,7 @@ pub trait FileExt { /// /// # Errors /// - /// This function will return the first error of - /// non-[`io::ErrorKind::Interrupted`] kind that [`seek_write`] returns. + /// This function will return the first error that [`seek_write`] returns. /// /// [`seek_write`]: FileExt::seek_write /// @@ -298,7 +292,6 @@ pub trait FileExt { buf = &buf[n..]; offset += n as u64 } - Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } diff --git a/library/std/src/sys/io/is_terminal/windows.rs b/library/std/src/sys/io/is_terminal/windows.rs index b0c718d71f9f3..6d66e93087911 100644 --- a/library/std/src/sys/io/is_terminal/windows.rs +++ b/library/std/src/sys/io/is_terminal/windows.rs @@ -1,4 +1,5 @@ use crate::ffi::c_void; +use crate::mem::MaybeUninit; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::sys::c; @@ -12,10 +13,44 @@ fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { return false; } - let mut out = 0; - if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } { - // False positives aren't possible. If we got a console then we definitely have a console. - return true; + cfg_select! { + target_vendor = "win7" => { + let mut out = 0; + if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } { + // False positives aren't possible. If we got a console then we definitely have a console. + return true; + } + + // Early return if the handle is not FILE_TYPE_PIPE, implying it cannot be an msys PTY. + if unsafe { c::GetFileType(handle.as_raw_handle()) != 0x0003 } { + return false; + } + } + _ => unsafe { + // `GetConsoleMode` incurs a remote RPC round-trip (= bad), so it's best avoided. + // `NtQueryVolumeInformationFile` lets us test the handle directly. + // The required `FILE_DEVICE_CONSOLE` type was introduced in Windows 8. + // Fun fact: `GetFileType` uses `NtQueryVolumeInformationFile` internally. + let mut io_status = MaybeUninit::::uninit(); + let mut device_info = MaybeUninit::::uninit(); + let status = c::NtQueryVolumeInformationFile( + handle.as_raw_handle(), + io_status.as_mut_ptr(), + device_info.as_mut_ptr().cast(), + size_of_val(&device_info) as u32, + c::FileFsDeviceInformation, + ); + if status < 0 { + return false; + } + + match device_info.assume_init_ref().DeviceType { + c::FILE_DEVICE_CONSOLE => return true, + // If it's a named pipe, it may be an msys PTY. + c::FILE_DEVICE_NAMED_PIPE => {} // aka FILE_TYPE_PIPE + _ => return false, + } + }, } // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. @@ -23,11 +58,6 @@ fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { } fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool { - // Early return if the handle is not a pipe. - if unsafe { c::GetFileType(handle.as_raw_handle()) != c::FILE_TYPE_PIPE } { - return false; - } - /// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack /// allocate /// diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 25031968a7e02..67345d183a2d2 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -20,6 +20,9 @@ pub type WCHAR = u16; pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::without_provenance_mut(-1i32 as _); +pub const FILE_DEVICE_NAMED_PIPE: u32 = 0x00000011; +pub const FILE_DEVICE_CONSOLE: u32 = 0x00000050; + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170 pub const EXIT_SUCCESS: u32 = 0; pub const EXIT_FAILURE: u32 = 1; @@ -188,9 +191,6 @@ pub const FILE_CURRENT: u32 = cu(windows_sys::FILE_CURRENT); pub const FSCTL_GET_REPARSE_POINT: u32 = cu(windows_sys::FSCTL_GET_REPARSE_POINT); pub const FSCTL_SET_REPARSE_POINT: u32 = cu(windows_sys::FSCTL_SET_REPARSE_POINT); -// GetFileType return value (DWORD) -pub const FILE_TYPE_PIPE: u32 = cu(windows_sys::FILE_TYPE_PIPE); - // GetACP return value (UINT) pub const CP_UTF8: u32 = windows_sys::CP_UTF8.cast_unsigned(); @@ -473,6 +473,7 @@ cfg_select! { target_vendor = "uwp" => { windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : u32, shareaccess : u32, createdisposition : u32, createoptions : u32, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); + windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtQueryVolumeInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fsinformation : *mut core::ffi::c_void, length : u32, fsinformationclass : FS_INFORMATION_CLASS) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 70af36f27907e..71f94954b1356 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2032,6 +2032,7 @@ FILE_FLAG_RANDOM_ACCESS FILE_FLAG_SEQUENTIAL_SCAN FILE_FLAG_SESSION_AWARE FILE_FLAG_WRITE_THROUGH +FILE_FS_DEVICE_INFORMATION FILE_GENERIC_EXECUTE FILE_GENERIC_READ FILE_GENERIC_WRITE @@ -2084,7 +2085,6 @@ FILE_SYNCHRONOUS_IO_NONALERT FILE_TRAVERSE FILE_TYPE_CHAR FILE_TYPE_DISK -FILE_TYPE_PIPE FILE_TYPE_REMOTE FILE_TYPE_UNKNOWN FILE_WRITE_ATTRIBUTES @@ -2100,6 +2100,7 @@ FileCompressionInfo FileDispositionInfo FileDispositionInfoEx FileEndOfFileInfo +FileFsDeviceInformation FileFullDirectoryInfo FileFullDirectoryRestartInfo FileIdBothDirectoryInfo @@ -2303,6 +2304,7 @@ NO_ERROR NORMAL_PRIORITY_CLASS NtCreateFile NtOpenFile +NtQueryVolumeInformationFile NtReadFile NtSetInformationFile NtWriteFile diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 0c865494a6e7c..ae08f790bb40a 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -78,6 +78,7 @@ windows_link::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : windows_link::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : u32, lpmultibytestr : *const i8, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32); windows_link::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : u32, shareaccess : u32, createdisposition : u32, createoptions : u32, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); +windows_link::link!("ntdll.dll" "system" fn NtQueryVolumeInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fsinformation : *mut core::ffi::c_void, length : u32, fsinformationclass : FS_INFORMATION_CLASS) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtSetInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fileinformation : *const core::ffi::c_void, length : u32, fileinformationclass : FILE_INFORMATION_CLASS) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); @@ -2544,6 +2545,12 @@ pub const FILE_FLAG_RANDOM_ACCESS: i32 = 268435456; pub const FILE_FLAG_SEQUENTIAL_SCAN: i32 = 134217728; pub const FILE_FLAG_SESSION_AWARE: i32 = 8388608; pub const FILE_FLAG_WRITE_THROUGH: u32 = 2147483648; +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct FILE_FS_DEVICE_INFORMATION { + pub DeviceType: u32, + pub Characteristics: u32, +} pub const FILE_GENERIC_EXECUTE: i32 = 1179808; pub const FILE_GENERIC_READ: i32 = 1179785; pub const FILE_GENERIC_WRITE: i32 = 1179926; @@ -2678,7 +2685,6 @@ pub const FILE_SYNCHRONOUS_IO_NONALERT: i32 = 32; pub const FILE_TRAVERSE: i32 = 32; pub const FILE_TYPE_CHAR: i32 = 2; pub const FILE_TYPE_DISK: i32 = 1; -pub const FILE_TYPE_PIPE: i32 = 3; pub const FILE_TYPE_REMOTE: i32 = 32768; pub const FILE_TYPE_UNKNOWN: i32 = 0; pub const FILE_WRITE_ATTRIBUTES: i32 = 256; @@ -2718,6 +2724,7 @@ pub const FORMAT_MESSAGE_IGNORE_INSERTS: i32 = 512; pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: i32 = 8014; pub const FSCTL_GET_REPARSE_POINT: i32 = 589992; pub const FSCTL_SET_REPARSE_POINT: i32 = 589988; +pub type FS_INFORMATION_CLASS = i32; pub const FileAlignmentInfo: FILE_INFO_BY_HANDLE_CLASS = 17; pub const FileAllocationInfo: FILE_INFO_BY_HANDLE_CLASS = 5; pub const FileAttributeTagInfo: FILE_INFO_BY_HANDLE_CLASS = 9; @@ -2727,6 +2734,7 @@ pub const FileCompressionInfo: FILE_INFO_BY_HANDLE_CLASS = 8; pub const FileDispositionInfo: FILE_INFO_BY_HANDLE_CLASS = 4; pub const FileDispositionInfoEx: FILE_INFO_BY_HANDLE_CLASS = 21; pub const FileEndOfFileInfo: FILE_INFO_BY_HANDLE_CLASS = 6; +pub const FileFsDeviceInformation: FS_INFORMATION_CLASS = 4; pub const FileFullDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 14; pub const FileFullDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 15; pub const FileIdBothDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 10; diff --git a/library/std/src/sys/stdio/windows.rs b/library/std/src/sys/stdio/windows.rs index d9c9290ed21a2..82a4424d46989 100644 --- a/library/std/src/sys/stdio/windows.rs +++ b/library/std/src/sys/stdio/windows.rs @@ -22,35 +22,12 @@ pub struct Stdin { pub struct Stdout { incomplete_utf8: IncompleteUtf8, - write_mode: Option, } pub struct Stderr { incomplete_utf8: IncompleteUtf8, - write_mode: Option, } -/// How to write to a standard stream, cached for the duration of a lock -/// session (see `Stdout::refresh`). Re-querying this on every lock -/// acquisition keeps `SetStdHandle` calls made between lock sessions working -/// (see #40490), while a held lock skips the per-write console queries that -/// otherwise dominate bulk writes (see #154071). -#[derive(Clone, Copy)] -enum WriteMode { - /// The stream is a pipe or file, or a console using the UTF-8 code page: - /// bytes are written out unchanged. - Passthrough(c::HANDLE), - /// The stream is a console using a non-UTF-8 code page: data is converted - /// to UTF-16 and written with `WriteConsoleW`. - Utf16Console(c::HANDLE), -} - -// SAFETY: a `HANDLE` stored here is only an OS handle value for a standard -// stream (it is never dereferenced), and handle values are valid -// process-wide, on any thread. -unsafe impl Send for WriteMode {} -unsafe impl Sync for WriteMode {} - struct IncompleteUtf8 { bytes: [u8; 4], len: u8, @@ -100,62 +77,56 @@ pub fn get_handle(handle_id: u32) -> io::Result { } } -fn is_console(handle: c::HANDLE) -> bool { - // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported - // mode). This will only detect Windows Console, not other terminals connected to a pipe like - // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16. - let mut mode = 0; - unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } -} - -/// Returns true if the attached console's code page is currently UTF-8. +/// Returns true if `handle` refers to a proper console (aka conhost). #[cfg(not(target_vendor = "win7"))] -fn is_utf8_console() -> bool { - unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 } -} - -#[cfg(target_vendor = "win7")] -fn is_utf8_console() -> bool { - // Windows 7 has a fun "feature" where WriteFile on a console handle will return - // the number of UTF-16 code units written and not the number of bytes from the input string. - // So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code. - false +fn is_console(handle: c::HANDLE) -> bool { + cfg_select! { + target_vendor = "win7" => { + // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported + // mode). This will only detect Windows Console, not other terminals connected to a pipe like + // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16. + let mut mode = 0; + unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } + } + _ => unsafe { + // `GetConsoleMode` incurs a remote RPC round-trip (= bad), so it's best avoided. + // `NtQueryVolumeInformationFile` lets us test the handle directly. + // The required `FILE_DEVICE_CONSOLE` type was introduced in Windows 8. + // Fun fact: `GetFileType` uses `NtQueryVolumeInformationFile` internally. + let mut io_status = MaybeUninit::::uninit(); + let mut device_info = MaybeUninit::::uninit(); + let status = c::NtQueryVolumeInformationFile( + handle, + io_status.as_mut_ptr(), + device_info.as_mut_ptr().cast(), + size_of_val(&device_info) as u32, + c::FileFsDeviceInformation, + ); + status >= 0 && device_info.assume_init_ref().DeviceType == c::FILE_DEVICE_CONSOLE + }, + } } -fn write( - handle_id: u32, - data: &[u8], - incomplete_utf8: &mut IncompleteUtf8, - write_mode: &mut Option, -) -> io::Result { +fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result { if data.is_empty() { return Ok(0); } - let mode = match *write_mode { - Some(mode) => mode, - None => { - let handle = get_handle(handle_id)?; - let mode = if !is_console(handle) || is_utf8_console() { - WriteMode::Passthrough(handle) - } else { - WriteMode::Utf16Console(handle) - }; - // Only cache success; if there is no handle, keep erroring on - // every write like before. - *write_mode = Some(mode); - mode - } - }; + let handle = get_handle(handle_id)?; - match mode { - WriteMode::Passthrough(handle) => unsafe { + // At the time of writing, console IO on Windows is high latency: + // Windows has no TTY buffers, making every call a fully synchronous RPC. + // So, while always deferring to `write_console_utf16` wastes valuable heap for + // our UTF16 conversion, it handily beats doing 2 (or more) costly RPC round-trips. + if !is_console(handle) { + unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.write(data); let _ = handle.into_raw_handle(); // Don't close the handle ret - }, - WriteMode::Utf16Console(handle) => write_console_utf16(data, incomplete_utf8, handle), + } + } else { + write_console_utf16(data, incomplete_utf8, handle) } } @@ -481,21 +452,13 @@ impl IncompleteUtf8 { impl Stdout { pub const fn new() -> Stdout { - Stdout { incomplete_utf8: IncompleteUtf8::new(), write_mode: None } - } - - /// Forgets the cached stream state, so the next write re-queries the OS. - /// Called when a new lock session begins, so that changing the process - /// stdio handles (e.g. `SetStdHandle`) between lock sessions keeps - /// working (see #40490). - pub fn refresh(&mut self) { - self.write_mode = None; + Stdout { incomplete_utf8: IncompleteUtf8::new() } } } impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8, &mut self.write_mode) + write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8) } fn flush(&mut self) -> io::Result<()> { @@ -505,18 +468,13 @@ impl io::Write for Stdout { impl Stderr { pub const fn new() -> Stderr { - Stderr { incomplete_utf8: IncompleteUtf8::new(), write_mode: None } - } - - /// See `Stdout::refresh`. - pub fn refresh(&mut self) { - self.write_mode = None; + Stderr { incomplete_utf8: IncompleteUtf8::new() } } } impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8, &mut self.write_mode) + write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8) } fn flush(&mut self) -> io::Result<()> { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f6d5ada07f364..544d57d92613c 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -960,11 +960,17 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: )?; if let Some(list) = must_implement_one_of_functions.as_deref() { - write!( - w, - "
At least one of the `{}` methods is required.
", - fmt::from_fn(|f| list.iter().joined("`, `", f)), + w.write_str( + "\ +
At least one of the ", )?; + for (pos, method) in list.iter().enumerate() { + if pos > 0 { + w.write_str(", ")?; + } + write!(w, "{method}")?; + } + w.write_str(" methods is required.
")?; } for m in required_methods { diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 3befbf2cff8ff..e0bd1fc9792e6 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -337,30 +337,39 @@ fn sidebar_trait<'a>( ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const), ("required-associated-types", "Required Associated Types", req_assoc), ("provided-associated-types", "Provided Associated Types", prov_assoc), - ("required-methods", "Required Methods", req_method), - ("provided-methods", "Provided Methods", prov_method), - ("foreign-impls", "Implementations on Foreign Types", foreign_impls), ] .into_iter() .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)), ); + + let tcx = cx.tcx(); + // Because of `rustc_must_implement_one_of`, we might have a trait with no "required methods" + // but still want the link in the sidebar, so we handle it on its own. + let mut req_method_link = + LinkBlock::new(Link::new("required-methods", "Required Methods"), "", req_method); + let rustc_middle::ty::TraitDef { must_implement_one_of, .. } = tcx.trait_def(t.def_id); + req_method_link.force_render = must_implement_one_of.is_some(); + blocks.push(req_method_link); + + blocks.push(LinkBlock::new(Link::new("provided-methods", "Provided Methods"), "", prov_method)); + sidebar_assoc_items(cx, it, blocks, deref_id_map); - // Move the foreign impls block after dyn compatibility note to match the order of the headings - // in the main content. - let foreign_impls_block = blocks.pop_if(|b| b.heading.href == "foreign-impls"); - if !t.is_dyn_compatible(cx.tcx()) { + if !t.is_dyn_compatible(tcx) { blocks.push(LinkBlock::forced( Link::new("dyn-compatibility", "Dyn Compatibility"), "dyn-compatibility-note", )); } - if let Some(foreign_impls_block) = foreign_impls_block { - blocks.push(foreign_impls_block); - } + + blocks.push(LinkBlock::new( + Link::new("foreign-impls", "Implementations on Foreign Types"), + "", + foreign_impls, + )); blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl")); - if t.is_auto(cx.tcx()) { + if t.is_auto(tcx) { blocks.push(LinkBlock::forced( Link::new("synthetic-implementors", "Auto Implementors"), "impl-auto", diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 83d268a75d2b5..e3b0f3d573bf9 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -1190,6 +1190,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. + "NtQueryVolumeInformationFile" if this.frame_in_std() => { + let [_handle, _iosb, _info, _length, _class] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *_, *_, u32, i32) -> i32), + (link_name, abi, args), + )?; + // Return an error to make std think that nothing is a console. + // This means the normal NtWriteFile is used for stdout/stderr, greatly simplifying our lives. + let not_implemented = this.eval_windows("c", "STATUS_NOT_IMPLEMENTED"); + this.write_scalar(not_implemented, dest)?; + } "GetProcessHeap" if this.frame_in_std() => { let [] = this.check_shim_sig( shim_sig!(extern "system" fn() -> winapi::HANDLE), @@ -1215,24 +1225,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Pretend these does not exist / nothing happened, by returning zero. this.write_null(dest)?; } - "GetConsoleMode" if this.frame_in_std() => { - let [console, mode] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *_) -> winapi::BOOL), - (link_name, abi, args), - )?; - this.read_target_isize(console)?; - this.deref_pointer_as(mode, this.machine.layouts.u32)?; - // Indicate an error. - this.write_null(dest)?; - } - "GetFileType" if this.frame_in_std() => { - let [_file] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE) -> u32), - (link_name, abi, args), - )?; - // Return unknown file type. - this.write_null(dest)?; - } "AddVectoredExceptionHandler" if this.frame_in_std() => { let [_first, _handler] = this.check_shim_sig( shim_sig!(extern "system" fn(u32, *_) -> *_), diff --git a/tests/pretty/delegation/self-mapping-output.pp b/tests/pretty/delegation/self-mapping-output.pp index 5bce43315e1d0..3c77be7ed6831 100644 --- a/tests/pretty/delegation/self-mapping-output.pp +++ b/tests/pretty/delegation/self-mapping-output.pp @@ -24,7 +24,7 @@ struct W(S); impl Trait for W { #[attr = Inline(Hint)] - fn method(self: _) -> _ { from(Self { 0: Trait::method(self.0) }) } + fn method(self: _) -> _ { from(Self { _: Trait::method(self.0) }) } #[attr = Inline(Hint)] fn r#static() -> _ { Trait::r#static() } //~^ WARN: function cannot return without recursing [unconditional_recursion] @@ -34,7 +34,7 @@ impl W { #[attr = Inline(Hint)] - fn method(self: _) -> _ { from(Self { 0: Trait::method(self.0) }) } + fn method(self: _) -> _ { from(Self { _: Trait::method(self.0) }) } #[attr = Inline(Hint)] fn r#static() -> _ { Trait::r#static() } #[attr = Inline(Hint)] diff --git a/tests/rustdoc-html/impl/must_implement_one_of.rs b/tests/rustdoc-html/impl/must_implement_one_of.rs index ab965bab35667..2d905df0a4566 100644 --- a/tests/rustdoc-html/impl/must_implement_one_of.rs +++ b/tests/rustdoc-html/impl/must_implement_one_of.rs @@ -3,7 +3,7 @@ #[rustc_must_implement_one_of(a, b)] //@ matches c/trait.Trait.html '//*[@class="stab must_implement"]' \ -// 'At least one of the `a`, `b` methods is required.$' +// 'At least one of the a, b methods is required.$' pub trait Trait { fn a() {} fn b() {} diff --git a/tests/rustdoc-html/trait-rustc_must_implement_one_of.rs b/tests/rustdoc-html/trait-rustc_must_implement_one_of.rs new file mode 100644 index 0000000000000..57accd9fa0791 --- /dev/null +++ b/tests/rustdoc-html/trait-rustc_must_implement_one_of.rs @@ -0,0 +1,50 @@ +// This test ensures a few things around the `rustc_must_implement_one_of` attribute: +// 1. The listed methods are links +// 2. It gets a heading in the sidebar even if there is no "required methods" +// Regression test for . + +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#![crate_name = "foo"] + +//@ has 'foo/trait.Read.html' +//@ has - '//*[@class="stab must_implement"]/a[@href="#method.read"]' 'read' +//@ has - '//*[@class="stab must_implement"]/a[@href="#method.read_buf"]' 'read_buf' + +// There should be 3 headings in the sidebar: Required/Provided methods and "Implementors". +//@ count - '//*[@id="rustdoc-toc"]/h3' 3 +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#required-methods"]' 'Required Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#provided-methods"]' 'Provided Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#implementors"]' 'Implementors' + +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Read { + fn read_buf(&self) {} + fn read(&self) {} +} + +//@ has 'foo/trait.Tadam.html' +// There should still be 3 headings in the sidebar: Required/Provided methods and "Implementors". +//@ count - '//*[@id="rustdoc-toc"]/h3' 3 +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#required-methods"]' 'Required Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#provided-methods"]' 'Provided Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#implementors"]' 'Implementors' + +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Tadam { + fn read_buf(&self) {} + fn read(&self) {} + fn foo(&self) {} +} + +//@ has 'foo/trait.Other.html' +// There should be 2 headings in the sidebar: Provided methods and "Implementors". +//@ count - '//*[@id="rustdoc-toc"]/h3' 2 +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#provided-methods"]' 'Provided Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#implementors"]' 'Implementors' + +pub trait Other { + fn read_buf(&self) {} + fn read(&self) {} +} diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs deleted file mode 100644 index 369c2dd9c4877..0000000000000 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Test the `rustc::span_use_eq_ctxt` internal lint -//@ compile-flags: -Z unstable-options - -#![feature(rustc_private)] -#![deny(rustc::span_use_eq_ctxt)] -#![crate_type = "lib"] - -extern crate rustc_span; -use rustc_span::Span; - -pub fn f(s: Span, t: Span) -> bool { - s.ctxt() == t.ctxt() //~ ERROR use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` -} diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr deleted file mode 100644 index b52df0368b16d..0000000000000 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` - --> $DIR/span_use_eq_ctxt.rs:12:5 - | -LL | s.ctxt() == t.ctxt() - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/span_use_eq_ctxt.rs:5:9 - | -LL | #![deny(rustc::span_use_eq_ctxt)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.rs b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.rs new file mode 100644 index 0000000000000..c98afb8474448 --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +#![feature(test_binder_constraints, non_lifetime_binders)] +#![expect(incomplete_features)] + +core::test_binder_constraints! { + impl<'a> { + forall<'b> { + //~^ ERROR forall evaluated to false + 'b: 'a + } + } +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.stderr b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.stderr new file mode 100644 index 0000000000000..995363d521621 --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.stderr @@ -0,0 +1,8 @@ +error: forall evaluated to false + --> $DIR/test-forall-evaluates-to-false.rs:8:9 + | +LL | forall<'b> { + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/assumptions_on_binders/test-top-level-false-constraint.rs b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.rs new file mode 100644 index 0000000000000..cdd7eb7a321bc --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.rs @@ -0,0 +1,11 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +#![feature(test_binder_constraints, non_lifetime_binders)] +#![expect(incomplete_features)] + +core::test_binder_constraints! { + impl { or {} } + //~^ ERROR top level false constraints are not supported +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/test-top-level-false-constraint.stderr b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.stderr new file mode 100644 index 0000000000000..fb5380879fe3d --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.stderr @@ -0,0 +1,8 @@ +error: top level false constraints are not supported + --> $DIR/test-top-level-false-constraint.rs:7:10 + | +LL | impl { or {} } + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/show_spans-nested-items.rs b/tests/ui/attributes/show_spans-nested-items.rs new file mode 100644 index 0000000000000..52ff359ebd129 --- /dev/null +++ b/tests/ui/attributes/show_spans-nested-items.rs @@ -0,0 +1,28 @@ +// Illustrates the behavior of the `rustc_show_spans` internal attribute +// in the case of nested functions. + +//@ check-pass +#![feature(rustc_attrs)] + +#[rustc_show_spans(expr)] +fn outer() { + // No note because it's a nested function. + fn inner() { + let _ = 0; + } + + // The span will be over the `ty` (i.e, `u8`), not the `expr` (i.e., `1`). + #[rustc_show_spans(ty)] + fn inner_annotated() { + let _: u8 = 1; //~ NOTE showing the spans of the ty + } + + let _: u8 = 2; //~ NOTE showing the spans of the expr +} + +// No note as the annotation of the previous function does not affect this one. +fn not_annotated() { + let _ = 3; +} + +fn main() {} diff --git a/tests/ui/attributes/show_spans-nested-items.stderr b/tests/ui/attributes/show_spans-nested-items.stderr new file mode 100644 index 0000000000000..c1ca87bbce003 --- /dev/null +++ b/tests/ui/attributes/show_spans-nested-items.stderr @@ -0,0 +1,12 @@ +note: showing the spans of the ty + --> $DIR/show_spans-nested-items.rs:17:16 + | +LL | let _: u8 = 1; + | ^^ + +note: showing the spans of the expr + --> $DIR/show_spans-nested-items.rs:20:17 + | +LL | let _: u8 = 2; + | ^ + diff --git a/tests/ui/attributes/show_spans_macros.rs b/tests/ui/attributes/show_spans_macros.rs new file mode 100644 index 0000000000000..a8a435b009e16 --- /dev/null +++ b/tests/ui/attributes/show_spans_macros.rs @@ -0,0 +1,44 @@ +// Illustrates the behavior of the `rustc_show_spans` internal attribute. +// It shows how the parser combines spans of nodes built from tokens of +// different macro contexts. +// +// The objective is for a node's span to be a chain over its internal +// members and not only from first to last. That is, we should converge +// to `span(first).to(span(second))...to(span(last))` instead of just +// `span(first).to(span(last))`. This way we can report spans inside +// macro definitions when the children come from different contexts. + +//@ check-pass +#![feature(rustc_attrs)] + +mod foo { + pub const BAR: u8 = 1; +} + +// FIXME(#126763): the span should be `$a::$b` in the macro body. +macro_rules! path { + ($a:tt, $b:tt) => { + let _ = $a::$b; + }; +} + +#[rustc_show_spans(path)] +fn path_from_tts() { + path!(foo, BAR); +} + +// FIXME(#126763): the field access span should be `$a.$f` in the macro body. +macro_rules! field { + ($a:tt, $f:tt) => { + let _ = $a.$f; + }; +} + +const T: (u8,) = (1,); + +#[rustc_show_spans(expr)] +fn field_access() { + field!(T, 0); +} + +fn main() {} diff --git a/tests/ui/attributes/show_spans_macros.stderr b/tests/ui/attributes/show_spans_macros.stderr new file mode 100644 index 0000000000000..0fa76e9cc9392 --- /dev/null +++ b/tests/ui/attributes/show_spans_macros.stderr @@ -0,0 +1,18 @@ +note: showing the spans of the path + --> $DIR/show_spans_macros.rs:27:11 + | +LL | path!(foo, BAR); + | ^^^^^^^^ + +note: showing the spans of the expr + --> $DIR/show_spans_macros.rs:41:12 + | +LL | field!(T, 0); + | ^^^^ + +note: showing the spans of the expr + --> $DIR/show_spans_macros.rs:41:12 + | +LL | field!(T, 0); + | ^ + diff --git a/tests/ui/coercion/fudge-inference/fudge-eager-resolve-breakage-3.rs b/tests/ui/coercion/fudge-inference/fudge-eager-resolve-breakage-3.rs new file mode 100644 index 0000000000000..1368e8e019873 --- /dev/null +++ b/tests/ui/coercion/fudge-inference/fudge-eager-resolve-breakage-3.rs @@ -0,0 +1,42 @@ +//@ check-pass +// When we changed from lazy resolving to eager resolving, this was in some cases breaking +// We introduced a workaround in https://github.com/rust-lang/rust/pull/158447 to more often +// avoid breakage. This workaround was not perfect, and we knew this when landing, +// we just had no examples that showed the breakage until this one. +// +// The workaround would try to fudge a formally expected type, but if no structural changes +// to the type happened, would rollback the fudging again, and stuck to the formal expected type. +// +// This rollback behavior worked per function argument, and that's what goes wrong in this +// reproducer: g() is called with two parameters. The 2nd parameter gets fudged, and has a +// structural change (the return type is inferred to unit), so fudging gets accepted, +// not rolled back. In the process, the input of the closure, `x` becomes an unconstrained +// type variable, and type inference fails, despite us knowing that its type is `X` already, +// by the difinion of `g`. + +fn f() -> X { + let v = h(g(X, |x| x.m())); + // ^^^^^^^^^^^^^^ two parameters + v +} + +fn g(_: T, _: fn(T) -> U) -> Y { + loop {} +} + +fn h(_: Y) -> U { + loop {} +} + +struct X; + +impl X { + fn m(self) {} +} + +struct Y { + a: fn(T) -> T, + b: U, +} + +fn main() {} diff --git a/tests/ui/coherence/overlap-check-gat-alias-hang-151503.rs b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.rs new file mode 100644 index 0000000000000..21d6342dedb72 --- /dev/null +++ b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.rs @@ -0,0 +1,29 @@ +//! Regression test for . +//! +//! The overlap check for the two `MyFrom` impls used to hang. + +trait HasAlias {} +struct Dummy; +trait DummyTrait { + type DummyType; +} +impl DummyTrait for Dummy { + type DummyType = T; +} +type AliasOf = ::DummyType; +struct Matrix(T, S); +type OMatrix = Matrix>; + +trait SimdValue { + type Element; +} +impl SimdValue for OMatrix { + type Element = OMatrix; +} +trait MyFrom {} +impl MyFrom for T {} +impl MyFrom for OMatrix {} +//~^ ERROR conflicting implementations of trait `MyFrom>` for type `Matrix<_, _>` +impl HasAlias for Matrix {} + +fn main() {} diff --git a/tests/ui/coherence/overlap-check-gat-alias-hang-151503.stderr b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.stderr new file mode 100644 index 0000000000000..a20b12334f2fc --- /dev/null +++ b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.stderr @@ -0,0 +1,11 @@ +error[E0119]: conflicting implementations of trait `MyFrom>` for type `Matrix<_, _>` + --> $DIR/overlap-check-gat-alias-hang-151503.rs:25:1 + | +LL | impl MyFrom for T {} + | ----------------------- first implementation here +LL | impl MyFrom for OMatrix {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Matrix<_, _>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/const-generics/const-argument-cross-crate-mismatch.stderr b/tests/ui/const-generics/const-argument-cross-crate-mismatch.stderr index f58821283e19f..1e33dd31a4129 100644 --- a/tests/ui/const-generics/const-argument-cross-crate-mismatch.stderr +++ b/tests/ui/const-generics/const-argument-cross-crate-mismatch.stderr @@ -1,30 +1,29 @@ error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:6:67 + --> $DIR/const-argument-cross-crate-mismatch.rs:6:41 | LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); - | ------------------------- ^^^^^^^^^^ expected an array with a size of 3, found one with a size of 2 - | | - | arguments to this struct are incorrect + | --------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `2` + | | + | arguments to this function are incorrect | -note: tuple struct defined here - --> $DIR/auxiliary/const_generic_lib.rs:1:12 + = note: expected struct `Struct<3>` + found struct `Struct<2>` +note: function defined here + --> $DIR/auxiliary/const_generic_lib.rs:5:8 | -LL | pub struct Struct(pub [u8; N]); - | ^^^^^^ +LL | pub fn function(value: Struct<3>) -> u8 { + | ^^^^^^^^ error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:8:65 + --> $DIR/const-argument-cross-crate-mismatch.rs:8:39 | LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); - | ------------------------- ^^^^^^^^^^^^^^^ expected an array with a size of 2, found one with a size of 3 - | | - | arguments to this struct are incorrect + | ------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `3` + | | + | expected due to this | -note: tuple struct defined here - --> $DIR/auxiliary/const_generic_lib.rs:1:12 - | -LL | pub struct Struct(pub [u8; N]); - | ^^^^^^ + = note: expected struct `Struct<2>` + found struct `Struct<3>` error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr index b64c1c17df601..cdcfe80093f96 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr @@ -1,17 +1,39 @@ +error: unconstrained generic constant + --> $DIR/issue-62504.rs:18:25 + | +LL | ArrayHolder([0; Self::SIZE]) + | ^^^^^^^^^^ + | +help: try adding a `where` bound + | +LL | pub const fn new() -> Self where [(); Self::SIZE]: { + | +++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/issue-62504.rs:18:9 + | +LL | ArrayHolder([0; Self::SIZE]) + | ^^^^^^^^^^^ + | +help: try adding a `where` bound + | +LL | pub const fn new() -> Self where [(); Self::SIZE]: { + | +++++++++++++++++++++++ + error[E0308]: mismatched types - --> $DIR/issue-62504.rs:18:21 + --> $DIR/issue-62504.rs:18:9 | LL | ArrayHolder([0; Self::SIZE]) - | ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE` | = note: expected constant `X` found constant `Self::SIZE` error: unconstrained generic constant - --> $DIR/issue-62504.rs:18:25 + --> $DIR/issue-62504.rs:18:9 | LL | ArrayHolder([0; Self::SIZE]) - | ^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: try adding a `where` bound | @@ -19,7 +41,7 @@ LL | pub const fn new() -> Self where [(); Self::SIZE]: { | +++++++++++++++++++++++ error[E0284]: type annotations needed for `ArrayHolder<_>` - --> $DIR/issue-62504.rs:26:9 + --> $DIR/issue-62504.rs:30:9 | LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ----------- type must be known at this point @@ -35,7 +57,7 @@ LL | let mut array: ArrayHolder = ArrayHolder::new(); | ++++++++++++++++ error[E0284]: type annotations needed for `ArrayHolder<_>` - --> $DIR/issue-62504.rs:26:9 + --> $DIR/issue-62504.rs:30:9 | LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ------------------ type must be known at this point @@ -52,7 +74,7 @@ help: consider giving `array` an explicit type, where the value of const paramet LL | let mut array: ArrayHolder = ArrayHolder::new(); | ++++++++++++++++ -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0284, E0308. For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr index ef4655c8dc21f..8b8e4f87d7381 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr @@ -6,22 +6,35 @@ LL | ArrayHolder([0; Self::SIZE]) | = note: this may fail depending on what value the parameter takes +error: constant expression depends on a generic parameter + --> $DIR/issue-62504.rs:18:9 + | +LL | ArrayHolder([0; Self::SIZE]) + | ^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + error[E0308]: mismatched types - --> $DIR/issue-62504.rs:18:21 + --> $DIR/issue-62504.rs:18:9 | +LL | pub const fn new() -> Self { + | ---- expected `ArrayHolder` because of return type LL | ArrayHolder([0; Self::SIZE]) - | ----------- ^^^^^^^^^^^^^^^ expected an array with a size of X, found one with a size of Self::SIZE - | | - | arguments to this struct are incorrect + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE` | -note: tuple struct defined here - --> $DIR/issue-62504.rs:14:8 + = note: expected struct `ArrayHolder` + found struct `ArrayHolder` + +error: constant expression depends on a generic parameter + --> $DIR/issue-62504.rs:18:9 | -LL | struct ArrayHolder([u32; X]); - | ^^^^^^^^^^^ +LL | ArrayHolder([0; Self::SIZE]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes error[E0284]: type annotations needed for `ArrayHolder<_>` - --> $DIR/issue-62504.rs:26:9 + --> $DIR/issue-62504.rs:30:9 | LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ----------- type must be known at this point @@ -37,7 +50,7 @@ LL | let mut array: ArrayHolder = ArrayHolder::new(); | ++++++++++++++++ error[E0284]: type annotations needed for `ArrayHolder<_>` - --> $DIR/issue-62504.rs:26:9 + --> $DIR/issue-62504.rs:30:9 | LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ------------------ type must be known at this point @@ -54,7 +67,7 @@ help: consider giving `array` an explicit type, where the value of const paramet LL | let mut array: ArrayHolder = ArrayHolder::new(); | ++++++++++++++++ -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0284, E0308. For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.rs b/tests/ui/const-generics/generic_const_exprs/issue-62504.rs index f021e1036143b..830838905d378 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.rs @@ -18,7 +18,11 @@ impl ArrayHolder { ArrayHolder([0; Self::SIZE]) //~^ ERROR mismatched types //[full]~^^ ERROR unconstrained generic constant - //[min]~^^^ ERROR constant expression depends on a generic parameter + //[full]~^^^ ERROR unconstrained generic constant + //[full]~^^^^ ERROR unconstrained generic constant + //[min]~^^^^^ ERROR constant expression depends on a generic parameter + //[min]~| ERROR constant expression depends on a generic parameter + //[min]~| ERROR constant expression depends on a generic parameter } } diff --git a/tests/crashes/118603.rs b/tests/ui/const-generics/ice-unevaluated.rs similarity index 97% rename from tests/crashes/118603.rs rename to tests/ui/const-generics/ice-unevaluated.rs index 1a3e3bed59d40..d03b3c34ca5ee 100644 --- a/tests/crashes/118603.rs +++ b/tests/ui/const-generics/ice-unevaluated.rs @@ -1,5 +1,5 @@ -//@ known-bug: #118603 //@ compile-flags: -Copt-level=0 +//@ check-pass // ignore-tidy-file-linelength #![feature(generic_const_exprs)] diff --git a/tests/ui/const-generics/transmute_no_gate.rs b/tests/ui/const-generics/transmute_no_gate.rs index e1ac44390a5f8..72a1358480f81 100644 --- a/tests/ui/const-generics/transmute_no_gate.rs +++ b/tests/ui/const-generics/transmute_no_gate.rs @@ -83,9 +83,11 @@ fn main() { let _ = ident([[0; 8]; 16]); let _ = flatten([[0; 13]; 5]); let _: [[_; 5]; 13] = coagulate([0; 65]); + //~^ ERROR mismatched types let _ = flatten_3d([[[0; 3]; 13]; 5]); let _ = flatten_somewhat([[[0; 3]; 13]; 5]); let _ = known_size([16; 13]); let _: [u16; 5] = condense_bytes([16u8; 10]); + //~^ ERROR mismatched types let _ = singleton_each([16; 10]); } diff --git a/tests/ui/const-generics/transmute_no_gate.stderr b/tests/ui/const-generics/transmute_no_gate.stderr index 9c271b34849c0..01b53dc301cfb 100644 --- a/tests/ui/const-generics/transmute_no_gate.stderr +++ b/tests/ui/const-generics/transmute_no_gate.stderr @@ -1,3 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/transmute_no_gate.rs:85:35 + | +LL | let _: [[_; 5]; 13] = coagulate([0; 65]); + | --------- ^^^^^^^ expected an array with a size of _ * _, found one with a size of 65 + | | + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/transmute_no_gate.rs:25:4 + | +LL | fn coagulate(v: [u32; H*W]) -> [[u32; W];H] { + | ^^^^^^^^^ ------------- + +error[E0308]: mismatched types + --> $DIR/transmute_no_gate.rs:90:36 + | +LL | let _: [u16; 5] = condense_bytes([16u8; 10]); + | -------------- ^^^^^^^^^^ expected an array with a size of _ * 2, found one with a size of 10 + | | + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/transmute_no_gate.rs:57:4 + | +LL | fn condense_bytes(v: [u8; L * 2]) -> [u16; L] { + | ^^^^^^^^^^^^^^ -------------- + error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute_no_gate.rs:7:5 | @@ -79,6 +107,7 @@ LL | std::mem::transmute(v) = note: source type: `[[u32; 2 * H]; W + W]` (this type does not have a fixed size) = note: target type: `[[u32; W + W]; 2 * H]` (this type does not have a fixed size) -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0512`. +Some errors have detailed explanations: E0308, E0512. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/self-mapping-output-privacy.rs b/tests/ui/delegation/self-mapping-output-privacy.rs index f072d240608f8..ee5843884f268 100644 --- a/tests/ui/delegation/self-mapping-output-privacy.rs +++ b/tests/ui/delegation/self-mapping-output-privacy.rs @@ -13,6 +13,10 @@ impl Trait for S { mod private { pub struct W(super::S); + + pub struct W1 { + sss: super::S, + } } impl Trait for private::W { @@ -25,4 +29,14 @@ impl private::W { //~^ ERROR: field `0` of struct `W` is private } +impl Trait for private::W1 { + reuse Trait::method { S } + //~^ ERROR: field `sss` of struct `W1` is private +} + +impl private::W1 { + reuse Trait::method { S } + //~^ ERROR: field `sss` of struct `W1` is private +} + fn main() {} diff --git a/tests/ui/delegation/self-mapping-output-privacy.stderr b/tests/ui/delegation/self-mapping-output-privacy.stderr index 38f6b1a4a9ddf..113b43a2893f4 100644 --- a/tests/ui/delegation/self-mapping-output-privacy.stderr +++ b/tests/ui/delegation/self-mapping-output-privacy.stderr @@ -1,15 +1,27 @@ error[E0451]: field `0` of struct `W` is private - --> $DIR/self-mapping-output-privacy.rs:19:18 + --> $DIR/self-mapping-output-privacy.rs:23:18 | LL | reuse Trait::method { S } | ^^^^^^ private field error[E0451]: field `0` of struct `W` is private - --> $DIR/self-mapping-output-privacy.rs:24:18 + --> $DIR/self-mapping-output-privacy.rs:28:18 | LL | reuse Trait::method { S } | ^^^^^^ private field -error: aborting due to 2 previous errors +error[E0451]: field `sss` of struct `W1` is private + --> $DIR/self-mapping-output-privacy.rs:33:18 + | +LL | reuse Trait::method { S } + | ^^^^^^ private field + +error[E0451]: field `sss` of struct `W1` is private + --> $DIR/self-mapping-output-privacy.rs:38:18 + | +LL | reuse Trait::method { S } + | ^^^^^^ private field + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0451`. diff --git a/tests/ui/delegation/self-mapping-output.rs b/tests/ui/delegation/self-mapping-output.rs index 47b122b0accda..d20240bd6d88f 100644 --- a/tests/ui/delegation/self-mapping-output.rs +++ b/tests/ui/delegation/self-mapping-output.rs @@ -109,7 +109,6 @@ mod no_constructor { impl Trait for W { reuse Trait::method { self.0 } //~^ ERROR: no field `0` on type `&no_constructor::W` - //~| ERROR: struct `no_constructor::W` has no field named `0` reuse Trait::r#static; //~^ WARN: function cannot return without recursing [unconditional_recursion] reuse Trait::raw_S { self.0 } @@ -119,7 +118,6 @@ mod no_constructor { impl W { reuse Trait::method { self.0 } //~^ ERROR: no field `0` on type `&no_constructor::W` - //~| ERROR: struct `no_constructor::W` has no field named `0` reuse Trait::r#static; reuse Trait::raw_S { self.0 } //~^ ERROR: no field `0` on type `&no_constructor::W` @@ -142,7 +140,7 @@ mod more_than_one_field { struct W(S, S, S); impl Trait for W { reuse Trait::method { self.0 } - //~^ ERROR: missing fields `1` and `2` in initializer of `more_than_one_field::W` + //~^ ERROR: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W` reuse Trait::r#static; //~^ WARN: function cannot return without recursing [unconditional_recursion] reuse Trait::raw_S { self.0 } @@ -150,7 +148,7 @@ mod more_than_one_field { impl W { reuse Trait::method { self.0 } - //~^ ERROR: missing fields `1` and `2` in initializer of `more_than_one_field::W` + //~^ ERROR: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W` reuse Trait::r#static; reuse Trait::raw_S { self.0 } } diff --git a/tests/ui/delegation/self-mapping-output.stderr b/tests/ui/delegation/self-mapping-output.stderr index 86ce822a1821c..621ad70811381 100644 --- a/tests/ui/delegation/self-mapping-output.stderr +++ b/tests/ui/delegation/self-mapping-output.stderr @@ -1,15 +1,3 @@ -error[E0560]: struct `no_constructor::W` has no field named `0` - --> $DIR/self-mapping-output.rs:110:22 - | -LL | reuse Trait::method { self.0 } - | ^^^^^^ unknown field - | -help: a field with a similar name exists - | -LL - reuse Trait::method { self.0 } -LL + reuse Trait::s { self.0 } - | - error[E0609]: no field `0` on type `&no_constructor::W` --> $DIR/self-mapping-output.rs:110:36 | @@ -23,7 +11,7 @@ LL + reuse Trait::method { self.s } | error[E0609]: no field `0` on type `&no_constructor::W` - --> $DIR/self-mapping-output.rs:115:35 + --> $DIR/self-mapping-output.rs:114:35 | LL | reuse Trait::raw_S { self.0 } | ^ unknown field @@ -34,20 +22,8 @@ LL - reuse Trait::raw_S { self.0 } LL + reuse Trait::raw_S { self.s } | -error[E0560]: struct `no_constructor::W` has no field named `0` - --> $DIR/self-mapping-output.rs:120:22 - | -LL | reuse Trait::method { self.0 } - | ^^^^^^ unknown field - | -help: a field with a similar name exists - | -LL - reuse Trait::method { self.0 } -LL + reuse Trait::s { self.0 } - | - error[E0609]: no field `0` on type `&no_constructor::W` - --> $DIR/self-mapping-output.rs:120:36 + --> $DIR/self-mapping-output.rs:119:36 | LL | reuse Trait::method { self.0 } | ^ unknown field @@ -59,7 +35,7 @@ LL + reuse Trait::method { self.s } | error[E0609]: no field `0` on type `&no_constructor::W` - --> $DIR/self-mapping-output.rs:124:35 + --> $DIR/self-mapping-output.rs:122:35 | LL | reuse Trait::raw_S { self.0 } | ^ unknown field @@ -70,20 +46,20 @@ LL - reuse Trait::raw_S { self.0 } LL + reuse Trait::raw_S { self.s } | -error[E0063]: missing fields `1` and `2` in initializer of `more_than_one_field::W` - --> $DIR/self-mapping-output.rs:144:22 +error[E0063]: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W` + --> $DIR/self-mapping-output.rs:142:22 | LL | reuse Trait::method { self.0 } - | ^^^^^^ missing `1` and `2` + | ^^^^^^ missing `0`, `1` and `2` -error[E0063]: missing fields `1` and `2` in initializer of `more_than_one_field::W` - --> $DIR/self-mapping-output.rs:152:22 +error[E0063]: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W` + --> $DIR/self-mapping-output.rs:150:22 | LL | reuse Trait::method { self.0 } - | ^^^^^^ missing `1` and `2` + | ^^^^^^ missing `0`, `1` and `2` error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:184:25 + --> $DIR/self-mapping-output.rs:182:25 | LL | pub fn method(_: impl super::Trait) -> impl super::Trait { | ----------------- the found opaque type @@ -98,7 +74,7 @@ LL | reuse to_reuse::method { self.0 } found opaque type `impl non_trait_path_reuse::Trait` error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:186:25 + --> $DIR/self-mapping-output.rs:184:25 | LL | pub fn r#static() -> impl super::Trait { | ----------------- the found opaque type @@ -113,13 +89,13 @@ LL | reuse to_reuse::r#static; found opaque type `impl non_trait_path_reuse::Trait` error[E0609]: no field `0` on type `impl super::Trait` - --> $DIR/self-mapping-output.rs:191:39 + --> $DIR/self-mapping-output.rs:189:39 | LL | reuse to_reuse::method { self.0 } | ^ unknown field error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:213:31 + --> $DIR/self-mapping-output.rs:211:31 | LL | reuse Trait::method { self.0 } | ------ ^^^^^^ expected `&_`, found `()` @@ -129,7 +105,7 @@ LL | reuse Trait::method { self.0 } = note: expected reference `&_` found unit type `()` note: method defined here - --> $DIR/self-mapping-output.rs:199:12 + --> $DIR/self-mapping-output.rs:197:12 | LL | fn method(&self) -> (); | ^^^^^^ ---- @@ -139,14 +115,14 @@ LL | reuse Trait::method { &self.0 } | + error[E0283]: type annotations needed - --> $DIR/self-mapping-output.rs:216:22 + --> $DIR/self-mapping-output.rs:214:22 | LL | reuse Trait::r#static; | ^^^^^^^^ cannot infer type | = note: the type must implement `non_Self_return_type::Trait` help: the following types implement trait `non_Self_return_type::Trait` - --> $DIR/self-mapping-output.rs:205:5 + --> $DIR/self-mapping-output.rs:203:5 | LL | impl Trait for S { | ^^^^^^^^^^^^^^^^ `non_Self_return_type::S` @@ -155,7 +131,7 @@ LL | impl Trait for W { | ^^^^^^^^^^^^^^^^ `non_Self_return_type::W` error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:219:30 + --> $DIR/self-mapping-output.rs:217:30 | LL | reuse Trait::raw_S { self.0 } | ----- ^^^^^^ expected `&_`, found `()` @@ -165,7 +141,7 @@ LL | reuse Trait::raw_S { self.0 } = note: expected reference `&_` found unit type `()` note: method defined here - --> $DIR/self-mapping-output.rs:201:12 + --> $DIR/self-mapping-output.rs:199:12 | LL | fn raw_S(&self) -> S { S } | ^^^^^ ----- @@ -175,7 +151,7 @@ LL | reuse Trait::raw_S { &self.0 } | + error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:224:31 + --> $DIR/self-mapping-output.rs:222:31 | LL | reuse Trait::method { self.0 } | ------ ^^^^^^ expected `&_`, found `()` @@ -185,7 +161,7 @@ LL | reuse Trait::method { self.0 } = note: expected reference `&_` found unit type `()` note: method defined here - --> $DIR/self-mapping-output.rs:199:12 + --> $DIR/self-mapping-output.rs:197:12 | LL | fn method(&self) -> (); | ^^^^^^ ---- @@ -195,14 +171,14 @@ LL | reuse Trait::method { &self.0 } | + error[E0283]: type annotations needed - --> $DIR/self-mapping-output.rs:227:22 + --> $DIR/self-mapping-output.rs:225:22 | LL | reuse Trait::r#static; | ^^^^^^^^ cannot infer type | = note: the type must implement `non_Self_return_type::Trait` help: the following types implement trait `non_Self_return_type::Trait` - --> $DIR/self-mapping-output.rs:205:5 + --> $DIR/self-mapping-output.rs:203:5 | LL | impl Trait for S { | ^^^^^^^^^^^^^^^^ `non_Self_return_type::S` @@ -211,7 +187,7 @@ LL | impl Trait for W { | ^^^^^^^^^^^^^^^^ `non_Self_return_type::W` error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:230:30 + --> $DIR/self-mapping-output.rs:228:30 | LL | reuse Trait::raw_S { self.0 } | ----- ^^^^^^ expected `&_`, found `()` @@ -221,7 +197,7 @@ LL | reuse Trait::raw_S { self.0 } = note: expected reference `&_` found unit type `()` note: method defined here - --> $DIR/self-mapping-output.rs:201:12 + --> $DIR/self-mapping-output.rs:199:12 | LL | fn raw_S(&self) -> S { S } | ^^^^^ ----- @@ -231,7 +207,7 @@ LL | reuse Trait::raw_S { &self.0 } | + error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:256:38 + --> $DIR/self-mapping-output.rs:254:38 | LL | reuse ::method { self.0 } | ------ ^^^^^^ expected `&F`, found `&S` @@ -242,19 +218,19 @@ LL | reuse ::method { self.0 } = note: expected reference `&wrong_return_type::F` found reference `&wrong_return_type::S` note: method defined here - --> $DIR/self-mapping-output.rs:237:12 + --> $DIR/self-mapping-output.rs:235:12 | LL | fn method(&self) -> Self; | ^^^^^^ ---- error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:256:29 + --> $DIR/self-mapping-output.rs:254:29 | LL | reuse ::method { self.0 } | ^^^^^^ expected `S`, found `F` error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:260:29 + --> $DIR/self-mapping-output.rs:258:29 | LL | reuse ::r#static; | ^^^^^^^^ @@ -263,7 +239,7 @@ LL | reuse ::r#static; | expected `wrong_return_type::W` because of return type error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:263:37 + --> $DIR/self-mapping-output.rs:261:37 | LL | reuse ::raw_S { self.0 } | ----- ^^^^^^ expected `&F`, found `&S` @@ -273,13 +249,13 @@ LL | reuse ::raw_S { self.0 } = note: expected reference `&wrong_return_type::F` found reference `&wrong_return_type::S` note: method defined here - --> $DIR/self-mapping-output.rs:239:12 + --> $DIR/self-mapping-output.rs:237:12 | LL | fn raw_S(&self) -> S { S } | ^^^^^ ----- error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:268:38 + --> $DIR/self-mapping-output.rs:266:38 | LL | reuse ::method { self.0 } | ------ ^^^^^^ expected `&F`, found `&S` @@ -290,19 +266,19 @@ LL | reuse ::method { self.0 } = note: expected reference `&wrong_return_type::F` found reference `&wrong_return_type::S` note: method defined here - --> $DIR/self-mapping-output.rs:237:12 + --> $DIR/self-mapping-output.rs:235:12 | LL | fn method(&self) -> Self; | ^^^^^^ ---- error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:268:29 + --> $DIR/self-mapping-output.rs:266:29 | LL | reuse ::method { self.0 } | ^^^^^^ expected `S`, found `F` error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:272:29 + --> $DIR/self-mapping-output.rs:270:29 | LL | reuse ::r#static; | ^^^^^^^^ @@ -311,7 +287,7 @@ LL | reuse ::r#static; | expected `wrong_return_type::W` because of return type error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:275:37 + --> $DIR/self-mapping-output.rs:273:37 | LL | reuse ::raw_S { self.0 } | ----- ^^^^^^ expected `&F`, found `&S` @@ -321,13 +297,13 @@ LL | reuse ::raw_S { self.0 } = note: expected reference `&wrong_return_type::F` found reference `&wrong_return_type::S` note: method defined here - --> $DIR/self-mapping-output.rs:239:12 + --> $DIR/self-mapping-output.rs:237:12 | LL | fn raw_S(&self) -> S { S } | ^^^^^ ----- error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:301:31 + --> $DIR/self-mapping-output.rs:299:31 | LL | reuse Trait::method { F } | ------ ^ expected `&S`, found `&F` @@ -338,13 +314,13 @@ LL | reuse Trait::method { F } = note: expected reference `&wrong_target_expression::S` found reference `&wrong_target_expression::F` note: method defined here - --> $DIR/self-mapping-output.rs:282:12 + --> $DIR/self-mapping-output.rs:280:12 | LL | fn method(&self) -> Self; | ^^^^^^ ---- error[E0308]: mismatched types - --> $DIR/self-mapping-output.rs:310:31 + --> $DIR/self-mapping-output.rs:308:31 | LL | reuse Trait::method { F } | ------ ^ expected `&S`, found `&F` @@ -355,31 +331,31 @@ LL | reuse Trait::method { F } = note: expected reference `&wrong_target_expression::S` found reference `&wrong_target_expression::F` note: method defined here - --> $DIR/self-mapping-output.rs:282:12 + --> $DIR/self-mapping-output.rs:280:12 | LL | fn method(&self) -> Self; | ^^^^^^ ---- error[E0616]: field `0` of struct `private::W` is private - --> $DIR/self-mapping-output.rs:336:36 + --> $DIR/self-mapping-output.rs:334:36 | LL | reuse Trait::method { self.0 } | ^ private field error[E0616]: field `0` of struct `private::W` is private - --> $DIR/self-mapping-output.rs:342:35 + --> $DIR/self-mapping-output.rs:340:35 | LL | reuse Trait::raw_S { self.0 } | ^ private field error[E0616]: field `0` of struct `private::W` is private - --> $DIR/self-mapping-output.rs:347:36 + --> $DIR/self-mapping-output.rs:345:36 | LL | reuse Trait::method { self.0 } | ^ private field error[E0616]: field `0` of struct `private::W` is private - --> $DIR/self-mapping-output.rs:352:35 + --> $DIR/self-mapping-output.rs:350:35 | LL | reuse Trait::raw_S { self.0 } | ^ private field @@ -419,7 +395,7 @@ LL | reuse Trait::<'a, T, N>::r#static; = help: a `loop` may express intention better if this is on purpose warning: function cannot return without recursing - --> $DIR/self-mapping-output.rs:113:22 + --> $DIR/self-mapping-output.rs:112:22 | LL | reuse Trait::r#static; | ^^^^^^^^ @@ -430,7 +406,7 @@ LL | reuse Trait::r#static; = help: a `loop` may express intention better if this is on purpose warning: function cannot return without recursing - --> $DIR/self-mapping-output.rs:146:22 + --> $DIR/self-mapping-output.rs:144:22 | LL | reuse Trait::r#static; | ^^^^^^^^ @@ -441,7 +417,7 @@ LL | reuse Trait::r#static; = help: a `loop` may express intention better if this is on purpose warning: function cannot return without recursing - --> $DIR/self-mapping-output.rs:304:22 + --> $DIR/self-mapping-output.rs:302:22 | LL | reuse Trait::r#static; | ^^^^^^^^ @@ -452,7 +428,7 @@ LL | reuse Trait::r#static; = help: a `loop` may express intention better if this is on purpose warning: function cannot return without recursing - --> $DIR/self-mapping-output.rs:339:22 + --> $DIR/self-mapping-output.rs:337:22 | LL | reuse Trait::r#static; | ^^^^^^^^ @@ -462,7 +438,7 @@ LL | reuse Trait::r#static; | = help: a `loop` may express intention better if this is on purpose -error: aborting due to 31 previous errors; 7 warnings emitted +error: aborting due to 29 previous errors; 7 warnings emitted -Some errors have detailed explanations: E0063, E0283, E0308, E0560, E0609, E0616. +Some errors have detailed explanations: E0063, E0283, E0308, E0609, E0616. For more information about an error, try `rustc --explain E0063`. diff --git a/tests/ui/delegation/self-type-mapping-field-name.rs b/tests/ui/delegation/self-type-mapping-field-name.rs new file mode 100644 index 0000000000000..c57f701801da7 --- /dev/null +++ b/tests/ui/delegation/self-type-mapping-field-name.rs @@ -0,0 +1,56 @@ +//@ check-pass + +#![feature(fn_delegation)] + +mod simple_test { + trait Trait { + fn foo(&self) -> Self; + } + + struct S; + impl Trait for S { + fn foo(&self) -> S { S } + } + + struct W { s: S } + impl Trait for W { + reuse Trait::foo { self.s } + } + + impl W { + reuse Trait::foo { self.s } + } + + struct W1(S); + impl Trait for W1 { + reuse Trait::foo { self.0 } + } + + impl W1 { + reuse Trait::foo { self.0 } + } +} + +mod rfc { + struct BTreeMap { + _inner: std::collections::HashMap + } + + impl Clone for BTreeMap { + fn clone(&self) -> Self { + panic!() + } + + fn clone_from(&mut self, source: &Self) { + panic!() + } + } + + struct BTreeSet { + map: BTreeMap + } + + reuse impl Clone for BTreeSet { self.map } +} + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown/auxiliary/foreign.rs b/tests/ui/diagnostic_namespace/on_unknown/auxiliary/foreign.rs new file mode 100644 index 0000000000000..c9269fcb84ca2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/auxiliary/foreign.rs @@ -0,0 +1,12 @@ +#![feature(diagnostic_on_unknown)] + +mod a { + #[diagnostic::on_unknown( + message = "this={this}, unresolved={unresolved}", + )] + pub mod b {} +} + +pub mod c { + pub use super::a::b as d; +} diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.rs b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.rs new file mode 100644 index 0000000000000..d8bf14ccc54c3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.rs @@ -0,0 +1,20 @@ +#![feature(diagnostic_on_unknown)] + +mod a { + #[diagnostic::on_unknown( + message = "this={this}, unresolved={unresolved}", + )] + pub mod b {} +} + +mod c { + pub use super::a::b as d; +} + +use c::d::x; //~ ERROR this=b, unresolved=x + +fn main() { + let _ = c::d::X(); //~ ERROR this=b, unresolved=X + + let _: c::d::X; //~ ERROR this=b, unresolved=X +} diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.stderr b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.stderr new file mode 100644 index 0000000000000..a269acdf42b17 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.stderr @@ -0,0 +1,30 @@ +error[E0432]: this=b, unresolved=x + --> $DIR/expressions_and_paths_correct_mod_name.rs:14:5 + | +LL | use c::d::x; + | ^^^^^^- + | | + | no `x` in `a::b` + | + = note: unresolved import `c::d::x` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name.rs:17:19 + | +LL | let _ = c::d::X(); + | ^ not found in `c::d` + | + = note: cannot find function, tuple struct or tuple variant `X` in module `c::d` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name.rs:19:18 + | +LL | let _: c::d::X; + | ^ not found in `c::d` + | + = note: cannot find type `X` in module `c::d` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0432. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.rs b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.rs new file mode 100644 index 0000000000000..6cb8872984b36 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.rs @@ -0,0 +1,11 @@ +//@ aux-crate:foreign=foreign.rs + +extern crate foreign; + +use foreign::c::d::x; //~ ERROR this=b, unresolved=x + +fn main() { + let _ = foreign::c::d::X(); //~ ERROR this=b, unresolved=X + + let _: foreign::c::d::X; //~ ERROR this=b, unresolved=X +} diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.stderr b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.stderr new file mode 100644 index 0000000000000..ee1765828718f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.stderr @@ -0,0 +1,30 @@ +error[E0432]: this=b, unresolved=x + --> $DIR/expressions_and_paths_correct_mod_name_foreign.rs:5:5 + | +LL | use foreign::c::d::x; + | ^^^^^^^^^^^^^^^- + | | + | no `x` in `a::b` + | + = note: unresolved import `foreign::c::d::x` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name_foreign.rs:8:28 + | +LL | let _ = foreign::c::d::X(); + | ^ not found in `foreign::c::d` + | + = note: cannot find function, tuple struct or tuple variant `X` in module `foreign::c::d` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name_foreign.rs:10:27 + | +LL | let _: foreign::c::d::X; + | ^ not found in `foreign::c::d` + | + = note: cannot find type `X` in module `foreign::c::d` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0432. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/feature-gates/feature-gate-move_expr.stderr b/tests/ui/feature-gates/feature-gate-move_expr.stderr index 8b1da2c06893d..41a2ded842207 100644 --- a/tests/ui/feature-gates/feature-gate-move_expr.stderr +++ b/tests/ui/feature-gates/feature-gate-move_expr.stderr @@ -2,7 +2,7 @@ error[E0658]: `move(expr)` syntax is experimental --> $DIR/feature-gate-move_expr.rs:2:16 | LL | let _ = || move(2); - | ^^^^ + | ^^^^^^^ | = note: see issue #155050 for more information = help: add `#![feature(move_expr)]` to the crate attributes to enable diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr index d2ed88c1125ba..d7be74ee825cd 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | cmp_eq | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/ambig-hr-projection-issue-93340.rs:15:6 + | +LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider specifying the generic arguments | LL | cmp_eq:: diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr index cd1f2aa14acee..24c412caf2491 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed for `>::Out<_>` LL | let x = *x; | ^ | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/norm-before-method-resolution-opaque-type.rs:18:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ help: consider giving `x` an explicit type, where the placeholders `_` are specified | LL | let x: <_ as Trait<'static>>::Out<_> = *x; diff --git a/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr index 4f08d635cabdc..88ff9bd320111 100644 --- a/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr +++ b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | if false { is_trait(foo()) } else { Default::default() } | ^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_trait` | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/auto-trait-selection-freeze.rs:19:13 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^ help: consider specifying the generic arguments | LL | if false { is_trait::(foo()) } else { Default::default() } diff --git a/tests/ui/impl-trait/auto-trait-selection.next.stderr b/tests/ui/impl-trait/auto-trait-selection.next.stderr index dbad5680781ec..daf2f9a4617df 100644 --- a/tests/ui/impl-trait/auto-trait-selection.next.stderr +++ b/tests/ui/impl-trait/auto-trait-selection.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | if false { is_trait(foo()) } else { Default::default() } | ^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_trait` | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/auto-trait-selection.rs:15:13 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^ help: consider specifying the generic arguments | LL | if false { is_trait::(foo()) } else { Default::default() } diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr index 878e10557f26a..63e53bd75bdb2 100644 --- a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed for `(_,)` LL | let (x,) = (build(x),); | ^^^^ | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/recursive-in-exhaustiveness.rs:17:22 + | +LL | fn build(x: T) -> impl Sized { + | ^^^^^^^^^^ help: consider giving this pattern a type, where the placeholder `_` is specified | LL | let (x,): (_,) = (build(x),); @@ -16,7 +20,11 @@ error[E0282]: type annotations needed for `((_,),)` LL | let (x,) = (build2(x),); | ^^^^ | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/recursive-in-exhaustiveness.rs:27:23 + | +LL | fn build2(x: T) -> impl Sized { + | ^^^^^^^^^^ help: consider giving this pattern a type, where the placeholder `_` is specified | LL | let (x,): ((_,),) = (build2(x),); diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr index a2d25fafb2a0b..488ad507f041b 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | fn muh(x: A) -> B { | ^ cannot infer type | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/two_tait_defining_each_other2.rs:6:10 + | +LL | type A = impl Foo; + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/move-expr/left-operand-of-dot-call-index-try.rs b/tests/ui/move-expr/left-operand-of-dot-call-index-try.rs new file mode 100644 index 0000000000000..700101a8a27f2 --- /dev/null +++ b/tests/ui/move-expr/left-operand-of-dot-call-index-try.rs @@ -0,0 +1,21 @@ +// Ensure that move exprs can be the left operand of dot, call, index & try ops. We once used to +// incorrectly parse move exprs as prefix exprs instead of bottom ones which used to prevent that. + +//@ check-pass +//@ edition: 2018.. +#![feature(move_expr)] + +fn main() { + let _ = async || { move(async {}).await }; // Await(Move(_)) + + let _ = || -> Result<(), ()> { + move(((),)).0; // Field(Move(_), ..) + move(Err(()))?; // Try(Move(_)) + move(drop)(()); // Call(Move(_), ..) + move([0])[0]; // Index(Move(_), ..) + + // Previously, we would parse this as Field(Return(Move(_)), ..) (`(return move(…)).0`) + // instead of Return(Field(Move(_), ..)) and thus fail with "no field `0` on type `!`". + return move((Ok(()),)).0; // OK! + }; +} diff --git a/tests/ui/parser/issues/issue-93282.rs b/tests/ui/parser/issues/issue-93282.rs index 274245f1a465f..5b96969f5b971 100644 --- a/tests/ui/parser/issues/issue-93282.rs +++ b/tests/ui/parser/issues/issue-93282.rs @@ -1,7 +1,10 @@ fn main() { + //~^ HELP consider introducing lifetime `'a` here f<'a,> - //~^ ERROR expected - //~| ERROR expected + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + //~| ERROR use of undeclared lifetime name `'a` + //~| ERROR cannot find value `f` in this scope } fn bar(a: usize, b: usize) -> usize { @@ -12,5 +15,6 @@ fn foo() { let x = 1; bar('y, x); //~^ ERROR expected + //~| HELP add `'` to close the char literal //~| ERROR mismatched types } diff --git a/tests/ui/parser/issues/issue-93282.stderr b/tests/ui/parser/issues/issue-93282.stderr index c6140bb821e48..1daacff52f066 100644 --- a/tests/ui/parser/issues/issue-93282.stderr +++ b/tests/ui/parser/issues/issue-93282.stderr @@ -1,27 +1,16 @@ -error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/issue-93282.rs:2:9 - | -LL | f<'a,> - | ^ expected `while`, `for`, `loop` or `{` after a label - | -help: add `'` to close the char literal - | -LL | f<'a',> - | + - -error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `}`, or an operator, found `,` - --> $DIR/issue-93282.rs:2:9 +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/issue-93282.rs:3:6 | LL | f<'a,> - | ^ expected one of 9 possible tokens + | ^ | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments +help: add `::` | LL | f::<'a,> | ++ error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/issue-93282.rs:13:11 + --> $DIR/issue-93282.rs:16:11 | LL | bar('y, x); | ^ expected `while`, `for`, `loop` or `{` after a label @@ -31,8 +20,25 @@ help: add `'` to close the char literal LL | bar('y', x); | + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/issue-93282.rs:3:7 + | +LL | f<'a,> + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn main<'a>() { + | ++++ + +error[E0425]: cannot find value `f` in this scope + --> $DIR/issue-93282.rs:3:5 + | +LL | f<'a,> + | ^ not found in this scope + error[E0308]: mismatched types - --> $DIR/issue-93282.rs:13:9 + --> $DIR/issue-93282.rs:16:9 | LL | bar('y, x); | --- ^^ expected `usize`, found `char` @@ -40,11 +46,12 @@ LL | bar('y, x); | arguments to this function are incorrect | note: function defined here - --> $DIR/issue-93282.rs:7:4 + --> $DIR/issue-93282.rs:10:4 | LL | fn bar(a: usize, b: usize) -> usize { | ^^^ -------- -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0261, E0308, E0425. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime-macro.rs b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.rs new file mode 100644 index 0000000000000..366fc8ddfd982 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.rs @@ -0,0 +1,16 @@ +macro_rules! m { + ($e:expr) => { "expr" }; + (a < $l:lifetime >) => { "lifetime" }; +} + +fn test_macro() { + // Ensure turbofish recovery is disabled during macro matching to avoid spurious diagnostics. + // The second arm does NOT match; the expr-fragment error is a hard error. + let _ = m!(a < 'x >); + //~^ ERROR comparison operators cannot be chained + //~| HELP use `::<...>` instead of `<...>` + //~| HELP or use `(...)` + //~| ERROR expected `while`, `for`, `loop` or `{` after a label +} + +fn main() {} diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime-macro.stderr b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.stderr new file mode 100644 index 0000000000000..a05d7e668a521 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.stderr @@ -0,0 +1,20 @@ +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/missing-turbofish-lifetime-macro.rs:9:23 + | +LL | let _ = m!(a < 'x >); + | ^ expected `while`, `for`, `loop` or `{` after a label + +error: comparison operators cannot be chained + --> $DIR/missing-turbofish-lifetime-macro.rs:9:18 + | +LL | ($e:expr) => { "expr" }; + | ------- while parsing argument for this `expr` macro fragment +... +LL | let _ = m!(a < 'x >); + | ^ ^ + | + = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + = help: or use `(...)` if you meant to specify fn arguments + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.fixed b/tests/ui/parser/recover/missing-turbofish-lifetime.fixed new file mode 100644 index 0000000000000..f5d8e2e63e6d5 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.fixed @@ -0,0 +1,87 @@ +//! Regression test for +//! +//! Test that omitting the turbofish when passing a lifetime to an expression +//! emits a targeted suggestion rather than a cascading syntax error. + +#![allow(dead_code)] + +//@ run-rustfix + +struct Struct<'a> { + string: &'a str, +} + +fn struct_with_reserved_lifetime() { + let _ = Struct::<'_> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_named_lifetime<'a>() { + let _ = Struct::<'a> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_multichar_lifetime<'abc>() { + let _ = Struct::<'abc> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +struct TupleStruct<'a>(&'a str); + +fn tuple_struct_with_reserved_lifetime() { + let _ = TupleStruct::<'_>(""); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` +} + +struct Wrapper<'a>(&'a str); +impl<'a> Wrapper<'a> { fn new(s: &'a str) -> Self { Self(s) } } + +struct Struct2<'a> { x: i32, _p: std::marker::PhantomData<&'a ()> } +impl<'a> Struct2<'a> { fn method(&self) -> Option<()> { Some(()) } } + +fn f<'a: 'a>() {} + +struct StructT<'a, T> { a: &'a str, b: T } +struct Struct3<'a, 'b> { a: &'a str, b: &'b str } + +fn chaining_cases<'a, 'b, T>() -> Option<()> { + let _ = Wrapper::<'a>::new("hi"); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + f::<'_>(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with two lifetimes + let _ = Struct3::<'a, 'b> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with lifetime and type + let _ = StructT::<'a, &str> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + Some(()) +} + +fn main() {} diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.rs b/tests/ui/parser/recover/missing-turbofish-lifetime.rs new file mode 100644 index 0000000000000..1bc9e97878739 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.rs @@ -0,0 +1,87 @@ +//! Regression test for +//! +//! Test that omitting the turbofish when passing a lifetime to an expression +//! emits a targeted suggestion rather than a cascading syntax error. + +#![allow(dead_code)] + +//@ run-rustfix + +struct Struct<'a> { + string: &'a str, +} + +fn struct_with_reserved_lifetime() { + let _ = Struct<'_> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_named_lifetime<'a>() { + let _ = Struct<'a> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_multichar_lifetime<'abc>() { + let _ = Struct<'abc> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +struct TupleStruct<'a>(&'a str); + +fn tuple_struct_with_reserved_lifetime() { + let _ = TupleStruct<'_>(""); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` +} + +struct Wrapper<'a>(&'a str); +impl<'a> Wrapper<'a> { fn new(s: &'a str) -> Self { Self(s) } } + +struct Struct2<'a> { x: i32, _p: std::marker::PhantomData<&'a ()> } +impl<'a> Struct2<'a> { fn method(&self) -> Option<()> { Some(()) } } + +fn f<'a: 'a>() {} + +struct StructT<'a, T> { a: &'a str, b: T } +struct Struct3<'a, 'b> { a: &'a str, b: &'b str } + +fn chaining_cases<'a, 'b, T>() -> Option<()> { + let _ = Wrapper<'a>::new("hi"); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + f<'_>(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with two lifetimes + let _ = Struct3<'a, 'b> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with lifetime and type + let _ = StructT<'a, &str> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + Some(()) +} + +fn main() {} diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.stderr b/tests/ui/parser/recover/missing-turbofish-lifetime.stderr new file mode 100644 index 0000000000000..faea5463bdfd4 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.stderr @@ -0,0 +1,112 @@ +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:15:19 + | +LL | let _ = Struct<'_> { + | ^ + | +help: add `::` + | +LL | let _ = Struct::<'_> { + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:23:19 + | +LL | let _ = Struct<'a> { + | ^ + | +help: add `::` + | +LL | let _ = Struct::<'a> { + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:31:19 + | +LL | let _ = Struct<'abc> { + | ^ + | +help: add `::` + | +LL | let _ = Struct::<'abc> { + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:41:24 + | +LL | let _ = TupleStruct<'_>(""); + | ^ + | +help: add `::` + | +LL | let _ = TupleStruct::<'_>(""); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:58:20 + | +LL | let _ = Wrapper<'a>::new("hi"); + | ^ + | +help: add `::` + | +LL | let _ = Wrapper::<'a>::new("hi"); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:62:20 + | +LL | let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method(); + | ^ + | +help: add `::` + | +LL | let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method(); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:66:20 + | +LL | let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + | ^ + | +help: add `::` + | +LL | let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:70:6 + | +LL | f<'_>(); + | ^ + | +help: add `::` + | +LL | f::<'_>(); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:75:20 + | +LL | let _ = Struct3<'a, 'b> { a: "hi", b: "hi" }; + | ^ + | +help: add `::` + | +LL | let _ = Struct3::<'a, 'b> { a: "hi", b: "hi" }; + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:80:20 + | +LL | let _ = StructT<'a, &str> { a: "hi", b: "hi" }; + | ^ + | +help: add `::` + | +LL | let _ = StructT::<'a, &str> { a: "hi", b: "hi" }; + | ++ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/parser/require-parens-for-chained-comparison.rs b/tests/ui/parser/require-parens-for-chained-comparison.rs index 6152fff6c0365..4162e9f27f36d 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.rs +++ b/tests/ui/parser/require-parens-for-chained-comparison.rs @@ -20,18 +20,14 @@ fn main() { //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments let _ = f<'_, i8>(); - //~^ ERROR expected one of - //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments - //~| ERROR expected - //~| HELP add `'` to close the char literal - //~| ERROR labels cannot use keyword names + //~^ ERROR cannot find function `f` in this scope + //~| ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` f<'_>(); - //~^ ERROR comparison operators cannot be chained - //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments - //~| ERROR expected - //~| HELP add `'` to close the char literal - //~| ERROR labels cannot use keyword names + //~^ ERROR cannot find function `f` in this scope + //~| ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` let _ = f; //~^ ERROR comparison operators cannot be chained diff --git a/tests/ui/parser/require-parens-for-chained-comparison.stderr b/tests/ui/parser/require-parens-for-chained-comparison.stderr index 9edfae3625027..50f66927db118 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.stderr +++ b/tests/ui/parser/require-parens-for-chained-comparison.stderr @@ -53,64 +53,30 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum LL | let _ = f::(); | ++ -error: labels cannot use keyword names - --> $DIR/require-parens-for-chained-comparison.rs:22:15 +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/require-parens-for-chained-comparison.rs:22:14 | LL | let _ = f<'_, i8>(); - | ^^ - -error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/require-parens-for-chained-comparison.rs:22:17 - | -LL | let _ = f<'_, i8>(); - | ^ expected `while`, `for`, `loop` or `{` after a label - | -help: add `'` to close the char literal + | ^ | -LL | let _ = f<'_', i8>(); - | + - -error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, or an operator, found `,` - --> $DIR/require-parens-for-chained-comparison.rs:22:17 - | -LL | let _ = f<'_, i8>(); - | ^ expected one of 9 possible tokens - | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments +help: add `::` | LL | let _ = f::<'_, i8>(); | ++ -error: labels cannot use keyword names - --> $DIR/require-parens-for-chained-comparison.rs:29:7 - | -LL | f<'_>(); - | ^^ - -error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/require-parens-for-chained-comparison.rs:29:9 +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/require-parens-for-chained-comparison.rs:27:6 | LL | f<'_>(); - | ^ expected `while`, `for`, `loop` or `{` after a label + | ^ | -help: add `'` to close the char literal - | -LL | f<'_'>(); - | + - -error: comparison operators cannot be chained - --> $DIR/require-parens-for-chained-comparison.rs:29:6 - | -LL | f<'_>(); - | ^ ^ - | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments +help: add `::` | LL | f::<'_>(); | ++ error: comparison operators cannot be chained - --> $DIR/require-parens-for-chained-comparison.rs:36:14 + --> $DIR/require-parens-for-chained-comparison.rs:32:14 | LL | let _ = f; | ^ ^ @@ -118,5 +84,18 @@ LL | let _ = f; = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments = help: or use `(...)` if you meant to specify fn arguments -error: aborting due to 12 previous errors +error[E0425]: cannot find function `f` in this scope + --> $DIR/require-parens-for-chained-comparison.rs:22:13 + | +LL | let _ = f<'_, i8>(); + | ^ not found in this scope + +error[E0425]: cannot find function `f` in this scope + --> $DIR/require-parens-for-chained-comparison.rs:27:5 + | +LL | f<'_>(); + | ^ not found in this scope + +error: aborting due to 10 previous errors +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.rs b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.rs new file mode 100644 index 0000000000000..0d45d1ce9b1c4 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Znext-solver + +// Regression test for #155977: the new solver used to ICE while normalizing +// a higher-ranked where-clause in the param env. +// Fixed by eager param-env normalization in #156976. + +trait Trait { + type Output; +} + +fn walk2() +//~^ ERROR the trait bound +where + for<'a> F: 'a, + for<'a> >::Output: 'a, +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.stderr b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.stderr new file mode 100644 index 0000000000000..1889e3be86aba --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `for<'a> F: Trait<&'a u32>` is not satisfied + --> $DIR/higher-ranked-param-env-normalization-155977.rs:11:1 + | +LL | / fn walk2() +LL | | +LL | | where +LL | | for<'a> F: 'a, +LL | | for<'a> >::Output: 'a, + | |______________________________________________^ the trait `for<'a> Trait<&'a u32>` is not implemented for `F` + | +help: consider further restricting type parameter `F` with trait `Trait` + | +LL | for<'a> F: 'a + for<'a> Trait<&'a u32>, + | ++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr index fc285abc9fe40..a2d31d4d51ff6 100644 --- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr +++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | needs_send::(); | ^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `needs_send` | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/dont-type_of-tait-in-defining-scope.rs:7:12 + | +LL | type Foo = impl Send; + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr index fc285abc9fe40..5af23f405e054 100644 --- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr +++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | needs_send::(); | ^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `needs_send` | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/dont-type_of-tait-in-defining-scope.rs:10:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.stderr b/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.stderr index d6fc9584ef516..3e931b93f5dbf 100644 --- a/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.stderr +++ b/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed for `*mut _` LL | let r = random_paulis().unwrap(); | ^ | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/recursive-hidden-type-canonicalization.rs:18:35 + | +LL | fn random_paulis() -> Option<*mut impl Sized> { + | ^^^^^^^^^^ help: consider giving `r` an explicit type, where the placeholder `_` is specified | LL | let r: *mut _ = random_paulis().unwrap(); diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr index 3862479fee380..d91b4490abe88 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | let x = >::Assoc::default(); | ^^^ cannot infer type | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/constrain_in_projection2.rs:12:12 + | +LL | type Bar = impl Sized; + | ^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr index d025a821d1b45..2836a83ea76c9 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr @@ -13,7 +13,11 @@ error[E0282]: type annotations needed LL | fn convert(_i: In) -> Self::Out { | ^^^^^^^^^ cannot infer type | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/issue-84660-unsoundness.rs:12:12 + | +LL | type Bar = impl Foo; + | ^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr index a2e5b3cc972ef..81b929b370470 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | self.bar.next().unwrap(); | ^^^^ | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/method_resolution_trait_method_from_opaque.rs:18:13 + | +LL | type Tait = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | LL - self.bar.next().unwrap(); diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr index 9e8857201e72c..fac807b5bc3dc 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr @@ -4,7 +4,11 @@ error[E0282]: type annotations needed LL | fn foo() -> impl Foo { | ^^^^^^^^^^^^^^ cannot infer type | - = note: cannot infer type of hidden type of opaque +note: cannot infer type of hidden type of opaque + --> $DIR/nested-tait-inference2.rs:10:13 + | +LL | type FooX = impl Debug; + | ^^^^^^^^^^ error: aborting due to 1 previous error