Auto merge of #153217 - JonathanBrouwer:rollup-iXVG70B, r=JonathanBrouwer

Rollup of 12 pull requests

Successful merges:

 - rust-lang/rust#153211 (`rust-analyzer` subtree update)
 - rust-lang/rust#149027 (Improve cross-crate trait impl param mismatch suggestions )
 - rust-lang/rust#152730 (add field representing types)
 - rust-lang/rust#153136 (Correctly handle `#[doc(alias = "...")]` attribute on inlined reexports)
 - rust-lang/rust#152165 (Normalize capture place `ty`s to prevent ICE)
 - rust-lang/rust#152615 (refactor 'valid for read/write' definition: exclude null)
 - rust-lang/rust#153109 (Fix LegacyKeyValueFormat report from docker build: aarch64-gnu-debug)
 - rust-lang/rust#153172 (Fix comment about placeholders)
 - rust-lang/rust#153187 (Fix ICE when macro-expanded extern crate shadows std)
 - rust-lang/rust#153190 (Don't allow subdiagnostic to use variables from their parent)
 - rust-lang/rust#153200 (Remove redundant clone)
 - rust-lang/rust#153216 (mark two polonius tests as known-bug)
This commit is contained in:
bors
2026-02-28 12:23:16 +00:00
164 changed files with 3298 additions and 335 deletions
+1
View File
@@ -4706,6 +4706,7 @@ dependencies = [
"ena",
"indexmap",
"rustc-hash 2.1.1",
"rustc_abi",
"rustc_ast_ir",
"rustc_data_structures",
"rustc_error_messages",
+58 -1
View File
@@ -21,7 +21,64 @@
mod ty;
#[cfg(feature = "nightly")]
pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
pub use ty::{Layout, TyAbiInterface, TyAndLayout};
rustc_index::newtype_index! {
/// The *source-order* index of a field in a variant.
///
/// This is how most code after type checking refers to fields, rather than
/// using names (as names have hygiene complications and more complex lookup).
///
/// Particularly for `repr(Rust)` types, this may not be the same as *layout* order.
/// (It is for `repr(C)` `struct`s, however.)
///
/// For example, in the following types,
/// ```rust
/// # enum Never {}
/// # #[repr(u16)]
/// enum Demo1 {
/// Variant0 { a: Never, b: i32 } = 100,
/// Variant1 { c: u8, d: u64 } = 10,
/// }
/// struct Demo2 { e: u8, f: u16, g: u8 }
/// ```
/// `b` is `FieldIdx(1)` in `VariantIdx(0)`,
/// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and
/// `f` is `FieldIdx(1)` in `VariantIdx(0)`.
#[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))]
#[encodable]
#[orderable]
#[gate_rustc_only]
pub struct FieldIdx {}
}
impl FieldIdx {
/// The second field, at index 1.
///
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
}
rustc_index::newtype_index! {
/// The *source-order* index of a variant in a type.
///
/// For enums, these are always `0..variant_count`, regardless of any
/// custom discriminants that may have been defined, and including any
/// variants that may end up uninhabited due to field types. (Some of the
/// variants may not be present in a monomorphized ABI [`Variants`], but
/// those skipped variants are always counted when determining the *index*.)
///
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant
/// with variant index zero, aka [`FIRST_VARIANT`].
#[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))]
#[encodable]
#[orderable]
#[gate_rustc_only]
pub struct VariantIdx {
/// Equivalent to `VariantIdx(0)`.
const FIRST_VARIANT = 0;
}
}
// A variant is absent if it's uninhabited and only has ZST fields.
// Present uninhabited variants only require space for their fields,
+1 -54
View File
@@ -4,6 +4,7 @@
use rustc_data_structures::intern::Interned;
use rustc_macros::HashStable_Generic;
use crate::layout::{FieldIdx, VariantIdx};
use crate::{
AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche,
PointeeInfo, Primitive, Size, Variants,
@@ -11,60 +12,6 @@
// Explicitly import `Float` to avoid ambiguity with `Primitive::Float`.
rustc_index::newtype_index! {
/// The *source-order* index of a field in a variant.
///
/// This is how most code after type checking refers to fields, rather than
/// using names (as names have hygiene complications and more complex lookup).
///
/// Particularly for `repr(Rust)` types, this may not be the same as *layout* order.
/// (It is for `repr(C)` `struct`s, however.)
///
/// For example, in the following types,
/// ```rust
/// # enum Never {}
/// # #[repr(u16)]
/// enum Demo1 {
/// Variant0 { a: Never, b: i32 } = 100,
/// Variant1 { c: u8, d: u64 } = 10,
/// }
/// struct Demo2 { e: u8, f: u16, g: u8 }
/// ```
/// `b` is `FieldIdx(1)` in `VariantIdx(0)`,
/// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and
/// `f` is `FieldIdx(1)` in `VariantIdx(0)`.
#[derive(HashStable_Generic)]
#[encodable]
#[orderable]
pub struct FieldIdx {}
}
impl FieldIdx {
/// The second field, at index 1.
///
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
}
rustc_index::newtype_index! {
/// The *source-order* index of a variant in a type.
///
/// For enums, these are always `0..variant_count`, regardless of any
/// custom discriminants that may have been defined, and including any
/// variants that may end up uninhabited due to field types. (Some of the
/// variants may not be present in a monomorphized ABI [`Variants`], but
/// those skipped variants are always counted when determining the *index*.)
///
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant
/// with variant index zero, aka [`FIRST_VARIANT`].
#[derive(HashStable_Generic)]
#[encodable]
#[orderable]
pub struct VariantIdx {
/// Equivalent to `VariantIdx(0)`.
const FIRST_VARIANT = 0;
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
#[rustc_pass_by_value]
pub struct Layout<'a>(pub Interned<'a, LayoutData<FieldIdx, VariantIdx>>);
+2 -2
View File
@@ -64,9 +64,9 @@
#[cfg(feature = "nightly")]
pub use extern_abi::CVariadicStatus;
pub use extern_abi::{ExternAbi, all_names};
pub use layout::{FIRST_VARIANT, FieldIdx, LayoutCalculator, LayoutCalculatorError, VariantIdx};
#[cfg(feature = "nightly")]
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
pub use layout::{LayoutCalculator, LayoutCalculatorError};
pub use layout::{Layout, TyAbiInterface, TyAndLayout};
/// Requirements for a `StableHashingContext` to be used in this crate.
/// This is a hack to allow using the `HashStable_Generic` derive macro
+5
View File
@@ -2553,6 +2553,11 @@ pub enum TyKind {
/// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero<u32>`,
/// just as part of the type system.
Pat(Box<Ty>, Box<TyPat>),
/// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`).
///
/// Usually not written directly in user code but indirectly via the macro
/// `core::field::field_of!(...)`.
FieldOf(Box<Ty>, Option<Ident>, Ident),
/// Sometimes we need a dummy value when no error has occurred.
Dummy,
/// Placeholder for a kind that has failed to be defined.
+1
View File
@@ -298,6 +298,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
| ast::TyKind::ImplicitSelf
| ast::TyKind::CVarArgs
| ast::TyKind::Pat(..)
| ast::TyKind::FieldOf(..)
| ast::TyKind::Dummy
| ast::TyKind::Err(..) => break None,
}
+7
View File
@@ -1496,6 +1496,13 @@ fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
TyKind::Pat(ty, pat) => {
hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span))
}
TyKind::FieldOf(ty, variant, field) => hir::TyKind::FieldOf(
self.lower_ty_alloc(ty, itctx),
self.arena.alloc(hir::TyFieldPath {
variant: variant.map(|variant| self.lower_ident(variant)),
field: self.lower_ident(*field),
}),
),
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
}
@@ -1377,6 +1377,23 @@ pub fn print_type(&mut self, ty: &ast::Ty) {
self.word(" is ");
self.print_ty_pat(pat);
}
ast::TyKind::FieldOf(ty, variant, field) => {
self.word("builtin # field_of");
self.popen();
let ib = self.ibox(0);
self.print_type(ty);
self.word(",");
self.space();
if let Some(variant) = variant {
self.print_ident(*variant);
self.word(".");
}
self.print_ident(*field);
self.end(ib);
self.pclose();
}
}
self.end(ib);
}
@@ -12,8 +12,8 @@
use rustc_middle::mir::interpret::ReportedErrorInfo;
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_middle::ty::{self, FieldInfo, Ty, TyCtxt};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::{Span, Symbol, sym};
use rustc_target::callconv::FnAbi;
use tracing::debug;
@@ -23,8 +23,9 @@
use crate::interpret::{
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
throw_ub_custom, throw_unsup, throw_unsup_format, type_implements_dyn_trait,
compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust,
throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
type_implements_dyn_trait,
};
/// When hitting this many interpreted terminators we emit a deny by default lint
@@ -619,6 +620,27 @@ fn call_intrinsic(
ecx.write_type_info(ty, dest)?;
}
sym::field_offset => {
let frt_ty = instance.args.type_at(0);
ensure_monomorphic_enough(ecx.tcx.tcx, frt_ty)?;
let (ty, variant, field) = if let ty::Adt(def, args) = frt_ty.kind()
&& let Some(FieldInfo { base, variant_idx, field_idx, .. }) =
def.field_representing_type_info(ecx.tcx.tcx, args)
{
(base, variant_idx, field_idx)
} else {
span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}")
};
let layout = ecx.layout_of(ty)?;
let cx = ty::layout::LayoutCx::new(ecx.tcx.tcx, ecx.typing_env());
let layout = layout.for_variant(&cx, variant);
let offset = layout.fields.offset(field.index()).bytes();
ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?;
}
_ => {
// We haven't handled the intrinsic, let's see if we can use a fallback body.
if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
@@ -38,6 +38,8 @@
pub use self::projection::{OffsetMode, Projectable};
pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation};
pub use self::util::EnteredTraceSpan;
pub(crate) use self::util::{create_static_alloc, type_implements_dyn_trait};
pub(crate) use self::util::{
create_static_alloc, ensure_monomorphic_enough, type_implements_dyn_trait,
};
pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking};
pub use self::visitor::ValueVisitor;
+4
View File
@@ -221,6 +221,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
(internal, custom_mir, "1.65.0", None),
/// Implementation details of externally implementable items
(internal, eii_internals, "1.94.0", None),
/// Implementation details of field representing types.
(internal, field_representing_type_raw, "CURRENT_RUSTC_VERSION", None),
/// Outputs useful `assert!` messages
(unstable, generic_assert, "1.63.0", None),
/// Allows using the #[rustc_intrinsic] attribute.
@@ -486,6 +488,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
(unstable, ffi_const, "1.45.0", Some(58328)),
/// Allows the use of `#[ffi_pure]` on foreign functions.
(unstable, ffi_pure, "1.45.0", Some(58329)),
/// Experimental field projections.
(incomplete, field_projections, "CURRENT_RUSTC_VERSION", Some(145383)),
/// Allows marking trait functions as `final` to prevent overriding impls
(unstable, final_associated_functions, "CURRENT_RUSTC_VERSION", Some(131179)),
/// Controlling the behavior of fmt::Debug
+10
View File
@@ -1741,6 +1741,12 @@ pub fn innermost_block(&self) -> &Block<'hir> {
}
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct TyFieldPath {
pub variant: Option<Ident>,
pub field: Ident,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct TyPat<'hir> {
#[stable_hasher(ignore)]
@@ -3804,6 +3810,10 @@ pub enum TyKind<'hir, Unambig = ()> {
Err(rustc_span::ErrorGuaranteed),
/// Pattern types (`pattern_type!(u32 is 1..)`)
Pat(&'hir Ty<'hir>, &'hir TyPat<'hir>),
/// Field representing type (`field_of!(Struct, field)`).
///
/// The optional ident is the variant when an enum is passed `field_of!(Enum, Variant.field)`.
FieldOf(&'hir Ty<'hir>, &'hir TyFieldPath),
/// `TyKind::Infer` means the type should be inferred instead of it having been
/// specified. This can appear anywhere in a type.
///
+5
View File
@@ -1047,6 +1047,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -
try_visit!(visitor.visit_ty_unambig(ty));
try_visit!(visitor.visit_pattern_type_pattern(pat));
}
TyKind::FieldOf(ty, TyFieldPath { variant, field }) => {
try_visit!(visitor.visit_ty_unambig(ty));
visit_opt!(visitor, visit_ident, *variant);
try_visit!(visitor.visit_ident(*field));
}
}
V::Result::output()
}
+7
View File
@@ -435,6 +435,13 @@ fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
// Reborrowing related lang-items
Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0);
CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0);
// Field representing types.
FieldRepresentingType, sym::field_representing_type, field_representing_type, Target::Struct, GenericRequirement::Exact(3);
Field, sym::field, field, Target::Trait, GenericRequirement::Exact(0);
FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0);
FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0);
FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0);
}
/// The requirement imposed on the generics of a lang item
@@ -1,5 +1,6 @@
use core::ops::ControlFlow;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::iter;
use hir::def_id::{DefId, DefIdMap, LocalDefId};
@@ -18,7 +19,7 @@
Upcast,
};
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Span};
use rustc_span::{BytePos, DUMMY_SP, Span};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
@@ -1795,6 +1796,105 @@ fn compare_number_of_method_arguments<'tcx>(
),
);
// Only emit verbose suggestions when the trait span isnt local (e.g., cross-crate).
if !trait_m.def_id.is_local() {
let trait_sig = tcx.fn_sig(trait_m.def_id);
let trait_arg_idents = tcx.fn_arg_idents(trait_m.def_id);
let sm = tcx.sess.source_map();
// Find the span of the space between the parentheses in a method.
// fn foo(...) {}
// ^^^
let impl_inputs_span = if let (Some(first), Some(last)) =
(impl_m_sig.decl.inputs.first(), impl_m_sig.decl.inputs.last())
{
// We have inputs; construct the span from those.
// fn foo( a: i32, b: u32 ) {}
// ^^^^^^^^^^^^^^^^
let arg_idents = tcx.fn_arg_idents(impl_m.def_id);
let first_lo = arg_idents
.get(0)
.and_then(|id| id.map(|id| id.span.lo()))
.unwrap_or(first.span.lo());
Some(impl_m_sig.span.with_lo(first_lo).with_hi(last.span.hi()))
} else {
// We have no inputs; construct the span to the left of the last parenthesis
// fn foo( ) {}
// ^
// FIXME: Keep spans for function parentheses around to make this more robust.
sm.span_to_snippet(impl_m_sig.span).ok().and_then(|s| {
let right_paren = s.as_bytes().iter().rposition(|&b| b == b')')?;
let pos = impl_m_sig.span.lo() + BytePos(right_paren as u32);
Some(impl_m_sig.span.with_lo(pos).with_hi(pos))
})
};
let suggestion = match trait_number_args.cmp(&impl_number_args) {
Ordering::Greater => {
// Span is right before the end parenthesis:
// fn foo(a: i32 ) {}
// ^
let trait_inputs = trait_sig.skip_binder().inputs().skip_binder();
let missing = trait_inputs
.iter()
.enumerate()
.skip(impl_number_args)
.map(|(idx, ty)| {
let name = trait_arg_idents
.get(idx)
.and_then(|ident| *ident)
.map(|ident| ident.to_string())
.unwrap_or_else(|| "_".to_string());
format!("{name}: {ty}")
})
.collect::<Vec<_>>();
if missing.is_empty() {
None
} else {
impl_inputs_span.map(|s| {
let span = s.shrink_to_hi();
let prefix = if impl_number_args == 0 { "" } else { ", " };
let replacement = format!("{prefix}{}", missing.join(", "));
(
span,
format!(
"add the missing parameter{} from the trait",
pluralize!(trait_number_args - impl_number_args)
),
replacement,
)
})
}
}
Ordering::Less => impl_inputs_span.and_then(|full| {
// Span of the arguments that there are too many of:
// fn foo(a: i32, b: u32) {}
// ^^^^^^^^
let lo = if trait_number_args == 0 {
full.lo()
} else {
impl_m_sig
.decl
.inputs
.get(trait_number_args - 1)
.map(|arg| arg.span.hi())?
};
let span = full.with_lo(lo);
Some((
span,
format!(
"remove the extra parameter{} to match the trait",
pluralize!(impl_number_args - trait_number_args)
),
String::new(),
))
}),
Ordering::Equal => unreachable!(),
};
if let Some((span, msg, replacement)) = suggestion {
err.span_suggestion_verbose(span, msg, replacement, Applicability::MaybeIncorrect);
}
}
return Err(err.emit_unless_delay(delay));
}
@@ -117,6 +117,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::fabsf128
| sym::fadd_algebraic
| sym::fdiv_algebraic
| sym::field_offset
| sym::floorf16
| sym::floorf32
| sym::floorf64
@@ -297,6 +298,7 @@ pub(crate) fn check_intrinsic_type(
(1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize)
}
sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize),
sym::field_offset => (1, 0, vec![], tcx.types.usize),
sym::rustc_peek => (1, 0, vec![param(0)], param(0)),
sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()),
sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => {
+12
View File
@@ -88,6 +88,8 @@ pub(crate) enum AssocItemNotFoundLabel<'a> {
NotFound {
#[primary_span]
span: Span,
assoc_ident: Ident,
assoc_kind: &'static str,
},
#[label(
"there is {$identically_named ->
@@ -149,6 +151,7 @@ pub(crate) enum AssocItemNotFoundSugg<'a> {
trait_ref: String,
suggested_name: Symbol,
identically_named: bool,
assoc_kind: &'static str,
#[applicability]
applicability: Applicability,
},
@@ -1446,6 +1449,15 @@ pub struct NoVariantNamed<'tcx> {
pub ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag("no field `{$field}` on type `{$ty}`", code = E0609)]
pub struct NoFieldOnType<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub field: Ident,
}
// FIXME(fmease): Deduplicate:
#[derive(Diagnostic)]
@@ -141,7 +141,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
);
}
let assoc_kind_str = assoc_tag_str(assoc_tag);
let assoc_kind = assoc_tag_str(assoc_tag);
let qself_str = qself.to_string(tcx);
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
@@ -151,7 +151,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
let mut err = errors::AssocItemNotFound {
span: if is_dummy { span } else { assoc_ident.span },
assoc_ident,
assoc_kind: assoc_kind_str,
assoc_kind,
qself: &qself_str,
label: None,
sugg: None,
@@ -161,7 +161,8 @@ pub(super) fn report_unresolved_assoc_item<I>(
};
if is_dummy {
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span });
err.label =
Some(errors::AssocItemNotFoundLabel::NotFound { span, assoc_ident, assoc_kind });
return self.dcx().emit_err(err);
}
@@ -181,7 +182,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
{
err.sugg = Some(errors::AssocItemNotFoundSugg::Similar {
span: assoc_ident.span,
assoc_kind: assoc_kind_str,
assoc_kind,
suggested_name,
});
return self.dcx().emit_err(err);
@@ -224,7 +225,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
let trait_name = tcx.def_path_str(best_trait);
err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait {
span: assoc_ident.span,
assoc_kind: assoc_kind_str,
assoc_kind,
trait_name: &trait_name,
suggested_name,
identically_named: suggested_name == assoc_ident.name,
@@ -256,7 +257,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
span: assoc_ident.span,
trait_name: &trait_name,
assoc_kind: assoc_kind_str,
assoc_kind,
suggested_name,
});
return self.dcx().emit_err(err);
@@ -286,6 +287,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
trait_ref,
identically_named,
suggested_name,
assoc_kind,
applicability,
});
} else {
@@ -322,11 +324,15 @@ pub(super) fn report_unresolved_assoc_item<I>(
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
span: assoc_ident.span,
qself: &qself_str,
assoc_kind: assoc_kind_str,
assoc_kind,
suggested_name: *candidate_name,
});
} else {
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_ident.span });
err.label = Some(errors::AssocItemNotFoundLabel::NotFound {
span: assoc_ident.span,
assoc_ident,
assoc_kind,
});
}
self.dcx().emit_err(err)
@@ -21,6 +21,7 @@
use std::slice;
use rustc_abi::FIRST_VARIANT;
use rustc_ast::LitKind;
use rustc_data_structures::assert_matches;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
@@ -50,11 +51,11 @@
use tracing::{debug, instrument};
use crate::check::check_abi;
use crate::check_c_variadic_abi;
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoFieldOnType};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
use crate::{NoVariantNamed, check_c_variadic_abi};
/// The context in which an implied bound is being added to a item being lowered (i.e. a sizedness
/// trait or a default trait)
@@ -3050,6 +3051,14 @@ pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
self.record_ty(pat.hir_id, ty, pat.span);
pat_ty
}
hir::TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => self.lower_field_of(
self.lower_ty(ty),
self.item_def_id(),
ty.span,
hir_ty.hir_id,
*variant,
*field,
),
hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar),
};
@@ -3091,6 +3100,159 @@ fn lower_pat_ty_pat(
}
}
fn lower_field_of(
&self,
ty: Ty<'tcx>,
item_def_id: LocalDefId,
ty_span: Span,
hir_id: HirId,
variant: Option<Ident>,
field: Ident,
) -> Ty<'tcx> {
let dcx = self.dcx();
let tcx = self.tcx();
match ty.kind() {
ty::Adt(def, _) => {
let base_did = def.did();
let kind_name = tcx.def_descr(base_did);
let (variant_idx, variant) = if def.is_enum() {
let Some(variant) = variant else {
let err = dcx
.create_err(NoVariantNamed { span: field.span, ident: field, ty })
.with_span_help(
field.span.shrink_to_lo(),
"you might be missing a variant here: `Variant.`",
)
.emit();
return Ty::new_error(tcx, err);
};
if let Some(res) = def
.variants()
.iter_enumerated()
.find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == variant)
{
res
} else {
let err = dcx
.create_err(NoVariantNamed { span: variant.span, ident: variant, ty })
.emit();
return Ty::new_error(tcx, err);
}
} else {
if let Some(variant) = variant {
let adt_path = tcx.def_path_str(base_did);
struct_span_code_err!(
dcx,
variant.span,
E0609,
"{kind_name} `{adt_path}` does not have any variants",
)
.with_span_label(variant.span, "variant unknown")
.emit();
}
(FIRST_VARIANT, def.non_enum_variant())
};
let block = tcx.local_def_id_to_hir_id(item_def_id);
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(field, def.did(), block);
if let Some((field_idx, field)) = variant
.fields
.iter_enumerated()
.find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == ident)
{
if field.vis.is_accessible_from(def_scope, tcx) {
tcx.check_stability(field.did, Some(hir_id), ident.span, None);
} else {
let adt_path = tcx.def_path_str(base_did);
struct_span_code_err!(
dcx,
ident.span,
E0616,
"field `{ident}` of {kind_name} `{adt_path}` is private",
)
.with_span_label(ident.span, "private field")
.emit();
}
Ty::new_field_representing_type(tcx, ty, variant_idx, field_idx)
} else {
let err =
dcx.create_err(NoFieldOnType { span: ident.span, field: ident, ty }).emit();
Ty::new_error(tcx, err)
}
}
ty::Tuple(tys) => {
let index = match field.as_str().parse::<usize>() {
Ok(idx) => idx,
Err(_) => {
let err =
dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit();
return Ty::new_error(tcx, err);
}
};
if field.name != sym::integer(index) {
bug!("we parsed above, but now not equal?");
}
if tys.get(index).is_some() {
Ty::new_field_representing_type(tcx, ty, FIRST_VARIANT, index.into())
} else {
let err = dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit();
Ty::new_error(tcx, err)
}
}
// FIXME(FRTs): support type aliases
/*
ty::Alias(AliasTyKind::Free, ty) => {
return self.lower_field_of(
ty,
item_def_id,
ty_span,
hir_id,
variant,
field,
);
}*/
ty::Alias(..) => Ty::new_error(
tcx,
dcx.span_err(ty_span, format!("could not resolve fields of `{ty}`")),
),
ty::Error(err) => Ty::new_error(tcx, *err),
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Foreign(_)
| ty::Str
| ty::RawPtr(_, _)
| ty::Ref(_, _, _)
| ty::FnDef(_, _)
| ty::FnPtr(_, _)
| ty::UnsafeBinder(_)
| ty::Dynamic(_, _)
| ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(_, _)
| ty::Never
| ty::Param(_)
| ty::Bound(_, _)
| ty::Placeholder(_)
| ty::Slice(..) => Ty::new_error(
tcx,
dcx.span_err(ty_span, format!("type `{ty}` doesn't have fields")),
),
ty::Infer(_) => Ty::new_error(
tcx,
dcx.span_err(ty_span, format!("cannot use `{ty}` in this position")),
),
// FIXME(FRTs): support these types?
ty::Array(..) | ty::Pat(..) => Ty::new_error(
tcx,
dcx.span_err(ty_span, format!("type `{ty}` is not yet supported in `field_of!`")),
),
}
}
/// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR.
#[instrument(level = "debug", skip(self), ret)]
fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: Option<LocalDefId>) -> Ty<'tcx> {
+12 -1
View File
@@ -19,7 +19,7 @@
use rustc_hir::{
BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg, GenericBound,
GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind,
PreciseCapturingArg, RangeEnd, Term, TyPatKind,
PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind,
};
use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym};
@@ -464,6 +464,17 @@ fn print_type(&mut self, ty: &hir::Ty<'_>) {
self.word(" is ");
self.print_ty_pat(pat);
}
hir::TyKind::FieldOf(ty, TyFieldPath { variant, field }) => {
self.word("field_of!(");
self.print_type(ty);
self.word(", ");
if let Some(variant) = *variant {
self.print_ident(variant);
self.word(".");
}
self.print_ident(*field);
self.word(")");
}
}
self.end(ib)
}
-9
View File
@@ -490,15 +490,6 @@ pub(crate) fn new() -> Self {
}
}
#[derive(Diagnostic)]
#[diag("no field `{$field}` on type `{$ty}`", code = E0609)]
pub(crate) struct NoFieldOnType<'tcx> {
#[primary_span]
pub(crate) span: Span,
pub(crate) ty: Ty<'tcx>,
pub(crate) field: Ident,
}
#[derive(Diagnostic)]
#[diag("no field named `{$field}` on enum variant `{$container}::{$ident}`", code = E0609)]
pub(crate) struct NoFieldOnVariant<'tcx> {
+2 -1
View File
@@ -20,6 +20,7 @@
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal};
use rustc_hir_analysis::NoVariantNamed;
use rustc_hir_analysis::errors::NoFieldOnType;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _;
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin};
use rustc_infer::traits::query::NoSolution;
@@ -43,7 +44,7 @@
use crate::errors::{
AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer,
FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType,
FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn,
NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
};
+79 -23
View File
@@ -49,7 +49,9 @@
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::solve;
use tracing::{debug, instrument};
use super::FnCtxt;
@@ -196,17 +198,17 @@ fn analyze_closure(
let closure_def_id = closure_def_id.expect_local();
assert_eq!(self.tcx.hir_body_owner_def_id(body.id()), closure_def_id);
let closure_fcx = FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id);
let mut delegate = InferBorrowKind {
fcx: &closure_fcx,
closure_def_id,
capture_information: Default::default(),
fake_reads: Default::default(),
};
let _ = euv::ExprUseVisitor::new(
&FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id),
&mut delegate,
)
.consume_body(body);
let _ = euv::ExprUseVisitor::new(&closure_fcx, &mut delegate).consume_body(body);
// There are several curious situations with coroutine-closures where
// analysis is too aggressive with borrows when the coroutine-closure is
@@ -286,7 +288,7 @@ fn analyze_closure(
let hir::def::Res::Local(local_id) = path.res else {
bug!();
};
let place = self.place_for_root_variable(closure_def_id, local_id);
let place = closure_fcx.place_for_root_variable(closure_def_id, local_id);
delegate.capture_information.push((
place,
ty::CaptureInfo {
@@ -325,7 +327,7 @@ fn analyze_closure(
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
for var_hir_id in upvars.keys() {
let place = self.place_for_root_variable(closure_def_id, *var_hir_id);
let place = closure_fcx.place_for_root_variable(closure_def_id, *var_hir_id);
debug!("seed place {:?}", place);
@@ -559,17 +561,17 @@ fn coroutine_body_consumes_upvars(
bug!();
};
let coroutine_fcx =
FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id);
let mut delegate = InferBorrowKind {
fcx: &coroutine_fcx,
closure_def_id: coroutine_def_id,
capture_information: Default::default(),
fake_reads: Default::default(),
};
let _ = euv::ExprUseVisitor::new(
&FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id),
&mut delegate,
)
.consume_expr(body);
let _ = euv::ExprUseVisitor::new(&coroutine_fcx, &mut delegate).consume_expr(body);
let (_, kind, _) = self.process_collected_capture_information(
hir::CaptureBy::Ref,
@@ -1125,6 +1127,45 @@ fn perform_2229_migration_analysis(
);
}
}
fn normalize_capture_place(&self, span: Span, place: Place<'tcx>) -> Place<'tcx> {
let mut place = self.resolve_vars_if_possible(place);
// In the new solver, types in HIR `Place`s can contain unnormalized aliases,
// which can ICE later (e.g. when projecting fields for diagnostics).
if self.next_trait_solver() {
let cause = self.misc(span);
let at = self.at(&cause, self.param_env);
match solve::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
at,
place.clone(),
vec![],
) {
Ok((normalized, goals)) => {
if !goals.is_empty() {
let mut typeck_results = self.typeck_results.borrow_mut();
typeck_results.coroutine_stalled_predicates.extend(
goals
.into_iter()
// FIXME: throwing away the param-env :(
.map(|goal| (goal.predicate, self.misc(span))),
);
}
normalized
}
Err(errors) => {
let guar = self.infcx.err_ctxt().report_fulfillment_errors(errors);
place.base_ty = Ty::new_error(self.tcx, guar);
for proj in &mut place.projections {
proj.ty = Ty::new_error(self.tcx, guar);
}
place
}
}
} else {
// For the old solver we can rely on `normalize` to eagerly normalize aliases.
self.normalize(span, place)
}
}
/// Combines all the reasons for 2229 migrations
fn compute_2229_migrations_reasons(
@@ -1734,11 +1775,15 @@ fn place_for_root_variable(
) -> Place<'tcx> {
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id);
Place {
let place = Place {
base_ty: self.node_ty(var_hir_id),
base: PlaceBase::Upvar(upvar_id),
projections: Default::default(),
}
};
// Normalize eagerly when inserting into `capture_information`, so all downstream
// capture analysis can assume a normalized `Place`.
self.normalize_capture_place(self.tcx.hir_span(var_hir_id), place)
}
fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool {
@@ -1994,7 +2039,8 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span {
tcx.sess.source_map().end_point(owner_span)
}
struct InferBorrowKind<'tcx> {
struct InferBorrowKind<'fcx, 'a, 'tcx> {
fcx: &'fcx FnCtxt<'a, 'tcx>,
// The def-id of the closure whose kind and upvar accesses are being inferred.
closure_def_id: LocalDefId,
@@ -2028,7 +2074,7 @@ struct InferBorrowKind<'tcx> {
fake_reads: Vec<(Place<'tcx>, FakeReadCause, HirId)>,
}
impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
impl<'fcx, 'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'fcx, 'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn fake_read(
&mut self,
@@ -2042,8 +2088,10 @@ fn fake_read(
// such as deref of a raw pointer.
let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable);
let (place, _) =
restrict_capture_precision(place_with_id.place.clone(), dummy_capture_kind);
let span = self.fcx.tcx.hir_span(diag_expr_id);
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
let (place, _) = restrict_capture_precision(place, dummy_capture_kind);
let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind);
self.fake_reads.push((place, cause, diag_expr_id));
@@ -2054,8 +2102,11 @@ fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId)
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
assert_eq!(self.closure_def_id, upvar_id.closure_expr_id);
let span = self.fcx.tcx.hir_span(diag_expr_id);
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
self.capture_information.push((
place_with_id.place.clone(),
place,
ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
@@ -2069,8 +2120,11 @@ fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: Hir
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
assert_eq!(self.closure_def_id, upvar_id.closure_expr_id);
let span = self.fcx.tcx.hir_span(diag_expr_id);
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
self.capture_information.push((
place_with_id.place.clone(),
place,
ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
@@ -2092,14 +2146,16 @@ fn borrow(
// The region here will get discarded/ignored
let capture_kind = ty::UpvarCapture::ByRef(bk);
let span = self.fcx.tcx.hir_span(diag_expr_id);
let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone());
// We only want repr packed restriction to be applied to reading references into a packed
// struct, and not when the data is being moved. Therefore we call this method here instead
// of in `restrict_capture_precision`.
let (place, mut capture_kind) =
restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind);
let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture(place, capture_kind);
// Raw pointers don't inherit mutability
if place_with_id.place.deref_tys().any(Ty::is_raw_ptr) {
if place.deref_tys().any(Ty::is_raw_ptr) {
capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable);
}
+3 -2
View File
@@ -2320,8 +2320,9 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
if features.incomplete(name) {
let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
.map(|n| BuiltinFeatureIssueNote { n });
let help =
HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp);
let help = HAS_MIN_FEATURES
.contains(&name)
.then_some(BuiltinIncompleteFeaturesHelp { name });
cx.emit_span_lint(
INCOMPLETE_FEATURES,
@@ -87,7 +87,10 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
ty::AssocTag::Type,
item.owner_id.to_def_id(),
)
.map(|label| SupertraitAsDerefTargetLabel { label: tcx.def_span(label.def_id) });
.map(|label| SupertraitAsDerefTargetLabel {
label: tcx.def_span(label.def_id),
self_ty,
});
let span = tcx.def_span(item.owner_id.def_id);
cx.emit_span_lint(
DEREF_INTO_DYN_SUPERTRAIT,
+5 -7
View File
@@ -105,13 +105,11 @@ pub fn decorate_builtin_lint(
BuiltinLintDiag::RedundantImport(spans, ident) => {
let subs = spans
.into_iter()
.map(|(span, is_imported)| {
(match (span.is_dummy(), is_imported) {
(false, true) => lints::RedundantImportSub::ImportedHere,
(false, false) => lints::RedundantImportSub::DefinedHere,
(true, true) => lints::RedundantImportSub::ImportedPrelude,
(true, false) => lints::RedundantImportSub::DefinedPrelude,
})(span)
.map(|(span, is_imported)| match (span.is_dummy(), is_imported) {
(false, true) => lints::RedundantImportSub::ImportedHere { span, ident },
(false, false) => lints::RedundantImportSub::DefinedHere { span, ident },
(true, true) => lints::RedundantImportSub::ImportedPrelude { span, ident },
(true, false) => lints::RedundantImportSub::DefinedPrelude { span, ident },
})
.collect();
lints::RedundantImport { subs, ident }.decorate_lint(diag);
@@ -105,9 +105,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
Some(ConstItemInteriorMutationsSuggestionStatic::Spanful {
const_: const_item.vis_span.between(ident.span),
before: if !vis_span.is_empty() { " " } else { "" },
const_name,
})
} else {
Some(ConstItemInteriorMutationsSuggestionStatic::Spanless)
Some(ConstItemInteriorMutationsSuggestionStatic::Spanless { const_name })
}
} else {
None
+33 -16
View File
@@ -532,7 +532,9 @@ pub(crate) struct BuiltinInternalFeatures {
#[derive(Subdiagnostic)]
#[help("consider using `min_{$name}` instead, which is more stable and complete")]
pub(crate) struct BuiltinIncompleteFeaturesHelp;
pub(crate) struct BuiltinIncompleteFeaturesHelp {
pub name: Symbol,
}
#[derive(Subdiagnostic)]
#[note("see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information")]
@@ -663,14 +665,15 @@ pub(crate) struct SupertraitAsDerefTarget<'a> {
)]
pub label: Span,
#[subdiagnostic]
pub label2: Option<SupertraitAsDerefTargetLabel>,
pub label2: Option<SupertraitAsDerefTargetLabel<'a>>,
}
#[derive(Subdiagnostic)]
#[label("target type is a supertrait of `{$self_ty}`")]
pub(crate) struct SupertraitAsDerefTargetLabel {
pub(crate) struct SupertraitAsDerefTargetLabel<'a> {
#[primary_span]
pub label: Span,
pub self_ty: Ty<'a>,
}
// enum_intrinsics_non_enums.rs
@@ -958,9 +961,10 @@ pub(crate) enum ConstItemInteriorMutationsSuggestionStatic {
#[primary_span]
const_: Span,
before: &'static str,
const_name: Ident,
},
#[help("for a shared instance of `{$const_name}`, consider making it a `static` item instead")]
Spanless,
Spanless { const_name: Ident },
}
// reference_casting.rs
@@ -1972,11 +1976,8 @@ pub(crate) enum UseInclusiveRange<'a> {
#[diag("literal out of range for `{$ty}`")]
pub(crate) struct OverflowingBinHex<'a> {
pub ty: &'a str,
pub lit: String,
pub dec: u128,
pub actually: String,
#[subdiagnostic]
pub sign: OverflowingBinHexSign,
pub sign: OverflowingBinHexSign<'a>,
#[subdiagnostic]
pub sub: Option<OverflowingBinHexSub<'a>>,
#[subdiagnostic]
@@ -1984,14 +1985,14 @@ pub(crate) struct OverflowingBinHex<'a> {
}
#[derive(Subdiagnostic)]
pub(crate) enum OverflowingBinHexSign {
pub(crate) enum OverflowingBinHexSign<'a> {
#[note(
"the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}`"
)]
Positive,
Positive { lit: String, ty: &'a str, actually: String, dec: u128 },
#[note("the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}`")]
#[note("and the value `-{$lit}` will become `{$actually}{$ty}`")]
Negative,
Negative { lit: String, ty: &'a str, actually: String, dec: u128 },
}
#[derive(Subdiagnostic)]
@@ -2562,6 +2563,7 @@ pub(crate) struct UnusedDelimSuggestion {
#[suggestion_part(code = "{end_replace}")]
pub end_span: Span,
pub end_replace: &'static str,
pub delim: &'static str,
}
#[derive(Diagnostic)]
@@ -3131,20 +3133,35 @@ pub(crate) enum UnusedImportsSugg {
pub(crate) struct RedundantImport {
#[subdiagnostic]
pub subs: Vec<RedundantImportSub>,
pub ident: Ident,
}
#[derive(Subdiagnostic)]
pub(crate) enum RedundantImportSub {
#[label("the item `{$ident}` is already imported here")]
ImportedHere(#[primary_span] Span),
ImportedHere {
#[primary_span]
span: Span,
ident: Ident,
},
#[label("the item `{$ident}` is already defined here")]
DefinedHere(#[primary_span] Span),
DefinedHere {
#[primary_span]
span: Span,
ident: Ident,
},
#[label("the item `{$ident}` is already imported by the extern prelude")]
ImportedPrelude(#[primary_span] Span),
ImportedPrelude {
#[primary_span]
span: Span,
ident: Ident,
},
#[label("the item `{$ident}` is already defined by the extern prelude")]
DefinedPrelude(#[primary_span] Span),
DefinedPrelude {
#[primary_span]
span: Span,
ident: Ident,
},
}
#[derive(LintDiagnostic)]
+17 -12
View File
@@ -157,8 +157,21 @@ fn report_bin_hex_error(
(t.name_str(), actually.to_string())
}
};
let sign =
if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
let sign = if negative {
OverflowingBinHexSign::Negative {
lit: repr_str.clone(),
dec: val,
actually: actually.clone(),
ty: t,
}
} else {
OverflowingBinHexSign::Positive {
lit: repr_str.clone(),
dec: val,
actually: actually.clone(),
ty: t,
}
};
let sub = get_type_suggestion(cx.typeck_results().node_type(hir_id), val, negative).map(
|suggestion_ty| {
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
@@ -194,7 +207,7 @@ fn report_bin_hex_error(
Some(OverflowingBinHexSignBitSub {
span,
lit_no_suffix,
negative_val: actually.clone(),
negative_val: actually,
int_ty: int_ty.name_str(),
uint_ty: Integer::fit_unsigned(val).uint_ty_str(),
})
@@ -204,15 +217,7 @@ fn report_bin_hex_error(
cx.emit_span_lint(
OVERFLOWING_LITERALS,
span,
OverflowingBinHex {
ty: t,
lit: repr_str.clone(),
dec: val,
actually,
sign,
sub,
sign_bit_sub,
},
OverflowingBinHex { ty: t, sign, sub, sign_bit_sub },
)
}
+1
View File
@@ -350,6 +350,7 @@ fn emit_unused_delims(
start_replace: lo_replace,
end_span: hi,
end_replace: hi_replace,
delim: Self::DELIM_STR,
}
});
cx.emit_span_lint(
@@ -535,7 +535,7 @@ pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveErro
let mut calls = TokenStream::new();
for (kind, messages) in kind_messages {
let message = format_ident!("__message");
let message_stream = messages.diag_message(None);
let message_stream = messages.diag_message(Some(self.variant));
calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); });
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
@@ -315,7 +315,7 @@ fn add_library(
crate_name: tcx.crate_name(cnum),
non_static_deps: unavailable_as_static
.drain(..)
.map(|cnum| NonStaticCrateDep { crate_name_: tcx.crate_name(cnum) })
.map(|cnum| NonStaticCrateDep { sub_crate_name: tcx.crate_name(cnum) })
.collect(),
rustc_driver_help: linking_to_rustc_driver,
});
+2 -2
View File
@@ -48,10 +48,10 @@ pub struct CrateDepMultiple {
}
#[derive(Subdiagnostic)]
#[note("`{$crate_name}` was unavailable as a static crate, preventing fully static linking")]
#[note("`{$sub_crate_name}` was unavailable as a static crate, preventing fully static linking")]
pub struct NonStaticCrateDep {
/// It's different from `crate_name` in main Diagnostic.
pub crate_name_: Symbol,
pub sub_crate_name: Symbol,
}
#[derive(Diagnostic)]
+70 -2
View File
@@ -3,7 +3,7 @@
use std::ops::Range;
use std::str;
use rustc_abi::{FIRST_VARIANT, ReprOptions, VariantIdx};
use rustc_abi::{FIRST_VARIANT, FieldIdx, ReprOptions, VariantIdx};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::intern::Interned;
@@ -15,6 +15,8 @@
use rustc_index::{IndexSlice, IndexVec};
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_session::DataTypeKind;
use rustc_span::sym;
use rustc_type_ir::FieldInfo;
use rustc_type_ir::solve::AdtDestructorKind;
use tracing::{debug, info, trace};
@@ -23,8 +25,8 @@
};
use crate::ich::StableHashingContext;
use crate::mir::interpret::ErrorHandled;
use crate::ty;
use crate::ty::util::{Discr, IntTypeExt};
use crate::ty::{self, ConstKind};
#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
pub struct AdtFlags(u16);
@@ -58,6 +60,8 @@ impl AdtFlags: u16 {
const IS_PIN = 1 << 11;
/// Indicates whether the type is `#[pin_project]`.
const IS_PIN_PROJECT = 1 << 12;
/// Indicates whether the type is `FieldRepresentingType`.
const IS_FIELD_REPRESENTING_TYPE = 1 << 13;
}
}
rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -200,6 +204,51 @@ pub fn flags(self) -> AdtFlags {
pub fn repr(self) -> ReprOptions {
self.0.0.repr
}
pub fn field_representing_type_info(
self,
tcx: TyCtxt<'tcx>,
args: ty::GenericArgsRef<'tcx>,
) -> Option<FieldInfo<TyCtxt<'tcx>>> {
if !self.is_field_representing_type() {
return None;
}
let base = args.type_at(0);
let variant_idx = match args.const_at(1).kind() {
ConstKind::Value(v) => VariantIdx::from_u32(v.to_leaf().to_u32()),
_ => return None,
};
let field_idx = match args.const_at(2).kind() {
ConstKind::Value(v) => FieldIdx::from_u32(v.to_leaf().to_u32()),
_ => return None,
};
let (ty, variant, name) = match base.kind() {
ty::Adt(base_def, base_args) => {
let variant = base_def.variant(variant_idx);
let field = &variant.fields[field_idx];
(field.ty(tcx, base_args), base_def.is_enum().then_some(variant.name), field.name)
}
ty::Tuple(tys) => {
if variant_idx != FIRST_VARIANT {
bug!("expected variant of tuple to be FIRST_VARIANT, but found {variant_idx:?}")
}
(
if let Some(ty) = tys.get(field_idx.index()) {
*ty
} else {
bug!(
"expected valid tuple index, but got {field_idx:?}, tuple length: {}",
tys.len()
)
},
None,
sym::integer(field_idx.index()),
)
}
_ => panic!(),
};
Some(FieldInfo { base, ty, variant, variant_idx, name, field_idx })
}
}
impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
@@ -211,6 +260,10 @@ fn is_struct(self) -> bool {
self.is_struct()
}
fn is_packed(self) -> bool {
self.repr().packed()
}
fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
Some(interner.type_of(self.non_enum_variant().tail_opt()?.did))
}
@@ -223,6 +276,14 @@ fn is_manually_drop(self) -> bool {
self.is_manually_drop()
}
fn field_representing_type_info(
self,
tcx: TyCtxt<'tcx>,
args: ty::GenericArgsRef<'tcx>,
) -> Option<FieldInfo<TyCtxt<'tcx>>> {
self.field_representing_type_info(tcx, args)
}
fn all_field_tys(
self,
tcx: TyCtxt<'tcx>,
@@ -321,6 +382,9 @@ pub(super) fn new(
if tcx.is_lang_item(did, LangItem::Pin) {
flags |= AdtFlags::IS_PIN;
}
if tcx.is_lang_item(did, LangItem::FieldRepresentingType) {
flags |= AdtFlags::IS_FIELD_REPRESENTING_TYPE;
}
AdtDefData { did, variants, flags, repr }
}
@@ -449,6 +513,10 @@ pub fn is_pin_project(self) -> bool {
self.flags().contains(AdtFlags::IS_PIN_PROJECT)
}
pub fn is_field_representing_type(self) -> bool {
self.flags().contains(AdtFlags::IS_FIELD_REPRESENTING_TYPE)
}
/// Returns `true` if this type has a destructor.
pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
self.destructor(tcx).is_some()
@@ -755,6 +755,8 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> {
CoroutineReturn,
CoroutineYield,
DynMetadata,
FieldBase,
FieldType,
FutureOutput,
Metadata,
// tidy-alphabetical-end
@@ -786,6 +788,7 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> {
Destruct,
DiscriminantKind,
Drop,
Field,
Fn,
FnMut,
FnOnce,
+11 -1
View File
@@ -17,7 +17,7 @@
use rustc_macros::{Lift, extension};
use rustc_session::cstore::{ExternCrate, ExternCrateSource};
use rustc_span::{Ident, RemapPathScopeComponents, Symbol, kw, sym};
use rustc_type_ir::{Upcast as _, elaborate};
use rustc_type_ir::{FieldInfo, Upcast as _, elaborate};
use smallvec::SmallVec;
// `pretty` is a separate module only for organization.
@@ -793,6 +793,16 @@ fn pretty_print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
false => write!(self, "{}", self.tcx().item_name(def_id))?,
},
},
ty::Adt(def, args)
if let Some(FieldInfo { base, variant, name, .. }) =
def.field_representing_type_info(self.tcx(), args) =>
{
if let Some(variant) = variant {
write!(self, "field_of!({base}, {variant}.{name})")?;
} else {
write!(self, "field_of!({base}, {name})")?;
}
}
ty::Adt(def, args) => self.print_def_path(def.did(), args)?,
ty::Dynamic(data, r) => {
let print_r = self.should_print_optional_region(r);
+31 -2
View File
@@ -26,8 +26,8 @@
use crate::traits::ObligationCause;
use crate::ty::InferTy::*;
use crate::ty::{
self, AdtDef, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty,
TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
self, AdtDef, Const, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region,
Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, ValTree,
};
// Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
@@ -487,6 +487,35 @@ pub fn new_pat(tcx: TyCtxt<'tcx>, base: Ty<'tcx>, pat: ty::Pattern<'tcx>) -> Ty<
Ty::new(tcx, Pat(base, pat))
}
#[inline]
pub fn new_field_representing_type(
tcx: TyCtxt<'tcx>,
base: Ty<'tcx>,
variant: VariantIdx,
field: FieldIdx,
) -> Ty<'tcx> {
let Some(did) = tcx.lang_items().field_representing_type() else {
bug!("could not locate the `FieldRepresentingType` lang item")
};
let def = tcx.adt_def(did);
let args = tcx.mk_args(&[
base.into(),
Const::new_value(
tcx,
ValTree::from_scalar_int(tcx, variant.as_u32().into()),
tcx.types.u32,
)
.into(),
Const::new_value(
tcx,
ValTree::from_scalar_int(tcx, field.as_u32().into()),
tcx.types.u32,
)
.into(),
]);
Ty::new_adt(tcx, def, args)
}
#[inline]
#[instrument(level = "debug", skip(tcx))]
pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
@@ -115,12 +115,12 @@ fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool
CallToDeprecatedSafeFnRequiresUnsafe {
span,
function: with_no_trimmed_paths!(self.tcx.def_path_str(id)),
guarantee,
sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
start_of_line_suggestion: suggestion,
start_of_line: sm.span_extend_to_line(span).shrink_to_lo(),
left: span.shrink_to_lo(),
right: span.shrink_to_hi(),
guarantee,
},
},
);
+1 -1
View File
@@ -15,7 +15,6 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe {
#[label("call to unsafe function")]
pub(crate) span: Span,
pub(crate) function: String,
pub(crate) guarantee: String,
#[subdiagnostic]
pub(crate) sub: CallToDeprecatedSafeFnRequiresUnsafeSub,
}
@@ -33,6 +32,7 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub {
pub(crate) left: Span,
#[suggestion_part(code = " }}")]
pub(crate) right: Span,
pub(crate) guarantee: String,
}
#[derive(Diagnostic)]
@@ -389,4 +389,5 @@ pub(crate) struct ForceInlineFailure {
#[note("`{$callee}` is required to be inlined to: {$sym}")]
pub(crate) struct ForceInlineJustification {
pub sym: Symbol,
pub callee: String,
}
+4 -2
View File
@@ -251,15 +251,17 @@ fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str) {
};
let call_span = callsite.source_info.span;
let callee = tcx.def_path_str(callsite.callee.def_id());
tcx.dcx().emit_err(crate::errors::ForceInlineFailure {
call_span,
attr_span,
caller_span: tcx.def_span(self.def_id),
caller: tcx.def_path_str(self.def_id),
callee_span: tcx.def_span(callsite.callee.def_id()),
callee: tcx.def_path_str(callsite.callee.def_id()),
callee: callee.clone(),
reason,
justification: justification.map(|sym| crate::errors::ForceInlineJustification { sym }),
justification: justification
.map(|sym| crate::errors::ForceInlineJustification { sym, callee }),
});
}
}
@@ -32,8 +32,10 @@ impl<'a, Infcx, I> BoundVarReplacer<'a, Infcx, I>
Infcx: InferCtxtLike<Interner = I>,
I: Interner,
{
/// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
/// use a binding level above `universe_indices.len()`, we fail.
/// Returns a type with all bound vars replaced by placeholders,
/// together with mappings from the new placeholders back to the original variable.
///
/// Panics if there are any bound vars that use a binding level above `universe_indices.len()`.
pub fn replace_bound_vars<T: TypeFoldable<I>>(
infcx: &'a Infcx,
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
@@ -348,6 +348,11 @@ fn consider_structural_builtin_unsize_candidates(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Vec<Candidate<I>>;
fn consider_builtin_field_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution>;
}
/// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit
@@ -617,6 +622,7 @@ fn assemble_builtin_impl_candidates<G: GoalKind<D>>(
Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => {
G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal)
}
Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal),
_ => Err(NoSolution),
}
};
@@ -430,6 +430,13 @@ fn consider_structural_builtin_unsize_candidates(
) -> Vec<Candidate<I>> {
unreachable!("Unsize is not const")
}
fn consider_builtin_field_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<<D as SolverDelegate>::Interner, Self>,
) -> Result<Candidate<<D as SolverDelegate>::Interner>, NoSolution> {
unreachable!("Field is not const")
}
}
impl<D, I> EvalCtxt<'_, D>
@@ -39,9 +39,9 @@
///
/// We previously used `cx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
/// However, it feels unlikely that uncreasing the recursion limit by a power of two
/// to get one more iteration is every useful or desirable. We now instead used a constant
/// to get one more iteration is ever useful or desirable. We now instead used a constant
/// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations
/// is required, we can add a new attribute for that or revert this to be dependant on the
/// is required, we can add a new attribute for that or revert this to be dependent on the
/// recursion limit again. However, this feels very unlikely.
const FIXPOINT_STEP_LIMIT: usize = 8;
@@ -7,7 +7,7 @@
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _};
use rustc_type_ir::{self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Upcast as _};
use tracing::instrument;
use crate::delegate::SolverDelegate;
@@ -950,6 +950,29 @@ fn consider_builtin_bikeshed_guaranteed_no_drop_candidate(
) -> Result<Candidate<I>, NoSolution> {
unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal)
}
fn consider_builtin_field_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
let self_ty = goal.predicate.self_ty();
let ty::Adt(def, args) = self_ty.kind() else {
return Err(NoSolution);
};
let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(ecx.cx(), args)
else {
return Err(NoSolution);
};
let ty = match ecx.cx().as_lang_item(goal.predicate.def_id()) {
Some(SolverLangItem::FieldBase) => base,
Some(SolverLangItem::FieldType) => ty,
_ => panic!("unexpected associated type {:?} in `Field`", goal.predicate),
};
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.instantiate_normalizes_to_term(goal, ty.into());
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
}
impl<D, I> EvalCtxt<'_, D>
@@ -8,7 +8,7 @@
AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, SizedTraitKind,
};
use rustc_type_ir::{
self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef,
self as ty, FieldInfo, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef,
TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
};
use tracing::{debug, instrument, trace};
@@ -844,6 +844,54 @@ fn consider_structural_builtin_unsize_candidates(
}
})
}
fn consider_builtin_field_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
return Err(NoSolution);
}
if let ty::Adt(def, args) = goal.predicate.self_ty().kind()
&& let Some(FieldInfo { base, ty, .. }) =
def.field_representing_type_info(ecx.cx(), args)
&& {
let sized_trait = ecx.cx().require_trait_lang_item(SolverTraitLangItem::Sized);
// FIXME: add better support for builtin impls of traits that check for the bounds
// on the trait definition in std.
// NOTE: these bounds have to be kept in sync with the definition of the `Field`
// trait in `library/core/src/field.rs` as well as the old trait solver `fn
// assemble_candidates_for_field_trait` in
// `compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs`.
ecx.add_goal(
GoalSource::ImplWhereBound,
Goal {
param_env: goal.param_env,
predicate: TraitRef::new(ecx.cx(), sized_trait, [base]).upcast(ecx.cx()),
},
);
ecx.add_goal(
GoalSource::ImplWhereBound,
Goal {
param_env: goal.param_env,
predicate: TraitRef::new(ecx.cx(), sized_trait, [ty]).upcast(ecx.cx()),
},
);
ecx.try_evaluate_added_goals()? == Certainty::Yes
}
&& match base.kind() {
ty::Adt(def, _) => def.is_struct() && !def.is_packed(),
ty::Tuple(..) => true,
_ => false,
}
{
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
} else {
Err(NoSolution)
}
}
}
/// Small helper function to change the `def_id` of a trait predicate - this is not normally
+3 -1
View File
@@ -1016,7 +1016,6 @@ pub(crate) struct LeadingPlusNotSupported {
pub(crate) struct ParenthesesWithStructFields {
#[primary_span]
pub span: Span,
pub r#type: Path,
#[subdiagnostic]
pub braces_for_struct: BracesForStructLiteral,
#[subdiagnostic]
@@ -1029,6 +1028,7 @@ pub(crate) struct ParenthesesWithStructFields {
applicability = "maybe-incorrect"
)]
pub(crate) struct BracesForStructLiteral {
pub r#type: Path,
#[suggestion_part(code = " {{ ")]
pub first: Span,
#[suggestion_part(code = " }}")]
@@ -1041,6 +1041,7 @@ pub(crate) struct BracesForStructLiteral {
applicability = "maybe-incorrect"
)]
pub(crate) struct NoFieldsForFnCall {
pub r#type: Path,
#[suggestion_part(code = "")]
pub fields: Vec<Span>,
}
@@ -3176,6 +3177,7 @@ pub(crate) struct UnexpectedVertVertInPattern {
pub(crate) struct TrailingVertSuggestion {
#[primary_span]
pub span: Span,
pub token: Token,
}
#[derive(Diagnostic)]
+2 -9
View File
@@ -118,16 +118,9 @@ pub fn new_parser_from_file<'a>(
let msg = format!("couldn't read `{}`: {}", path.display(), e);
let mut err = psess.dcx().struct_fatal(msg);
if let Ok(contents) = std::fs::read(path)
&& let Err(utf8err) = String::from_utf8(contents.clone())
&& let Err(utf8err) = std::str::from_utf8(&contents)
{
utf8_error(
sm,
&path.display().to_string(),
sp,
&mut err,
utf8err.utf8_error(),
&contents,
);
utf8_error(sm, &path.display().to_string(), sp, &mut err, utf8err, &contents);
}
if let Some(sp) = sp {
err.span(sp);
+3 -2
View File
@@ -1140,7 +1140,7 @@ enum FloatComponent {
/// Parse the field access used in offset_of, matched by `$(e:expr)+`.
/// Currently returns a list of idents. However, it should be possible in
/// future to also do array indices, which might be arbitrary expressions.
fn parse_floating_field_access(&mut self) -> PResult<'a, Vec<Ident>> {
pub(crate) fn parse_floating_field_access(&mut self) -> PResult<'a, Vec<Ident>> {
let mut fields = Vec::new();
let mut trailing_dot = None;
@@ -1309,12 +1309,13 @@ fn maybe_recover_struct_lit_bad_delims(
self.dcx()
.create_err(errors::ParenthesesWithStructFields {
span,
r#type: path,
braces_for_struct: errors::BracesForStructLiteral {
first: open_paren,
second: close_paren,
r#type: path.clone(),
},
no_fields_for_fn: errors::NoFieldsForFnCall {
r#type: path,
fields: fields
.into_iter()
.map(|field| field.span.until(field.expr.span))
+1
View File
@@ -364,6 +364,7 @@ fn recover_trailing_vert(&mut self, lo: Option<Span>) -> bool {
start: lo,
suggestion: TrailingVertSuggestion {
span: self.prev_token.span.shrink_to_hi().with_hi(self.token.span.hi()),
token: self.token,
},
token: self.token,
note_double_vert: self.token.kind == token::OrOr,
+48
View File
@@ -329,6 +329,8 @@ fn parse_ty_common(
self.parse_borrowed_pointee()?
} else if self.eat_keyword_noexpect(kw::Typeof) {
self.parse_typeof_ty(lo)?
} else if self.is_builtin() {
self.parse_builtin_ty()?
} else if self.eat_keyword(exp!(Underscore)) {
// A type to be inferred `_`
TyKind::Infer
@@ -802,6 +804,52 @@ fn parse_typeof_ty(&mut self, lo: Span) -> PResult<'a, TyKind> {
Ok(TyKind::Err(guar))
}
fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> {
self.parse_builtin(|this, lo, ident| {
Ok(match ident.name {
sym::field_of => Some(this.parse_ty_field_of(lo)?),
_ => None,
})
})
}
pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> {
let container = self.parse_ty()?;
self.expect(exp!(Comma))?;
let fields = self.parse_floating_field_access()?;
let trailing_comma = self.eat_noexpect(&TokenKind::Comma);
if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) {
if trailing_comma {
e.note("unexpected third argument to field_of");
} else {
e.note("field_of expects dot-separated field and variant names");
}
e.emit();
}
// Eat tokens until the macro call ends.
if self.may_recover() {
while !self.token.kind.is_close_delim_or_eof() {
self.bump();
}
}
match *fields {
[] => Err(self.dcx().struct_span_err(
self.token.span,
"`field_of!` expects dot-separated field and variant names",
)),
[field] => Ok(TyKind::FieldOf(container, None, field)),
[variant, field] => Ok(TyKind::FieldOf(container, Some(variant), field)),
_ => Err(self.dcx().struct_span_err(
fields.iter().map(|f| f.span).collect::<Vec<_>>(),
"`field_of!` only supports a single field or a variant with a field",
)),
}
}
/// Parses a function pointer type (`TyKind::FnPtr`).
/// ```ignore (illustrative)
/// [unsafe] [extern "ABI"] fn (S) -> T
+2
View File
@@ -410,6 +410,7 @@ fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) {
TraitObject,
Infer,
Pat,
FieldOf,
Err
]
);
@@ -688,6 +689,7 @@ fn visit_ty(&mut self, t: &'v ast::Ty) {
MacCall,
CVarArgs,
Dummy,
FieldOf,
Err
]
);
+2 -2
View File
@@ -35,9 +35,9 @@ pub(crate) struct CycleStack {
#[derive(Subdiagnostic)]
pub(crate) enum StackCount {
#[note("...which immediately requires {$stack_bottom} again")]
Single,
Single { stack_bottom: String },
#[note("...which again requires {$stack_bottom}, completing the cycle")]
Multiple,
Multiple { stack_bottom: String },
}
#[derive(Subdiagnostic)]
+7 -2
View File
@@ -438,7 +438,12 @@ pub(crate) fn report_cycle<'a>(
let mut cycle_stack = Vec::new();
use crate::error::StackCount;
let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
let stack_bottom = stack[0].frame.info.description.to_owned();
let stack_count = if stack.len() == 1 {
StackCount::Single { stack_bottom: stack_bottom.clone() }
} else {
StackCount::Multiple { stack_bottom: stack_bottom.clone() }
};
for i in 1..stack.len() {
let frame = &stack[i].frame;
@@ -467,7 +472,7 @@ pub(crate) fn report_cycle<'a>(
let cycle_diag = crate::error::Cycle {
span,
cycle_stack,
stack_bottom: stack[0].frame.info.description.to_owned(),
stack_bottom,
alias,
cycle_usage,
stack_count,
+8 -5
View File
@@ -249,20 +249,23 @@ pub(crate) fn report_conflict(
};
let label = match new_binding.is_import_user_facing() {
true => errors::NameDefinedMultipleTimeLabel::Reimported { span },
false => errors::NameDefinedMultipleTimeLabel::Redefined { span },
true => errors::NameDefinedMultipleTimeLabel::Reimported { span, name },
false => errors::NameDefinedMultipleTimeLabel::Redefined { span, name },
};
let old_binding_label =
(!old_binding.span.is_dummy() && old_binding.span != span).then(|| {
let span = self.tcx.sess.source_map().guess_head_span(old_binding.span);
match old_binding.is_import_user_facing() {
true => {
errors::NameDefinedMultipleTimeOldBindingLabel::Import { span, old_kind }
}
true => errors::NameDefinedMultipleTimeOldBindingLabel::Import {
span,
old_kind,
name,
},
false => errors::NameDefinedMultipleTimeOldBindingLabel::Definition {
span,
old_kind,
name,
},
}
});
+4
View File
@@ -1126,11 +1126,13 @@ pub(crate) enum NameDefinedMultipleTimeLabel {
Reimported {
#[primary_span]
span: Span,
name: Symbol,
},
#[label("`{$name}` redefined here")]
Redefined {
#[primary_span]
span: Span,
name: Symbol,
},
}
@@ -1141,12 +1143,14 @@ pub(crate) enum NameDefinedMultipleTimeOldBindingLabel {
#[primary_span]
span: Span,
old_kind: &'static str,
name: Symbol,
},
#[label("previous definition of the {$old_kind} `{$name}` here")]
Definition {
#[primary_span]
span: Span,
old_kind: &'static str,
name: Symbol,
},
}
+6 -2
View File
@@ -1048,7 +1048,9 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option<UnresolvedImportErr
message,
} => {
if no_ambiguity {
assert!(import.imported_module.get().is_none());
if !self.issue_145575_hack_applied {
assert!(import.imported_module.get().is_none());
}
self.report_error(
span,
ResolutionError::FailedToResolve {
@@ -1072,7 +1074,9 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option<UnresolvedImportErr
..
} => {
if no_ambiguity {
assert!(import.imported_module.get().is_none());
if !self.issue_145575_hack_applied {
assert!(import.imported_module.get().is_none());
}
let module = if let Some(ModuleOrUniformRoot::Module(m)) = module {
m.opt_def_id()
} else {
+8
View File
@@ -945,7 +945,15 @@
ffi_const,
ffi_pure,
ffi_returns_twice,
field,
field_base,
field_init_shorthand,
field_of,
field_offset,
field_projections,
field_representing_type,
field_representing_type_raw,
field_type,
fields,
file,
final_associated_functions,
@@ -3288,12 +3288,14 @@ fn report_closure_error(
err.fn_once_label = Some(ClosureFnOnceLabel {
span: *span,
place: ty::place_to_string_for_capture(self.tcx, place),
trait_prefix,
})
}
(ty::ClosureKind::FnMut, Some((span, place))) => {
err.fn_mut_label = Some(ClosureFnMutLabel {
span: *span,
place: ty::place_to_string_for_capture(self.tcx, place),
trait_prefix,
})
}
_ => {}
@@ -157,6 +157,7 @@ pub struct ClosureFnOnceLabel {
#[primary_span]
pub span: Span,
pub place: String,
pub trait_prefix: &'static str,
}
#[derive(Subdiagnostic)]
@@ -165,6 +166,7 @@ pub struct ClosureFnMutLabel {
#[primary_span]
pub span: Span,
pub place: String,
pub trait_prefix: &'static str,
}
#[derive(Diagnostic)]
@@ -13,7 +13,7 @@
use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData};
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{
self, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast,
self, FieldInfo, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast,
};
use rustc_middle::{bug, span_bug};
use rustc_span::sym;
@@ -987,6 +987,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
| LangItem::Future
| LangItem::Iterator
| LangItem::AsyncIterator
| LangItem::Field
| LangItem::Fn
| LangItem::FnMut
| LangItem::FnOnce
@@ -1547,6 +1548,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
}
});
(metadata_ty.into(), obligations)
} else if tcx.is_lang_item(trait_def_id, LangItem::Field) {
let ty::Adt(def, args) = self_ty.kind() else {
bug!("only field representing types can implement `Field`")
};
let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(tcx, args) else {
bug!("only field representing types can implement `Field`")
};
if tcx.is_lang_item(item_def_id, LangItem::FieldBase) {
(base.into(), PredicateObligations::new())
} else if tcx.is_lang_item(item_def_id, LangItem::FieldType) {
(ty.into(), PredicateObligations::new())
} else {
bug!("unexpected associated type {:?} in `Field`", obligation.predicate);
}
} else {
bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);
};
@@ -12,14 +12,18 @@
use hir::def_id::DefId;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind};
use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
use rustc_infer::traits::{Obligation, PolyTraitObligation, PredicateObligation, SelectionError};
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{self, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate};
use rustc_middle::ty::{
self, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, TypingMode, elaborate,
};
use rustc_middle::{bug, span_bug};
use rustc_span::DUMMY_SP;
use tracing::{debug, instrument, trace};
use super::SelectionCandidate::*;
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::util;
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
@@ -128,6 +132,9 @@ pub(super) fn assemble_candidates<'o>(
&mut candidates,
);
}
Some(LangItem::Field) => {
self.assemble_candidates_for_field_trait(obligation, &mut candidates);
}
_ => {
// We re-match here for traits that can have both builtin impls and user written impls.
// After the builtin impls we need to also add user written impls, which we do not want to
@@ -1439,4 +1446,52 @@ fn assemble_candidates_for_bikeshed_guaranteed_no_drop_trait(
}
}
}
fn assemble_candidates_for_field_trait(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
if let ty::Adt(def, args) = obligation.predicate.self_ty().skip_binder().kind()
&& let Some(FieldInfo { base, ty, .. }) =
def.field_representing_type_info(self.tcx(), args)
// NOTE: these bounds have to be kept in sync with the definition of the `Field` trait
// in `library/core/src/field.rs` as well as the new trait solver `fn
// consider_builtin_field_candidate` in
// `compiler/rustc_next_trait_solver/src/solve/trait_goals.rs`.
&& match self.infcx.evaluate_obligation(&PredicateObligation::new(
self.tcx(),
obligation.cause.clone(),
obligation.param_env,
TraitRef::new(
self.tcx(),
self.tcx().require_lang_item(LangItem::Sized, DUMMY_SP),
[base],
),
)) {
Ok(res) if res.must_apply_modulo_regions() => true,
_ => false,
}
&& match self.infcx.evaluate_obligation(&PredicateObligation::new(
self.tcx(),
obligation.cause.clone(),
obligation.param_env,
TraitRef::new(
self.tcx(),
self.tcx().require_lang_item(LangItem::Sized, DUMMY_SP),
[ty],
),
)) {
Ok(res) if res.must_apply_modulo_regions() => true,
_ => false,
}
&& match base.kind() {
ty::Adt(def, _) => def.is_struct() && !def.repr().packed(),
ty::Tuple(..) => true,
_ => false,
}
{
candidates.vec.push(BuiltinCandidate);
}
}
}
@@ -262,6 +262,7 @@ fn confirm_builtin_candidate(
Some(
LangItem::Destruct
| LangItem::DiscriminantKind
| LangItem::Field
| LangItem::FnPtrTrait
| LangItem::PointeeTrait
| LangItem::Tuple
+16
View File
@@ -386,6 +386,22 @@ fn resolve_associated_item<'tcx>(
assert_eq!(name, sym::transmute);
let args = tcx.erase_and_anonymize_regions(rcvr_args);
Some(ty::Instance::new_raw(trait_item_id, args))
} else if tcx.is_lang_item(trait_ref.def_id, LangItem::Field) {
if tcx.is_lang_item(trait_item_id, LangItem::FieldOffset) {
let self_ty = trait_ref.self_ty();
match self_ty.kind() {
ty::Adt(def, _) if def.is_field_representing_type() => {}
_ => bug!("expected field representing type, found {self_ty}"),
}
Some(Instance {
def: ty::InstanceKind::Item(
tcx.lang_items().get(LangItem::FieldOffset).unwrap(),
),
args: rcvr_args,
})
} else {
bug!("unexpected associated associated item")
}
} else {
Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args)
}
+1
View File
@@ -11,6 +11,7 @@ derive-where = "1.2.7"
ena = "0.14.4"
indexmap = "2.0.0"
rustc-hash = "2.0.0"
rustc_abi = { path = "../rustc_abi", default-features = false }
rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
rustc_error_messages = { path = "../rustc_error_messages", optional = true }
+11 -1
View File
@@ -13,7 +13,9 @@
use crate::relate::Relate;
use crate::solve::{AdtDestructorKind, SizedTraitKind};
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
use crate::{self as ty, ClauseKind, CollectAndApply, Interner, PredicateKind, UpcastFrom};
use crate::{
self as ty, ClauseKind, CollectAndApply, FieldInfo, Interner, PredicateKind, UpcastFrom,
};
pub trait Ty<I: Interner<Ty = Self>>:
Copy
@@ -577,6 +579,8 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
fn is_struct(self) -> bool;
fn is_packed(self) -> bool;
/// Returns the type of the struct tail.
///
/// Expects the `AdtDef` to be a struct. If it is not, then this will panic.
@@ -586,6 +590,12 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
fn is_manually_drop(self) -> bool;
fn field_representing_type_info(
self,
interner: I,
args: I::GenericArgs,
) -> Option<FieldInfo<I>>;
// FIXME: perhaps use `all_fields` and expose `FieldDef`.
fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>;
+3
View File
@@ -9,6 +9,8 @@ pub enum SolverLangItem {
CoroutineReturn,
CoroutineYield,
DynMetadata,
FieldBase,
FieldType,
FutureOutput,
Metadata,
// tidy-alphabetical-end
@@ -36,6 +38,7 @@ pub enum SolverTraitLangItem {
Destruct,
DiscriminantKind,
Drop,
Field,
Fn,
FnMut,
FnOnce,
+10
View File
@@ -13,6 +13,7 @@
use std::fmt;
use std::hash::Hash;
use rustc_abi::{FieldIdx, VariantIdx};
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable, Encodable, HashStable_NoContext};
@@ -440,3 +441,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
pub struct FieldInfo<I: Interner> {
pub base: I::Ty,
pub ty: I::Ty,
pub variant: Option<I::Symbol>,
pub variant_idx: VariantIdx,
pub name: I::Symbol,
pub field_idx: FieldIdx,
}
+83
View File
@@ -0,0 +1,83 @@
//! Field Reflection
use crate::marker::PhantomData;
/// Field Representing Type
#[unstable(feature = "field_representing_type_raw", issue = "none")]
#[lang = "field_representing_type"]
#[expect(missing_debug_implementations)]
#[fundamental]
pub struct FieldRepresentingType<T: ?Sized, const VARIANT: u32, const FIELD: u32> {
_phantom: PhantomData<T>,
}
// SAFETY: `FieldRepresentingType` doesn't contain any `T`
unsafe impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Send
for FieldRepresentingType<T, VARIANT, FIELD>
{
}
// SAFETY: `FieldRepresentingType` doesn't contain any `T`
unsafe impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Sync
for FieldRepresentingType<T, VARIANT, FIELD>
{
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Copy
for FieldRepresentingType<T, VARIANT, FIELD>
{
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Clone
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn clone(&self) -> Self {
*self
}
}
/// Expands to the field representing type of the given field.
///
/// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the
/// variant must also be specified. Only a single field is supported.
#[unstable(feature = "field_projections", issue = "145383")]
#[allow_internal_unstable(field_representing_type_raw, builtin_syntax)]
// NOTE: when stabilizing this macro, we can never add new trait impls for `FieldRepresentingType`,
// since it is `#[fundamental]` and thus could break users of this macro, since the compiler expands
// it to `FieldRepresentingType<...>`. Thus stabilizing this requires careful thought about the
// completeness of the trait impls for `FieldRepresentingType`.
pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) {
builtin # field_of($Container, $($fields)+)
}
/// Type representing a field of a `struct`, `union`, `enum` variant or tuple.
///
/// # Safety
///
/// Given a valid value of type `Self::Base`, there exists a valid value of type `Self::Type` at
/// byte offset `OFFSET`
#[lang = "field"]
#[unstable(feature = "field_projections", issue = "145383")]
#[rustc_deny_explicit_impl]
#[rustc_dyn_incompatible_trait]
// NOTE: the compiler provides the impl of `Field` for `FieldRepresentingType` when it can guarantee
// the safety requirements of this trait. It also has to manually add the correct trait bounds on
// associated types (and the `Self` type). Thus any changes to the bounds here must be reflected in
// the old and new trait solver:
// - `fn assemble_candidates_for_field_trait` in
// `compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs`, and
// - `fn consider_builtin_field_candidate` in
// `compiler/rustc_next_trait_solver/src/solve/trait_goals.rs`.
pub unsafe trait Field: Send + Sync + Copy {
/// The type of the base where this field exists in.
#[lang = "field_base"]
type Base;
/// The type of the field.
#[lang = "field_type"]
type Type;
/// The offset of the field in bytes.
#[lang = "field_offset"]
const OFFSET: usize = crate::intrinsics::field_offset::<Self>();
}
+14
View File
@@ -2812,6 +2812,20 @@ pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(
#[lang = "offset_of"]
pub const fn offset_of<T: PointeeSized>(variant: u32, field: u32) -> usize;
/// The offset of a field queried by its field representing type.
///
/// Returns the offset of the field represented by `F`. This function essentially does the same as
/// the [`offset_of`] intrinsic, but expects the field to be represented by a generic rather than
/// the variant and field indices. This also is a safe intrinsic and can only be evaluated at
/// compile-time, so it should only appear in constants or inline const blocks.
///
/// There should be no need to call this intrinsic manually, as its value is used to define
/// [`Field::OFFSET`](crate::field::Field::OFFSET), which is publicly accessible.
#[rustc_intrinsic]
#[unstable(feature = "field_projections", issue = "145383")]
#[rustc_const_unstable(feature = "field_projections", issue = "145383")]
pub const fn field_offset<F: crate::field::Field>() -> usize;
/// Returns the number of variants of the type `T` cast to a `usize`;
/// if `T` has no variants, returns `0`. Uninhabited variants will be counted.
///
+3
View File
@@ -137,6 +137,7 @@
#![feature(extern_types)]
#![feature(f16)]
#![feature(f128)]
#![feature(field_projections)]
#![feature(freeze_impls)]
#![feature(fundamental)]
#![feature(funnel_shifts)]
@@ -274,6 +275,8 @@ pub mod autodiff {
pub mod convert;
pub mod default;
pub mod error;
#[unstable(feature = "field_projections", issue = "145383")]
pub mod field;
pub mod index;
pub mod marker;
pub mod ops;
+32 -29
View File
@@ -15,22 +15,19 @@
//! The precise rules for validity are not determined yet. The guarantees that are
//! provided at this point are very minimal:
//!
//! * For memory accesses of [size zero][zst], *every* pointer is valid, including the [null]
//! pointer. The following points are only concerned with non-zero-sized accesses.
//! * A [null] pointer is *never* valid.
//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be
//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocation]
//! it is derived from; a pointer is dereferenceable if the memory range of the given size
//! starting at the pointer is entirely contained within the bounds of that allocation. Note
//! * A [null] pointer is *never* valid for reads/writes.
//! * For memory accesses of [size zero][zst], *every* non-null pointer is valid for reads/writes.
//! The following points are only concerned with non-zero-sized accesses.
//! * For a pointer to be valid for reads/writes, it is necessary, but not always sufficient, that
//! the pointer be *dereferenceable*. The [provenance] of the pointer is used to determine which
//! [allocation] it is derived from; a pointer is dereferenceable if the memory range of the given
//! size starting at the pointer is entirely contained within the bounds of that allocation. Note
//! that in Rust, every (stack-allocated) variable is considered a separate allocation.
//! * All accesses performed by functions in this module are *non-atomic* in the sense
//! of [atomic operations] used to synchronize between threads. This means it is
//! undefined behavior to perform two concurrent accesses to the same location from different
//! threads unless both accesses only read from memory. Notice that this explicitly
//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot
//! be used for inter-thread synchronization, regardless of whether they are acting on
//! Rust memory or not.
//! * The result of casting a reference to a pointer is valid for as long as the
//! threads unless both accesses only read from memory.
//! * The result of casting a reference to a pointer is valid for reads/writes for as long as the
//! underlying allocation is live and no reference (just raw pointers) is used to
//! access the same memory. That is, reference and pointer accesses cannot be
//! interleaved.
@@ -41,6 +38,13 @@
//! information, see the [book] as well as the section in the reference devoted
//! to [undefined behavior][ub].
//!
//! Note that some operations such as [`read`] and [`write`][`write()`] do allow null pointers if
//! the total size of the access is zero. However, other operations internally convert pointers into
//! references. Therefore, the general notion of "valid for reads/writes" excludes null pointers,
//! and the specific operations that permit null pointers mention that as an exception. Furthermore,
//! [`read_volatile`] and [`write_volatile`] can be used in even more situations; see their
//! documentation for details.
//!
//! We say that a pointer is "dangling" if it is not valid for any non-zero-sized accesses. This
//! means out-of-bounds pointers, pointers to freed memory, null pointers, and pointers created with
//! [`NonNull::dangling`] are all dangling.
@@ -450,9 +454,9 @@
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes or that number must be 0.
///
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes.
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes or that number must be 0.
///
/// * Both `src` and `dst` must be properly aligned.
///
@@ -568,11 +572,11 @@
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes or that number must be 0.
///
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes, and must remain valid even
/// when `src` is read for `count * size_of::<T>()` bytes. (This means if the memory ranges
/// overlap, the `dst` pointer must not be invalidated by `src` reads.)
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes or that number must be 0,
/// and `dst` must remain valid even when `src` is read for `count * size_of::<T>()` bytes. (This
/// means if the memory ranges overlap, the `dst` pointer must not be invalidated by `src` reads.)
///
/// * Both `src` and `dst` must be properly aligned.
///
@@ -1508,7 +1512,7 @@ macro_rules! swap_prefix {
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `dst` must be [valid] for both reads and writes.
/// * `dst` must be [valid] for both reads and writes or `T` must be a ZST.
///
/// * `dst` must be properly aligned.
///
@@ -1555,10 +1559,9 @@ macro_rules! swap_prefix {
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
);
if T::IS_ZST {
// `dst` may be valid for read and writes while also being null, in which case we cannot
// call `mem::replace`. However, we also don't have to actually do anything since there
// isn't actually any data to be copied anyway. All values of type `T` are
// bit-identical, so we can just return `src` here.
// If `T` is a ZST, `dst` is allowed to be null. However, we also don't have to actually
// do anything since there isn't actually any data to be copied anyway. All values of
// type `T` are bit-identical, so we can just return `src` here.
return src;
}
mem::replace(&mut *dst, src)
@@ -1572,7 +1575,7 @@ macro_rules! swap_prefix {
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be [valid] for reads.
/// * `src` must be [valid] for reads or `T` must be a ZST.
///
/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the
/// case.
@@ -1824,7 +1827,7 @@ macro_rules! swap_prefix {
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `dst` must be [valid] for writes.
/// * `dst` must be [valid] for writes or `T` must be a ZST.
///
/// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the
/// case.
@@ -2047,8 +2050,8 @@ macro_rules! swap_prefix {
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be either [valid] for reads, or it must point to memory outside of all Rust
/// allocations and reading from that memory must:
/// * `src` must be either [valid] for reads, or `T` must be a ZST, or `src` must point to memory
/// outside of all Rust allocations and reading from that memory must:
/// - not trap, and
/// - not cause any memory inside a Rust allocation to be modified.
///
@@ -2135,8 +2138,8 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `dst` must be either [valid] for writes, or it must point to memory outside of all Rust
/// allocations and writing to that memory must:
/// * `dst` must be either [valid] for writes, or `T` must be a ZST, or `dst` must point to memory
/// outside of all Rust allocations and writing to that memory must:
/// - not trap, and
/// - not cause any memory inside a Rust allocation to be modified.
///
+2
View File
@@ -495,6 +495,8 @@
pub use core::convert;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::default;
#[unstable(feature = "field_projections", issue = "145383")]
pub use core::field;
#[stable(feature = "futures_api", since = "1.36.0")]
pub use core::future;
#[stable(feature = "core_hint", since = "1.27.0")]
@@ -29,19 +29,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
ENV RUSTBUILD_FORCE_CLANG_BASED_TESTS 1
ENV RUSTBUILD_FORCE_CLANG_BASED_TESTS="1"
# llvm.use-linker conflicts with downloading CI LLVM
ENV NO_DOWNLOAD_CI_LLVM 1
ENV NO_DOWNLOAD_CI_LLVM="1"
ENV RUST_CONFIGURE_ARGS \
--build=aarch64-unknown-linux-gnu \
ENV RUST_CONFIGURE_ARGS="--build=aarch64-unknown-linux-gnu \
--enable-debug \
--enable-lld \
--set llvm.use-linker=lld \
--set target.aarch64-unknown-linux-gnu.linker=clang \
--set target.aarch64-unknown-linux-gnu.cc=clang \
--set target.aarch64-unknown-linux-gnu.cxx=clang++
--set target.aarch64-unknown-linux-gnu.cxx=clang++"
# This job appears to be checking two separate things:
# - That we can build the compiler with `--enable-debug`
@@ -52,6 +51,5 @@ ENV RUST_CONFIGURE_ARGS \
# Currently we only run the subset of tests with "clang" in their name.
# - See also FIXME(#132034)
ENV SCRIPT \
python3 ../x.py --stage 2 build && \
python3 ../x.py --stage 2 test tests/run-make tests/run-make-cargo
ENV SCRIPT="python3 ../x.py --stage 2 build && \
python3 ../x.py --stage 2 test tests/run-make tests/run-make-cargo"
+32 -1
View File
@@ -1809,6 +1809,14 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
}
TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))),
TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()),
TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => {
let field_str = if let Some(variant) = variant {
format!("{variant}.{field}")
} else {
format!("{field}")
};
Type::FieldOf(Box::new(clean_ty(ty, cx)), field_str.into())
}
TyKind::Array(ty, const_arg) => {
// NOTE(min_const_generics): We can't use `const_eval_poly` for constants
// as we currently do not supply the parent generics to anonymous constants
@@ -2701,7 +2709,29 @@ fn add_without_unwanted_attributes<'hir>(
}
hir::Attribute::Parsed(AttributeKind::Doc(box d)) => {
// Remove attributes from `normal` that should not be inherited by `use` re-export.
let DocAttribute { hidden, inline, cfg, .. } = d;
let DocAttribute {
aliases,
hidden,
inline,
cfg,
auto_cfg: _,
auto_cfg_change: _,
fake_variadic: _,
keyword: _,
attribute: _,
masked: _,
notable_trait: _,
search_unbox: _,
html_favicon_url: _,
html_logo_url: _,
html_playground_url: _,
html_root_url: _,
html_no_source: _,
issue_tracker_base_url: _,
rust_logo: _,
test_attrs: _,
no_crate_inject: _,
} = d;
let mut attr = DocAttribute::default();
if is_inline {
attr.cfg = cfg.clone();
@@ -2709,6 +2739,7 @@ fn add_without_unwanted_attributes<'hir>(
attr.inline = inline.clone();
attr.hidden = hidden.clone();
}
attr.aliases = aliases.clone();
attrs.push((
Cow::Owned(hir::Attribute::Parsed(AttributeKind::Doc(Box::new(attr)))),
import_parent,
+4
View File
@@ -1349,6 +1349,7 @@ pub(crate) enum Type {
/// The `String` field is a stringified version of the array's length parameter.
Array(Box<Type>, Box<str>),
Pat(Box<Type>, Box<str>),
FieldOf(Box<Type>, Box<str>),
/// A raw pointer type: `*const i32`, `*mut i32`
RawPointer(Mutability, Box<Type>),
/// A reference type: `&i32`, `&'a mut Foo`
@@ -1562,6 +1563,7 @@ pub(crate) fn def_id(&self, cache: &Cache) -> Option<DefId> {
Slice(..) => PrimitiveType::Slice,
Array(..) => PrimitiveType::Array,
Type::Pat(..) => PrimitiveType::Pat,
Type::FieldOf(..) => PrimitiveType::FieldOf,
RawPointer(..) => PrimitiveType::RawPointer,
QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache),
Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None,
@@ -1609,6 +1611,7 @@ pub(crate) enum PrimitiveType {
Slice,
Array,
Pat,
FieldOf,
Tuple,
Unit,
RawPointer,
@@ -1764,6 +1767,7 @@ pub(crate) fn as_sym(&self) -> Symbol {
Char => sym::char,
Array => sym::array,
Pat => sym::pat,
FieldOf => sym::field_of,
Slice => sym::slice,
Tuple => sym::tuple,
Unit => sym::unit,
+5
View File
@@ -967,6 +967,11 @@ fn fmt_type(
fmt::Display::fmt(&print_type(t, cx), f)?;
write!(f, " is {pat}")
}
clean::Type::FieldOf(t, field) => {
write!(f, "field_of!(")?;
fmt::Display::fmt(&print_type(t, cx), f)?;
write!(f, ", {field})")
}
clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link(
f,
PrimitiveType::Array,
@@ -2049,6 +2049,7 @@ fn get_index_type_id(
}
// Not supported yet
clean::Type::Pat(..)
| clean::Type::FieldOf(..)
| clean::Generic(_)
| clean::SelfTy
| clean::ImplTrait(_)
+2
View File
@@ -579,6 +579,8 @@ fn from_clean(ty: &clean::Type, renderer: &JsonRenderer<'_>) -> Self {
type_: Box::new(t.into_json(renderer)),
__pat_unstable_do_not_use: p.to_string(),
},
// FIXME(FRTs): implement
clean::Type::FieldOf(..) => todo!(),
ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)),
Infer => Type::Infer,
RawPointer(mutability, type_) => Type::RawPointer {
@@ -852,6 +852,7 @@ fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self {
| TyKind::Ptr(_)
| TyKind::FnPtr(_)
| TyKind::Pat(..)
| TyKind::FieldOf(..)
| TyKind::Never
| TyKind::Tup(_)
| TyKind::Path(_) => Self::Deref,
@@ -531,6 +531,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
// experimental
| TyKind::Pat(..)
| TyKind::FieldOf(..)
// unused
| TyKind::CVarArgs
@@ -14,7 +14,7 @@
GenericParam, GenericParamKind, GenericParamSource, Generics, HirId, HirIdMap, InlineAsmOperand, ItemId, ItemKind,
LetExpr, Lifetime, LifetimeKind, LifetimeParamKind, Node, ParamName, Pat, PatExpr, PatExprKind, PatField, PatKind,
Path, PathSegment, PreciseCapturingArgKind, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty,
TyKind, TyPat, TyPatKind, UseKind, WherePredicate, WherePredicateKind,
TyFieldPath, TyKind, TyPat, TyPatKind, UseKind, WherePredicate, WherePredicateKind,
};
use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
use rustc_lint::LateContext;
@@ -1529,6 +1529,13 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
self.hash_ty(ty);
self.hash_ty_pat(pat);
},
TyKind::FieldOf(base, TyFieldPath { variant, field }) => {
self.hash_ty(base);
if let Some(variant) = variant {
self.hash_name(variant.name);
}
self.hash_name(field.name);
},
TyKind::Ptr(mut_ty) => {
self.hash_ty(mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
-1
View File
@@ -257,7 +257,6 @@ macro_rules! generate {
f64_legacy_const_nan,
f64_legacy_const_neg_infinity,
f64_legacy_const_radix,
field,
file_options,
filter,
filter_map,
@@ -328,11 +328,7 @@ fn check_ptr_ptr_cast(
//
// Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
// and is unaffected by this check.
(Some(src_principal), Some(dst_principal)) => {
if src_principal == dst_principal {
return Ok(());
}
(Some(src_principal), Some(_)) => {
// We need to reconstruct trait object types.
// `m_src` and `m_dst` won't work for us here because they will potentially
// contain wrappers, which we do not care about.
@@ -2,7 +2,7 @@
use ide_db::FileId;
use syntax::{
AstNode, TextRange,
ast::{self, HasVisibility as _, edit_in_place::HasVisibilityEdit, make},
ast::{self, HasVisibility as _, syntax_factory::SyntaxFactory},
};
use crate::{AssistContext, AssistId, Assists};
@@ -59,10 +59,12 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
let (vis_owner, target, target_file, target_name) = target_data_for_def(ctx.db(), def)?;
let make = SyntaxFactory::without_mappings();
let missing_visibility = if current_module.krate(ctx.db()) == target_module.krate(ctx.db()) {
make::visibility_pub_crate()
make.visibility_pub_crate()
} else {
make::visibility_pub()
make.visibility_pub()
};
let assist_label = match target_name {
@@ -75,15 +77,36 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
}
};
acc.add(AssistId::quick_fix("fix_visibility"), assist_label, target, |edit| {
edit.edit_file(target_file);
acc.add(AssistId::quick_fix("fix_visibility"), assist_label, target, |builder| {
let mut editor = builder.make_editor(vis_owner.syntax());
let vis_owner = edit.make_mut(vis_owner);
vis_owner.set_visibility(Some(missing_visibility.clone_for_update()));
if let Some(current_visibility) = vis_owner.visibility() {
editor.replace(current_visibility.syntax(), missing_visibility.syntax());
} else {
let vis_before = vis_owner
.syntax()
.children_with_tokens()
.find(|it| {
!matches!(
it.kind(),
syntax::SyntaxKind::WHITESPACE
| syntax::SyntaxKind::COMMENT
| syntax::SyntaxKind::ATTR
)
})
.unwrap_or_else(|| vis_owner.syntax().first_child_or_token().unwrap());
if let Some((cap, vis)) = ctx.config.snippet_cap.zip(vis_owner.visibility()) {
edit.add_tabstop_before(cap, vis);
editor.insert_all(
syntax::syntax_editor::Position::before(vis_before),
vec![missing_visibility.syntax().clone().into(), make.whitespace(" ").into()],
);
}
if let Some(cap) = ctx.config.snippet_cap {
editor.add_annotation(missing_visibility.syntax(), builder.make_tabstop_before(cap));
}
builder.add_file_edits(target_file, editor);
})
}
@@ -1,7 +1,7 @@
use syntax::{
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
T,
ast::{self, AstNode, HasAttrs, edit::IndentLevel, make},
ast::{self, AstNode, HasAttrs, edit::IndentLevel, syntax_factory::SyntaxFactory},
syntax_editor::{Element, Position},
};
@@ -42,13 +42,15 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
};
acc.add(AssistId::generate("generate_derive"), "Add `#[derive]`", target, |edit| {
let make = SyntaxFactory::without_mappings();
match derive_attr {
None => {
let derive = make::attr_outer(make::meta_token_tree(
make::ext::ident_path("derive"),
make::token_tree(T!['('], vec![]).clone_for_update(),
))
.clone_for_update();
let derive =
make.attr_outer(make.meta_token_tree(
make.ident_path("derive"),
make.token_tree(T!['('], vec![]),
));
let mut editor = edit.make_editor(nominal.syntax());
let indent = IndentLevel::from_node(nominal.syntax());
@@ -57,11 +59,12 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
.children_with_tokens()
.find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
.map_or(Position::first_child_of(nominal.syntax()), Position::before);
editor.insert_all(
after_attrs_and_comments,
vec![
derive.syntax().syntax_element(),
make::tokens::whitespace(&format!("\n{indent}")).syntax_element(),
make.whitespace(&format!("\n{indent}")).syntax_element(),
],
);
@@ -72,7 +75,9 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
.expect("failed to get token tree out of Meta")
.r_paren_token()
.expect("make::attr_outer was expected to have a R_PAREN");
let tabstop_before = edit.make_tabstop_before(cap);
editor.add_annotation(delimiter, tabstop_before);
edit.add_file_edits(ctx.vfs_file_id(), editor);
}
@@ -57,7 +57,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
let if_exprs = successors(Some(if_expr.clone()), |expr| match expr.else_branch()? {
ast::ElseBranch::IfExpr(expr) => Some(expr),
ast::ElseBranch::Block(block) => {
let block = unwrap_trivial_block(block).clone_for_update();
let block = unwrap_trivial_block(block);
else_block = Some(block.reset_indent().indent(IndentLevel(1)));
None
}
@@ -91,7 +91,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
guard
};
let body = if_expr.then_branch()?.clone_for_update().indent(IndentLevel(1));
let body = if_expr.then_branch()?.indent(IndentLevel(1));
cond_bodies.push((cond, guard, body));
}
@@ -114,7 +114,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
let make_match_arm =
|(pat, guard, body): (_, Option<ast::Expr>, ast::BlockExpr)| {
// Dedent from original position, then indent for match arm
let body = body.dedent(indent).indent(IndentLevel::single());
let body = body.dedent(indent);
let body = unwrap_trivial_block(body);
match (pat, guard.map(|it| make.match_guard(it))) {
(Some(pat), guard) => make.match_arm(pat, guard, body),
@@ -127,8 +127,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
}
};
let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
let match_expr =
make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms)).indent(indent);
let expr = scrutinee_to_be_expr.reset_indent();
let match_expr = make.expr_match(expr, make.match_arm_list(arms)).indent(indent);
match_expr.into()
};
@@ -246,7 +246,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
first_arm.guard(),
second_arm.guard(),
)?;
let scrutinee = match_expr.expr()?;
let scrutinee = match_expr.expr()?.reset_indent();
let guard = guard.and_then(|it| it.condition());
let let_ = match &if_let_pat {
@@ -293,10 +293,8 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
} else {
condition
};
let then_expr =
then_expr.clone_for_update().reset_indent().indent(IndentLevel::single());
let else_expr =
else_expr.clone_for_update().reset_indent().indent(IndentLevel::single());
let then_expr = then_expr.reset_indent();
let else_expr = else_expr.reset_indent();
let then_block = make_block_expr(then_expr);
let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
let if_let_expr = make
@@ -956,7 +954,9 @@ fn nested_indent() {
r#"
fn main() {
if true {
$0if let Ok(rel_path) = path.strip_prefix(root_path) {
$0if let Ok(rel_path) = path.strip_prefix(root_path)
.and(x)
{
let rel_path = RelativePathBuf::from_path(rel_path)
.ok()?;
Some((*id, rel_path))
@@ -971,7 +971,8 @@ fn main() {
r#"
fn main() {
if true {
match path.strip_prefix(root_path) {
match path.strip_prefix(root_path)
.and(x) {
Ok(rel_path) => {
let rel_path = RelativePathBuf::from_path(rel_path)
.ok()?;
@@ -993,7 +994,9 @@ fn main() {
r#"
fn main() {
if true {
$0if let Ok(rel_path) = path.strip_prefix(root_path) {
$0if let Ok(rel_path) = path.strip_prefix(root_path)
.and(x)
{
Foo {
x: 1
}
@@ -1008,7 +1011,8 @@ fn main() {
r#"
fn main() {
if true {
match path.strip_prefix(root_path) {
match path.strip_prefix(root_path)
.and(x) {
Ok(rel_path) => {
Foo {
x: 1
@@ -1023,7 +1027,33 @@ fn main() {
}
}
"#,
)
);
check_assist(
replace_if_let_with_match,
r#"
fn main() {
if true {
$0if true
&& false
{
foo()
}
}
}
"#,
r#"
fn main() {
if true {
match true
&& false {
true => foo(),
false => (),
}
}
}
"#,
);
}
#[test]
@@ -1878,7 +1908,9 @@ fn nested_indent_match_to_if_let() {
r#"
fn main() {
if true {
$0match path.strip_prefix(root_path) {
$0match path.strip_prefix(root_path)
.and(x)
{
Ok(rel_path) => Foo {
x: 2
}
@@ -1892,7 +1924,8 @@ fn main() {
r#"
fn main() {
if true {
if let Ok(rel_path) = path.strip_prefix(root_path) {
if let Ok(rel_path) = path.strip_prefix(root_path)
.and(x) {
Foo {
x: 2
}
@@ -1911,7 +1944,9 @@ fn main() {
r#"
fn main() {
if true {
$0match path.strip_prefix(root_path) {
$0match path.strip_prefix(root_path)
.and(x)
{
Ok(rel_path) => {
let rel_path = RelativePathBuf::from_path(rel_path)
.ok()?;
@@ -1929,7 +1964,8 @@ fn main() {
r#"
fn main() {
if true {
if let Ok(rel_path) = path.strip_prefix(root_path) {
if let Ok(rel_path) = path.strip_prefix(root_path)
.and(x) {
let rel_path = RelativePathBuf::from_path(rel_path)
.ok()?;
Some((*id, rel_path))
@@ -95,7 +95,7 @@ pub(crate) fn complete_pattern(
if refutable || single_variant_enum(variant.parent_enum(ctx.db)) =>
{
acc.add_variant_pat(ctx, pattern_ctx, None, variant, Some(name.clone()));
true
false
}
hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
hir::ModuleDef::Const(..) => refutable,
@@ -122,7 +122,6 @@ fn foo() {
st Record
st Tuple
st Unit
ev TupleV
bn Record {} Record { field$1 }$0
bn Tuple() Tuple($1)$0
bn TupleV() TupleV($1)$0
@@ -159,8 +158,6 @@ fn foo(foo: Foo) { match foo { Foo { x: $0 } } }
expect![[r#"
en Bar
st Foo
ev Nil
ev Value
bn Foo {} Foo { x$1 }$0
bn Nil Nil$0
bn Value Value$0
@@ -189,7 +186,6 @@ fn foo() {
st Record
st Tuple
st Unit
ev Variant
bn Record {} Record { field$1 }$0
bn Tuple() Tuple($1)$0
bn Variant Variant$0
@@ -354,6 +350,34 @@ fn func() {
);
}
#[test]
fn enum_unqualified() {
check_with_base_items(
r#"
use Enum::*;
fn func() {
if let $0 = unknown {}
}
"#,
expect![[r#"
ct CONST
en Enum
ma makro!() macro_rules! makro
md module
st Record
st Tuple
st Unit
bn Record {} Record { field$1 }$0
bn RecordV {} RecordV { field$1 }$0
bn Tuple() Tuple($1)$0
bn TupleV() TupleV($1)$0
bn UnitV UnitV$0
kw mut
kw ref
"#]],
);
}
#[test]
fn completes_in_record_field_pat() {
check(
@@ -61,8 +61,6 @@ fn foo(baz: Baz) {
en Baz
en Result
md core
ev Err
ev Ok
bn Baz::Bar Baz::Bar$0
bn Baz::Foo Baz::Foo$0
bn Err() Err($1)$0
@@ -89,10 +87,6 @@ fn foo(baz: Baz) {
en Baz
en Result
md core
ev Bar
ev Err
ev Foo
ev Ok
bn Bar Bar$0
bn Err() Err($1)$0
bn Foo Foo$0
@@ -517,11 +517,13 @@ trait Trait<'a> {}
fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) {
x as _
//^^^^^^ error: cannot add auto trait to dyn bound via pointer cast
}
// (to test diagnostic list formatting)
fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) {
x as _
//^^^^^^ error: cannot add auto trait to dyn bound via pointer cast
}
"#,
);
@@ -26,7 +26,7 @@
use itertools::Itertools;
use proc_macro_api::{
MacroDylib, ProcMacroClient,
bidirectional_protocol::msg::{SubRequest, SubResponse},
bidirectional_protocol::msg::{ParentSpan, SubRequest, SubResponse},
};
use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
use span::{Span, SpanAnchor, SyntaxContext};
@@ -659,6 +659,44 @@ fn expand(
ctx: current_span.ctx.into_u32(),
})
}
SubRequest::SpanParent { file_id, ast_id, start, end, ctx } => {
let span = Span {
range: TextRange::new(TextSize::from(start), TextSize::from(end)),
anchor: SpanAnchor {
file_id: span::EditionedFileId::from_raw(file_id),
ast_id: span::ErasedFileAstId::from_raw(ast_id),
},
// SAFETY: We only receive spans from the server. If someone mess up the communication UB can happen,
// but that will be their problem.
ctx: unsafe { SyntaxContext::from_u32(ctx) },
};
if let Some(macro_call_id) = span.ctx.outer_expn(db) {
let macro_call_loc = db.lookup_intern_macro_call(macro_call_id.into());
let call_site_file = macro_call_loc.kind.file_id();
let call_site_ast_id = macro_call_loc.kind.erased_ast_id();
if let Some(editioned_file_id) = call_site_file.file_id() {
let range = db
.ast_id_map(editioned_file_id.into())
.get_erased(call_site_ast_id)
.text_range();
let parent_span = Some(ParentSpan {
file_id: editioned_file_id.editioned_file_id(db).as_u32(),
ast_id: span::ROOT_ERASED_FILE_AST_ID.into_raw(),
start: u32::from(range.start()),
end: u32::from(range.end()),
ctx: macro_call_loc.ctxt.into_u32(),
});
return Ok(SubResponse::SpanParentResult { parent_span });
}
}
Ok(SubResponse::SpanParentResult { parent_span: None })
}
};
match self.0.expand(
subtree.view(),
@@ -22,6 +22,7 @@ pub enum SubRequest {
LineColumn { file_id: u32, ast_id: u32, offset: u32 },
ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 },
SpanSource { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 },
SpanParent { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 },
}
#[derive(Debug, Serialize, Deserialize)]
@@ -50,11 +51,23 @@ pub enum SubResponse {
end: u32,
ctx: u32,
},
SpanParentResult {
parent_span: Option<ParentSpan>,
},
Cancel {
reason: String,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ParentSpan {
pub file_id: u32,
pub ast_id: u32,
pub start: u32,
pub end: u32,
pub ctx: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum BidirectionalMessage {
Request(Request),
@@ -309,6 +309,40 @@ fn span_source(
other => handle_failure(other),
}
}
fn span_parent(
&mut self,
proc_macro_srv::span::Span { range, anchor, ctx }: proc_macro_srv::span::Span,
) -> Option<proc_macro_srv::span::Span> {
let response = self.roundtrip(bidirectional::SubRequest::SpanParent {
file_id: anchor.file_id.as_u32(),
ast_id: anchor.ast_id.into_raw(),
start: range.start().into(),
end: range.end().into(),
ctx: ctx.into_u32(),
});
match response {
Ok(bidirectional::SubResponse::SpanParentResult { parent_span }) => {
parent_span.map(|bidirectional::ParentSpan { file_id, ast_id, start, end, ctx }| {
proc_macro_srv::span::Span {
range: proc_macro_srv::span::TextRange::new(
proc_macro_srv::span::TextSize::new(start),
proc_macro_srv::span::TextSize::new(end),
),
anchor: proc_macro_srv::span::SpanAnchor {
file_id: proc_macro_srv::span::EditionedFileId::from_raw(file_id),
ast_id: proc_macro_srv::span::ErasedFileAstId::from_raw(ast_id),
},
// SAFETY: spans originate from the server. If the protocol is violated,
// undefined behavior is the callers responsibility.
ctx: unsafe { proc_macro_srv::span::SyntaxContext::from_u32(ctx) },
}
})
}
other => handle_failure(other),
}
}
}
fn handle_expand_ra(
@@ -121,6 +121,7 @@ pub trait ProcMacroClientInterface {
fn byte_range(&mut self, span: Span) -> Range<usize>;
fn span_source(&mut self, span: Span) -> Span;
fn span_parent(&mut self, span: Span) -> Option<Span>;
}
const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
@@ -164,8 +164,10 @@ fn span_source_text(&mut self, span: Self::Span) -> Option<String> {
self.callback.as_mut()?.source_text(span)
}
fn span_parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
// FIXME requires db, looks up the parent call site
fn span_parent(&mut self, span: Self::Span) -> Option<Self::Span> {
if let Some(ref mut callback) = self.callback {
return callback.span_parent(span);
}
None
}
fn span_source(&mut self, span: Self::Span) -> Self::Span {
@@ -146,6 +146,10 @@ fn byte_range(&mut self, span: Span) -> Range<usize> {
fn span_source(&mut self, span: Span) -> Span {
span
}
fn span_parent(&mut self, _span: Span) -> Option<Span> {
None
}
}
pub fn assert_expand_with_callback(
@@ -158,14 +158,15 @@ pub(crate) fn make_lockfile_copy(
build: semver::BuildMetadata::EMPTY,
};
const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version =
semver::Version {
major: 1,
minor: 95,
patch: 0,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
};
// TODO: turn this into a const and remove pre once 1.95 is stable
#[allow(non_snake_case)]
let MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version = semver::Version {
major: 1,
minor: 95,
patch: 0,
pre: semver::Prerelease::new("nightly").unwrap(),
build: semver::BuildMetadata::EMPTY,
};
let usage = if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV {
LockfileUsage::WithEnvVar
@@ -414,7 +414,8 @@ pub(crate) fn on_sync_mut<N>(
let params = match not.extract::<N::Params>(N::METHOD) {
Ok(it) => it,
Err(ExtractError::JsonError { method, error }) => {
panic!("Invalid request\nMethod: {method}\n error: {error}",)
tracing::error!(method = %method, error = %error, "invalid notification");
return self;
}
Err(ExtractError::MethodMismatch(not)) => {
self.not = Some(not);

Some files were not shown because too many files have changed in this diff Show More