From d5bc581f5db6ba5198ccec45d938422778f56bff Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sun, 30 Apr 2023 02:20:53 +0100 Subject: [PATCH 01/17] Migrate `mir_transform` to translatable diagnostics --- Cargo.lock | 3 + compiler/rustc_driver_impl/Cargo.toml | 8 +- compiler/rustc_driver_impl/src/lib.rs | 1 + .../rustc_errors/src/diagnostic_builder.rs | 8 + compiler/rustc_mir_transform/Cargo.toml | 2 + compiler/rustc_mir_transform/messages.ftl | 66 +++++ .../src/check_const_item_mutation.rs | 65 ++--- .../src/check_packed_ref.rs | 23 +- .../rustc_mir_transform/src/check_unsafety.rs | 68 ++--- .../src/const_prop_lint.rs | 76 +++--- compiler/rustc_mir_transform/src/errors.rs | 245 ++++++++++++++++++ .../src/ffi_unwind_calls.rs | 16 +- .../src/function_item_references.rs | 33 +-- compiler/rustc_mir_transform/src/generator.rs | 38 +-- compiler/rustc_mir_transform/src/lib.rs | 8 + .../src/lower_intrinsics.rs | 10 +- 16 files changed, 470 insertions(+), 200 deletions(-) create mode 100644 compiler/rustc_mir_transform/messages.ftl create mode 100644 compiler/rustc_mir_transform/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index f6ee8157b40ef..b6253f539bb6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3351,6 +3351,7 @@ dependencies = [ "rustc_middle", "rustc_mir_build", "rustc_mir_dataflow", + "rustc_mir_transform", "rustc_monomorphize", "rustc_parse", "rustc_passes", @@ -3859,8 +3860,10 @@ dependencies = [ "rustc_const_eval", "rustc_data_structures", "rustc_errors", + "rustc_fluent_macro", "rustc_hir", "rustc_index", + "rustc_macros", "rustc_middle", "rustc_mir_dataflow", "rustc_serialize", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index cc4c5a0cacddf..d7d97fcc3e7b7 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -51,6 +51,7 @@ rustc_interface = { path = "../rustc_interface" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } +rustc_mir_transform = { path = "../rustc_mir_transform" } [target.'cfg(unix)'.dependencies] libc = "0.2" @@ -64,5 +65,8 @@ features = [ [features] llvm = ['rustc_interface/llvm'] max_level_info = ['rustc_log/max_level_info'] -rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler', - 'rustc_middle/rustc_use_parallel_compiler'] +rustc_use_parallel_compiler = [ + 'rustc_data_structures/rustc_use_parallel_compiler', + 'rustc_interface/rustc_use_parallel_compiler', + 'rustc_middle/rustc_use_parallel_compiler' +] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 5fac485de6417..c629c55264a12 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -99,6 +99,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_middle::DEFAULT_LOCALE_RESOURCE, rustc_mir_build::DEFAULT_LOCALE_RESOURCE, rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE, + rustc_mir_transform::DEFAULT_LOCALE_RESOURCE, rustc_monomorphize::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_passes::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 3064d2bedbe12..c8e57aa39af7c 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -570,6 +570,14 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { Some((diagnostic, handler)) } + /// Retrieves the [`Handler`] if available + pub fn handler(&self) -> Option<&Handler> { + match self.inner.state { + DiagnosticBuilderState::Emittable(handler) => Some(handler), + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None, + } + } + /// Buffers the diagnostic for later emission, /// unless handler has disabled such buffering. pub fn buffer(self, buffered_diagnostics: &mut Vec) { diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 962536669e0db..eca5f98a2c01c 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -24,6 +24,8 @@ rustc_session = { path = "../rustc_session" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_span = { path = "../rustc_span" } +rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_macros = { path = "../rustc_macros" } [dev-dependencies] coverage_test_macros = { path = "src/coverage/test_macros" } diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl new file mode 100644 index 0000000000000..8c85cb5f76d86 --- /dev/null +++ b/compiler/rustc_mir_transform/messages.ftl @@ -0,0 +1,66 @@ +mir_transform_const_modify = attempting to modify a `const` item + .note = each usage of a `const` item creates a new temporary; the original `const` item will not be modified + +mir_transform_const_mut_borrow = taking a mutable reference to a `const` item + .note = each usage of a `const` item creates a new temporary + .note2 = the mutable reference will refer to this temporary, not the original `const` item + .note3 = mutable reference created due to call to this method + +mir_transform_const_defined_here = `const` item defined here + +mir_transform_unaligned_packed_ref = reference to packed field is unaligned + .note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + .note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + .help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +mir_transform_unused_unsafe = unnecessary `unsafe` block + .label = because it's nested under this `unsafe` block + +mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in_unsafe_fn_allowed -> + [true] function or block + *[false] block + } + .not_inherited = items do not inherit unsafety from separate enclosing items + +mir_transform_call_to_unsafe_label = call to unsafe function +mir_transform_call_to_unsafe_note = consult the function's documentation for information on how to avoid undefined behavior +mir_transform_use_of_asm_label = use of inline assembly +mir_transform_use_of_asm_note = inline assembly is entirely unchecked and can cause undefined behavior +mir_transform_initializing_valid_range_label = initializing type with `rustc_layout_scalar_valid_range` attr +mir_transform_initializing_valid_range_note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior +mir_transform_const_ptr2int_label = cast of pointer to int +mir_transform_const_ptr2int_note = casting pointers to integers in constants +mir_transform_use_of_static_mut_label = use of mutable static +mir_transform_use_of_static_mut_note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior +mir_transform_use_of_extern_static_label = use of extern static +mir_transform_use_of_extern_static_note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior +mir_transform_deref_ptr_label = dereference of raw pointer +mir_transform_deref_ptr_note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior +mir_transform_union_access_label = access to union field +mir_transform_union_access_note = the field may not be properly initialized: using uninitialized data will cause undefined behavior +mir_transform_mutation_layout_constrained_label = mutation of layout constrained field +mir_transform_mutation_layout_constrained_note = mutating layout constrained fields cannot statically be checked for valid values +mir_transform_mutation_layout_constrained_borrow_label = borrow of layout constrained field with interior mutability +mir_transform_mutation_layout_constrained_borrow_note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values +mir_transform_target_feature_call_label = call to function with `#[target_feature]` +mir_transform_target_feature_call_note = can only be called if the required target features are available + +mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe block (error E0133) + +mir_transform_arithmetic_overflow = this arithmetic operation will overflow +mir_transform_operation_will_panic = this operation will panic at runtime + +mir_transform_ffi_unwind_call = call to {$foreign -> + [true] foreign function + *[false] function pointer + } with FFI-unwind ABI + +mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer + .suggestion = cast `{$ident}` to obtain a function pointer + +mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be + .label = the value is held across this suspend point + .note = {$reason} + .help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point + +mir_transform_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 57b24c9c552a5..b79150737d612 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -1,11 +1,12 @@ -use rustc_errors::{DiagnosticBuilder, DiagnosticMessage}; +use rustc_hir::HirId; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::CONST_ITEM_MUTATION; use rustc_span::def_id::DefId; +use rustc_span::Span; -use crate::MirLint; +use crate::{errors, MirLint}; pub struct CheckConstItemMutation; @@ -58,16 +59,14 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { } } - fn lint_const_item_usage( + /// If we should lint on this usage, return the [`HirId`], source [`Span`] + /// and [`Span`] of the const item to use in the lint. + fn should_lint_const_item_usage( &self, place: &Place<'tcx>, const_item: DefId, location: Location, - msg: impl Into, - decorate: impl for<'a, 'b> FnOnce( - &'a mut DiagnosticBuilder<'b, ()>, - ) -> &'a mut DiagnosticBuilder<'b, ()>, - ) { + ) -> Option<(HirId, Span, Span)> { // Don't lint on borrowing/assigning when a dereference is involved. // If we 'leave' the temporary via a dereference, we must // be modifying something else @@ -83,16 +82,9 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { .assert_crate_local() .lint_root; - self.tcx.struct_span_lint_hir( - CONST_ITEM_MUTATION, - lint_root, - source_info.span, - msg, - |lint| { - decorate(lint) - .span_note(self.tcx.def_span(const_item), "`const` item defined here") - }, - ); + Some((lint_root, source_info.span, self.tcx.def_span(const_item))) + } else { + None } } } @@ -104,10 +96,14 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> { // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error, // so emitting a lint would be redundant. if !lhs.projection.is_empty() { - if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) { - self.lint_const_item_usage(&lhs, def_id, loc, "attempting to modify a `const` item",|lint| { - lint.note("each usage of a `const` item creates a new temporary; the original `const` item will not be modified") - }) + if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) + && let Some((lint_root, span, item)) = self.should_lint_const_item_usage(&lhs, def_id, loc) { + self.tcx.emit_spanned_lint( + CONST_ITEM_MUTATION, + lint_root, + span, + errors::ConstMutate::Modify { konst: item } + ); } } // We are looking for MIR of the form: @@ -143,17 +139,22 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> { }); let lint_loc = if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc }; - self.lint_const_item_usage(place, def_id, lint_loc, "taking a mutable reference to a `const` item", |lint| { - lint - .note("each usage of a `const` item creates a new temporary") - .note("the mutable reference will refer to this temporary, not the original `const` item"); - - if let Some((method_did, _substs)) = method_did { - lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method"); - } - lint - }); + let method_call = if let Some((method_did, _)) = method_did { + Some(self.tcx.def_span(method_did)) + } else { + None + }; + if let Some((lint_root, span, item)) = + self.should_lint_const_item_usage(place, def_id, lint_loc) + { + self.tcx.emit_spanned_lint( + CONST_ITEM_MUTATION, + lint_root, + span, + errors::ConstMutate::MutBorrow { method_call, konst: item }, + ); + } } } self.super_rvalue(rvalue, loc); diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index b9bc89fcf8fa4..2e6cf603d5968 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -1,10 +1,9 @@ -use rustc_errors::struct_span_err; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; -use crate::util; use crate::MirLint; +use crate::{errors, util}; pub struct CheckPackedRef; @@ -49,25 +48,7 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { // shouldn't do. span_bug!(self.source_info.span, "builtin derive created an unaligned reference"); } else { - struct_span_err!( - self.tcx.sess, - self.source_info.span, - E0793, - "reference to packed field is unaligned" - ) - .note( - "packed structs are only aligned by one byte, and many modern architectures \ - penalize unaligned field accesses" - ) - .note( - "creating a misaligned reference is undefined behavior (even if that \ - reference is never dereferenced)", - ).help( - "copy the field contents to a local variable, or replace the \ - reference with a raw pointer and use `read_unaligned`/`write_unaligned` \ - (loads and stores via `*p` must be properly aligned even when using raw pointers)" - ) - .emit(); + self.tcx.sess.emit_err(errors::UnalignedPackedRef { span: self.source_info.span }); } } } diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index ce6d865a7dcef..bdb4f20da1059 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -1,5 +1,4 @@ use rustc_data_structures::unord::{UnordItems, UnordSet}; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -15,6 +14,8 @@ use rustc_session::lint::Level; use std::ops::Bound; +use crate::errors; + pub struct UnsafetyChecker<'a, 'tcx> { body: &'a Body<'tcx>, body_did: LocalDefId, @@ -509,21 +510,12 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResu fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) { let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id)); - let msg = "unnecessary `unsafe` block"; - tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg, |lint| { - lint.span_label(span, msg); - match kind { - UnusedUnsafe::Unused => {} - UnusedUnsafe::InUnsafeBlock(id) => { - lint.span_label( - tcx.sess.source_map().guess_head_span(tcx.hir().span(id)), - "because it's nested under this `unsafe` block", - ); - } - } - - lint - }); + let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind { + Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) + } else { + None + }; + tcx.emit_spanned_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent }); } pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { @@ -537,26 +529,11 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id); for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() { - let (description, note) = details.description_and_note(); + let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span }; match kind { UnsafetyViolationKind::General => { - // once - let unsafe_fn_msg = if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { - " function or" - } else { - "" - }; - - let mut err = struct_span_err!( - tcx.sess, - source_info.span, - E0133, - "{} is unsafe and requires unsafe{} block", - description, - unsafe_fn_msg, - ); - err.span_label(source_info.span, description).note(note); + let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root); let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| { if let Node::Expr(block) = node && let ExprKind::Block(block, _) = block.kind @@ -572,22 +549,23 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { false } }); - if let Some((id, _)) = note_non_inherited { - let span = tcx.hir().span(id); - err.span_label( - tcx.sess.source_map().guess_head_span(span), - "items do not inherit unsafety from separate enclosing items", - ); - } - - err.emit(); + let enclosing = if let Some((id, _)) = note_non_inherited { + Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) + } else { + None + }; + tcx.sess.emit_err(errors::RequiresUnsafe { + span: source_info.span, + enclosing, + details, + op_in_unsafe_fn_allowed, + }); } - UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir( + UnsafetyViolationKind::UnsafeFn => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, lint_root, source_info.span, - format!("{} is unsafe and requires unsafe block (error E0133)", description,), - |lint| lint.span_label(source_info.span, description).note(note), + errors::UnsafeOpInUnsafeFn { details }, ), } } diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index a4049d08d7b82..adb09c509d287 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -1,6 +1,8 @@ //! Propagates constants for early reporting of statically known //! assertion failures +use std::fmt::Debug; + use either::Left; use rustc_const_eval::interpret::Immediate; @@ -17,7 +19,6 @@ use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{ self, ConstInt, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt, }; -use rustc_session::lint; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; use rustc_trait_selection::traits; @@ -25,6 +26,7 @@ use rustc_trait_selection::traits; use crate::const_prop::CanConstProp; use crate::const_prop::ConstPropMachine; use crate::const_prop::ConstPropMode; +use crate::errors::AssertLint; use crate::MirLint; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -311,18 +313,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - fn report_assert_as_lint( - &self, - lint: &'static lint::Lint, - location: Location, - message: &'static str, - panic: AssertKind, - ) { - let source_info = self.body().source_info(location); + fn report_assert_as_lint(&self, source_info: &SourceInfo, lint: AssertLint) { if let Some(lint_root) = self.lint_root(*source_info) { - self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, message, |lint| { - lint.span_label(source_info.span, format!("{:?}", panic)) - }); + self.tcx.emit_spanned_lint(lint.lint(), lint_root, source_info.span, lint); } } @@ -335,11 +328,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // `AssertKind` only has an `OverflowNeg` variant, so make sure that is // appropriate to use. assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); + let source_info = self.body().source_info(location); self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - location, - "this arithmetic operation will overflow", - AssertKind::OverflowNeg(val.to_const_int()), + source_info, + AssertLint::ArithmeticOverflow( + source_info.span, + AssertKind::OverflowNeg(val.to_const_int()), + ), ); return None; } @@ -370,23 +365,23 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let r_bits = r.to_scalar().to_bits(right_size).ok(); if r_bits.map_or(false, |b| b >= left_size.bits() as u128) { debug!("check_binary_op: reporting assert for {:?}", location); + let source_info = self.body().source_info(location); + let panic = AssertKind::Overflow( + op, + match l { + Some(l) => l.to_const_int(), + // Invent a dummy value, the diagnostic ignores it anyway + None => ConstInt::new( + ScalarInt::try_from_uint(1_u8, left_size).unwrap(), + left_ty.is_signed(), + left_ty.is_ptr_sized_integral(), + ), + }, + r.to_const_int(), + ); self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - location, - "this arithmetic operation will overflow", - AssertKind::Overflow( - op, - match l { - Some(l) => l.to_const_int(), - // Invent a dummy value, the diagnostic ignores it anyway - None => ConstInt::new( - ScalarInt::try_from_uint(1_u8, left_size).unwrap(), - left_ty.is_signed(), - left_ty.is_ptr_sized_integral(), - ), - }, - r.to_const_int(), - ), + source_info, + AssertLint::ArithmeticOverflow(source_info.span, panic), ); return None; } @@ -398,11 +393,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, &l, &r)?; Ok(overflow) })? { + let source_info = self.body().source_info(location); self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - location, - "this arithmetic operation will overflow", - AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), + source_info, + AssertLint::ArithmeticOverflow( + source_info.span, + AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), + ), ); return None; } @@ -543,11 +540,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // Need proper const propagator for these. _ => return None, }; + let source_info = self.body().source_info(location); self.report_assert_as_lint( - lint::builtin::UNCONDITIONAL_PANIC, - location, - "this operation will panic at runtime", - msg, + source_info, + AssertLint::UnconditionalPanic(source_info.span, msg), ); } diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs new file mode 100644 index 0000000000000..602e40d513104 --- /dev/null +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -0,0 +1,245 @@ +use rustc_errors::{ + DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler, IntoDiagnostic, +}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails}; +use rustc_session::lint::{self, Lint}; +use rustc_span::Span; + +#[derive(LintDiagnostic)] +pub(crate) enum ConstMutate { + #[diag(mir_transform_const_modify)] + #[note] + Modify { + #[note(mir_transform_const_defined_here)] + konst: Span, + }, + #[diag(mir_transform_const_mut_borrow)] + #[note] + #[note(mir_transform_note2)] + MutBorrow { + #[note(mir_transform_note3)] + method_call: Option, + #[note(mir_transform_const_defined_here)] + konst: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(mir_transform_unaligned_packed_ref, code = "E0793")] +#[note] +#[note(mir_transform_note_ub)] +#[help] +pub(crate) struct UnalignedPackedRef { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_unused_unsafe)] +pub(crate) struct UnusedUnsafe { + #[label(mir_transform_unused_unsafe)] + pub span: Span, + #[label] + pub nested_parent: Option, +} + +pub(crate) struct RequiresUnsafe { + pub span: Span, + pub details: RequiresUnsafeDetail, + pub enclosing: Option, + pub op_in_unsafe_fn_allowed: bool, +} + +// The primary message for this diagnostic should be '{$label} is unsafe and...', +// so we need to eagerly translate the label here, which isn't supported by the derive API +// We could also exhaustively list out the primary messages for all unsafe violations, +// but this would result in a lot of duplication. +impl<'sess, G: EmissionGuarantee> IntoDiagnostic<'sess, G> for RequiresUnsafe { + #[track_caller] + fn into_diagnostic(self, handler: &'sess Handler) -> DiagnosticBuilder<'sess, G> { + let mut diag = + handler.struct_diagnostic(crate::fluent_generated::mir_transform_requires_unsafe); + diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string())); + diag.set_span(self.span); + diag.span_label(self.span, self.details.label()); + diag.note(self.details.note()); + let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter()); + diag.set_arg("details", desc); + diag.set_arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed); + if let Some(sp) = self.enclosing { + diag.span_label(sp, crate::fluent_generated::mir_transform_not_inherited); + } + diag + } +} + +#[derive(Copy, Clone)] +pub(crate) struct RequiresUnsafeDetail { + pub span: Span, + pub violation: UnsafetyViolationDetails, +} + +impl RequiresUnsafeDetail { + fn note(self) -> DiagnosticMessage { + use UnsafetyViolationDetails::*; + match self.violation { + CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_note, + UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_note, + InitializingTypeWith => { + crate::fluent_generated::mir_transform_initializing_valid_range_note + } + CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_note, + UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_note, + UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_note, + DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_note, + AccessToUnionField => crate::fluent_generated::mir_transform_union_access_note, + MutationOfLayoutConstrainedField => { + crate::fluent_generated::mir_transform_mutation_layout_constrained_note + } + BorrowOfLayoutConstrainedField => { + crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_note + } + CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_note, + } + } + + fn label(self) -> DiagnosticMessage { + use UnsafetyViolationDetails::*; + match self.violation { + CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_label, + UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_label, + InitializingTypeWith => { + crate::fluent_generated::mir_transform_initializing_valid_range_label + } + CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_label, + UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_label, + UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_label, + DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_label, + AccessToUnionField => crate::fluent_generated::mir_transform_union_access_label, + MutationOfLayoutConstrainedField => { + crate::fluent_generated::mir_transform_mutation_layout_constrained_label + } + BorrowOfLayoutConstrainedField => { + crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_label + } + CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_label, + } + } +} + +pub(crate) struct UnsafeOpInUnsafeFn { + pub details: RequiresUnsafeDetail, +} + +impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn { + #[track_caller] + fn decorate_lint<'b>( + self, + diag: &'b mut DiagnosticBuilder<'a, ()>, + ) -> &'b mut DiagnosticBuilder<'a, ()> { + let desc = diag + .handler() + .expect("lint should not yet be emitted") + .eagerly_translate_to_string(self.details.label(), [].into_iter()); + diag.set_arg("details", desc); + diag.span_label(self.details.span, self.details.label()); + diag.note(self.details.note()); + diag + } + + fn msg(&self) -> DiagnosticMessage { + crate::fluent_generated::mir_transform_unsafe_op_in_unsafe_fn + } +} + +pub(crate) enum AssertLint

{ + ArithmeticOverflow(Span, AssertKind

), + UnconditionalPanic(Span, AssertKind

), +} + +impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint

{ + fn decorate_lint<'b>( + self, + diag: &'b mut DiagnosticBuilder<'a, ()>, + ) -> &'b mut DiagnosticBuilder<'a, ()> { + diag.span_label(self.span(), format!("{:?}", self.panic())); + diag + } + + fn msg(&self) -> DiagnosticMessage { + match self { + AssertLint::ArithmeticOverflow(..) => { + crate::fluent_generated::mir_transform_arithmetic_overflow + } + AssertLint::UnconditionalPanic(..) => { + crate::fluent_generated::mir_transform_operation_will_panic + } + } + } +} + +impl

AssertLint

{ + pub fn lint(&self) -> &'static Lint { + match self { + AssertLint::ArithmeticOverflow(..) => lint::builtin::ARITHMETIC_OVERFLOW, + AssertLint::UnconditionalPanic(..) => lint::builtin::UNCONDITIONAL_PANIC, + } + } + pub fn span(&self) -> Span { + match self { + AssertLint::ArithmeticOverflow(sp, _) | AssertLint::UnconditionalPanic(sp, _) => *sp, + } + } + pub fn panic(&self) -> &AssertKind

{ + match self { + AssertLint::ArithmeticOverflow(_, p) | AssertLint::UnconditionalPanic(_, p) => p, + } + } +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_ffi_unwind_call)] +pub(crate) struct FfiUnwindCall { + #[label(mir_transform_ffi_unwind_call)] + pub span: Span, + pub foreign: bool, +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_fn_item_ref)] +pub(crate) struct FnItemRef { + #[suggestion(code = "{sugg}", applicability = "unspecified")] + pub span: Span, + pub sugg: String, + pub ident: String, +} + +#[derive(LintDiagnostic)] +#[diag(mir_transform_must_not_suspend)] +pub(crate) struct MustNotSupend<'a> { + #[label] + pub yield_sp: Span, + #[subdiagnostic] + pub reason: Option, + #[help] + pub src_sp: Span, + pub pre: &'a str, + pub def_path: String, + pub post: &'a str, +} + +#[derive(Subdiagnostic)] +#[note(mir_transform_note)] +pub(crate) struct MustNotSuspendReason { + #[primary_span] + pub span: Span, + pub reason: String, +} + +#[derive(Diagnostic)] +#[diag(mir_transform_simd_shuffle_last_const)] +pub(crate) struct SimdShuffleLastConst { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index db68adc8bc979..ac1de989a7204 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -8,6 +8,8 @@ use rustc_session::lint::builtin::FFI_UNWIND_CALLS; use rustc_target::spec::abi::Abi; use rustc_target::spec::PanicStrategy; +use crate::errors; + fn abi_can_unwind(abi: Abi) -> bool { use Abi::*; match abi { @@ -107,13 +109,13 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { .lint_root; let span = terminator.source_info.span; - let msg = match fn_def_id { - Some(_) => "call to foreign function with FFI-unwind ABI", - None => "call to function pointer with FFI-unwind ABI", - }; - tcx.struct_span_lint_hir(FFI_UNWIND_CALLS, lint_root, span, msg, |lint| { - lint.span_label(span, msg) - }); + let foreign = fn_def_id.is_some(); + tcx.emit_spanned_lint( + FFI_UNWIND_CALLS, + lint_root, + span, + errors::FfiUnwindCall { span, foreign }, + ); tainted = true; } diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index f26c6de9648c0..5989dbebf2db9 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -1,5 +1,4 @@ use itertools::Itertools; -use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -8,7 +7,7 @@ use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; use rustc_span::{symbol::sym, Span}; use rustc_target::spec::abi::Abi; -use crate::MirLint; +use crate::{errors, MirLint}; pub struct FunctionItemReferences; @@ -174,27 +173,21 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder(); let variadic = if fn_sig.c_variadic() { ", ..." } else { "" }; let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" }; - self.tcx.struct_span_lint_hir( + let sugg = format!( + "{} as {}{}fn({}{}){}", + if params.is_empty() { ident.clone() } else { format!("{}::<{}>", ident, params) }, + unsafety, + abi, + vec!["_"; num_args].join(", "), + variadic, + ret, + ); + + self.tcx.emit_spanned_lint( FUNCTION_ITEM_REFERENCES, lint_root, span, - "taking a reference to a function item does not give a function pointer", - |lint| { - lint.span_suggestion( - span, - format!("cast `{}` to obtain a function pointer", ident), - format!( - "{} as {}{}fn({}{}){}", - if params.is_empty() { ident } else { format!("{}::<{}>", ident, params) }, - unsafety, - abi, - vec!["_"; num_args].join(", "), - variadic, - ret, - ), - Applicability::Unspecified, - ) - }, + errors::FnItemRef { span, sugg, ident }, ); } } diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index e44dd084b2de5..719a4f349cbf7 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -51,6 +51,7 @@ //! Otherwise it drops all the values in scope at the last suspension point. use crate::deref_separator::deref_finder; +use crate::errors; use crate::simplify; use crate::MirPass; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -1892,36 +1893,21 @@ fn check_must_not_suspend_def( data: SuspendCheckData<'_>, ) -> bool { if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) { - let msg = rustc_errors::DelayDm(|| { - format!( - "{}`{}`{} held across a suspend point, but should not be", - data.descr_pre, - tcx.def_path_str(def_id), - data.descr_post, - ) + let reason = attr.value_str().map(|s| errors::MustNotSuspendReason { + span: data.source_span, + reason: s.as_str().to_string(), }); - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( rustc_session::lint::builtin::MUST_NOT_SUSPEND, hir_id, data.source_span, - msg, - |lint| { - // add span pointing to the offending yield/await - lint.span_label(data.yield_span, "the value is held across this suspend point"); - - // Add optional reason note - if let Some(note) = attr.value_str() { - // FIXME(guswynn): consider formatting this better - lint.span_note(data.source_span, note.as_str()); - } - - // Add some quick suggestions on what to do - // FIXME: can `drop` work as a suggestion here as well? - lint.span_help( - data.source_span, - "consider using a block (`{ ... }`) \ - to shrink the value's scope, ending before the suspend point", - ) + errors::MustNotSupend { + yield_sp: data.yield_span, + reason, + src_sp: data.source_span, + pre: data.descr_pre, + def_path: tcx.def_path_str(def_id), + post: data.descr_post, }, ); diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 25d7db0ee605a..bcaebad543b25 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,4 +1,6 @@ #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #![feature(box_patterns)] #![feature(drain_filter)] #![feature(let_chains)] @@ -69,6 +71,7 @@ pub mod dump_mir; mod early_otherwise_branch; mod elaborate_box_derefs; mod elaborate_drops; +mod errors; mod ffi_unwind_calls; mod function_item_references; mod generator; @@ -105,6 +108,11 @@ use rustc_const_eval::transform::promote_consts; use rustc_const_eval::transform::validate; use rustc_mir_dataflow::rustc_peek; +use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; +use rustc_fluent_macro::fluent_messages; + +fluent_messages! { "../messages.ftl" } + pub fn provide(providers: &mut Providers) { check_unsafety::provide(providers); coverage::query::provide(providers); diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 62b727674c5d1..e95f60718ef9f 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -1,6 +1,6 @@ //! Lowers intrinsic calls -use crate::MirPass; +use crate::{errors, MirPass}; use rustc_middle::mir::*; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -287,11 +287,7 @@ fn resolve_rust_intrinsic<'tcx>( } fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) { - match &args[2] { - Operand::Constant(_) => {} // all good - _ => { - let msg = "last argument of `simd_shuffle` is required to be a `const` item"; - tcx.sess.span_err(span, msg); - } + if !matches!(args[2], Operand::Constant(_)) { + tcx.sess.emit_err(errors::SimdShuffleLastConst { span }); } } From e8139dfd5a747842a8303f33d8c842378913d594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 21 Mar 2023 01:46:52 +0100 Subject: [PATCH 02/17] IAT: Introduce AliasKind::Inherent --- .../rustc_hir_analysis/src/astconv/mod.rs | 61 ++++---- .../src/coherence/orphan.rs | 13 ++ .../src/collect/resolve_bound_vars.rs | 2 +- .../rustc_hir_analysis/src/collect/type_of.rs | 2 +- .../src/constrained_generic_params.rs | 2 +- .../src/outlives/implicit_infer.rs | 3 + .../src/fn_ctxt/adjust_fulfillment_errors.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 2 +- compiler/rustc_infer/src/infer/combine.rs | 3 +- .../src/infer/error_reporting/mod.rs | 4 +- .../infer/error_reporting/note_and_explain.rs | 7 +- .../rustc_infer/src/infer/opaque_types.rs | 2 + compiler/rustc_lint/src/builtin.rs | 7 +- compiler/rustc_lint/src/types.rs | 4 +- compiler/rustc_middle/src/query/mod.rs | 10 ++ compiler/rustc_middle/src/ty/context.rs | 14 +- compiler/rustc_middle/src/ty/error.rs | 4 +- compiler/rustc_middle/src/ty/flags.rs | 14 +- .../rustc_middle/src/ty/inhabitedness/mod.rs | 6 + compiler/rustc_middle/src/ty/layout.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- compiler/rustc_middle/src/ty/relate.rs | 5 + compiler/rustc_middle/src/ty/sty.rs | 51 +++++-- compiler/rustc_middle/src/ty/visit.rs | 3 + compiler/rustc_privacy/src/lib.rs | 33 ++++ compiler/rustc_trait_selection/messages.ftl | 2 + compiler/rustc_trait_selection/src/errors.rs | 8 + .../src/solve/assembly/structural_traits.rs | 2 +- .../src/solve/trait_goals.rs | 2 +- .../src/traits/coherence.rs | 2 +- .../src/traits/error_reporting/mod.rs | 15 +- .../rustc_trait_selection/src/traits/mod.rs | 3 +- .../src/traits/project.rs | 143 +++++++++++++++++- .../src/traits/query/normalize.rs | 17 ++- .../src/traits/select/candidate_assembly.rs | 2 +- .../src/traits/select/confirmation.rs | 2 +- .../src/traits/select/mod.rs | 2 +- .../rustc_trait_selection/src/traits/wf.rs | 3 + compiler/rustc_traits/src/chalk/lowering.rs | 1 + .../src/normalize_projection_ty.rs | 29 +++- compiler/rustc_type_ir/src/lib.rs | 27 ++-- compiler/rustc_type_ir/src/sty.rs | 1 + src/librustdoc/clean/mod.rs | 4 + .../clippy/clippy_lints/src/dereference.rs | 1 + tests/rustdoc/inherent-projections.rs | 14 ++ .../bugs/cycle-iat-inside-of-adt.rs | 10 ++ .../bugs/cycle-iat-inside-of-adt.stderr | 49 ++++++ .../cycle-iat-inside-of-where-predicate.rs | 16 ++ ...cycle-iat-inside-of-where-predicate.stderr | 37 +++++ .../bugs/ice-substitution.rs | 23 --- .../bugs/ice-substitution.stderr | 6 - .../bugs/inference-fail.rs | 15 -- .../bugs/lack-of-regionck.rs | 19 --- .../bugs/wf-check-skipped.rs | 15 ++ .../dispatch-on-self-type-0.rs | 2 +- .../former-subst-ice.rs | 16 ++ .../generic-associated-types-bad.item.stderr | 15 ++ .../generic-associated-types-bad.local.stderr | 15 ++ ...generic-associated-types-bad.region.stderr | 11 ++ .../generic-associated-types-bad.rs | 26 ++++ .../inference-fail.rs | 11 ++ .../{bugs => }/inference-fail.stderr | 6 +- .../ui/associated-inherent-types/inference.rs | 39 +++++ .../late-bound-regions.rs | 25 +++ .../late-bound-regions.stderr | 12 ++ .../normalization-overflow.rs | 12 ++ .../normalization-overflow.stderr | 8 + .../private-in-public.rs | 26 ++++ .../private-in-public.stderr | 34 +++++ .../associated-inherent-types/regionck-0.rs | 14 ++ .../regionck-0.stderr | 10 ++ .../associated-inherent-types/regionck-1.rs | 13 ++ .../regionck-1.stderr | 29 ++++ .../associated-inherent-types/regionck-2.rs | 14 ++ .../regionck-2.stderr | 18 +++ .../type-alias-bounds-are-enforced.rs | 26 ++++ .../unsatisfied-bounds-inferred-type.rs | 12 ++ .../unsatisfied-bounds-inferred-type.stderr | 17 +++ ...tisfied-bounds-where-clause-on-assoc-ty.rs | 14 ++ ...ied-bounds-where-clause-on-assoc-ty.stderr | 18 +++ 82 files changed, 1008 insertions(+), 167 deletions(-) create mode 100644 tests/rustdoc/inherent-projections.rs create mode 100644 tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs create mode 100644 tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr create mode 100644 tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs create mode 100644 tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr delete mode 100644 tests/ui/associated-inherent-types/bugs/ice-substitution.rs delete mode 100644 tests/ui/associated-inherent-types/bugs/ice-substitution.stderr delete mode 100644 tests/ui/associated-inherent-types/bugs/inference-fail.rs delete mode 100644 tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs create mode 100644 tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs create mode 100644 tests/ui/associated-inherent-types/former-subst-ice.rs create mode 100644 tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr create mode 100644 tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr create mode 100644 tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr create mode 100644 tests/ui/associated-inherent-types/generic-associated-types-bad.rs create mode 100644 tests/ui/associated-inherent-types/inference-fail.rs rename tests/ui/associated-inherent-types/{bugs => }/inference-fail.stderr (53%) create mode 100644 tests/ui/associated-inherent-types/inference.rs create mode 100644 tests/ui/associated-inherent-types/late-bound-regions.rs create mode 100644 tests/ui/associated-inherent-types/late-bound-regions.stderr create mode 100644 tests/ui/associated-inherent-types/normalization-overflow.rs create mode 100644 tests/ui/associated-inherent-types/normalization-overflow.stderr create mode 100644 tests/ui/associated-inherent-types/private-in-public.rs create mode 100644 tests/ui/associated-inherent-types/private-in-public.stderr create mode 100644 tests/ui/associated-inherent-types/regionck-0.rs create mode 100644 tests/ui/associated-inherent-types/regionck-0.stderr create mode 100644 tests/ui/associated-inherent-types/regionck-1.rs create mode 100644 tests/ui/associated-inherent-types/regionck-1.stderr create mode 100644 tests/ui/associated-inherent-types/regionck-2.rs create mode 100644 tests/ui/associated-inherent-types/regionck-2.stderr create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs create mode 100644 tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs create mode 100644 tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr create mode 100644 tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs create mode 100644 tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index c199b9da6feea..e50e4800621fc 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2378,6 +2378,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(None); } + // + // Select applicable inherent associated type candidates modulo regions. + // + // In contexts that have no inference context, just make a new one. // We do need a local variable to store it, though. let infcx_; @@ -2390,7 +2394,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - let param_env = tcx.param_env(block.owner.to_def_id()); + // FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors + // when inside of an ADT (#108491) or where clause. + let param_env = tcx.param_env(block.owner); let cause = ObligationCause::misc(span, block.owner.def_id); let mut fulfillment_errors = Vec::new(); @@ -2398,6 +2404,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let universe = infcx.create_next_universe(); // Regions are not considered during selection. + // FIXME(non_lifetime_binders): Here we are "truncating" or "flattening" the universes + // of type and const binders. Is that correct in the selection phase? See also #109505. let self_ty = tcx.replace_escaping_bound_vars_uncached( self_ty, FnMutDelegate { @@ -2413,41 +2421,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { candidates .iter() - .filter_map(|&(impl_, (assoc_item, def_scope))| { + .copied() + .filter(|&(impl_, _)| { infcx.probe(|_| { let ocx = ObligationCtxt::new_in_snapshot(&infcx); - let impl_ty = tcx.type_of(impl_); let impl_substs = infcx.fresh_item_substs(impl_); - let impl_ty = impl_ty.subst(tcx, impl_substs); + let impl_ty = tcx.type_of(impl_).subst(tcx, impl_substs); let impl_ty = ocx.normalize(&cause, param_env, impl_ty); - // Check that the Self-types can be related. - // FIXME(fmease): Should we use `eq` here? - ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?; + // Check that the self types can be related. + // FIXME(inherent_associated_types): Should we use `eq` here? Method probing uses + // `sup` for this situtation, too. What for? To constrain inference variables? + if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() + { + return false; + } // Check whether the impl imposes obligations we have to worry about. - let impl_bounds = tcx.predicates_of(impl_); - let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); - + let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_substs); let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); - let impl_obligations = traits::predicates_for_generics( |_, _| cause.clone(), param_env, impl_bounds, ); - ocx.register_obligations(impl_obligations); let mut errors = ocx.select_where_possible(); if !errors.is_empty() { fulfillment_errors.append(&mut errors); - return None; + return false; } - // FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot. - Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs))) + true }) }) .collect() @@ -2456,24 +2463,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if applicable_candidates.len() > 1 { return Err(self.complain_about_ambiguous_inherent_assoc_type( name, - applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(), + applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), span, )); } - if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() { + if let Some((impl_, (assoc_item, def_scope))) = applicable_candidates.pop() { self.check_assoc_ty(assoc_item, name, def_scope, block, span); - // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, we still - // need to relate the Self-type with fresh item substs & register region obligations for - // regionck to prove/disprove. - - let item_substs = - self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs); + // FIXME(fmease): Currently creating throwaway `parent_substs` to please + // `create_substs_for_associated_item`. Modify the latter instead (or sth. similar) to + // not require the parent substs logic. + let parent_substs = InternalSubsts::identity_for_item(tcx, impl_); + let substs = + self.create_substs_for_associated_item(span, assoc_item, segment, parent_substs); + let substs = tcx.mk_substs_from_iter( + std::iter::once(ty::GenericArg::from(self_ty)) + .chain(substs.into_iter().skip(parent_substs.len())), + ); - // FIXME(fmease, #106722): Check if the bounds on the parameters of the - // associated type hold, if any. - let ty = tcx.type_of(assoc_item).subst(tcx, item_substs); + let ty = tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(assoc_item, substs)); return Ok(Some((ty, assoc_item))); } diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5ce8a83aad7f7..272177dfbd0f8 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -210,6 +210,19 @@ fn do_orphan_check_impl<'tcx>( NonlocalImpl::DisallowOther, ), + // ``` + // struct S(T); + // impl S { + // type This = T; + // } + // impl AutoTrait for S::This {} + // ``` + // FIXME(inherent_associated_types): The example code above currently leads to a cycle + ty::Alias(AliasKind::Inherent, _) => ( + LocalImpl::Disallow { problematic_kind: "associated type" }, + NonlocalImpl::DisallowOther, + ), + // type Opaque = impl Trait; // impl AutoTrait for Opaque {} ty::Alias(AliasKind::Opaque, _) => ( diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 2e5d058c6ed4d..cb80e5b135086 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1938,7 +1938,7 @@ fn is_late_bound_map( ty::Param(param_ty) => { self.arg_is_constrained[param_ty.index as usize] = true; } - ty::Alias(ty::Projection, _) => return ControlFlow::Continue(()), + ty::Alias(ty::Projection | ty::Inherent, _) => return ControlFlow::Continue(()), _ => (), } t.super_visit_with(self) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c20fbfd1e4080..8df0166f76b4b 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -127,7 +127,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { // the def_id that this query was called with. We filter to only type and const args here // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't // but it can't hurt to be safe ^^ - if let ty::Alias(ty::Projection, projection) = ty.kind() { + if let ty::Alias(ty::Projection | ty::Inherent, projection) = ty.kind() { let generics = tcx.generics_of(projection.def_id); let arg_index = segment diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index e18b0f082798b..9200c2aecf55c 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -59,7 +59,7 @@ struct ParameterCollector { impl<'tcx> TypeVisitor> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match *t.kind() { - ty::Alias(ty::Projection, ..) if !self.include_nonconstraining => { + ty::Alias(ty::Projection | ty::Inherent, ..) if !self.include_nonconstraining => { // projections are not injective return ControlFlow::Continue(()); } diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index d53c429ca15ca..0cd2fc1aa299a 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -210,6 +210,9 @@ fn insert_required_predicates_to_be_wf<'tcx>( ); } + // FIXME(inherent_associated_types): Handle this case properly. + ty::Alias(ty::Inherent, _) => {} + _ => {} } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 70ce45e21ea55..05e5d850bf958 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -843,7 +843,7 @@ fn find_param_in_ty<'tcx>( return true; } if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, ..) = ty.kind() + && let ty::Alias(ty::Projection | ty::Inherent, ..) = ty.kind() { // This logic may seem a bit strange, but typically when // we have a projection type in a function signature, the diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 73a7bbebb6581..67f45f9aa3f0d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -300,7 +300,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { match ty.kind() { ty::Adt(adt_def, _) => Some(*adt_def), // FIXME(#104767): Should we handle bound regions here? - ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => { + ty::Alias(ty::Projection | ty::Inherent, _) if !ty.has_escaping_bound_vars() => { self.normalize(span, ty).ty_adt_def() } _ => None, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 30f0978d190c6..3741672e5683c 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2211,7 +2211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Adt(_, _) | ty::Str - | ty::Alias(ty::Projection, _) + | ty::Alias(ty::Projection | ty::Inherent, _) | ty::Param(_) => format!("{deref_ty}"), // we need to test something like <&[_]>::len or <(&[u32])>::len // and Vec::function(); diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index c9e13be02ff9a..44c8b08196e52 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -125,7 +125,8 @@ impl<'tcx> InferCtxt<'tcx> { bug!() } - (_, ty::Alias(AliasKind::Projection, _)) | (ty::Alias(AliasKind::Projection, _), _) + (_, ty::Alias(AliasKind::Projection | AliasKind::Inherent, _)) + | (ty::Alias(AliasKind::Projection | AliasKind::Inherent, _), _) if self.tcx.trait_solver_next() => { relation.register_type_relate_obligation(a, b); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 98da5ba65b7b4..ce70f39cc4014 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2354,7 +2354,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), GenericKind::Alias(ref p) => match p.kind(self.tcx) { - ty::AliasKind::Projection => format!("the associated type `{}`", p), + ty::AliasKind::Projection | ty::AliasKind::Inherent => { + format!("the associated type `{}`", p) + } ty::AliasKind::Opaque => format!("the opaque type `{}`", p), }, }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index a31163519404c..064811bd29d77 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -71,9 +71,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { #traits-as-parameters", ); } - (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => { + (ty::Alias(ty::Projection | ty::Inherent, _), ty::Alias(ty::Projection | ty::Inherent, _)) => { diag.note("an associated type was expected, but a different one was found"); } + // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p)) if !tcx.is_impl_trait_in_trait(proj.def_id) => { @@ -222,7 +223,7 @@ impl Trait for X { diag.span_label(p_span, "this type parameter"); } } - (ty::Alias(ty::Projection, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { + (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { self.expected_projection( diag, proj_ty, @@ -231,7 +232,7 @@ impl Trait for X { cause.code(), ); } - (_, ty::Alias(ty::Projection, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { + (_, ty::Alias(ty::Projection | ty::Inherent, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { let msg = format!( "consider constraining the associated type `{}` to `{}`", values.found, values.expected, diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 334395945ea96..362b22b23a8da 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -549,6 +549,7 @@ impl<'tcx> InferCtxt<'tcx> { // We can't normalize associated types from `rustc_infer`, // but we can eagerly register inference variables for them. // FIXME(RPITIT): Don't replace RPITITs with inference vars. + // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. ty::Alias(ty::Projection, projection_ty) if !projection_ty.has_escaping_bound_vars() && !tcx.is_impl_trait_in_trait(projection_ty.def_id) => @@ -569,6 +570,7 @@ impl<'tcx> InferCtxt<'tcx> { hidden_ty } // FIXME(RPITIT): This can go away when we move to associated types + // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. ty::Alias( ty::Projection, ty::AliasTy { def_id: def_id2, substs: substs2, .. }, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index aeb791901bd23..08c02e1c253cb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -62,6 +62,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef}; use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; use rustc_span::edition::Edition; @@ -1461,6 +1462,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // Bounds are respected for `type X = impl Trait` return; } + if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() { + // Bounds are respected for `type X = … Type::Inherent …` + return; + } // There must not be a where clause if type_alias_generics.predicates.is_empty() { return; @@ -1580,7 +1585,6 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::Clause; use rustc_middle::ty::PredicateKind::*; @@ -2917,6 +2921,7 @@ impl ClashingExternDeclarations { | (Generator(..), Generator(..)) | (GeneratorWitness(..), GeneratorWitness(..)) | (Alias(ty::Projection, ..), Alias(ty::Projection, ..)) + | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..)) | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false, // These definitely should have been caught above. diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a6ba742201a3b..125b4dc550310 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1119,14 +1119,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, // so they are currently ignored for the purposes of this lint. - ty::Param(..) | ty::Alias(ty::Projection, ..) + ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) if matches!(self.mode, CItemKind::Definition) => { FfiSafe } ty::Param(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Infer(..) | ty::Bound(..) | ty::Error(_) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index be2657d25a694..ab73832ae68a6 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1821,6 +1821,16 @@ rustc_queries! { desc { "normalizing `{}`", goal.value.value } } + /// Do not call this query directly: invoke `normalize` instead. + query normalize_inherent_projection_ty( + goal: CanonicalProjectionGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{}`", goal.value.value } + } + /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead. query try_normalize_generic_arg_after_erasing_regions( goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c62254cd79cb1..212720f3b694f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1846,7 +1846,17 @@ impl<'tcx> TyCtxt<'tcx> { let substs = substs.into_iter().map(Into::into); #[cfg(debug_assertions)] { - let n = self.generics_of(_def_id).count(); + let generics = self.generics_of(_def_id); + + let n = if let DefKind::AssocTy = self.def_kind(_def_id) + && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id)) + { + // If this is an inherent projection. + + generics.params.len() + 1 + } else { + generics.count() + }; assert_eq!( (n, Some(n)), substs.size_hint(), @@ -2007,7 +2017,7 @@ impl<'tcx> TyCtxt<'tcx> { debug_assert_matches!( (kind, self.def_kind(alias_ty.def_id)), (ty::Opaque, DefKind::OpaqueTy) - | (ty::Projection, DefKind::AssocTy) + | (ty::Projection | ty::Inherent, DefKind::AssocTy) | (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder) ); self.mk_ty_from_kind(Alias(kind, alias_ty)) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 1be61e16dbed8..49ab9b79e96f3 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -265,7 +265,7 @@ impl<'tcx> Ty<'tcx> { ty::Infer(ty::FreshTy(_)) => "fresh type".into(), ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), - ty::Alias(ty::Projection, _) => "associated type".into(), + ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(), ty::Param(p) => format!("type parameter `{p}`").into(), ty::Alias(ty::Opaque, ..) => if tcx.ty_is_opaque_future(self) { "future".into() } else { "opaque type".into() }, ty::Error(_) => "type error".into(), @@ -312,7 +312,7 @@ impl<'tcx> Ty<'tcx> { ty::Tuple(..) => "tuple".into(), ty::Placeholder(..) => "higher-ranked type".into(), ty::Bound(..) => "bound type variable".into(), - ty::Alias(ty::Projection, _) => "associated type".into(), + ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(), ty::Param(_) => "type parameter".into(), ty::Alias(ty::Opaque, ..) => "opaque type".into(), } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 68002bfcfbd13..d64875a9f00e8 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -176,14 +176,14 @@ impl FlagComputation { self.add_substs(substs); } - &ty::Alias(ty::Projection, data) => { - self.add_flags(TypeFlags::HAS_TY_PROJECTION); - self.add_alias_ty(data); - } + &ty::Alias(kind, data) => { + self.add_flags(match kind { + ty::Projection => TypeFlags::HAS_TY_PROJECTION, + ty::Inherent => TypeFlags::HAS_TY_INHERENT, + ty::Opaque => TypeFlags::HAS_TY_OPAQUE, + }); - &ty::Alias(ty::Opaque, ty::AliasTy { substs, .. }) => { - self.add_flags(TypeFlags::HAS_TY_OPAQUE); - self.add_substs(substs); + self.add_alias_ty(data); } &ty::Dynamic(obj, r, _) => { diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 92a040068dd75..9e672004cf9c0 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -113,6 +113,12 @@ impl<'tcx> Ty<'tcx> { } Never => InhabitedPredicate::False, Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self), + // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above. + // However it's unclear if the substs passed to `InhabitedPredicate::subst` are of the correct + // format, i.e. don't contain parent substs. If you hit this case, please verify this beforehand. + Alias(ty::Inherent, _) => { + bug!("unimplemented: inhabitedness checking for inherent projections") + } Tuple(tys) if tys.is_empty() => InhabitedPredicate::True, // use a query for more complex cases Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self), diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f2a2e67cf82dc..47cf48f46cf89 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -324,7 +324,7 @@ impl<'tcx> SizeSkeleton<'tcx> { let non_zero = !ty.is_unsafe_ptr(); let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); match tail.kind() { - ty::Param(_) | ty::Alias(ty::Projection, _) => { + ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => { debug_assert!(tail.has_non_region_param()); Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 23d6242899f2e..9fc0110b354f4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1004,7 +1004,7 @@ impl<'tcx> Term<'tcx> { match self.unpack() { TermKind::Ty(ty) => match ty.kind() { ty::Alias(kind, alias_ty) => match kind { - AliasKind::Projection => Some(*alias_ty), + AliasKind::Projection | AliasKind::Inherent => Some(*alias_ty), AliasKind::Opaque => None, }, _ => None, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6ac9f95045069..8a4d3820d7bc7 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -729,7 +729,7 @@ pub trait PrettyPrinter<'tcx>: ty::Foreign(def_id) => { p!(print_def_path(def_id, &[])); } - ty::Alias(ty::Projection, ref data) => { + ty::Alias(ty::Projection | ty::Inherent, ref data) => { if !(self.should_print_verbose() || NO_QUERIES.with(|q| q.get())) && self.tcx().is_impl_trait_in_trait(data.def_id) { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 7f28ed6c26337..da43475941ee5 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -550,6 +550,11 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>( Ok(tcx.mk_projection(projection_ty.def_id, projection_ty.substs)) } + (&ty::Alias(ty::Inherent, a_data), &ty::Alias(ty::Inherent, b_data)) => { + let alias_ty = relation.relate(a_data, b_data)?; + Ok(tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(alias_ty.def_id, alias_ty.substs))) + } + ( &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, substs: a_substs, .. }), &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, substs: b_substs, .. }), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 646384eebc888..8d0737e1eeeeb 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1190,9 +1190,9 @@ where /// Represents the projection of an associated type. /// -/// For a projection, this would be `>::N`. -/// -/// For an opaque type, there is no explicit syntax. +/// * For a projection, this would be `>::N<...>`. +/// * For an inherent projection, this would be `Ty::N<...>`. +/// * For an opaque type, there is no explicit syntax. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct AliasTy<'tcx> { @@ -1201,12 +1201,16 @@ pub struct AliasTy<'tcx> { /// For a projection, these are the substitutions for the trait and the /// GAT substitutions, if there are any. /// + /// For an inherent projection, they consist of the self type and the GAT substitutions, + /// if there are any. + /// /// For RPIT the substitutions are for the generics of the function, /// while for TAIT it is used for the generic parameters of the alias. pub substs: SubstsRef<'tcx>, - /// The `DefId` of the `TraitItem` for the associated type `N` if this is a projection, - /// or the `OpaqueType` item if this is an opaque. + /// The `DefId` of the `TraitItem` or `ImplItem` for the associated type `N` depending on whether + /// this is a projection or an inherent projection or the `DefId` of the `OpaqueType` item if + /// this is an opaque. /// /// During codegen, `tcx.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. @@ -1224,6 +1228,7 @@ pub struct AliasTy<'tcx> { impl<'tcx> AliasTy<'tcx> { pub fn kind(self, tcx: TyCtxt<'tcx>) -> ty::AliasKind { match tcx.def_kind(self.def_id) { + DefKind::AssocTy if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(self.def_id)) => ty::Inherent, DefKind::AssocTy | DefKind::ImplTraitPlaceholder => ty::Projection, DefKind::OpaqueTy => ty::Opaque, kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), @@ -1236,6 +1241,17 @@ impl<'tcx> AliasTy<'tcx> { } /// The following methods work only with associated type projections. +impl<'tcx> AliasTy<'tcx> { + pub fn self_ty(self) -> Ty<'tcx> { + self.substs.type_at(0) + } + + pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1))) + } +} + +/// The following methods work only with trait associated type projections. impl<'tcx> AliasTy<'tcx> { pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { match tcx.def_kind(self.def_id) { @@ -1274,13 +1290,28 @@ impl<'tcx> AliasTy<'tcx> { let def_id = self.trait_def_id(tcx); ty::TraitRef::new(tcx, def_id, self.substs.truncate_to(tcx, tcx.generics_of(def_id))) } +} - pub fn self_ty(self) -> Ty<'tcx> { - self.substs.type_at(0) - } +/// The following methods work only with inherent associated type projections. +impl<'tcx> AliasTy<'tcx> { + /// Transform the substitutions to have the given `impl` substs as the base and the GAT substs on top of that. + /// + /// Does the following transformation: + /// + /// ```text + /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m] + /// + /// I_i impl subst + /// P_j GAT subst + /// ``` + pub fn rebase_substs_onto_impl( + self, + impl_substs: ty::SubstsRef<'tcx>, + tcx: TyCtxt<'tcx>, + ) -> ty::SubstsRef<'tcx> { + debug_assert_eq!(self.kind(tcx), ty::Inherent); - pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { - tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1))) + tcx.mk_substs_from_iter(impl_substs.into_iter().chain(self.substs.into_iter().skip(1))) } } diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 5eaa58d69ed66..520bb55e031c7 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -49,6 +49,9 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable> { fn has_projections(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PROJECTION) } + fn has_inherent_projections(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INHERENT) + } fn has_opaque_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 04ac585076f9c..d6eb5463870ef 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -243,6 +243,39 @@ where // This will also visit substs if necessary, so we don't need to recurse. return self.visit_projection_ty(proj); } + ty::Alias(ty::Inherent, data) => { + if self.def_id_visitor.skip_assoc_tys() { + // Visitors searching for minimal visibility/reachability want to + // conservatively approximate associated types like `Type::Alias` + // as visible/reachable even if `Type` is private. + // Ideally, associated types should be substituted in the same way as + // free type aliases, but this isn't done yet. + return ControlFlow::Continue(()); + } + + self.def_id_visitor.visit_def_id( + data.def_id, + "associated type", + &LazyDefPathStr { def_id: data.def_id, tcx }, + )?; + + struct LazyDefPathStr<'tcx> { + def_id: DefId, + tcx: TyCtxt<'tcx>, + } + impl<'tcx> fmt::Display for LazyDefPathStr<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.tcx.def_path_str(self.def_id)) + } + } + + // This will also visit substs if necessary, so we don't need to recurse. + return if self.def_id_visitor.shallow() { + ControlFlow::Continue(()) + } else { + data.substs.iter().try_for_each(|subst| subst.visit_with(self)) + }; + } ty::Dynamic(predicates, ..) => { // All traits in the list are considered the "primary" part of the type // and are visited by shallow visitors. diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 14eb4a5502d5c..8fea3fc140d72 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -20,3 +20,5 @@ trait_selection_negative_positive_conflict = found both positive and negative im .negative_implementation_in_crate = negative implementation in crate `{$negative_impl_cname}` .positive_implementation_here = positive implementation here .positive_implementation_in_crate = positive implementation in crate `{$positive_impl_cname}` + +trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index df7c4df1868c7..54e22cc3d7fe2 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -89,3 +89,11 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> { diag } } + +#[derive(Diagnostic)] +#[diag(trait_selection_inherent_projection_normalization_overflow)] +pub struct InherentProjectionNormalizationOverflow { + #[primary_span] + pub span: Span, + pub ty: String, +} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 996dc329dcb9b..0ede32c753c69 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -33,7 +33,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 6c98fadd1482a..04b38edc1265f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -655,7 +655,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) => Some(Err(NoSolution)), ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index b7690f79933d9..402b09419c877 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -673,7 +673,7 @@ impl<'tcx> TypeVisitor> for OrphanChecker<'tcx> { | ty::RawPtr(..) | ty::Never | ty::Tuple(..) - | ty::Alias(ty::Projection, ..) => self.found_non_local_ty(ty), + | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty), ty::Param(..) => self.found_param_ty(ty), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 8f2a5d649f091..3884153202eb7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1686,13 +1686,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::Tuple(..) => Some(10), ty::Param(..) => Some(11), ty::Alias(ty::Projection, ..) => Some(12), - ty::Alias(ty::Opaque, ..) => Some(13), - ty::Never => Some(14), - ty::Adt(..) => Some(15), - ty::Generator(..) => Some(16), - ty::Foreign(..) => Some(17), - ty::GeneratorWitness(..) => Some(18), - ty::GeneratorWitnessMIR(..) => Some(19), + ty::Alias(ty::Inherent, ..) => Some(13), + ty::Alias(ty::Opaque, ..) => Some(14), + ty::Never => Some(15), + ty::Adt(..) => Some(16), + ty::Generator(..) => Some(17), + ty::Foreign(..) => Some(18), + ty::GeneratorWitness(..) => Some(19), + ty::GeneratorWitnessMIR(..) => Some(20), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 8b8c50f6b836c..5878accd84aab 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -49,7 +49,8 @@ pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::ObjectSafetyViolation; -pub use self::project::{normalize_projection_type, NormalizeExt}; +pub use self::project::NormalizeExt; +pub use self::project::{normalize_inherent_projection, normalize_projection_type}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::specialization_graph::FutureCompatOverlapError; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8c74860cdf341..852780fccace9 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -16,6 +16,7 @@ use super::{ }; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::TypeErrCtxtExt as _; @@ -370,10 +371,14 @@ pub(crate) fn needs_normalization<'tcx, T: TypeVisitable>>( reveal: Reveal, ) -> bool { match reveal { - Reveal::UserFacing => value - .has_type_flags(ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_CT_PROJECTION), + Reveal::UserFacing => value.has_type_flags( + ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION, + ), Reveal::All => value.has_type_flags( ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT | ty::TypeFlags::HAS_TY_OPAQUE | ty::TypeFlags::HAS_CT_PROJECTION, ), @@ -616,6 +621,51 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx ); normalized_ty } + + ty::Inherent if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + + let data = data.fold_with(self); + + // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` + // here like `ty::Projection`? + normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ) + } + + ty::Inherent => { + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let ty = normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + ty, + ) + } } } @@ -1204,6 +1254,95 @@ fn normalize_to_error<'a, 'tcx>( Normalized { value: new_value, obligations: vec![trait_obligation] } } +/// Confirm and normalize the given inherent projection. +#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] +pub fn normalize_inherent_projection<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec>, +) -> Ty<'tcx> { + let tcx = selcx.tcx(); + + if !tcx.recursion_limit().value_within_limit(depth) { + // Halt compilation because it is important that overflows never be masked. + tcx.sess.emit_fatal(InherentProjectionNormalizationOverflow { + span: cause.span, + ty: alias_ty.to_string(), + }); + } + + let impl_def_id = tcx.parent(alias_ty.def_id); + let impl_substs = selcx.infcx.fresh_substs_for_item(cause.span, impl_def_id); + + let impl_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs); + let impl_ty = + normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations); + + // Infer the generic parameters of the impl by unifying the + // impl type with the self type of the projection. + let self_ty = alias_ty.self_ty(); + match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { + Ok(mut ok) => obligations.append(&mut ok.obligations), + Err(_) => { + tcx.sess.delay_span_bug( + cause.span, + format!( + "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" + ), + ); + } + } + + let substs = alias_ty.rebase_substs_onto_impl(impl_substs, tcx); + + // Register the obligations arising from the impl and from the associated type itself. + let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, substs); + for (predicate, span) in predicates { + let predicate = normalize_with_depth_to( + selcx, + param_env, + cause.clone(), + depth + 1, + predicate, + obligations, + ); + + let nested_cause = ObligationCause::new( + cause.span, + cause.body_id, + // FIXME(inherent_associated_types): Since we can't pass along the self type to the + // cause code, inherent projections will be printed with identity substitutions in + // diagnostics which is not ideal. + // Consider creating separate cause codes for this specific situation. + if span.is_dummy() { + super::ItemObligation(alias_ty.def_id) + } else { + super::BindingObligation(alias_ty.def_id, span) + }, + ); + + obligations.push(Obligation::with_depth( + tcx, + nested_cause, + depth + 1, + param_env, + predicate, + )); + } + + let ty = tcx.type_of(alias_ty.def_id).subst(tcx, substs); + + let mut ty = selcx.infcx.resolve_vars_if_possible(ty); + if ty.has_projections() { + ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); + } + + ty +} + enum Projected<'tcx> { Progress(Progress<'tcx>), NoProgress(ty::Term<'tcx>), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a986a9b6a71b1..8bf934cb78ae1 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -257,11 +257,11 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> ty::Opaque => ty.try_super_fold_with(self)?, - ty::Projection => { + ty::Projection | ty::Inherent => { // See note in `rustc_trait_selection::traits::project` - let tcx = self.infcx.tcx; let infcx = self.infcx; + let tcx = infcx.tcx; // Just an optimization: When we don't have escaping bound vars, // we don't need to replace them with placeholders. let (data, maps) = if data.has_escaping_bound_vars() { @@ -276,12 +276,15 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> let mut orig_values = OriginalQueryValues::default(); // HACK(matthewjasper) `'static` is special-cased in selection, // so we cannot canonicalize it. - let c_data = self - .infcx + let c_data = infcx .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values); debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); - let result = tcx.normalize_projection_ty(c_data)?; + let result = match kind { + ty::Projection => tcx.normalize_projection_ty(c_data), + ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), + _ => unreachable!(), + }?; // We don't expect ambiguity. if result.is_ambiguous() { // Rustdoc normalizes possibly not well-formed types, so only @@ -294,8 +297,8 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> } return Err(NoSolution); } - let InferOk { value: result, obligations } = - self.infcx.instantiate_query_response_and_region_obligations( + let InferOk { value: result, obligations } = infcx + .instantiate_query_response_and_region_obligations( self.cause, self.param_env, &orig_values, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 33f502f818285..a8fb55df2d302 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -498,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // this trait and type. } ty::Param(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) | ty::Bound(..) => { // In these cases, we don't know what the actual diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 422285d947400..616187b69dde4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1268,7 +1268,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If we have a projection type, make sure to normalize it so we replace it // with a fresh infer variable - ty::Alias(ty::Projection, ..) => { + ty::Alias(ty::Projection | ty::Inherent, ..) => { let predicate = normalize_with_depth_to( self, obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 246d3ea2ef298..e4f5a84f4244e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2315,7 +2315,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble constituent types of unexpected type: {:?}", t); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 22710c7c059a9..e5edceda94446 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -605,6 +605,9 @@ impl<'tcx> WfPredicates<'tcx> { walker.skip_current_subtree(); // Subtree handled by compute_projection. self.compute_projection(data); } + ty::Alias(ty::Inherent, _) => { + // WF if their substs are WF. + } ty::Adt(def, substs) => { // WfNominalType diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 4d225e36b22cd..2f9e480d8bd52 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -372,6 +372,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { substitution: substs.lower_into(interner), })) } + ty::Alias(ty::Inherent, _) => unimplemented!(), ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id: chalk_ir::OpaqueTyId(def_id), diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index e805eb4282119..36d80a06ee7e9 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -10,7 +10,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext}; use std::sync::atomic::Ordering; pub(crate) fn provide(p: &mut Providers) { - *p = Providers { normalize_projection_ty, ..*p }; + *p = Providers { normalize_projection_ty, normalize_inherent_projection_ty, ..*p }; } fn normalize_projection_ty<'tcx>( @@ -42,3 +42,30 @@ fn normalize_projection_ty<'tcx>( }, ) } + +fn normalize_inherent_projection_ty<'tcx>( + tcx: TyCtxt<'tcx>, + goal: CanonicalProjectionGoal<'tcx>, +) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { + debug!("normalize_provider(goal={:#?})", goal); + + tcx.infer_ctxt().enter_canonical_trait_query( + &goal, + |ocx, ParamEnvAnd { param_env, value: goal }| { + let selcx = &mut SelectionContext::new(ocx.infcx); + let cause = ObligationCause::dummy(); + let mut obligations = vec![]; + let answer = traits::normalize_inherent_projection( + selcx, + param_env, + goal, + cause, + 0, + &mut obligations, + ); + ocx.register_obligations(obligations); + + Ok(NormalizationResult { normalized_ty: answer }) + }, + ) +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 1e91e26e2afe9..7e5a4d1c73532 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -229,29 +229,32 @@ bitflags! { /// Does this have `Projection`? const HAS_TY_PROJECTION = 1 << 10; + /// Does this have `Inherent`? + const HAS_TY_INHERENT = 1 << 11; /// Does this have `Opaque`? - const HAS_TY_OPAQUE = 1 << 11; + const HAS_TY_OPAQUE = 1 << 12; /// Does this have `ConstKind::Unevaluated`? - const HAS_CT_PROJECTION = 1 << 12; + const HAS_CT_PROJECTION = 1 << 13; /// Could this type be normalized further? const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_TY_INHERENT.bits | TypeFlags::HAS_CT_PROJECTION.bits; /// Is an error type/const reachable? - const HAS_ERROR = 1 << 13; + const HAS_ERROR = 1 << 14; /// Does this have any region that "appears free" in the type? /// Basically anything but `ReLateBound` and `ReErased`. - const HAS_FREE_REGIONS = 1 << 14; + const HAS_FREE_REGIONS = 1 << 15; /// Does this have any `ReLateBound` regions? - const HAS_RE_LATE_BOUND = 1 << 15; + const HAS_RE_LATE_BOUND = 1 << 16; /// Does this have any `Bound` types? - const HAS_TY_LATE_BOUND = 1 << 16; + const HAS_TY_LATE_BOUND = 1 << 17; /// Does this have any `ConstKind::Bound` consts? - const HAS_CT_LATE_BOUND = 1 << 17; + const HAS_CT_LATE_BOUND = 1 << 18; /// Does this have any bound variables? /// Used to check if a global bound is safe to evaluate. const HAS_LATE_BOUND = TypeFlags::HAS_RE_LATE_BOUND.bits @@ -259,20 +262,20 @@ bitflags! { | TypeFlags::HAS_CT_LATE_BOUND.bits; /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 18; + const HAS_RE_ERASED = 1 << 19; /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 19; + const STILL_FURTHER_SPECIALIZABLE = 1 << 20; /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 20; + const HAS_TY_FRESH = 1 << 21; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 21; + const HAS_CT_FRESH = 1 << 22; /// Does this have `Generator` or `GeneratorWitness`? - const HAS_TY_GENERATOR = 1 << 22; + const HAS_TY_GENERATOR = 1 << 23; } } diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index 4c1f2dd0e5334..f7344bacc028c 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -37,6 +37,7 @@ pub enum DynKind { #[derive(Encodable, Decodable, HashStable_Generic)] pub enum AliasKind { Projection, + Inherent, Opaque, } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 23449a25c3a5e..72b60d95b8c15 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1836,6 +1836,10 @@ pub(crate) fn clean_middle_ty<'tcx>( clean_projection(bound_ty.rebind(*data), cx, parent_def_id) } + // FIXME(fmease): Clean inherent projections properly. This requires making the trait ref in + // `QPathData` optional or alternatively adding a new `clean::Type` variant. + ty::Alias(ty::Inherent, _data) => Type::Infer, + ty::Param(ref p) => { if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { ImplTrait(bounds) diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 7f3f26bed7c7d..b27ffe73ffda4 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1424,6 +1424,7 @@ fn ty_auto_deref_stability<'tcx>( continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), + ty::Alias(ty::Inherent, _) => unreachable!("inherent projection should have been normalized away above"), ty::Alias(ty::Projection, _) if ty.has_non_region_param() => { TyPosition::new_deref_stable_for_result(precedence, ty) }, diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs new file mode 100644 index 0000000000000..bdd881ab456d2 --- /dev/null +++ b/tests/rustdoc/inherent-projections.rs @@ -0,0 +1,14 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(fmease): Properly render inherent projections. + +// @has inherent_projections/fn.create.html +// @has - '//pre[@class="rust item-decl"]' "create() -> _" +pub fn create() -> Owner::Metadata {} + +pub struct Owner; + +impl Owner { + pub type Metadata = (); +} diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs new file mode 100644 index 0000000000000..f41574403d887 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs @@ -0,0 +1,10 @@ +// known-bug: #108491 + +// FIXME(inherent_associated_types): This should pass. + +struct Foo { + bar: Self::Bar, +} +impl Foo { + pub type Bar = usize; +} diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr new file mode 100644 index 0000000000000..f313c4946714c --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr @@ -0,0 +1,49 @@ +error[E0601]: `main` function not found in crate `cycle_iat_inside_of_adt` + --> $DIR/cycle-iat-inside-of-adt.rs:10:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/cycle-iat-inside-of-adt.rs` + +error[E0391]: cycle detected when computing predicates of `Foo` + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ + | +note: ...which requires computing predicates of `Foo`... + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ +note: ...which requires computing inferred outlives predicates of `Foo`... + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ + = note: ...which requires computing the inferred outlives predicates for items in this crate... +note: ...which requires computing type of `Foo::bar`... + --> $DIR/cycle-iat-inside-of-adt.rs:6:5 + | +LL | bar: Self::Bar, + | ^^^^^^^^^^^^^^ +note: ...which requires computing normalized predicates of `Foo`... + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ + = note: ...which again requires computing predicates of `Foo`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | / struct Foo { +LL | | bar: Self::Bar, +LL | | } +LL | | impl Foo { +LL | | pub type Bar = usize; +LL | | } + | |_^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0391, E0601. +For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs new file mode 100644 index 0000000000000..0c2a38b1173d9 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs @@ -0,0 +1,16 @@ +// known-bug: unknown + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(inherent_associated_types): This shouldn't lead to a cycle error. + +fn user() where S::P: std::fmt::Debug {} + +struct S; + +impl S { + type P = (); +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr new file mode 100644 index 0000000000000..aaa9a39ea0f4a --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr @@ -0,0 +1,37 @@ +error[E0391]: cycle detected when computing predicates of `user` + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing predicates of `user`... + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires computing explicit predicates of `user`... + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires computing normalized predicates of `user`... + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires computing predicates of `user`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/cycle-iat-inside-of-where-predicate.rs:3:1 + | +LL | / #![feature(inherent_associated_types)] +LL | | #![allow(incomplete_features)] +LL | | +LL | | // FIXME(inherent_associated_types): This shouldn't lead to a cycle error. +... | +LL | | +LL | | fn main() {} + | |____________^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs deleted file mode 100644 index 53ac79e0561ba..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs +++ /dev/null @@ -1,23 +0,0 @@ -// known-bug: unknown -// failure-status: 101 -// normalize-stderr-test "note: .*\n\n" -> "" -// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" -// rustc-env:RUST_BACKTRACE=0 - -// FIXME: I presume a type variable that couldn't be solved by `resolve_vars_if_possible` -// escapes the InferCtxt snapshot. - -#![feature(inherent_associated_types)] -#![allow(incomplete_features)] - -struct Cont(T); - -impl Cont { - type Out = Vec; -} - -pub fn weird(x: T) { - let _: Cont<_>::Out = vec![true]; -} - -fn main() {} diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr b/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr deleted file mode 100644 index 1648cfb266b56..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [typeck] type-checking `weird` -#1 [used_trait_imports] finding used_trait_imports `weird` -end of query stack diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.rs b/tests/ui/associated-inherent-types/bugs/inference-fail.rs deleted file mode 100644 index a920b412b1a49..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/inference-fail.rs +++ /dev/null @@ -1,15 +0,0 @@ -// known-bug: unknown - -#![feature(inherent_associated_types)] -#![allow(incomplete_features)] - -struct S(T); - -impl S<()> { - type P = i128; -} - -fn main() { - // We fail to infer `_ == ()` here. - let _: S<_>::P; -} diff --git a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs deleted file mode 100644 index 632dbf3854b2b..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs +++ /dev/null @@ -1,19 +0,0 @@ -// known-bug: unknown -// check-pass - -// We currently don't region-check inherent associated type projections at all. - -#![feature(inherent_associated_types)] -#![allow(incomplete_features, dead_code)] - -struct S(T); - -impl S<&'static ()> { - type T = (); -} - -fn usr<'a>() { - let _: S::<&'a ()>::T; // this should *fail* but it doesn't! -} - -fn main() {} diff --git a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs new file mode 100644 index 0000000000000..c7f66e645bb57 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs @@ -0,0 +1,15 @@ +// known-bug: #100041 +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(inherent_associated_types): This should fail. + +struct Foo; + +impl Foo { + type Bar = (); +} + +fn main() -> Foo::Bar::> {} diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs index f846bfa4168ac..83be4f43b5e94 100644 --- a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs @@ -31,7 +31,7 @@ fn main() { let _: Select::Projection = (); let _: Choose::Result = (); - let _: Choose::Result = vec![true]; + let _: Choose<&str>::Result = vec!["…"]; // regression test for issue #108957 } // Test if we use the correct `ParamEnv` when proving obligations. diff --git a/tests/ui/associated-inherent-types/former-subst-ice.rs b/tests/ui/associated-inherent-types/former-subst-ice.rs new file mode 100644 index 0000000000000..48390b9430b60 --- /dev/null +++ b/tests/ui/associated-inherent-types/former-subst-ice.rs @@ -0,0 +1,16 @@ +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Cont(T); + +impl Cont { + type Out = Vec; +} + +pub fn weird(x: T) { + let _: Cont<_>::Out = vec![true]; +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr new file mode 100644 index 0000000000000..464b59c249fe1 --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/generic-associated-types-bad.rs:16:10 + | +LL | const _: Ty::Pr = String::new(); + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `Ty::Pr` + --> $DIR/generic-associated-types-bad.rs:10:16 + | +LL | type Pr = T; + | ^^^^ required by this bound in `Ty::Pr` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr new file mode 100644 index 0000000000000..4f371b24e8034 --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `Vec<()>: Copy` is not satisfied + --> $DIR/generic-associated-types-bad.rs:20:12 + | +LL | let _: Ty::Pr>; + | ^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Vec<()>` + | +note: required by a bound in `Ty::Pr` + --> $DIR/generic-associated-types-bad.rs:10:16 + | +LL | type Pr = T; + | ^^^^ required by this bound in `Ty::Pr` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr new file mode 100644 index 0000000000000..74ec39424edcb --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/generic-associated-types-bad.rs:25:12 + | +LL | fn user<'a>() { + | -- lifetime `'a` defined here +LL | #[cfg(region)] +LL | let _: Ty::Static<&'a str> = ""; + | ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.rs b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs new file mode 100644 index 0000000000000..e66392a0a9411 --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs @@ -0,0 +1,26 @@ +// revisions: item local region + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +#[derive(Clone, Copy)] +pub enum Ty {} + +impl Ty { + type Pr = T; + + type Static = Q; +} + +#[cfg(item)] +const _: Ty::Pr = String::new(); //[item]~ the trait bound `String: Copy` is not satisfied + +fn main() { + #[cfg(local)] + let _: Ty::Pr>; //[local]~ ERROR the trait bound `Vec<()>: Copy` is not satisfied +} + +fn user<'a>() { + #[cfg(region)] + let _: Ty::Static<&'a str> = ""; //[region]~ ERROR lifetime may not live long enough +} diff --git a/tests/ui/associated-inherent-types/inference-fail.rs b/tests/ui/associated-inherent-types/inference-fail.rs new file mode 100644 index 0000000000000..939a4ff60f2bb --- /dev/null +++ b/tests/ui/associated-inherent-types/inference-fail.rs @@ -0,0 +1,11 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { type P = (); } + +fn main() { + // There is no way to infer this type. + let _: S<_>::P = (); //~ ERROR type annotations needed +} diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr b/tests/ui/associated-inherent-types/inference-fail.stderr similarity index 53% rename from tests/ui/associated-inherent-types/bugs/inference-fail.stderr rename to tests/ui/associated-inherent-types/inference-fail.stderr index 425691bd6c4f6..f29144e4aa755 100644 --- a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr +++ b/tests/ui/associated-inherent-types/inference-fail.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/inference-fail.rs:14:14 + --> $DIR/inference-fail.rs:10:12 | -LL | let _: S<_>::P; - | ^ cannot infer type +LL | let _: S<_>::P = (); + | ^^^^^^^ cannot infer type for type parameter `T` error: aborting due to previous error diff --git a/tests/ui/associated-inherent-types/inference.rs b/tests/ui/associated-inherent-types/inference.rs new file mode 100644 index 0000000000000..38179214fa124 --- /dev/null +++ b/tests/ui/associated-inherent-types/inference.rs @@ -0,0 +1,39 @@ +// Testing inference capabilities. +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +use std::convert::identity; + +struct Container(T); + +impl Container { + type Sink = (); +} + +impl Container { + type Thing = Any; +} + +impl Container<(T, ())> { + type Output = ((), Wrapped); +} + +fn main() { + // Inferred via the Self type of the impl. + let _: Container<_>::Sink; + + // Inferred via the RHS: + + let _: Container<_>::Thing = 0; + + let _: Container>::Thing = Wrapped(false); + + let _: Container<_>::Output = (drop(1), Wrapped("...")); + + let binding: Container<_>::Thing = Default::default(); // unsolved at this point + identity::(binding); // constrained and solved here +} + +struct Wrapped(T); diff --git a/tests/ui/associated-inherent-types/late-bound-regions.rs b/tests/ui/associated-inherent-types/late-bound-regions.rs new file mode 100644 index 0000000000000..488a2cda649ce --- /dev/null +++ b/tests/ui/associated-inherent-types/late-bound-regions.rs @@ -0,0 +1,25 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Test if we correctly normalize `S<'a>::P` with respect to late-bound regions. + +type Function = for<'a> fn(&'a i32) -> S<'a>::P; + +struct S<'a>(&'a ()); + +trait Inter { + type P; +} + +impl<'a> S<'a> { + type P = &'a i32; +} + +fn ret_ref_local<'e>() -> &'e i32 { + let f: Function = |x| x; + + let local = 0; + f(&local) //~ ERROR cannot return value referencing local variable `local` +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/late-bound-regions.stderr b/tests/ui/associated-inherent-types/late-bound-regions.stderr new file mode 100644 index 0000000000000..4706fcca91d06 --- /dev/null +++ b/tests/ui/associated-inherent-types/late-bound-regions.stderr @@ -0,0 +1,12 @@ +error[E0515]: cannot return value referencing local variable `local` + --> $DIR/late-bound-regions.rs:22:5 + | +LL | f(&local) + | ^^------^ + | | | + | | `local` is borrowed here + | returns a value referencing data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/associated-inherent-types/normalization-overflow.rs b/tests/ui/associated-inherent-types/normalization-overflow.rs new file mode 100644 index 0000000000000..4228238aa7b7a --- /dev/null +++ b/tests/ui/associated-inherent-types/normalization-overflow.rs @@ -0,0 +1,12 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(fmease): I'd prefer to report a cycle error here instead of an overflow one. + +struct T; + +impl T { + type This = Self::This; //~ ERROR overflow evaluating associated type `T::This` +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/normalization-overflow.stderr b/tests/ui/associated-inherent-types/normalization-overflow.stderr new file mode 100644 index 0000000000000..16bb64281e3af --- /dev/null +++ b/tests/ui/associated-inherent-types/normalization-overflow.stderr @@ -0,0 +1,8 @@ +error: overflow evaluating associated type `T::This` + --> $DIR/normalization-overflow.rs:9:17 + | +LL | type This = Self::This; + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/associated-inherent-types/private-in-public.rs b/tests/ui/associated-inherent-types/private-in-public.rs new file mode 100644 index 0000000000000..a4b372537c789 --- /dev/null +++ b/tests/ui/associated-inherent-types/private-in-public.rs @@ -0,0 +1,26 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] +#![crate_type = "lib"] + +#![deny(private_in_public)] + +pub type PubAlias0 = PubTy::PrivAssocTy; +//~^ ERROR private associated type `PubTy::PrivAssocTy` in public interface (error E0446) +//~| WARNING this was previously accepted +pub type PubAlias1 = PrivTy::PubAssocTy; +//~^ ERROR private type `PrivTy` in public interface (error E0446) +//~| WARNING this was previously accepted +pub type PubAlias2 = PubTy::PubAssocTy; +//~^ ERROR private type `PrivTy` in public interface (error E0446) +//~| WARNING this was previously accepted + +pub struct PubTy; +impl PubTy { + type PrivAssocTy = (); + pub type PubAssocTy = T; +} + +struct PrivTy; +impl PrivTy { + pub type PubAssocTy = (); +} diff --git a/tests/ui/associated-inherent-types/private-in-public.stderr b/tests/ui/associated-inherent-types/private-in-public.stderr new file mode 100644 index 0000000000000..f0a64e96179ac --- /dev/null +++ b/tests/ui/associated-inherent-types/private-in-public.stderr @@ -0,0 +1,34 @@ +error: private associated type `PubTy::PrivAssocTy` in public interface (error E0446) + --> $DIR/private-in-public.rs:7:1 + | +LL | pub type PubAlias0 = PubTy::PrivAssocTy; + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 +note: the lint level is defined here + --> $DIR/private-in-public.rs:5:9 + | +LL | #![deny(private_in_public)] + | ^^^^^^^^^^^^^^^^^ + +error: private type `PrivTy` in public interface (error E0446) + --> $DIR/private-in-public.rs:10:1 + | +LL | pub type PubAlias1 = PrivTy::PubAssocTy; + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 + +error: private type `PrivTy` in public interface (error E0446) + --> $DIR/private-in-public.rs:13:1 + | +LL | pub type PubAlias2 = PubTy::PubAssocTy; + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 + +error: aborting due to 3 previous errors + diff --git a/tests/ui/associated-inherent-types/regionck-0.rs b/tests/ui/associated-inherent-types/regionck-0.rs new file mode 100644 index 0000000000000..7c94539ace1f9 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-0.rs @@ -0,0 +1,14 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S<&'static ()> { + type T = (); +} + +fn user<'a>() { + let _: S::<&'a ()>::T; //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/regionck-0.stderr b/tests/ui/associated-inherent-types/regionck-0.stderr new file mode 100644 index 0000000000000..3a438ee630e1e --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-0.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/regionck-0.rs:11:12 + | +LL | fn user<'a>() { + | -- lifetime `'a` defined here +LL | let _: S::<&'a ()>::T; + | ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/tests/ui/associated-inherent-types/regionck-1.rs b/tests/ui/associated-inherent-types/regionck-1.rs new file mode 100644 index 0000000000000..ec663cd0f77fa --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-1.rs @@ -0,0 +1,13 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct U; + +impl U { + // Don't imply any bounds here. + + type NoTyOutliv<'a, T> = &'a T; //~ ERROR the parameter type `T` may not live long enough + type NoReOutliv<'a, 'b> = &'a &'b (); //~ ERROR reference has a longer lifetime than the data it references +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/regionck-1.stderr b/tests/ui/associated-inherent-types/regionck-1.stderr new file mode 100644 index 0000000000000..b17d89ca306f4 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-1.stderr @@ -0,0 +1,29 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/regionck-1.rs:9:30 + | +LL | type NoTyOutliv<'a, T> = &'a T; + | ^^^^^- help: consider adding a where clause: `where T: 'a` + | | + | ...so that the reference type `&'a T` does not outlive the data it points at + +error[E0491]: in type `&'a &'b ()`, reference has a longer lifetime than the data it references + --> $DIR/regionck-1.rs:10:31 + | +LL | type NoReOutliv<'a, 'b> = &'a &'b (); + | ^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'a` as defined here + --> $DIR/regionck-1.rs:10:21 + | +LL | type NoReOutliv<'a, 'b> = &'a &'b (); + | ^^ +note: but the referenced data is only valid for the lifetime `'b` as defined here + --> $DIR/regionck-1.rs:10:25 + | +LL | type NoReOutliv<'a, 'b> = &'a &'b (); + | ^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0309, E0491. +For more information about an error, try `rustc --explain E0309`. diff --git a/tests/ui/associated-inherent-types/regionck-2.rs b/tests/ui/associated-inherent-types/regionck-2.rs new file mode 100644 index 0000000000000..7a0b8b0830015 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-2.rs @@ -0,0 +1,14 @@ +// Regression test for issue #109299. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Lexer<'d>(&'d ()); + +impl Lexer<'static> { + type Cursor = (); +} + +fn test(_: Lexer::Cursor) {} //~ ERROR mismatched types + +fn main() {} diff --git a/tests/ui/associated-inherent-types/regionck-2.stderr b/tests/ui/associated-inherent-types/regionck-2.stderr new file mode 100644 index 0000000000000..b0a4ed35d5609 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-2.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/regionck-2.rs:12:12 + | +LL | fn test(_: Lexer::Cursor) {} + | ^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected struct `Lexer<'static>` + found struct `Lexer<'_>` +note: the anonymous lifetime defined here... + --> $DIR/regionck-2.rs:12:12 + | +LL | fn test(_: Lexer::Cursor) {} + | ^^^^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs new file mode 100644 index 0000000000000..b32b4288ac9f6 --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs @@ -0,0 +1,26 @@ +// check-pass +// compile-flags: --crate-type=lib + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Bounds on the self type play a major role in the resolution of inherent associated types (*). +// As a result of that, if a type alias contains any then its bounds have to be respected and the +// lint `type_alias_bounds` should not fire. +// +// FIXME(inherent_associated_types): In the current implementation that is. We might move the +// selection phase of IATs from hir_typeck to trait_selection resulting in us not requiring the +// ParamEnv that early allowing us to ignore bounds on type aliases again. +// Triage this before stabilization. + +#![deny(type_alias_bounds)] + +pub type Alias = (Source::Assoc,); + + +pub struct Source(T); +pub trait Bound {} + +impl Source { + pub type Assoc = (); +} diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs new file mode 100644 index 0000000000000..d081c4d5b78e6 --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs @@ -0,0 +1,12 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { + type T = T; +} + +fn main() { + let _: S<_>::T = String::new(); //~ ERROR the trait bound `String: Copy` is not satisfied +} diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr new file mode 100644 index 0000000000000..ecf30f4cdec58 --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/unsatisfied-bounds-inferred-type.rs:11:12 + | +LL | let _: S<_>::T = String::new(); + | ^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `S::T` + --> $DIR/unsatisfied-bounds-inferred-type.rs:6:9 + | +LL | impl S { + | ^^^^ required by this bound in `S::T` +LL | type T = T; + | - required by a bound in this associated type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs new file mode 100644 index 0000000000000..97bd2c421604a --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs @@ -0,0 +1,14 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { + type X = () + where + T: Copy; +} + +fn main() { + let _: S::::X; //~ ERROR the trait bound `String: Copy` is not satisfied +} diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr new file mode 100644 index 0000000000000..d4968cd05dcee --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/unsatisfied-bounds-where-clause-on-assoc-ty.rs:13:12 + | +LL | let _: S::::X; + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `S::X` + --> $DIR/unsatisfied-bounds-where-clause-on-assoc-ty.rs:9:12 + | +LL | type X = () + | - required by a bound in this associated type +LL | where +LL | T: Copy; + | ^^^^ required by this bound in `S::X` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 46ec3486116dacbd4fcfa1d92943de7fcc17921a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 31 Mar 2023 02:02:46 +0200 Subject: [PATCH 03/17] IAT: Add a few regression tests --- .../associated-inherent-types/issue-109768.rs | 12 +++++++ .../issue-109768.stderr | 35 +++++++++++++++++++ .../associated-inherent-types/issue-109789.rs | 22 ++++++++++++ .../issue-109789.stderr | 21 +++++++++++ .../associated-inherent-types/issue-109790.rs | 17 +++++++++ 5 files changed, 107 insertions(+) create mode 100644 tests/ui/associated-inherent-types/issue-109768.rs create mode 100644 tests/ui/associated-inherent-types/issue-109768.stderr create mode 100644 tests/ui/associated-inherent-types/issue-109789.rs create mode 100644 tests/ui/associated-inherent-types/issue-109789.stderr create mode 100644 tests/ui/associated-inherent-types/issue-109790.rs diff --git a/tests/ui/associated-inherent-types/issue-109768.rs b/tests/ui/associated-inherent-types/issue-109768.rs new file mode 100644 index 0000000000000..a3ae2e2ab4476 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109768.rs @@ -0,0 +1,12 @@ +// incremental + +struct Wrapper(T); + +struct Local(T, U); + +impl Local { //~ ERROR missing generics for struct `Local` + type AssocType3 = T; //~ ERROR inherent associated types are unstable + + const WRAPPED_ASSOC_3: Wrapper = Wrapper(); +} +//~^ ERROR `main` function not found diff --git a/tests/ui/associated-inherent-types/issue-109768.stderr b/tests/ui/associated-inherent-types/issue-109768.stderr new file mode 100644 index 0000000000000..97706d4062a97 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109768.stderr @@ -0,0 +1,35 @@ +error[E0601]: `main` function not found in crate `issue_109768` + --> $DIR/issue-109768.rs:11:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/issue-109768.rs` + +error[E0107]: missing generics for struct `Local` + --> $DIR/issue-109768.rs:7:9 + | +LL | impl Local { + | ^^^^^ expected 2 generic arguments + | +note: struct defined here, with 2 generic parameters: `T`, `U` + --> $DIR/issue-109768.rs:5:8 + | +LL | struct Local(T, U); + | ^^^^^ - - +help: add missing generic arguments + | +LL | impl Local { + | ++++++ + +error[E0658]: inherent associated types are unstable + --> $DIR/issue-109768.rs:8:5 + | +LL | type AssocType3 = T; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #8995 for more information + = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0601, E0658. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/associated-inherent-types/issue-109789.rs b/tests/ui/associated-inherent-types/issue-109789.rs new file mode 100644 index 0000000000000..0b5ba7d1fb55d --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109789.rs @@ -0,0 +1,22 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Foo(T); + +impl Foo { + type Assoc = u32; +} + +trait Other {} +impl Other for u32 {} + +// FIXME(inherent_associated_types): Avoid emitting two diagnostics (they only differ in span). +// FIXME(inherent_associated_types): Enhancement: Spruce up the diagnostic by saying something like +// "implementation is not general enough" as is done for traits via +// `try_report_trait_placeholder_mismatch`. + +fn bar(_: Foo fn(&'a ())>::Assoc) {} +//~^ ERROR mismatched types +//~| ERROR mismatched types + +fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109789.stderr b/tests/ui/associated-inherent-types/issue-109789.stderr new file mode 100644 index 0000000000000..7af338274a12b --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109789.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/issue-109789.rs:18:1 + | +LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected struct `Foo` + found struct `Foo fn(&'a ())>` + +error[E0308]: mismatched types + --> $DIR/issue-109789.rs:18:11 + | +LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected struct `Foo` + found struct `Foo fn(&'a ())>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/issue-109790.rs b/tests/ui/associated-inherent-types/issue-109790.rs new file mode 100644 index 0000000000000..b2be19a28f442 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109790.rs @@ -0,0 +1,17 @@ +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Foo(T); + +impl<'a> Foo { + type Assoc = &'a (); +} + +trait Other {} +impl Other for u32 {} + +fn bar(_: for<'a> fn(Foo::Assoc)) {} + +fn main() {} From 61e1eda6db042413cf1794407fd10b7edc90059d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 29 Mar 2023 03:24:35 +0200 Subject: [PATCH 04/17] IAT: Rustdoc integration --- src/librustdoc/clean/auto_trait.rs | 5 +- src/librustdoc/clean/inline.rs | 7 ++- src/librustdoc/clean/mod.rs | 60 ++++++++++++++----- src/librustdoc/clean/types.rs | 4 +- src/librustdoc/html/format.rs | 46 ++++++++++---- src/librustdoc/html/render/mod.rs | 4 +- src/librustdoc/json/conversions.rs | 2 +- src/rustdoc-json-types/lib.rs | 8 ++- src/tools/jsondoclint/src/validator.rs | 4 +- tests/rustdoc/inherent-projections.rs | 38 ++++++++++-- .../intra-doc/inherent-associated-types.rs | 45 ++++++++++++++ 11 files changed, 183 insertions(+), 40 deletions(-) create mode 100644 tests/rustdoc/intra-doc/inherent-associated-types.rs diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index fb32b6ef1d3f5..baf2b0a858529 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -556,7 +556,10 @@ where WherePredicate::EqPredicate { lhs, rhs, bound_params } => { match *lhs { Type::QPath(box QPathData { - ref assoc, ref self_type, ref trait_, .. + ref assoc, + ref self_type, + trait_: Some(ref trait_), + .. }) => { let ty = &*self_type; let mut new_trait = trait_.clone(); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 951f54e93663c..c852f9cca2bfd 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -706,7 +706,12 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: g.where_predicates.retain(|pred| match pred { clean::WherePredicate::BoundPredicate { - ty: clean::QPath(box clean::QPathData { self_type: clean::Generic(ref s), trait_, .. }), + ty: + clean::QPath(box clean::QPathData { + self_type: clean::Generic(ref s), + trait_: Some(trait_), + .. + }), bounds, .. } => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 72b60d95b8c15..657f3c9ec4577 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -441,7 +441,7 @@ fn clean_projection<'tcx>( assoc: projection_to_path_segment(ty, cx), should_show_cast, self_type, - trait_, + trait_: Some(trait_), })) } @@ -1330,7 +1330,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let mut bounds: Vec = Vec::new(); generics.where_predicates.retain_mut(|pred| match *pred { WherePredicate::BoundPredicate { - ty: QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }), + ty: + QPath(box QPathData { + ref assoc, + ref self_type, + trait_: Some(ref trait_), + .. + }), bounds: ref mut pred_bounds, .. } => { @@ -1492,25 +1498,30 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx), should_show_cast, self_type, - trait_, + trait_: Some(trait_), })) } hir::QPath::TypeRelative(qself, segment) => { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - let res = match ty.kind() { + let self_type = clean_ty(qself, cx); + + let (trait_, should_show_cast) = match ty.kind() { ty::Alias(ty::Projection, proj) => { - Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id) + let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); + let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); + register_res(cx, trait_.res); + let self_def_id = res.opt_def_id(); + let should_show_cast = + compute_should_show_cast(self_def_id, &trait_, &self_type); + + (Some(trait_), should_show_cast) } + ty::Alias(ty::Inherent, _) => (None, false), // Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s. ty::Error(_) => return Type::Infer, - // Otherwise, this is an inherent associated type. - _ => return clean_middle_ty(ty::Binder::dummy(ty), cx, None), + _ => bug!("clean: expected associated type, found `{ty:?}`"), }; - let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); - register_res(cx, trait_.res); - let self_def_id = res.opt_def_id(); - let self_type = clean_ty(qself, cx); - let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type); + Type::QPath(Box::new(QPathData { assoc: clean_path_segment(segment, cx), should_show_cast, @@ -1836,9 +1847,28 @@ pub(crate) fn clean_middle_ty<'tcx>( clean_projection(bound_ty.rebind(*data), cx, parent_def_id) } - // FIXME(fmease): Clean inherent projections properly. This requires making the trait ref in - // `QPathData` optional or alternatively adding a new `clean::Type` variant. - ty::Alias(ty::Inherent, _data) => Type::Infer, + ty::Alias(ty::Inherent, alias_ty) => { + let alias_ty = bound_ty.rebind(alias_ty); + let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None); + + Type::QPath(Box::new(QPathData { + assoc: PathSegment { + name: cx.tcx.associated_item(alias_ty.skip_binder().def_id).name, + args: GenericArgs::AngleBracketed { + args: substs_to_args( + cx, + alias_ty.map_bound(|ty| ty.substs.as_slice()), + true, + ) + .into(), + bindings: Default::default(), + }, + }, + should_show_cast: false, + self_type, + trait_: None, + })) + } ty::Param(ref p) => { if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 7371b44465bab..38664c3e359a6 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1660,7 +1660,7 @@ impl Type { pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> { if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self { - Some((self_type, trait_.def_id(), assoc.clone())) + Some((self_type, trait_.as_ref()?.def_id(), assoc.clone())) } else { None } @@ -1704,7 +1704,7 @@ pub(crate) struct QPathData { pub self_type: Type, /// FIXME: compute this field on demand. pub should_show_cast: bool, - pub trait_: Path, + pub trait_: Option, } /// A primitive (aka, builtin) type. diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 1c6810bdaf9be..d963d6092c48f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1116,14 +1116,17 @@ fn fmt_type<'cx>( ref trait_, should_show_cast, }) => { + // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719), + // we need to surround them with angle brackets in some cases (e.g. `::P`). + if f.alternate() { - if should_show_cast { + if let Some(trait_) = trait_ && should_show_cast { write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{:#}::", self_type.print(cx))? } } else { - if should_show_cast { + if let Some(trait_) = trait_ && should_show_cast { write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{}::", self_type.print(cx))? @@ -1139,15 +1142,36 @@ fn fmt_type<'cx>( // the ugliness comes from inlining across crates where // everything comes in as a fully resolved QPath (hard to // look at). - if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) { - write!( - f, - "{name}", - shortty = ItemType::AssocType, - name = assoc.name, - path = join_with_double_colon(&path), - ) + if !f.alternate() { + // FIXME(inherent_associated_types): We always link to the very first associated + // type (in respect to source order) that bears the given name (`assoc.name`) and that is + // affiliated with the computed `DefId`. This is obviously incorrect when we have + // multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself + // through here and map it to the corresponding HTML ID that was generated by + // `render::Context::derive_id` when the impl blocks were rendered. + // There is no such mapping unfortunately. + // As a hack, we could badly imitate `derive_id` here by keeping *count* when looking + // for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()` + // considering privacy, `doc(hidden)`, etc. + // I don't feel like that right now :cold_sweat:. + + let parent_href = match trait_ { + Some(trait_) => href(trait_.def_id(), cx).ok(), + None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()), + }; + + if let Some((url, _, path)) = parent_href { + write!( + f, + "{name}", + shortty = ItemType::AssocType, + name = assoc.name, + path = join_with_double_colon(&path), + ) + } else { + write!(f, "{}", assoc.name) + } } else { write!(f, "{}", assoc.name) }?; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index e09c6480060d0..d6773169639c3 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2202,7 +2202,9 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec { } clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => { work.push_back(self_type); - process_path(trait_.def_id()); + if let Some(trait_) = trait_ { + process_path(trait_.def_id()); + } } _ => {} } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index b5bebb7059380..b1cef20b434a6 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -574,7 +574,7 @@ impl FromWithTcx for Type { name: assoc.name.to_string(), args: Box::new(assoc.args.into_tcx(tcx)), self_type: Box::new(self_type.into_tcx(tcx)), - trait_: trait_.into_tcx(tcx), + trait_: trait_.map(|trait_| trait_.into_tcx(tcx)), }, } } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 3cf8ceed62036..3556834071fc9 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 24; +pub const FORMAT_VERSION: u32 = 25; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -581,13 +581,15 @@ pub enum Type { #[serde(rename = "type")] type_: Box, }, - /// `::Name` or associated types like `T::Item` where `T: Iterator` + /// Associated types like `::Name` and `T::Item` where + /// `T: Iterator` or inherent associated types like `Struct::Name`. QualifiedPath { name: String, args: Box, self_type: Box, + /// `None` iff this is an *inherent* associated type. #[serde(rename = "trait")] - trait_: Path, + trait_: Option, }, } diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index a1f675a3b40f6..bf8a64acf08d6 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -273,7 +273,9 @@ impl<'a> Validator<'a> { Type::QualifiedPath { name: _, args, self_type, trait_ } => { self.check_generic_args(&**args); self.check_type(&**self_type); - self.check_path(trait_, PathKind::Trait); + if let Some(trait_) = trait_ { + self.check_path(trait_, PathKind::Trait); + } } } } diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs index bdd881ab456d2..9bda0acaf83fe 100644 --- a/tests/rustdoc/inherent-projections.rs +++ b/tests/rustdoc/inherent-projections.rs @@ -1,10 +1,9 @@ #![feature(inherent_associated_types)] #![allow(incomplete_features)] -// FIXME(fmease): Properly render inherent projections. - -// @has inherent_projections/fn.create.html -// @has - '//pre[@class="rust item-decl"]' "create() -> _" +// @has 'inherent_projections/fn.create.html' +// @has - '//pre[@class="rust item-decl"]' "create() -> Owner::Metadata" +// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Owner.html#associatedtype.Metadata' pub fn create() -> Owner::Metadata {} pub struct Owner; @@ -12,3 +11,34 @@ pub struct Owner; impl Owner { pub type Metadata = (); } + +// Make sure we handle bound vars correctly. +// @has 'inherent_projections/type.User.html' '//pre[@class="rust item-decl"]' "for<'a> fn(_: Carrier<'a>::Focus)" +pub type User = for<'a> fn(Carrier<'a>::Focus); + +pub struct Carrier<'a>(&'a ()); + +impl<'a> Carrier<'a> { + pub type Focus = &'a mut i32; +} + +//////////////////////////////////////// + +// FIXME(inherent_associated_types): Below we link to `Proj` but we should link to `Proj-1`. +// The current test checks for the buggy behavior for demonstration purposes. + +// @has 'inherent_projections/type.Test.html' +// @has - '//pre[@class="rust item-decl"]' "Parametrized" +// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj' +// @!has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj-1' +pub type Test = Parametrized::Proj; + +pub struct Parametrized(T); + +impl Parametrized { + pub type Proj = (); +} + +impl Parametrized { + pub type Proj = String; +} diff --git a/tests/rustdoc/intra-doc/inherent-associated-types.rs b/tests/rustdoc/intra-doc/inherent-associated-types.rs new file mode 100644 index 0000000000000..2b28d2ae60bd4 --- /dev/null +++ b/tests/rustdoc/intra-doc/inherent-associated-types.rs @@ -0,0 +1,45 @@ +#![feature(inherent_associated_types)] + +#![allow(incomplete_features)] +#![deny(rustdoc::broken_intra_doc_links)] + +// @has inherent_associated_types/index.html + +// @has - '//a/@href' 'enum.Simple.html#associatedtype.Type' +//! [`Simple::Type`] + +pub enum Simple {} + +impl Simple { + pub type Type = (); +} + +//////////////////////////////////////// + +// @has 'inherent_associated_types/type.Test0.html' '//a/@href' \ +// 'struct.Parametrized.html#associatedtype.Proj' +/// [`Parametrized::Proj`] +pub type Test0 = (); + +// FIXME(inherent_associated_types): The intra-doc link below should point to `Proj-1` not `Proj`. +// The current test checks for the buggy behavior for demonstration purposes. +// The same bug happens for inherent associated functions and constants (see #85960, #93398). +// +// Further, at some point we should reject the intra-doc link `Parametrized::Proj`. +// It currently links to `Parametrized::Proj`. + +// @has 'inherent_associated_types/type.Test1.html' +// @has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj' +// @!has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj-1' +/// [`Parametrized::Proj`] +pub type Test1 = (); + +pub struct Parametrized(T); + +impl Parametrized { + pub type Proj = (); +} + +impl Parametrized { + pub type Proj = String; +} From cd6dec33c2b484fbcb672cb1f80b68b602c1a6f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 21 Apr 2023 22:02:41 +0200 Subject: [PATCH 05/17] IAT: Proper WF computation --- .../src/traits/project.rs | 66 +++++++++----- .../rustc_trait_selection/src/traits/wf.rs | 87 ++++++++++++------- 2 files changed, 99 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 852780fccace9..8e684b7ac239b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1274,29 +1274,14 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( }); } - let impl_def_id = tcx.parent(alias_ty.def_id); - let impl_substs = selcx.infcx.fresh_substs_for_item(cause.span, impl_def_id); - - let impl_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs); - let impl_ty = - normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations); - - // Infer the generic parameters of the impl by unifying the - // impl type with the self type of the projection. - let self_ty = alias_ty.self_ty(); - match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { - Ok(mut ok) => obligations.append(&mut ok.obligations), - Err(_) => { - tcx.sess.delay_span_bug( - cause.span, - format!( - "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" - ), - ); - } - } - - let substs = alias_ty.rebase_substs_onto_impl(impl_substs, tcx); + let substs = compute_inherent_assoc_ty_substs( + selcx, + param_env, + alias_ty, + cause.clone(), + depth, + obligations, + ); // Register the obligations arising from the impl and from the associated type itself. let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, substs); @@ -1343,6 +1328,41 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( ty } +pub fn compute_inherent_assoc_ty_substs<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec>, +) -> ty::SubstsRef<'tcx> { + let tcx = selcx.tcx(); + + let impl_def_id = tcx.parent(alias_ty.def_id); + let impl_substs = selcx.infcx.fresh_substs_for_item(cause.span, impl_def_id); + + let impl_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs); + let impl_ty = + normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations); + + // Infer the generic parameters of the impl by unifying the + // impl type with the self type of the projection. + let self_ty = alias_ty.self_ty(); + match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { + Ok(mut ok) => obligations.append(&mut ok.obligations), + Err(_) => { + tcx.sess.delay_span_bug( + cause.span, + format!( + "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" + ), + ); + } + } + + alias_ty.rebase_substs_onto_impl(impl_substs, tcx) +} + enum Projected<'tcx> { Progress(Progress<'tcx>), NoProgress(ty::Term<'tcx>), diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index e5edceda94446..086ab32b52007 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -58,15 +58,8 @@ pub fn obligations<'tcx>( GenericArgKind::Lifetime(..) => return Some(Vec::new()), }; - let mut wf = WfPredicates { - tcx: infcx.tcx, - param_env, - body_id, - span, - out: vec![], - recursion_depth, - item: None, - }; + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None }; wf.compute(arg); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); @@ -91,7 +84,7 @@ pub fn unnormalized_obligations<'tcx>( debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id: CRATE_DEF_ID, span: DUMMY_SP, @@ -116,7 +109,7 @@ pub fn trait_obligations<'tcx>( item: &'tcx hir::Item<'tcx>, ) -> Vec> { let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id, span, @@ -138,7 +131,7 @@ pub fn predicate_obligations<'tcx>( span: Span, ) -> Vec> { let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id, span, @@ -190,8 +183,8 @@ pub fn predicate_obligations<'tcx>( wf.normalize(infcx) } -struct WfPredicates<'tcx> { - tcx: TyCtxt<'tcx>, +struct WfPredicates<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, span: Span, @@ -290,9 +283,9 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( } } -impl<'tcx> WfPredicates<'tcx> { +impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx + self.infcx.tcx } fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { @@ -325,7 +318,7 @@ impl<'tcx> WfPredicates<'tcx> { /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) { - let tcx = self.tcx; + let tcx = self.tcx(); let trait_ref = &trait_pred.trait_ref; // Negative trait predicates don't require supertraits to hold, just @@ -369,7 +362,6 @@ impl<'tcx> WfPredicates<'tcx> { self.out.extend(obligations); } - let tcx = self.tcx(); self.out.extend( trait_ref .substs @@ -436,13 +428,45 @@ impl<'tcx> WfPredicates<'tcx> { let obligations = self.nominal_obligations_without_const(data.def_id, data.substs); self.out.extend(obligations); + self.compute_projection_substs(data.substs); + } + + fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { + // An inherent projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its substs are wf + // + // (*) The predicates of an inherent associated type include the + // predicates of the impl that it's contained in. + + if !data.self_ty().has_escaping_bound_vars() { + // FIXME(inherent_associated_types): Should this happen inside of a snapshot? + // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm! + let substs = traits::project::compute_inherent_assoc_ty_substs( + &mut traits::SelectionContext::new(self.infcx), + self.param_env, + data, + self.cause(traits::WellFormed(None)), + self.recursion_depth, + &mut self.out, + ); + // Inherent projection types do not require const predicates. + let obligations = self.nominal_obligations_without_const(data.def_id, substs); + self.out.extend(obligations); + } + + self.compute_projection_substs(data.substs); + } + + fn compute_projection_substs(&mut self, substs: SubstsRef<'tcx>) { let tcx = self.tcx(); let cause = self.cause(traits::WellFormed(None)); let param_env = self.param_env; let depth = self.recursion_depth; self.out.extend( - data.substs + substs .iter() .filter(|arg| { matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) @@ -464,9 +488,9 @@ impl<'tcx> WfPredicates<'tcx> { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); let trait_ref = - ty::TraitRef::from_lang_item(self.tcx, LangItem::Sized, cause.span, [subty]); + ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [subty]); self.out.push(traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, @@ -605,8 +629,9 @@ impl<'tcx> WfPredicates<'tcx> { walker.skip_current_subtree(); // Subtree handled by compute_projection. self.compute_projection(data); } - ty::Alias(ty::Inherent, _) => { - // WF if their substs are WF. + ty::Alias(ty::Inherent, data) => { + walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection. + self.compute_inherent_projection(data); } ty::Adt(def, substs) => { @@ -700,7 +725,7 @@ impl<'tcx> WfPredicates<'tcx> { // All of the requirements on type parameters // have already been checked for `impl Trait` in // return position. We do need to check type-alias-impl-trait though. - if self.tcx.is_type_alias_impl_trait(def_id) { + if self.tcx().is_type_alias_impl_trait(def_id) { let obligations = self.nominal_obligations(def_id, substs); self.out.extend(obligations); } @@ -770,15 +795,15 @@ impl<'tcx> WfPredicates<'tcx> { substs: SubstsRef<'tcx>, remap_constness: bool, ) -> Vec> { - let predicates = self.tcx.predicates_of(def_id); + let predicates = self.tcx().predicates_of(def_id); let mut origins = vec![def_id; predicates.predicates.len()]; let mut head = predicates; while let Some(parent) = head.parent { - head = self.tcx.predicates_of(parent); + head = self.tcx().predicates_of(parent); origins.extend(iter::repeat(parent).take(head.predicates.len())); } - let predicates = predicates.instantiate(self.tcx, substs); + let predicates = predicates.instantiate(self.tcx(), substs); trace!("{:#?}", predicates); debug_assert_eq!(predicates.predicates.len(), origins.len()); @@ -791,10 +816,10 @@ impl<'tcx> WfPredicates<'tcx> { }; let cause = self.cause(code); if remap_constness { - pred = pred.without_const(self.tcx); + pred = pred.without_const(self.tcx()); } traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, @@ -859,7 +884,7 @@ impl<'tcx> WfPredicates<'tcx> { // Note: in fact we only permit builtin traits, not `Bar<'d>`, I // am looking forward to the future here. if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { - let implicit_bounds = object_region_bounds(self.tcx, data); + let implicit_bounds = object_region_bounds(self.tcx(), data); let explicit_bound = region; @@ -869,7 +894,7 @@ impl<'tcx> WfPredicates<'tcx> { let outlives = ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); self.out.push(traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, From 47171e0c500e791280e269a73c382bfac15148b2 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 6 Nov 2022 21:24:20 +0000 Subject: [PATCH 06/17] Use `landingpad filter` to encode aborting landing pad --- compiler/rustc_codegen_gcc/src/builder.rs | 4 ++++ compiler/rustc_codegen_llvm/src/builder.rs | 7 +++++++ compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/builder.rs | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index a66ddb6a09f2b..5fba09d795ce8 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1227,6 +1227,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { (value1, value2) } + fn filter_landing_pad(&mut self, pers_fn: RValue<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) { + self.cleanup_landing_pad(pers_fn) + } + #[cfg(feature="master")] fn resume(&mut self, exn0: RValue<'gcc>, _exn1: RValue<'gcc>) { let exn_type = exn0.get_type(); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 2fd6db8cbfeab..10da8cc4c2552 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -992,6 +992,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { (self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1)) } + fn filter_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) { + let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false); + let landing_pad = self.landing_pad(ty, pers_fn, 1 /* FIXME should this be 0? */); + self.add_clause(landing_pad, self.const_array(self.type_i8p(), &[])); + (self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1)) + } + fn resume(&mut self, exn0: &'ll Value, exn1: &'ll Value) { let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false); let mut exn = self.const_poison(ty); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index c1613a9640a8b..a832999225ab6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1600,7 +1600,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx = Bx::build(self.cx, llbb); let llpersonality = self.cx.eh_personality(); - bx.cleanup_landing_pad(llpersonality); + bx.filter_landing_pad(llpersonality); funclet = None; } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 57de7e9620e87..853c6934c2c24 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -274,6 +274,7 @@ pub trait BuilderMethods<'a, 'tcx>: // These are used by everyone except msvc fn cleanup_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value); + fn filter_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value); fn resume(&mut self, exn0: Self::Value, exn1: Self::Value); // These are used only by msvc From 62237536da0e17fdaa5c03965563a200296c6e12 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 6 Nov 2022 21:32:55 +0000 Subject: [PATCH 07/17] Parse catch filter in personality function --- library/std/src/personality/dwarf/eh.rs | 5 ++++- library/std/src/personality/gcc.rs | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/library/std/src/personality/dwarf/eh.rs b/library/std/src/personality/dwarf/eh.rs index 87585a8fcd0d7..79624703a4cf7 100644 --- a/library/std/src/personality/dwarf/eh.rs +++ b/library/std/src/personality/dwarf/eh.rs @@ -47,6 +47,7 @@ pub enum EHAction { None, Cleanup(usize), Catch(usize), + Filter(usize), Terminate, } @@ -142,9 +143,11 @@ unsafe fn interpret_cs_action( let ttype_index = action_reader.read_sleb128(); if ttype_index == 0 { EHAction::Cleanup(lpad) - } else { + } else if ttype_index > 0 { // Stop unwinding Rust panics at catch_unwind. EHAction::Catch(lpad) + } else { + EHAction::Filter(lpad) } } } diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs index 0421b47be024e..4c29c332b75a8 100644 --- a/library/std/src/personality/gcc.rs +++ b/library/std/src/personality/gcc.rs @@ -135,7 +135,7 @@ cfg_if::cfg_if! { EHAction::None | EHAction::Cleanup(_) => { return continue_unwind(exception_object, context); } - EHAction::Catch(_) => { + EHAction::Catch(_) | EHAction::Filter(_) => { // EHABI requires the personality routine to update the // SP value in the barrier cache of the exception object. (*exception_object).private[5] = @@ -147,7 +147,7 @@ cfg_if::cfg_if! { } else { match eh_action { EHAction::None => return continue_unwind(exception_object, context), - EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => { + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { uw::_Unwind_SetGR( context, UNWIND_DATA_REG.0, @@ -201,13 +201,13 @@ cfg_if::cfg_if! { if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { match eh_action { EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, - EHAction::Catch(_) => uw::_URC_HANDLER_FOUND, + EHAction::Catch(_) | EHAction::Filter(_) => uw::_URC_HANDLER_FOUND, EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, } } else { match eh_action { EHAction::None => uw::_URC_CONTINUE_UNWIND, - EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => { + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { uw::_Unwind_SetGR( context, UNWIND_DATA_REG.0, From 37f7d322b8cd9e9712c3cc9cf3a550371bd164e0 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 6 Nov 2022 21:35:10 +0000 Subject: [PATCH 08/17] Prevent aborting guard from aborting the process in a forced unwind --- library/std/src/personality/gcc.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs index 4c29c332b75a8..82edb11cbd146 100644 --- a/library/std/src/personality/gcc.rs +++ b/library/std/src/personality/gcc.rs @@ -147,6 +147,7 @@ cfg_if::cfg_if! { } else { match eh_action { EHAction::None => return continue_unwind(exception_object, context), + EHAction::Filter(_) if state & uw::_US_FORCE_UNWIND as c_int != 0 => return continue_unwind(exception_object, context), EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { uw::_Unwind_SetGR( context, @@ -207,6 +208,8 @@ cfg_if::cfg_if! { } else { match eh_action { EHAction::None => uw::_URC_CONTINUE_UNWIND, + // Forced unwinding hits a terminate action. + EHAction::Filter(_) if actions as i32 & uw::_UA_FORCE_UNWIND as i32 != 0 => uw::_URC_CONTINUE_UNWIND, EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { uw::_Unwind_SetGR( context, From ecd04fd1b553c06e3e48c0f8485de46758e51636 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 7 Apr 2023 17:15:41 +0100 Subject: [PATCH 09/17] Add test for unwinding past terminating POF --- .../forced-unwind-terminate-pof/Makefile | 9 +++++++++ .../run-make/forced-unwind-terminate-pof/foo.rs | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/run-make/forced-unwind-terminate-pof/Makefile create mode 100644 tests/run-make/forced-unwind-terminate-pof/foo.rs diff --git a/tests/run-make/forced-unwind-terminate-pof/Makefile b/tests/run-make/forced-unwind-terminate-pof/Makefile new file mode 100644 index 0000000000000..871621520b910 --- /dev/null +++ b/tests/run-make/forced-unwind-terminate-pof/Makefile @@ -0,0 +1,9 @@ +# ignore-cross-compile +# only-linux +include ../tools.mk + +all: foo + $(call RUN,foo) | $(CGREP) -v "cannot unwind" + +foo: foo.rs + $(RUSTC) $< diff --git a/tests/run-make/forced-unwind-terminate-pof/foo.rs b/tests/run-make/forced-unwind-terminate-pof/foo.rs new file mode 100644 index 0000000000000..0a51287313f6e --- /dev/null +++ b/tests/run-make/forced-unwind-terminate-pof/foo.rs @@ -0,0 +1,17 @@ +// Tests that forced unwind through POF Rust frames wouldn't trigger our terminating guards. + +#![feature(c_unwind)] +#![no_main] + +extern "C-unwind" { + fn pthread_exit(v: *mut core::ffi::c_void) -> !; +} + +unsafe extern "C" fn call_pthread_exit() { + pthread_exit(core::ptr::null_mut()); +} + +#[no_mangle] +unsafe extern "C-unwind" fn main(_argc: core::ffi::c_int, _argv: *mut *mut core::ffi::c_char) { + call_pthread_exit(); +} From 97926360e755547dc4663ab75af1e37e9a68e58b Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 7 Apr 2023 20:11:19 +0100 Subject: [PATCH 10/17] Fix num reserved clauses for landing pad --- compiler/rustc_codegen_llvm/src/builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 10da8cc4c2552..4d0bcd53d1562 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -985,7 +985,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn cleanup_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) { let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false); - let landing_pad = self.landing_pad(ty, pers_fn, 1 /* FIXME should this be 0? */); + let landing_pad = self.landing_pad(ty, pers_fn, 0); unsafe { llvm::LLVMSetCleanup(landing_pad, llvm::True); } @@ -994,7 +994,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn filter_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) { let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false); - let landing_pad = self.landing_pad(ty, pers_fn, 1 /* FIXME should this be 0? */); + let landing_pad = self.landing_pad(ty, pers_fn, 1); self.add_clause(landing_pad, self.const_array(self.type_i8p(), &[])); (self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1)) } From 91afde57a28b1619f140fbe8463d150ce7c8adca Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 8 Apr 2023 13:12:24 +0100 Subject: [PATCH 11/17] Add todo for filter landing pad --- compiler/rustc_codegen_gcc/src/builder.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 5fba09d795ce8..869344ce92d7d 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1228,6 +1228,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn filter_landing_pad(&mut self, pers_fn: RValue<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) { + // TODO(antoyo): generate the correct landing pad self.cleanup_landing_pad(pers_fn) } From 16abe6c83d2c9eba8f54be8fbe25ed49791e1e45 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 9 Apr 2023 12:05:35 +0100 Subject: [PATCH 12/17] Fix codegen test --- tests/codegen/vec-shrink-panik.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/codegen/vec-shrink-panik.rs b/tests/codegen/vec-shrink-panik.rs index b3c3483fea982..4e5d8dc063241 100644 --- a/tests/codegen/vec-shrink-panik.rs +++ b/tests/codegen/vec-shrink-panik.rs @@ -25,7 +25,7 @@ pub fn issue71861(vec: Vec) -> Box<[u32]> { // Call to panic_cannot_unwind in case of double-panic is expected // on LLVM 16 and older, but other panics are not. - // CHECK: cleanup + // CHECK: filter // old-NEXT: ; call core::panicking::panic_cannot_unwind // old-NEXT: panic_cannot_unwind @@ -40,7 +40,7 @@ pub fn issue75636<'a>(iter: &[&'a str]) -> Box<[&'a str]> { // Call to panic_cannot_unwind in case of double-panic is expected, // on LLVM 16 and older, but other panics are not. - // CHECK: cleanup + // CHECK: filter // old-NEXT: ; call core::panicking::panic_cannot_unwind // old-NEXT: panic_cannot_unwind From 2b9279f3131056a1a1dd5de7513de4eb98987770 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 3 May 2023 23:22:57 +0000 Subject: [PATCH 13/17] Diagnostic args are still args if they're documented --- .../src/diagnostics/subdiagnostic.rs | 12 ++--- .../rustc_macros/src/diagnostics/utils.rs | 3 +- .../diagnostic-derive-doc-comment-field.rs | 47 +++++++++++++++++++ ...diagnostic-derive-doc-comment-field.stderr | 43 +++++++++++++++++ 4 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs create mode 100644 tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 62d49c1c64e27..83b47f8acfbd6 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -4,17 +4,16 @@ use crate::diagnostics::error::{ invalid_attr, span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - build_field_mapping, is_doc_comment, new_code_ident, - report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo, - FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, + build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident, + report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, + should_generate_set_arg, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, + HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; -use super::utils::{build_suggestion_code, AllowMultipleAlternatives}; - /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. pub(crate) struct SubdiagnosticDeriveBuilder { diag: syn::Ident, @@ -212,7 +211,6 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { /// Generates the code for a field with no attributes. fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream { let ast = binding.ast(); - assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg"); let diag = &self.parent.diag; let ident = ast.ident.as_ref().unwrap(); @@ -580,7 +578,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .variant .bindings() .iter() - .filter(|binding| binding.ast().attrs.is_empty()) + .filter(|binding| should_generate_set_arg(binding.ast())) .map(|binding| self.generate_field_set_arg(binding)) .collect(); diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index b9b09c66230b8..bc97e39bebd8a 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -851,7 +851,8 @@ impl quote::IdentFragment for SubdiagnosticKind { /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic /// call (like `span_label`). pub(super) fn should_generate_set_arg(field: &Field) -> bool { - field.attrs.is_empty() + // Perhaps this should be an exhaustive list... + field.attrs.iter().all(|attr| is_doc_comment(attr)) } pub(super) fn is_doc_comment(attr: &Attribute) -> bool { diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs new file mode 100644 index 0000000000000..7e1f7b1c5c10d --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs @@ -0,0 +1,47 @@ +// check-fail +// Tests that a doc comment will not preclude a field from being considered a diagnostic argument + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Subdiagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +// ignore-stage1 +// ignore-beta +// ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_errors; +extern crate rustc_fluent_macro; +extern crate rustc_macros; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_errors::{Applicability, DiagnosticMessage, SubdiagnosticMessage}; +use rustc_fluent_macro::fluent_messages; +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_span::Span; + +fluent_messages! { "./example.ftl" } + +struct NotIntoDiagnosticArg; + +#[derive(Diagnostic)] +//~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied +#[diag(no_crate_example)] +struct Test { + #[primary_span] + span: Span, + /// A doc comment + arg: NotIntoDiagnosticArg, +} + +#[derive(Subdiagnostic)] +//~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied +#[label(no_crate_example)] +struct SubTest { + #[primary_span] + span: Span, + /// A doc comment + arg: NotIntoDiagnosticArg, +} diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr new file mode 100644 index 0000000000000..27044748d0803 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr @@ -0,0 +1,43 @@ +error[E0277]: the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied + --> $DIR/diagnostic-derive-doc-comment-field.rs:29:10 + | +LL | #[derive(Diagnostic)] + | ^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` + | + = help: the following other types implement trait `IntoDiagnosticArg`: + &'a T + &'a std::path::Path + &'a str + &rustc_target::spec::TargetTriple + Box<(dyn std::error::Error + 'static)> + CString + CguReuse + Cow<'a, str> + and 42 others +note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:747:5 + = note: this error originates in the derive macro `Diagnostic` which comes from the expansion of the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied + --> $DIR/diagnostic-derive-doc-comment-field.rs:39:10 + | +LL | #[derive(Subdiagnostic)] + | ^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` + | + = help: the following other types implement trait `IntoDiagnosticArg`: + &'a T + &'a std::path::Path + &'a str + &rustc_target::spec::TargetTriple + Box<(dyn std::error::Error + 'static)> + CString + CguReuse + Cow<'a, str> + and 42 others +note: required by a bound in `Diagnostic::set_arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:964:5 + = note: this error originates in the derive macro `Subdiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From a156bd771457110415b1eec74cf52c9502d461a3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 3 May 2023 23:53:44 +0000 Subject: [PATCH 14/17] Make spans a bit better --- .../src/diagnostics/diagnostic_builder.rs | 9 ++-- .../src/diagnostics/subdiagnostic.rs | 21 ++++++---- .../rustc_macros/src/diagnostics/utils.rs | 6 +++ .../diagnostic-derive-doc-comment-field.rs | 6 ++- ...diagnostic-derive-doc-comment-field.stderr | 41 +++++++------------ .../session-diagnostic/diagnostic-derive.rs | 2 +- .../diagnostic-derive.stderr | 9 ++-- 7 files changed, 48 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 427c82c410b91..cd6e36874603f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -9,7 +9,7 @@ use crate::diagnostics::utils::{ FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::{Ident, Span, TokenStream}; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, quote_spanned}; use syn::Token; use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type}; use synstructure::{BindingInfo, Structure, VariantInfo}; @@ -251,7 +251,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { let diag = &self.parent.diag; let field = binding_info.ast(); - let field_binding = &binding_info.binding; + let mut field_binding = binding_info.binding.clone(); + field_binding.set_span(field.ty.span()); let ident = field.ident.as_ref().unwrap(); let ident = format_ident!("{}", ident); // strip `r#` prefix, if present @@ -284,9 +285,9 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_)); let (binding, needs_destructure) = if needs_clone { // `primary_span` can accept a `Vec` so don't destructure that. - (quote! { #field_binding.clone() }, false) + (quote_spanned! {inner_ty.span()=> #field_binding.clone() }, false) } else { - (quote! { #field_binding }, true) + (quote_spanned! {inner_ty.span()=> #field_binding }, true) }; let generated_code = self diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 83b47f8acfbd6..374ba1a45c06e 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -209,18 +209,20 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } /// Generates the code for a field with no attributes. - fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream { - let ast = binding.ast(); - + fn generate_field_set_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { let diag = &self.parent.diag; - let ident = ast.ident.as_ref().unwrap(); - // strip `r#` prefix, if present - let ident = format_ident!("{}", ident); + + let field = binding_info.ast(); + let mut field_binding = binding_info.binding.clone(); + field_binding.set_span(field.ty.span()); + + let ident = field.ident.as_ref().unwrap(); + let ident = format_ident!("{}", ident); // strip `r#` prefix, if present quote! { #diag.set_arg( stringify!(#ident), - #binding + #field_binding ); } } @@ -397,7 +399,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { clone_suggestion_code: bool, ) -> Result { let span = attr.span().unwrap(); - let ident = &list.path.segments.last().unwrap().ident; + let mut ident = list.path.segments.last().unwrap().ident.clone(); + ident.set_span(info.ty.span()); let name = ident.to_string(); let name = name.as_str(); @@ -496,7 +499,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .variant .bindings() .iter() - .filter(|binding| !binding.ast().attrs.is_empty()) + .filter(|binding| !should_generate_set_arg(binding.ast())) .map(|binding| self.generate_field_attr_code(binding, kind_stats)) .collect(); diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index bc97e39bebd8a..e2434981f8d17 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -207,6 +207,12 @@ impl<'ty> FieldInnerTy<'ty> { FieldInnerTy::Plain(..) => quote! { #inner }, } } + + pub fn span(&self) -> proc_macro2::Span { + match self { + FieldInnerTy::Option(ty) | FieldInnerTy::Vec(ty) | FieldInnerTy::Plain(ty) => ty.span(), + } + } } /// Field information passed to the builder. Deliberately omits attrs to discourage the diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs index 7e1f7b1c5c10d..642b58b0753fd 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs @@ -1,5 +1,7 @@ // check-fail // Tests that a doc comment will not preclude a field from being considered a diagnostic argument +// normalize-stderr-test "the following other types implement trait `IntoDiagnosticArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr" +// normalize-stderr-test "diagnostic_builder\.rs:[0-9]+:[0-9]+" -> "diagnostic_builder.rs:LL:CC" // The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, // changing the output of this test. Since Subdiagnostic is strictly internal to the compiler @@ -27,21 +29,21 @@ fluent_messages! { "./example.ftl" } struct NotIntoDiagnosticArg; #[derive(Diagnostic)] -//~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied #[diag(no_crate_example)] struct Test { #[primary_span] span: Span, /// A doc comment arg: NotIntoDiagnosticArg, + //~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied } #[derive(Subdiagnostic)] -//~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied #[label(no_crate_example)] struct SubTest { #[primary_span] span: Span, /// A doc comment arg: NotIntoDiagnosticArg, + //~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied } diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr index 27044748d0803..e4b8958b4fae8 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr @@ -1,42 +1,29 @@ error[E0277]: the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied - --> $DIR/diagnostic-derive-doc-comment-field.rs:29:10 + --> $DIR/diagnostic-derive-doc-comment-field.rs:37:10 | LL | #[derive(Diagnostic)] - | ^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` + | ---------- required by a bound introduced by this call +... +LL | arg: NotIntoDiagnosticArg, + | ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` | - = help: the following other types implement trait `IntoDiagnosticArg`: - &'a T - &'a std::path::Path - &'a str - &rustc_target::spec::TargetTriple - Box<(dyn std::error::Error + 'static)> - CString - CguReuse - Cow<'a, str> - and 42 others + = help: normalized in stderr note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg` - --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:747:5 - = note: this error originates in the derive macro `Diagnostic` which comes from the expansion of the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info) + --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC + = note: this error originates in the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied - --> $DIR/diagnostic-derive-doc-comment-field.rs:39:10 + --> $DIR/diagnostic-derive-doc-comment-field.rs:47:10 | LL | #[derive(Subdiagnostic)] - | ^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` + | ------------- required by a bound introduced by this call +... +LL | arg: NotIntoDiagnosticArg, + | ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` | - = help: the following other types implement trait `IntoDiagnosticArg`: - &'a T - &'a std::path::Path - &'a str - &rustc_target::spec::TargetTriple - Box<(dyn std::error::Error + 'static)> - CString - CguReuse - Cow<'a, str> - and 42 others + = help: normalized in stderr note: required by a bound in `Diagnostic::set_arg` --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:964:5 - = note: this error originates in the derive macro `Subdiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 80ab03ab24c86..39e34d73f9a43 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -339,12 +339,12 @@ struct ErrorWithDefaultLabelAttr<'a> { } #[derive(Diagnostic)] -//~^ ERROR the trait bound `Hello: IntoDiagnosticArg` is not satisfied #[diag(no_crate_example, code = "E0123")] struct ArgFieldWithoutSkip { #[primary_span] span: Span, other: Hello, + //~^ ERROR the trait bound `Hello: IntoDiagnosticArg` is not satisfied } #[derive(Diagnostic)] diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 5e1bea4e38cc3..801e4b5793cc1 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -641,15 +641,18 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Hello: IntoDiagnosticArg` is not satisfied - --> $DIR/diagnostic-derive.rs:341:10 + --> $DIR/diagnostic-derive.rs:346:12 | LL | #[derive(Diagnostic)] - | ^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello` + | ---------- required by a bound introduced by this call +... +LL | other: Hello, + | ^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello` | = help: normalized in stderr note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg` --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC - = note: this error originates in the derive macro `Diagnostic` which comes from the expansion of the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 84 previous errors From 6b76588222eb1a69f681c00f9d073edb43401ba7 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 3 May 2023 10:40:08 +0800 Subject: [PATCH 15/17] suggest struct when we get colon in fileds in enum --- compiler/rustc_parse/src/parser/item.rs | 9 +++++++++ tests/ui/structs-enums/issue-103869.fixed | 13 +++++++++++++ tests/ui/{parser => structs-enums}/issue-103869.rs | 11 ++++++++--- .../{parser => structs-enums}/issue-103869.stderr | 9 +++++++-- 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 tests/ui/structs-enums/issue-103869.fixed rename tests/ui/{parser => structs-enums}/issue-103869.rs (56%) rename tests/ui/{parser => structs-enums}/issue-103869.stderr (71%) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6ca88200dc51b..2a47711a8d3e6 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1262,6 +1262,7 @@ impl<'a> Parser<'a> { } } + let prev_span = self.prev_token.span; let id = self.parse_ident()?; let mut generics = self.parse_generics()?; generics.where_clause = self.parse_where_clause()?; @@ -1275,6 +1276,14 @@ impl<'a> Parser<'a> { self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant()).map_err( |mut e| { e.span_label(id.span, "while parsing this enum"); + if self.token == token::Colon { + e.span_suggestion_verbose( + prev_span, + "perhaps you meant to use `struct` here", + "struct".to_string(), + Applicability::MaybeIncorrect, + ); + } self.recover_stmt(); e }, diff --git a/tests/ui/structs-enums/issue-103869.fixed b/tests/ui/structs-enums/issue-103869.fixed new file mode 100644 index 0000000000000..49fe32c71091c --- /dev/null +++ b/tests/ui/structs-enums/issue-103869.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +struct VecOrMap { + //~^ HELP: perhaps you meant to use `struct` here + vec: Vec, + //~^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `:` + //~| HELP: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` +} + +fn main() { + let o = VecOrMap { vec: vec![1, 2, 3] }; + println!("{:?}", o.vec); +} diff --git a/tests/ui/parser/issue-103869.rs b/tests/ui/structs-enums/issue-103869.rs similarity index 56% rename from tests/ui/parser/issue-103869.rs rename to tests/ui/structs-enums/issue-103869.rs index 9213437458a59..729079e050115 100644 --- a/tests/ui/parser/issue-103869.rs +++ b/tests/ui/structs-enums/issue-103869.rs @@ -1,8 +1,13 @@ -enum VecOrMap{ +// run-rustfix + +enum VecOrMap { + //~^ HELP: perhaps you meant to use `struct` here vec: Vec, //~^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `:` //~| HELP: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` - map: HashMap } -fn main() {} +fn main() { + let o = VecOrMap { vec: vec![1, 2, 3] }; + println!("{:?}", o.vec); +} diff --git a/tests/ui/parser/issue-103869.stderr b/tests/ui/structs-enums/issue-103869.stderr similarity index 71% rename from tests/ui/parser/issue-103869.stderr rename to tests/ui/structs-enums/issue-103869.stderr index 9eb20e2005a4a..4665ebf89a3c9 100644 --- a/tests/ui/parser/issue-103869.stderr +++ b/tests/ui/structs-enums/issue-103869.stderr @@ -1,12 +1,17 @@ error: expected one of `(`, `,`, `=`, `{`, or `}`, found `:` - --> $DIR/issue-103869.rs:2:8 + --> $DIR/issue-103869.rs:5:8 | -LL | enum VecOrMap{ +LL | enum VecOrMap { | -------- while parsing this enum +LL | LL | vec: Vec, | ^ expected one of `(`, `,`, `=`, `{`, or `}` | = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` +help: perhaps you meant to use `struct` here + | +LL | struct VecOrMap { + | ~~~~~~ error: aborting due to previous error From 0e8703da3e33a76d12186e36a23c87870bc5d7bb Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 6 May 2023 02:27:32 +0800 Subject: [PATCH 16/17] make it more accurate by parsing ty --- compiler/rustc_parse/src/parser/item.rs | 28 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2a47711a8d3e6..49e05efd39d5c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1274,18 +1274,28 @@ impl<'a> Parser<'a> { (thin_vec![], false) } else { self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant()).map_err( - |mut e| { - e.span_label(id.span, "while parsing this enum"); + |mut err| { + err.span_label(id.span, "while parsing this enum"); if self.token == token::Colon { - e.span_suggestion_verbose( - prev_span, - "perhaps you meant to use `struct` here", - "struct".to_string(), - Applicability::MaybeIncorrect, - ); + let snapshot = self.create_snapshot_for_diagnostic(); + self.bump(); + match self.parse_ty() { + Ok(_) => { + err.span_suggestion_verbose( + prev_span, + "perhaps you meant to use `struct` here", + "struct".to_string(), + Applicability::MaybeIncorrect, + ); + } + Err(e) => { + e.cancel(); + } + } + self.restore_snapshot(snapshot); } self.recover_stmt(); - e + err }, )? }; From 3bbb69eaa02f5dfa6bcf23e667ec0614c64d3502 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 8 May 2023 16:04:20 +0200 Subject: [PATCH 17/17] Fix miscompilation when adding default method to `Future` --- compiler/rustc_ty_utils/src/instance.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index ec577072e196f..eb3c21163ab2a 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -194,21 +194,18 @@ fn resolve_associated_item<'tcx>( }) } traits::ImplSource::Future(future_data) => { - if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::poll { - // For compiler developers who'd like to add new items to `Future`, - // you either need to generate a shim body, or perhaps return - // `InstanceDef::Item` pointing to a trait default method body if - // it is given a default implementation by the trait. - span_bug!( - tcx.def_span(future_data.generator_def_id), - "no definition for `{trait_ref}::{}` for built-in async generator type", - tcx.item_name(trait_item_id) - ) + if Some(trait_item_id) == tcx.lang_items().future_poll_fn() { + // `Future::poll` is generated by the compiler. + Some(Instance { + def: ty::InstanceDef::Item(future_data.generator_def_id), + substs: future_data.substs, + }) + } else { + // All other methods are default methods of the `Future` trait. + // (this assumes that `ImplSource::Future` is only used for methods on `Future`) + debug_assert!(tcx.impl_defaultness(trait_item_id).has_value()); + Some(Instance::new(trait_item_id, rcvr_substs)) } - Some(Instance { - def: ty::InstanceDef::Item(future_data.generator_def_id), - substs: future_data.substs, - }) } traits::ImplSource::Closure(closure_data) => { if cfg!(debug_assertions)