mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-23 21:23:20 +00:00
Auto merge of #3210 - rust-lang:rustup-2023-12-05, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
88d905cb3a
@ -197,23 +197,24 @@ impl<T> TypedArena<T> {
|
||||
start_ptr
|
||||
}
|
||||
|
||||
/// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`.
|
||||
///
|
||||
/// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before
|
||||
/// storing the elements in the arena.
|
||||
#[inline]
|
||||
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
|
||||
// This implementation is entirely separate to
|
||||
// `DroplessIterator::alloc_from_iter`, even though conceptually they
|
||||
// are the same.
|
||||
// Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
|
||||
// is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
|
||||
// reference to `self` and adding elements to the arena during iteration.
|
||||
//
|
||||
// `DroplessIterator` (in the fast case) writes elements from the
|
||||
// iterator one at a time into the allocated memory. That's easy
|
||||
// because the elements don't implement `Drop`. But for `TypedArena`
|
||||
// they do implement `Drop`, which means that if the iterator panics we
|
||||
// could end up with some allocated-but-uninitialized elements, which
|
||||
// will then cause UB in `TypedArena::drop`.
|
||||
// For this reason, if we pre-allocated any space for the elements of this iterator, we'd
|
||||
// have to track that some uninitialized elements are followed by some initialized elements,
|
||||
// else we might accidentally drop uninitialized memory if something panics or if the
|
||||
// iterator doesn't fill all the length we expected.
|
||||
//
|
||||
// Instead we use an approach where any iterator panic will occur
|
||||
// before the memory is allocated. This function is much less hot than
|
||||
// `DroplessArena::alloc_from_iter`, so it doesn't need to be
|
||||
// hyper-optimized.
|
||||
// So we collect all the elements beforehand, which takes care of reentrancy and panic
|
||||
// safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
|
||||
// doesn't need to be hyper-optimized.
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
|
||||
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
|
||||
@ -485,8 +486,9 @@ impl DroplessArena {
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `mem` is valid for writes up to
|
||||
/// `size_of::<T>() * len`.
|
||||
/// The caller must ensure that `mem` is valid for writes up to `size_of::<T>() * len`, and that
|
||||
/// that memory stays allocated and not shared for the lifetime of `self`. This must hold even
|
||||
/// if `iter.next()` allocates onto `self`.
|
||||
#[inline]
|
||||
unsafe fn write_from_iter<T, I: Iterator<Item = T>>(
|
||||
&self,
|
||||
@ -516,6 +518,8 @@ impl DroplessArena {
|
||||
|
||||
#[inline]
|
||||
pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
|
||||
// Warning: this function is reentrant: `iter` could hold a reference to `&self` and
|
||||
// allocate additional elements while we're iterating.
|
||||
let iter = iter.into_iter();
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(!mem::needs_drop::<T>());
|
||||
@ -524,7 +528,7 @@ impl DroplessArena {
|
||||
|
||||
match size_hint {
|
||||
(min, Some(max)) if min == max => {
|
||||
// We know the exact number of elements the iterator will produce here
|
||||
// We know the exact number of elements the iterator expects to produce here.
|
||||
let len = min;
|
||||
|
||||
if len == 0 {
|
||||
@ -532,10 +536,15 @@ impl DroplessArena {
|
||||
}
|
||||
|
||||
let mem = self.alloc_raw(Layout::array::<T>(len).unwrap()) as *mut T;
|
||||
// SAFETY: `write_from_iter` doesn't touch `self`. It only touches the slice we just
|
||||
// reserved. If the iterator panics or doesn't output `len` elements, this will
|
||||
// leave some unallocated slots in the arena, which is fine because we do not call
|
||||
// `drop`.
|
||||
unsafe { self.write_from_iter(iter, len, mem) }
|
||||
}
|
||||
(_, _) => {
|
||||
outline(move || -> &mut [T] {
|
||||
// Takes care of reentrancy.
|
||||
let mut vec: SmallVec<[_; 8]> = iter.collect();
|
||||
if vec.is_empty() {
|
||||
return &mut [];
|
||||
|
@ -14,8 +14,8 @@ use rustc_ast::*;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_target::asm;
|
||||
use std::collections::hash_map::Entry;
|
||||
@ -227,7 +227,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.create_def(
|
||||
parent_def_id.def_id,
|
||||
node_id,
|
||||
DefPathData::AnonConst,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
*op_sp,
|
||||
);
|
||||
|
@ -13,10 +13,9 @@ use rustc_ast::*;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_session::errors::report_lit_error;
|
||||
use rustc_span::source_map::{respan, Spanned};
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_span::{DesugaringKind, Span};
|
||||
use thin_vec::{thin_vec, ThinVec};
|
||||
@ -376,7 +375,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
self.create_def(
|
||||
parent_def_id.def_id,
|
||||
node_id,
|
||||
DefPathData::AnonConst,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
f.span,
|
||||
);
|
||||
|
@ -3,7 +3,6 @@ use super::ResolverAstLoweringExt;
|
||||
use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
|
||||
use super::{FnDeclKind, LoweringContext, ParamMode};
|
||||
|
||||
use hir::definitions::DefPathData;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::AssocCtxt;
|
||||
use rustc_ast::*;
|
||||
@ -1367,7 +1366,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let def_id = self.create_def(
|
||||
self.local_def_id(parent_node_id),
|
||||
param_node_id,
|
||||
DefPathData::TypeNs(sym::host),
|
||||
sym::host,
|
||||
DefKind::ConstParam,
|
||||
span,
|
||||
);
|
||||
@ -1427,13 +1426,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
|
||||
if let Some((span, hir_id, def_id)) = host_param_parts {
|
||||
let const_node_id = self.next_node_id();
|
||||
let anon_const = self.create_def(
|
||||
def_id,
|
||||
const_node_id,
|
||||
DefPathData::AnonConst,
|
||||
DefKind::AnonConst,
|
||||
span,
|
||||
);
|
||||
let anon_const =
|
||||
self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span);
|
||||
|
||||
let const_id = self.next_id();
|
||||
let const_expr_id = self.next_id();
|
||||
|
@ -58,7 +58,6 @@ use rustc_errors::{DiagnosticArgFromDisplay, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_middle::{
|
||||
@ -499,20 +498,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
&mut self,
|
||||
parent: LocalDefId,
|
||||
node_id: ast::NodeId,
|
||||
data: DefPathData,
|
||||
name: Symbol,
|
||||
def_kind: DefKind,
|
||||
span: Span,
|
||||
) -> LocalDefId {
|
||||
debug_assert_ne!(node_id, ast::DUMMY_NODE_ID);
|
||||
assert!(
|
||||
self.opt_local_def_id(node_id).is_none(),
|
||||
"adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
|
||||
"adding a def'n for node-id {:?} and def kind {:?} but a previous def'n exists: {:?}",
|
||||
node_id,
|
||||
data,
|
||||
def_kind,
|
||||
self.tcx.hir().def_key(self.local_def_id(node_id)),
|
||||
);
|
||||
|
||||
let def_id = self.tcx.at(span).create_def(parent, data, def_kind).def_id();
|
||||
let def_id = self.tcx.at(span).create_def(parent, name, def_kind).def_id();
|
||||
|
||||
debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
|
||||
self.resolver.node_id_to_def_id.insert(node_id, def_id);
|
||||
@ -809,7 +808,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let _def_id = self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
param,
|
||||
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
|
||||
kw::UnderscoreLifetime,
|
||||
DefKind::LifetimeParam,
|
||||
ident.span,
|
||||
);
|
||||
@ -1227,7 +1226,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let def_id = self.create_def(
|
||||
parent_def_id.def_id,
|
||||
node_id,
|
||||
DefPathData::AnonConst,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
span,
|
||||
);
|
||||
@ -1465,7 +1464,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
*def_node_id,
|
||||
DefPathData::TypeNs(ident.name),
|
||||
ident.name,
|
||||
DefKind::TyParam,
|
||||
span,
|
||||
);
|
||||
@ -1619,7 +1618,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let opaque_ty_def_id = self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
opaque_ty_node_id,
|
||||
DefPathData::ImplTrait,
|
||||
kw::Empty,
|
||||
DefKind::OpaqueTy,
|
||||
opaque_ty_span,
|
||||
);
|
||||
@ -1674,7 +1673,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let duplicated_lifetime_def_id = self.create_def(
|
||||
opaque_ty_def_id,
|
||||
duplicated_lifetime_node_id,
|
||||
DefPathData::LifetimeNs(lifetime.ident.name),
|
||||
lifetime.ident.name,
|
||||
DefKind::LifetimeParam,
|
||||
lifetime.ident.span,
|
||||
);
|
||||
@ -2549,7 +2548,7 @@ impl<'hir> GenericArgsCtor<'hir> {
|
||||
let def_id = lcx.create_def(
|
||||
lcx.current_hir_id_owner.def_id,
|
||||
id,
|
||||
DefPathData::AnonConst,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
span,
|
||||
);
|
||||
|
@ -489,6 +489,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> {
|
||||
if place.layout.is_unsized() {
|
||||
let tail = self.tcx.struct_tail_with_normalize(place.layout.ty, |ty| ty, || {});
|
||||
if matches!(tail.kind(), ty::Foreign(..)) {
|
||||
// Unsized locals and, at least conceptually, even unsized arguments must be copied
|
||||
// around, which requires dynamically determining their size. Therefore, we cannot
|
||||
// allow `extern` types here. Consult t-opsem before removing this check.
|
||||
panic!("unsized locals must not be `extern` types");
|
||||
}
|
||||
}
|
||||
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
|
||||
|
||||
if place.layout.is_zst() {
|
||||
|
@ -594,7 +594,7 @@ fn push_unqualified_item_name(
|
||||
DefPathData::CrateRoot => {
|
||||
output.push_str(tcx.crate_name(def_id.krate).as_str());
|
||||
}
|
||||
DefPathData::ClosureExpr => {
|
||||
DefPathData::Closure => {
|
||||
let label = coroutine_kind_label(tcx.coroutine_kind(def_id));
|
||||
|
||||
push_disambiguated_special_name(
|
||||
|
@ -414,6 +414,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
|
||||
// value is through `undef`/`poison`, and the store itself is useless.
|
||||
}
|
||||
OperandValue::Ref(r, None, source_align) => {
|
||||
assert!(dest.layout.is_sized(), "cannot directly store unsized values");
|
||||
if flags.contains(MemFlags::NONTEMPORAL) {
|
||||
// HACK(nox): This is inefficient but there is no nontemporal memcpy.
|
||||
let ty = bx.backend_type(dest.layout);
|
||||
|
@ -143,7 +143,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||
// Simple cases, which don't need DST adjustment:
|
||||
// * no metadata available - just log the case
|
||||
// * known alignment - sized types, `[T]`, `str` or a foreign type
|
||||
// * packed struct - there is no alignment padding
|
||||
// Note that looking at `field.align` is incorrect since that is not necessarily equal
|
||||
// to the dynamic alignment of the type.
|
||||
match field.ty.kind() {
|
||||
_ if self.llextra.is_none() => {
|
||||
debug!(
|
||||
@ -154,14 +155,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||
}
|
||||
_ if field.is_sized() => return simple(),
|
||||
ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(),
|
||||
ty::Adt(def, _) => {
|
||||
if def.repr().packed() {
|
||||
// FIXME(eddyb) generalize the adjustment when we
|
||||
// start supporting packing to larger alignments.
|
||||
assert_eq!(self.layout.align.abi.bytes(), 1);
|
||||
return simple();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -186,7 +179,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||
let unaligned_offset = bx.cx().const_usize(offset.bytes());
|
||||
|
||||
// Get the alignment of the field
|
||||
let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
|
||||
let (_, mut unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
|
||||
|
||||
// For packed types, we need to cap alignment.
|
||||
if let ty::Adt(def, _) = self.layout.ty.kind()
|
||||
&& let Some(packed) = def.repr().pack
|
||||
{
|
||||
let packed = bx.const_usize(packed.bytes());
|
||||
let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed);
|
||||
unsized_align = bx.select(cmp, unsized_align, packed)
|
||||
}
|
||||
|
||||
// Bump the unaligned offset up to the appropriate alignment
|
||||
let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align);
|
||||
|
@ -282,9 +282,7 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> {
|
||||
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data
|
||||
== DefPathData::ClosureExpr
|
||||
{
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
|
||||
write!(f, "inside closure")
|
||||
} else {
|
||||
// Note: this triggers a `good_path_delayed_bug` state, which means that if we ever
|
||||
@ -299,7 +297,7 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
impl<'tcx> FrameInfo<'tcx> {
|
||||
pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote {
|
||||
let span = self.span;
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
|
||||
errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
|
||||
} else {
|
||||
let instance = format!("{}", self.instance);
|
||||
|
@ -3,17 +3,22 @@
|
||||
//! and miri.
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::{
|
||||
self,
|
||||
interpret::{Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar},
|
||||
BinOp, ConstValue, NonDivergingIntrinsic,
|
||||
};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_middle::{
|
||||
mir::{
|
||||
self,
|
||||
interpret::{
|
||||
Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar,
|
||||
},
|
||||
BinOp, ConstValue, NonDivergingIntrinsic,
|
||||
},
|
||||
ty::layout::TyAndLayout,
|
||||
};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{Abi, Primitive, Size};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
use super::{
|
||||
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
|
||||
@ -22,23 +27,6 @@ use super::{
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
|
||||
let size = match kind {
|
||||
Primitive::Int(integer, _) => integer.size(),
|
||||
_ => bug!("invalid `{}` argument: {:?}", name, bits),
|
||||
};
|
||||
let extra = 128 - u128::from(size.bits());
|
||||
let bits_out = match name {
|
||||
sym::ctpop => u128::from(bits.count_ones()),
|
||||
sym::ctlz => u128::from(bits.leading_zeros()) - extra,
|
||||
sym::cttz => u128::from((bits << extra).trailing_zeros()) - extra,
|
||||
sym::bswap => (bits << extra).swap_bytes(),
|
||||
sym::bitreverse => (bits << extra).reverse_bits(),
|
||||
_ => bug!("not a numeric intrinsic: {}", name),
|
||||
};
|
||||
Scalar::from_uint(bits_out, size)
|
||||
}
|
||||
|
||||
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
|
||||
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> {
|
||||
let path = crate::util::type_name(tcx, ty);
|
||||
@ -179,30 +167,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
| sym::bswap
|
||||
| sym::bitreverse => {
|
||||
let ty = instance_args.type_at(0);
|
||||
let layout_of = self.layout_of(ty)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
let val = self.read_scalar(&args[0])?;
|
||||
let bits = val.to_bits(layout_of.size)?;
|
||||
let kind = match layout_of.abi {
|
||||
Abi::Scalar(scalar) => scalar.primitive(),
|
||||
_ => span_bug!(
|
||||
self.cur_span(),
|
||||
"{} called on invalid type {:?}",
|
||||
intrinsic_name,
|
||||
ty
|
||||
),
|
||||
};
|
||||
let (nonzero, actual_intrinsic_name) = match intrinsic_name {
|
||||
sym::cttz_nonzero => (true, sym::cttz),
|
||||
sym::ctlz_nonzero => (true, sym::ctlz),
|
||||
other => (false, other),
|
||||
};
|
||||
if nonzero && bits == 0 {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_call_nonzero_intrinsic,
|
||||
name = intrinsic_name,
|
||||
);
|
||||
}
|
||||
let out_val = numeric_intrinsic(actual_intrinsic_name, bits, kind);
|
||||
let out_val = self.numeric_intrinsic(intrinsic_name, val, layout)?;
|
||||
self.write_scalar(out_val, dest)?;
|
||||
}
|
||||
sym::saturating_add | sym::saturating_sub => {
|
||||
@ -493,6 +460,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn numeric_intrinsic(
|
||||
&self,
|
||||
name: Symbol,
|
||||
val: Scalar<M::Provenance>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
assert!(layout.ty.is_integral(), "invalid type for numeric intrinsic: {}", layout.ty);
|
||||
let bits = val.to_bits(layout.size)?;
|
||||
let extra = 128 - u128::from(layout.size.bits());
|
||||
let bits_out = match name {
|
||||
sym::ctpop => u128::from(bits.count_ones()),
|
||||
sym::ctlz_nonzero | sym::cttz_nonzero if bits == 0 => {
|
||||
throw_ub_custom!(fluent::const_eval_call_nonzero_intrinsic, name = name,);
|
||||
}
|
||||
sym::ctlz | sym::ctlz_nonzero => u128::from(bits.leading_zeros()) - extra,
|
||||
sym::cttz | sym::cttz_nonzero => u128::from((bits << extra).trailing_zeros()) - extra,
|
||||
sym::bswap => (bits << extra).swap_bytes(),
|
||||
sym::bitreverse => (bits << extra).reverse_bits(),
|
||||
_ => bug!("not a numeric intrinsic: {}", name),
|
||||
};
|
||||
Ok(Scalar::from_uint(bits_out, layout.size))
|
||||
}
|
||||
|
||||
pub fn exact_div(
|
||||
&mut self,
|
||||
a: &ImmTy<'tcx, M::Provenance>,
|
||||
|
@ -163,7 +163,17 @@ where
|
||||
// With custom DSTS, this *will* execute user-defined code, but the same
|
||||
// happens at run-time so that's okay.
|
||||
match self.size_and_align_of(&base_meta, &field_layout)? {
|
||||
Some((_, align)) => (base_meta, offset.align_to(align)),
|
||||
Some((_, align)) => {
|
||||
// For packed types, we need to cap alignment.
|
||||
let align = if let ty::Adt(def, _) = base.layout().ty.kind()
|
||||
&& let Some(packed) = def.repr().pack
|
||||
{
|
||||
align.min(packed)
|
||||
} else {
|
||||
align
|
||||
};
|
||||
(base_meta, offset.align_to(align))
|
||||
}
|
||||
None => {
|
||||
// For unsized types with an extern type tail we perform no adjustments.
|
||||
// NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::definitions::DefPathData;
|
||||
use crate::hir;
|
||||
|
||||
use rustc_ast as ast;
|
||||
@ -45,6 +46,7 @@ pub enum NonMacroAttrKind {
|
||||
}
|
||||
|
||||
/// What kind of definition something is; e.g., `mod` vs `struct`.
|
||||
/// `enum DefPathData` may need to be updated if a new variant is added here.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)]
|
||||
pub enum DefKind {
|
||||
// Type namespace
|
||||
@ -221,6 +223,41 @@ impl DefKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn def_path_data(self, name: Symbol) -> DefPathData {
|
||||
match self {
|
||||
DefKind::Mod
|
||||
| DefKind::Struct
|
||||
| DefKind::Union
|
||||
| DefKind::Enum
|
||||
| DefKind::Variant
|
||||
| DefKind::Trait
|
||||
| DefKind::TyAlias
|
||||
| DefKind::ForeignTy
|
||||
| DefKind::TraitAlias
|
||||
| DefKind::AssocTy
|
||||
| DefKind::TyParam
|
||||
| DefKind::ExternCrate => DefPathData::TypeNs(name),
|
||||
DefKind::Fn
|
||||
| DefKind::Const
|
||||
| DefKind::ConstParam
|
||||
| DefKind::Static(..)
|
||||
| DefKind::AssocFn
|
||||
| DefKind::AssocConst
|
||||
| DefKind::Field => DefPathData::ValueNs(name),
|
||||
DefKind::Macro(..) => DefPathData::MacroNs(name),
|
||||
DefKind::LifetimeParam => DefPathData::LifetimeNs(name),
|
||||
DefKind::Ctor(..) => DefPathData::Ctor,
|
||||
DefKind::Use => DefPathData::Use,
|
||||
DefKind::ForeignMod => DefPathData::ForeignMod,
|
||||
DefKind::AnonConst => DefPathData::AnonConst,
|
||||
DefKind::InlineConst => DefPathData::AnonConst,
|
||||
DefKind::OpaqueTy => DefPathData::OpaqueTy,
|
||||
DefKind::GlobalAsm => DefPathData::GlobalAsm,
|
||||
DefKind::Impl { .. } => DefPathData::Impl,
|
||||
DefKind::Closure => DefPathData::Closure,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_fn_like(self) -> bool {
|
||||
matches!(self, DefKind::Fn | DefKind::AssocFn | DefKind::Closure)
|
||||
|
@ -246,6 +246,7 @@ impl DefPath {
|
||||
}
|
||||
}
|
||||
|
||||
/// New variants should only be added in synchronization with `enum DefKind`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
|
||||
pub enum DefPathData {
|
||||
// Root: these should only be used for the root nodes, because
|
||||
@ -271,7 +272,7 @@ pub enum DefPathData {
|
||||
/// Something in the lifetime namespace.
|
||||
LifetimeNs(Symbol),
|
||||
/// A closure expression.
|
||||
ClosureExpr,
|
||||
Closure,
|
||||
|
||||
// Subportions of items:
|
||||
/// Implicit constructor for a unit or tuple-like struct or enum variant.
|
||||
@ -280,9 +281,7 @@ pub enum DefPathData {
|
||||
AnonConst,
|
||||
/// An existential `impl Trait` type node.
|
||||
/// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
|
||||
ImplTrait,
|
||||
/// `impl Trait` generated associated type node.
|
||||
ImplTraitAssocTy,
|
||||
OpaqueTy,
|
||||
}
|
||||
|
||||
impl Definitions {
|
||||
@ -403,16 +402,17 @@ impl DefPathData {
|
||||
pub fn get_opt_name(&self) -> Option<Symbol> {
|
||||
use self::DefPathData::*;
|
||||
match *self {
|
||||
TypeNs(name) if name == kw::Empty => None,
|
||||
TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
|
||||
|
||||
Impl | ForeignMod | CrateRoot | Use | GlobalAsm | ClosureExpr | Ctor | AnonConst
|
||||
| ImplTrait | ImplTraitAssocTy => None,
|
||||
Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst
|
||||
| OpaqueTy => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> DefPathDataName {
|
||||
use self::DefPathData::*;
|
||||
match *self {
|
||||
TypeNs(name) if name == kw::Empty => DefPathDataName::Anon { namespace: sym::opaque },
|
||||
TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => {
|
||||
DefPathDataName::Named(name)
|
||||
}
|
||||
@ -422,10 +422,10 @@ impl DefPathData {
|
||||
ForeignMod => DefPathDataName::Anon { namespace: kw::Extern },
|
||||
Use => DefPathDataName::Anon { namespace: kw::Use },
|
||||
GlobalAsm => DefPathDataName::Anon { namespace: sym::global_asm },
|
||||
ClosureExpr => DefPathDataName::Anon { namespace: sym::closure },
|
||||
Closure => DefPathDataName::Anon { namespace: sym::closure },
|
||||
Ctor => DefPathDataName::Anon { namespace: sym::constructor },
|
||||
AnonConst => DefPathDataName::Anon { namespace: sym::constant },
|
||||
ImplTrait | ImplTraitAssocTy => DefPathDataName::Anon { namespace: sym::opaque },
|
||||
OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,7 +326,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
) -> RelateResult<'tcx, ty::Const<'tcx>> {
|
||||
let span =
|
||||
self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span;
|
||||
let Generalization { value, needs_wf: _ } = generalize::generalize(
|
||||
// FIXME(generic_const_exprs): Occurs check failures for unevaluated
|
||||
// constants and generic expressions are not yet handled correctly.
|
||||
let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize(
|
||||
self,
|
||||
&mut CombineDelegate { infcx: self, span, param_env },
|
||||
ct,
|
||||
@ -445,7 +447,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
// `'?2` and `?3` are fresh region/type inference
|
||||
// variables. (Down below, we will relate `a_ty <: b_ty`,
|
||||
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
|
||||
let Generalization { value: b_ty, needs_wf } = generalize::generalize(
|
||||
let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize(
|
||||
self.infcx,
|
||||
&mut CombineDelegate {
|
||||
infcx: self.infcx,
|
||||
@ -457,7 +459,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
ambient_variance,
|
||||
)?;
|
||||
|
||||
debug!(?b_ty);
|
||||
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
|
||||
|
||||
if needs_wf {
|
||||
@ -477,19 +478,52 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
// relations wind up attributed to the same spans. We need
|
||||
// to associate causes/spans with each of the relations in
|
||||
// the stack to get this right.
|
||||
match ambient_variance {
|
||||
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
|
||||
ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
|
||||
ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
|
||||
ty::Contravariant,
|
||||
ty::VarianceDiagInfo::default(),
|
||||
a_ty,
|
||||
b_ty,
|
||||
),
|
||||
ty::Variance::Bivariant => {
|
||||
unreachable!("no code should be generalizing bivariantly (currently)")
|
||||
if b_ty.is_ty_var() {
|
||||
// This happens for cases like `<?0 as Trait>::Assoc == ?0`.
|
||||
// We can't instantiate `?0` here as that would result in a
|
||||
// cyclic type. We instead delay the unification in case
|
||||
// the alias can be normalized to something which does not
|
||||
// mention `?0`.
|
||||
|
||||
// FIXME(-Ztrait-solver=next): replace this with `AliasRelate`
|
||||
let &ty::Alias(kind, data) = a_ty.kind() else {
|
||||
bug!("generalization should only result in infer vars for aliases");
|
||||
};
|
||||
if !self.infcx.next_trait_solver() {
|
||||
// The old solver only accepts projection predicates for associated types.
|
||||
match kind {
|
||||
ty::AliasKind::Projection => {}
|
||||
ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque => {
|
||||
return Err(TypeError::CyclicTy(a_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
// FIXME: This does not handle subtyping correctly, we should switch to
|
||||
// alias-relate in the new solver and could instead create a new inference
|
||||
// variable for `a_ty`, emitting `Projection(a_ty, a_infer)` and
|
||||
// `a_infer <: b_ty`.
|
||||
self.obligations.push(Obligation::new(
|
||||
self.tcx(),
|
||||
self.trace.cause.clone(),
|
||||
self.param_env,
|
||||
ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
|
||||
))
|
||||
} else {
|
||||
match ambient_variance {
|
||||
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
|
||||
ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
|
||||
ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
|
||||
ty::Contravariant,
|
||||
ty::VarianceDiagInfo::default(),
|
||||
a_ty,
|
||||
b_ty,
|
||||
),
|
||||
ty::Variance::Bivariant => {
|
||||
unreachable!("no code should be generalizing bivariantly (currently)")
|
||||
}
|
||||
}?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
use std::mem;
|
||||
|
||||
use rustc_data_structures::sso::SsoHashMap;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::visit::MaxUniverse;
|
||||
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::infer::nll_relate::TypeRelatingDelegate;
|
||||
use crate::infer::type_variable::TypeVariableValue;
|
||||
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
|
||||
use crate::infer::{InferCtxt, RegionVariableOrigin};
|
||||
|
||||
/// Attempts to generalize `term` for the type variable `for_vid`.
|
||||
@ -38,27 +41,30 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
|
||||
root_vid,
|
||||
for_universe,
|
||||
root_term: term.into(),
|
||||
in_alias: false,
|
||||
needs_wf: false,
|
||||
cache: Default::default(),
|
||||
};
|
||||
|
||||
assert!(!term.has_escaping_bound_vars());
|
||||
let value = generalizer.relate(term, term)?;
|
||||
let value_may_be_infer = generalizer.relate(term, term)?;
|
||||
let needs_wf = generalizer.needs_wf;
|
||||
Ok(Generalization { value, needs_wf })
|
||||
Ok(Generalization { value_may_be_infer, needs_wf })
|
||||
}
|
||||
|
||||
/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
|
||||
/// in the generalizer code.
|
||||
pub trait GeneralizerDelegate<'tcx> {
|
||||
pub(super) trait GeneralizerDelegate<'tcx> {
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx>;
|
||||
|
||||
fn forbid_inference_vars() -> bool;
|
||||
|
||||
fn span(&self) -> Span;
|
||||
|
||||
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
|
||||
}
|
||||
|
||||
pub struct CombineDelegate<'cx, 'tcx> {
|
||||
pub(super) struct CombineDelegate<'cx, 'tcx> {
|
||||
pub infcx: &'cx InferCtxt<'tcx>,
|
||||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
pub span: Span,
|
||||
@ -73,6 +79,10 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
|
||||
false
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
|
||||
// FIXME: This is non-ideal because we don't give a
|
||||
// very descriptive origin for this region variable.
|
||||
@ -93,6 +103,10 @@ where
|
||||
<Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
<Self as TypeRelatingDelegate<'tcx>>::span(&self)
|
||||
}
|
||||
|
||||
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
|
||||
<Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe)
|
||||
}
|
||||
@ -139,6 +153,13 @@ struct Generalizer<'me, 'tcx, D> {
|
||||
|
||||
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
|
||||
|
||||
/// This is set once we're generalizing the arguments of an alias.
|
||||
///
|
||||
/// This is necessary to correctly handle
|
||||
/// `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. This equality can
|
||||
/// hold by either normalizing the outer or the inner associated type.
|
||||
in_alias: bool,
|
||||
|
||||
/// See the field `needs_wf` in `Generalization`.
|
||||
needs_wf: bool,
|
||||
}
|
||||
@ -193,7 +214,7 @@ where
|
||||
opt_variances,
|
||||
a_subst,
|
||||
b_subst,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -309,6 +330,44 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
ty::Alias(kind, data) => {
|
||||
// An occurs check failure inside of an alias does not mean
|
||||
// that the types definitely don't unify. We may be able
|
||||
// to normalize the alias after all.
|
||||
//
|
||||
// We handle this by lazily equating the alias and generalizing
|
||||
// it to an inference variable.
|
||||
let is_nested_alias = mem::replace(&mut self.in_alias, true);
|
||||
let result = match self.relate(data, data) {
|
||||
Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)),
|
||||
Err(e) => {
|
||||
if is_nested_alias {
|
||||
return Err(e);
|
||||
} else {
|
||||
let mut visitor = MaxUniverse::new();
|
||||
t.visit_with(&mut visitor);
|
||||
let infer_replacement_is_complete =
|
||||
self.for_universe.can_name(visitor.max_universe())
|
||||
&& !t.has_escaping_bound_vars();
|
||||
if !infer_replacement_is_complete {
|
||||
warn!("may incompletely handle alias type: {t:?}");
|
||||
}
|
||||
|
||||
debug!("generalization failure in alias");
|
||||
Ok(self.infcx.next_ty_var_in_universe(
|
||||
TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: self.delegate.span(),
|
||||
},
|
||||
self.for_universe,
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
self.in_alias = is_nested_alias;
|
||||
result
|
||||
}
|
||||
|
||||
_ => relate::structurally_relate_tys(self, t, t),
|
||||
}?;
|
||||
|
||||
@ -456,8 +515,16 @@ where
|
||||
/// not only the generalized type, but also a bool flag
|
||||
/// indicating whether further WF checks are needed.
|
||||
#[derive(Debug)]
|
||||
pub struct Generalization<T> {
|
||||
pub value: T,
|
||||
pub(super) struct Generalization<T> {
|
||||
/// When generalizing `<?0 as Trait>::Assoc` or
|
||||
/// `<T as Bar<<?0 as Foo>::Assoc>>::Assoc`
|
||||
/// for `?0` generalization returns an inference
|
||||
/// variable.
|
||||
///
|
||||
/// This has to be handled wotj care as it can
|
||||
/// otherwise very easily result in infinite
|
||||
/// recursion.
|
||||
pub(super) value_may_be_infer: T,
|
||||
|
||||
/// If true, then the generalized type may not be well-formed,
|
||||
/// even if the source type is well-formed, so we should add an
|
||||
@ -484,5 +551,5 @@ pub struct Generalization<T> {
|
||||
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
|
||||
/// holds, which in turn implies that `?C::Item == ?D`. So once
|
||||
/// `?C` is constrained, that should suffice to restrict `?D`.
|
||||
pub needs_wf: bool,
|
||||
pub(super) needs_wf: bool,
|
||||
}
|
||||
|
@ -214,13 +214,17 @@ where
|
||||
}
|
||||
|
||||
fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
let Generalization { value: ty, needs_wf: _ } = generalize::generalize(
|
||||
let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize(
|
||||
self.infcx,
|
||||
&mut self.delegate,
|
||||
ty,
|
||||
for_vid,
|
||||
self.ambient_variance,
|
||||
)?;
|
||||
|
||||
if ty.is_ty_var() {
|
||||
span_bug!(self.delegate.span(), "occurs check failure in MIR typeck");
|
||||
}
|
||||
Ok(ty)
|
||||
}
|
||||
|
||||
|
@ -977,7 +977,7 @@ impl<'tcx> TyCtxtAt<'tcx> {
|
||||
pub fn create_def(
|
||||
self,
|
||||
parent: LocalDefId,
|
||||
data: hir::definitions::DefPathData,
|
||||
name: Symbol,
|
||||
def_kind: DefKind,
|
||||
) -> TyCtxtFeed<'tcx, LocalDefId> {
|
||||
// This function modifies `self.definitions` using a side-effect.
|
||||
@ -1000,6 +1000,7 @@ impl<'tcx> TyCtxtAt<'tcx> {
|
||||
// This is fine because:
|
||||
// - those queries are `eval_always` so we won't miss their result changing;
|
||||
// - this write will have happened before these queries are called.
|
||||
let data = def_kind.def_path_data(name);
|
||||
let key = self.untracked.definitions.write().create_def(parent, data);
|
||||
|
||||
let feed = TyCtxtFeed { tcx: self.tcx, key };
|
||||
|
@ -215,7 +215,7 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
};
|
||||
matches!(
|
||||
tcx.def_key(def_id).disambiguated_data.data,
|
||||
DefPathData::Ctor | DefPathData::ClosureExpr
|
||||
DefPathData::Ctor | DefPathData::Closure
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ pub trait Printer<'tcx>: Sized {
|
||||
|
||||
match key.disambiguated_data.data {
|
||||
// Closures' own generics are only captures, don't print them.
|
||||
DefPathData::ClosureExpr => {}
|
||||
DefPathData::Closure => {}
|
||||
// This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
|
||||
// Anon consts doesn't have their own generics, and inline consts' own
|
||||
// generics are their inferred types, so don't print them.
|
||||
|
@ -1804,13 +1804,13 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> {
|
||||
// (but also some things just print a `DefId` generally so maybe we need this?)
|
||||
fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace {
|
||||
match tcx.def_key(def_id).disambiguated_data.data {
|
||||
DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => {
|
||||
DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::OpaqueTy => {
|
||||
Namespace::TypeNS
|
||||
}
|
||||
|
||||
DefPathData::ValueNs(..)
|
||||
| DefPathData::AnonConst
|
||||
| DefPathData::ClosureExpr
|
||||
| DefPathData::Closure
|
||||
| DefPathData::Ctor => Namespace::ValueNS,
|
||||
|
||||
DefPathData::MacroNs(..) => Namespace::MacroNS,
|
||||
|
@ -599,9 +599,9 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
||||
// an or-pattern. Panics if `self` is empty.
|
||||
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
|
||||
self.head().flatten_or_pat().into_iter().map(move |pat| {
|
||||
let mut new_pats = smallvec![pat];
|
||||
new_pats.extend_from_slice(&self.pats[1..]);
|
||||
PatStack { pats: new_pats }
|
||||
let mut new = self.clone();
|
||||
new.pats[0] = pat;
|
||||
new
|
||||
})
|
||||
}
|
||||
|
||||
@ -732,18 +732,11 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
}
|
||||
|
||||
/// Build a new matrix from an iterator of `MatchArm`s.
|
||||
fn new<'a>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
iter: impl Iterator<Item = &'a MatchArm<'p, 'tcx>>,
|
||||
scrut_ty: Ty<'tcx>,
|
||||
) -> Self
|
||||
where
|
||||
'p: 'a,
|
||||
{
|
||||
fn new(cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>) -> Self {
|
||||
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP));
|
||||
let wildcard_row = PatStack::from_pattern(wild_pattern);
|
||||
let mut matrix = Matrix { rows: vec![], wildcard_row };
|
||||
for (row_id, arm) in iter.enumerate() {
|
||||
let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), wildcard_row };
|
||||
for (row_id, arm) in arms.iter().enumerate() {
|
||||
let v = MatrixRow {
|
||||
pats: PatStack::from_pattern(arm.pat),
|
||||
parent_row: row_id, // dummy, we won't read it
|
||||
@ -806,7 +799,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
ctor: &Constructor<'tcx>,
|
||||
) -> Matrix<'p, 'tcx> {
|
||||
let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor);
|
||||
let mut matrix = Matrix { rows: vec![], wildcard_row };
|
||||
let mut matrix = Matrix { rows: Vec::new(), wildcard_row };
|
||||
for (i, row) in self.rows().enumerate() {
|
||||
if ctor.is_covered_by(pcx, row.head().ctor()) {
|
||||
let new_row = row.pop_head_constructor(pcx, ctor, i);
|
||||
@ -1386,7 +1379,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
|
||||
arms: &[MatchArm<'p, 'tcx>],
|
||||
scrut_ty: Ty<'tcx>,
|
||||
) -> UsefulnessReport<'p, 'tcx> {
|
||||
let mut matrix = Matrix::new(cx, arms.iter(), scrut_ty);
|
||||
let mut matrix = Matrix::new(cx, arms, scrut_ty);
|
||||
let non_exhaustiveness_witnesses =
|
||||
compute_exhaustiveness_and_reachability(cx, &mut matrix, true);
|
||||
|
||||
|
@ -1,60 +1,14 @@
|
||||
//! Random access inspection of the results of a dataflow analysis.
|
||||
|
||||
use crate::{framework::BitSetExt, CloneAnalysis};
|
||||
use crate::framework::BitSetExt;
|
||||
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::{self, BasicBlock, Location};
|
||||
|
||||
use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned};
|
||||
|
||||
// `AnalysisResults` is needed as an impl such as the following has an unconstrained type
|
||||
// parameter:
|
||||
// ```
|
||||
// impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R>
|
||||
// where
|
||||
// A: Analysis<'tcx>,
|
||||
// E: Borrow<EntrySets<'tcx, A>>,
|
||||
// R: Results<'tcx, A, E>,
|
||||
// {}
|
||||
// ```
|
||||
|
||||
/// A type representing the analysis results consumed by a `ResultsCursor`.
|
||||
pub trait AnalysisResults<'tcx, A>: BorrowMut<Results<'tcx, A, Self::EntrySets>>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
/// The type containing the entry sets for this `Results` type.
|
||||
///
|
||||
/// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`.
|
||||
type EntrySets: Borrow<EntrySets<'tcx, A>>;
|
||||
}
|
||||
impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
E: Borrow<EntrySets<'tcx, A>>,
|
||||
{
|
||||
type EntrySets = E;
|
||||
}
|
||||
impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
E: Borrow<EntrySets<'tcx, A>>,
|
||||
{
|
||||
type EntrySets = E;
|
||||
}
|
||||
|
||||
/// A `ResultsCursor` that borrows the underlying `Results`.
|
||||
pub type ResultsRefCursor<'res, 'mir, 'tcx, A> =
|
||||
ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>;
|
||||
|
||||
/// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This
|
||||
/// allows multiple cursors over the same `Results`.
|
||||
pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> =
|
||||
ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>;
|
||||
use super::{Analysis, Direction, Effect, EffectIndex, Results};
|
||||
|
||||
/// Allows random access inspection of the results of a dataflow analysis.
|
||||
///
|
||||
@ -62,15 +16,12 @@ pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> =
|
||||
/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are
|
||||
/// visited in *reverse* order—performance will be quadratic in the number of statements in the
|
||||
/// block. The order in which basic blocks are inspected has no impact on performance.
|
||||
///
|
||||
/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
|
||||
/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
|
||||
pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
|
||||
pub struct ResultsCursor<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
results: R,
|
||||
results: Results<'tcx, A>,
|
||||
state: A::Domain,
|
||||
|
||||
pos: CursorPosition,
|
||||
@ -84,7 +35,7 @@ where
|
||||
reachable_blocks: BitSet<BasicBlock>,
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
|
||||
impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
@ -99,30 +50,13 @@ where
|
||||
}
|
||||
|
||||
/// Unwraps this cursor, returning the underlying `Results`.
|
||||
pub fn into_results(self) -> R {
|
||||
pub fn into_results(self) -> Results<'tcx, A> {
|
||||
self.results
|
||||
}
|
||||
}
|
||||
|
||||
impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>
|
||||
where
|
||||
A: Analysis<'tcx> + CloneAnalysis,
|
||||
{
|
||||
/// Creates a new cursor over the same `Results`. Note that the cursor's position is *not*
|
||||
/// copied.
|
||||
pub fn new_cursor(&self) -> Self {
|
||||
Self::new(self.body, self.results.reclone_analysis())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
R: AnalysisResults<'tcx, A>,
|
||||
{
|
||||
/// Returns a new cursor that can inspect `results`.
|
||||
pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
|
||||
let bottom_value = results.borrow().analysis.bottom_value(body);
|
||||
pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
|
||||
let bottom_value = results.analysis.bottom_value(body);
|
||||
ResultsCursor {
|
||||
body,
|
||||
results,
|
||||
@ -147,23 +81,23 @@ where
|
||||
}
|
||||
|
||||
/// Returns the underlying `Results`.
|
||||
pub fn results(&self) -> &Results<'tcx, A, R::EntrySets> {
|
||||
self.results.borrow()
|
||||
pub fn results(&self) -> &Results<'tcx, A> {
|
||||
&self.results
|
||||
}
|
||||
|
||||
/// Returns the underlying `Results`.
|
||||
pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> {
|
||||
self.results.borrow_mut()
|
||||
pub fn mut_results(&mut self) -> &mut Results<'tcx, A> {
|
||||
&mut self.results
|
||||
}
|
||||
|
||||
/// Returns the `Analysis` used to generate the underlying `Results`.
|
||||
pub fn analysis(&self) -> &A {
|
||||
&self.results.borrow().analysis
|
||||
&self.results.analysis
|
||||
}
|
||||
|
||||
/// Returns the `Analysis` used to generate the underlying `Results`.
|
||||
pub fn mut_analysis(&mut self) -> &mut A {
|
||||
&mut self.results.borrow_mut().analysis
|
||||
&mut self.results.analysis
|
||||
}
|
||||
|
||||
/// Resets the cursor to hold the entry set for the given basic block.
|
||||
@ -175,7 +109,7 @@ where
|
||||
#[cfg(debug_assertions)]
|
||||
assert!(self.reachable_blocks.contains(block));
|
||||
|
||||
self.state.clone_from(self.results.borrow().entry_set_for_block(block));
|
||||
self.state.clone_from(self.results.entry_set_for_block(block));
|
||||
self.pos = CursorPosition::block_entry(block);
|
||||
self.state_needs_reset = false;
|
||||
}
|
||||
@ -264,11 +198,10 @@ where
|
||||
)
|
||||
};
|
||||
|
||||
let analysis = &mut self.results.borrow_mut().analysis;
|
||||
let target_effect_index = effect.at_index(target.statement_index);
|
||||
|
||||
A::Direction::apply_effects_in_range(
|
||||
analysis,
|
||||
&mut self.results.analysis,
|
||||
&mut self.state,
|
||||
target.block,
|
||||
block_data,
|
||||
@ -284,12 +217,12 @@ where
|
||||
/// This can be used, e.g., to apply the call return effect directly to the cursor without
|
||||
/// creating an extra copy of the dataflow state.
|
||||
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
|
||||
f(&mut self.results.borrow_mut().analysis, &mut self.state);
|
||||
f(&mut self.results.analysis, &mut self.state);
|
||||
self.state_needs_reset = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
|
||||
impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
|
||||
where
|
||||
A: crate::GenKillAnalysis<'tcx>,
|
||||
A::Domain: BitSetExt<A::Idx>,
|
||||
|
@ -5,9 +5,7 @@ use crate::errors::{
|
||||
};
|
||||
use crate::framework::BitSetExt;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::ffi::OsString;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rustc_ast as ast;
|
||||
@ -24,42 +22,37 @@ use rustc_span::symbol::{sym, Symbol};
|
||||
use super::fmt::DebugWithContext;
|
||||
use super::graphviz;
|
||||
use super::{
|
||||
visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis,
|
||||
GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
|
||||
ResultsVisitor,
|
||||
visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis, GenKillSet,
|
||||
JoinSemiLattice, ResultsCursor, ResultsVisitor,
|
||||
};
|
||||
|
||||
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
|
||||
|
||||
/// A dataflow analysis that has converged to fixpoint.
|
||||
pub struct Results<'tcx, A, E = EntrySets<'tcx, A>>
|
||||
#[derive(Clone)]
|
||||
pub struct Results<'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
pub analysis: A,
|
||||
pub(super) entry_sets: E,
|
||||
pub(super) _marker: PhantomData<&'tcx ()>,
|
||||
pub(super) entry_sets: EntrySets<'tcx, A>,
|
||||
}
|
||||
|
||||
/// `Results` type with a cloned `Analysis` and borrowed entry sets.
|
||||
pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>;
|
||||
|
||||
impl<'tcx, A, E> Results<'tcx, A, E>
|
||||
impl<'tcx, A> Results<'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
E: Borrow<EntrySets<'tcx, A>>,
|
||||
{
|
||||
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
||||
pub fn into_results_cursor<'mir>(
|
||||
self,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
) -> ResultsCursor<'mir, 'tcx, A, Self> {
|
||||
) -> ResultsCursor<'mir, 'tcx, A> {
|
||||
ResultsCursor::new(body, self)
|
||||
}
|
||||
|
||||
/// Gets the dataflow state for the given block.
|
||||
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
|
||||
&self.entry_sets.borrow()[block]
|
||||
&self.entry_sets[block]
|
||||
}
|
||||
|
||||
pub fn visit_with<'mir>(
|
||||
@ -80,52 +73,6 @@ where
|
||||
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
|
||||
}
|
||||
}
|
||||
impl<'tcx, A> Results<'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
||||
pub fn as_results_cursor<'a, 'mir>(
|
||||
&'a mut self,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
) -> ResultsRefCursor<'a, 'mir, 'tcx, A> {
|
||||
ResultsCursor::new(body, self)
|
||||
}
|
||||
}
|
||||
impl<'tcx, A> Results<'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx> + CloneAnalysis,
|
||||
{
|
||||
/// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
|
||||
pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> {
|
||||
Results {
|
||||
analysis: self.analysis.clone_analysis(),
|
||||
entry_sets: &self.entry_sets,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
||||
pub fn cloned_results_cursor<'mir>(
|
||||
&self,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> {
|
||||
self.clone_analysis().into_results_cursor(body)
|
||||
}
|
||||
}
|
||||
impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>>
|
||||
where
|
||||
A: Analysis<'tcx> + CloneAnalysis,
|
||||
{
|
||||
/// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
|
||||
pub fn reclone_analysis(&self) -> Self {
|
||||
Results {
|
||||
analysis: self.analysis.clone_analysis(),
|
||||
entry_sets: self.entry_sets,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A solver for dataflow problems.
|
||||
pub struct Engine<'mir, 'tcx, A>
|
||||
@ -291,29 +238,31 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
let mut results = Results { analysis, entry_sets, _marker: PhantomData };
|
||||
let results = Results { analysis, entry_sets };
|
||||
|
||||
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
|
||||
let res = write_graphviz_results(tcx, body, &mut results, pass_name);
|
||||
let (res, results) = write_graphviz_results(tcx, body, results, pass_name);
|
||||
if let Err(e) = res {
|
||||
error!("Failed to write graphviz dataflow results: {}", e);
|
||||
}
|
||||
results
|
||||
} else {
|
||||
results
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
// Graphviz
|
||||
|
||||
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
|
||||
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`.
|
||||
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
|
||||
/// the same.
|
||||
fn write_graphviz_results<'tcx, A>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &mir::Body<'tcx>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
results: Results<'tcx, A>,
|
||||
pass_name: Option<&'static str>,
|
||||
) -> std::io::Result<()>
|
||||
) -> (std::io::Result<()>, Results<'tcx, A>)
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
A::Domain: DebugWithContext<A>,
|
||||
@ -324,23 +273,30 @@ where
|
||||
let def_id = body.source.def_id();
|
||||
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
|
||||
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
|
||||
return Ok(());
|
||||
return (Ok(()), results);
|
||||
};
|
||||
|
||||
let mut file = match attrs.output_path(A::NAME) {
|
||||
Some(path) => {
|
||||
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
let file = try {
|
||||
match attrs.output_path(A::NAME) {
|
||||
Some(path) => {
|
||||
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
let f = fs::File::create(&path)?;
|
||||
io::BufWriter::new(f)
|
||||
}
|
||||
io::BufWriter::new(fs::File::create(&path)?)
|
||||
}
|
||||
|
||||
None if dump_enabled(tcx, A::NAME, def_id) => {
|
||||
create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
||||
}
|
||||
None if dump_enabled(tcx, A::NAME, def_id) => {
|
||||
create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
||||
}
|
||||
|
||||
_ => return Ok(()),
|
||||
_ => return (Ok(()), results),
|
||||
}
|
||||
};
|
||||
let mut file = match file {
|
||||
Ok(f) => f,
|
||||
Err(e) => return (Err(e), results),
|
||||
};
|
||||
|
||||
let style = match attrs.formatter {
|
||||
@ -356,11 +312,14 @@ where
|
||||
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
|
||||
render_opts.push(dot::RenderOption::DarkTheme);
|
||||
}
|
||||
with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)?);
|
||||
let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts));
|
||||
|
||||
file.write_all(&buf)?;
|
||||
let lhs = try {
|
||||
r?;
|
||||
file.write_all(&buf)?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
(lhs, graphviz.into_results())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -12,7 +12,7 @@ use rustc_middle::mir::graphviz_safe_def_name;
|
||||
use rustc_middle::mir::{self, BasicBlock, Body, Location};
|
||||
|
||||
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
|
||||
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsRefCursor, ResultsVisitor};
|
||||
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum OutputStyle {
|
||||
@ -29,27 +29,31 @@ impl OutputStyle {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Formatter<'res, 'mir, 'tcx, A>
|
||||
pub(crate) struct Formatter<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
body: &'mir Body<'tcx>,
|
||||
results: RefCell<&'res mut Results<'tcx, A>>,
|
||||
results: RefCell<Option<Results<'tcx, A>>>,
|
||||
style: OutputStyle,
|
||||
reachable: BitSet<BasicBlock>,
|
||||
}
|
||||
|
||||
impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A>
|
||||
impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
body: &'mir Body<'tcx>,
|
||||
results: &'res mut Results<'tcx, A>,
|
||||
results: Results<'tcx, A>,
|
||||
style: OutputStyle,
|
||||
) -> Self {
|
||||
let reachable = mir::traversal::reachable_as_bitset(body);
|
||||
Formatter { body, results: results.into(), style, reachable }
|
||||
Formatter { body, results: Some(results).into(), style, reachable }
|
||||
}
|
||||
|
||||
pub(crate) fn into_results(self) -> Results<'tcx, A> {
|
||||
self.results.into_inner().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +73,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A>
|
||||
impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
A::Domain: DebugWithContext<A>,
|
||||
@ -88,14 +92,19 @@ where
|
||||
|
||||
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
|
||||
let mut label = Vec::new();
|
||||
let mut results = self.results.borrow_mut();
|
||||
let mut fmt = BlockFormatter {
|
||||
results: results.as_results_cursor(self.body),
|
||||
style: self.style,
|
||||
bg: Background::Light,
|
||||
};
|
||||
self.results.replace_with(|results| {
|
||||
// `Formatter::result` is a `RefCell<Option<_>>` so we can replace
|
||||
// the value with `None`, move it into the results cursor, move it
|
||||
// back out, and return it to the refcell wrapped in `Some`.
|
||||
let mut fmt = BlockFormatter {
|
||||
results: results.take().unwrap().into_results_cursor(self.body),
|
||||
style: self.style,
|
||||
bg: Background::Light,
|
||||
};
|
||||
|
||||
fmt.write_node_label(&mut label, *block).unwrap();
|
||||
fmt.write_node_label(&mut label, *block).unwrap();
|
||||
Some(fmt.results.into_results())
|
||||
});
|
||||
dot::LabelText::html(String::from_utf8(label).unwrap())
|
||||
}
|
||||
|
||||
@ -109,7 +118,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A>
|
||||
impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
@ -143,16 +152,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockFormatter<'res, 'mir, 'tcx, A>
|
||||
struct BlockFormatter<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
results: ResultsRefCursor<'res, 'mir, 'tcx, A>,
|
||||
results: ResultsCursor<'mir, 'tcx, A>,
|
||||
bg: Background,
|
||||
style: OutputStyle,
|
||||
}
|
||||
|
||||
impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A>
|
||||
impl<'mir, 'tcx, A> BlockFormatter<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
A::Domain: DebugWithContext<A>,
|
||||
|
@ -45,9 +45,9 @@ pub mod graphviz;
|
||||
pub mod lattice;
|
||||
mod visitor;
|
||||
|
||||
pub use self::cursor::{ResultsClonedCursor, ResultsCursor, ResultsRefCursor};
|
||||
pub use self::cursor::ResultsCursor;
|
||||
pub use self::direction::{Backward, Direction, Forward};
|
||||
pub use self::engine::{Engine, EntrySets, Results, ResultsCloned};
|
||||
pub use self::engine::{Engine, Results};
|
||||
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
|
||||
pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
|
||||
|
||||
@ -246,21 +246,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines an `Analysis` which can be cloned for use in multiple `ResultsCursor`s or
|
||||
/// `ResultsVisitor`s. Note this need not be a full clone, only enough of one to be used with a new
|
||||
/// `ResultsCursor` or `ResultsVisitor`
|
||||
pub trait CloneAnalysis {
|
||||
fn clone_analysis(&self) -> Self;
|
||||
}
|
||||
impl<'tcx, A> CloneAnalysis for A
|
||||
where
|
||||
A: Analysis<'tcx> + Copy,
|
||||
{
|
||||
fn clone_analysis(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
/// A gen/kill dataflow problem.
|
||||
///
|
||||
/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
|
||||
|
@ -267,8 +267,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
|
||||
let body = analysis.body;
|
||||
|
||||
let mut cursor =
|
||||
Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData }
|
||||
.into_results_cursor(body);
|
||||
Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
|
||||
|
||||
cursor.allow_unreachable();
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use rustc_middle::mir::{self, BasicBlock, Location};
|
||||
|
||||
use super::{Analysis, Direction, EntrySets, Results};
|
||||
use super::{Analysis, Direction, Results};
|
||||
|
||||
/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
|
||||
/// dataflow state at that location.
|
||||
@ -143,10 +141,9 @@ pub trait ResultsVisitable<'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E>
|
||||
impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
E: Borrow<EntrySets<'tcx, A>>,
|
||||
{
|
||||
type FlowState = A::Domain;
|
||||
|
||||
|
@ -5,7 +5,7 @@ use rustc_middle::mir::*;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::MaybeBorrowedLocals;
|
||||
use crate::{GenKill, ResultsClonedCursor};
|
||||
use crate::{GenKill, ResultsCursor};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MaybeStorageLive<'a> {
|
||||
@ -18,12 +18,6 @@ impl<'a> MaybeStorageLive<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CloneAnalysis for MaybeStorageLive<'_> {
|
||||
fn clone_analysis(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
|
||||
type Domain = BitSet<Local>;
|
||||
|
||||
@ -158,28 +152,21 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
|
||||
}
|
||||
}
|
||||
|
||||
type BorrowedLocalsResults<'res, 'mir, 'tcx> =
|
||||
ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>;
|
||||
type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>;
|
||||
|
||||
/// Dataflow analysis that determines whether each local requires storage at a
|
||||
/// given location; i.e. whether its storage can go away without being observed.
|
||||
pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> {
|
||||
borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>,
|
||||
pub struct MaybeRequiresStorage<'mir, 'tcx> {
|
||||
borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> {
|
||||
pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self {
|
||||
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
|
||||
pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self {
|
||||
MaybeRequiresStorage { borrowed_locals }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> {
|
||||
fn clone_analysis(&self) -> Self {
|
||||
Self { borrowed_locals: self.borrowed_locals.new_cursor() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||
impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
||||
type Domain = BitSet<Local>;
|
||||
|
||||
const NAME: &'static str = "requires_storage";
|
||||
@ -198,7 +185,7 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
||||
type Idx = Local;
|
||||
|
||||
fn domain_size(&self, body: &Body<'tcx>) -> usize {
|
||||
@ -355,7 +342,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||
impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
|
||||
/// Kill locals that are fully moved and have not been borrowed.
|
||||
fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) {
|
||||
let body = self.borrowed_locals.body();
|
||||
@ -364,12 +351,12 @@ impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> {
|
||||
borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>,
|
||||
struct MoveVisitor<'a, 'mir, 'tcx, T> {
|
||||
borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>,
|
||||
trans: &'a mut T,
|
||||
}
|
||||
|
||||
impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T>
|
||||
impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx, T>
|
||||
where
|
||||
T: GenKill<Local>,
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
#![feature(let_chains)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
@ -23,7 +24,7 @@ pub use self::framework::{
|
||||
fmt, lattice, visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis,
|
||||
JoinSemiLattice, MaybeReachable, Results, ResultsCursor, ResultsVisitable, ResultsVisitor,
|
||||
};
|
||||
use self::framework::{Backward, CloneAnalysis, ResultsClonedCursor, SwitchIntEdgeEffects};
|
||||
use self::framework::{Backward, SwitchIntEdgeEffects};
|
||||
use self::move_paths::MoveData;
|
||||
|
||||
pub mod debuginfo;
|
||||
|
@ -679,15 +679,15 @@ fn locals_live_across_suspend_points<'tcx>(
|
||||
let borrowed_locals_results =
|
||||
MaybeBorrowedLocals.into_engine(tcx, body).pass_name("coroutine").iterate_to_fixpoint();
|
||||
|
||||
let mut borrowed_locals_cursor = borrowed_locals_results.cloned_results_cursor(body);
|
||||
let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body);
|
||||
|
||||
// Calculate the MIR locals that we actually need to keep storage around
|
||||
// for.
|
||||
let mut requires_storage_results =
|
||||
MaybeRequiresStorage::new(borrowed_locals_results.cloned_results_cursor(body))
|
||||
let mut requires_storage_cursor =
|
||||
MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body))
|
||||
.into_engine(tcx, body)
|
||||
.iterate_to_fixpoint();
|
||||
let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body);
|
||||
.iterate_to_fixpoint()
|
||||
.into_results_cursor(body);
|
||||
|
||||
// Calculate the liveness of MIR locals ignoring borrows.
|
||||
let mut liveness = MaybeLiveLocals
|
||||
@ -763,7 +763,7 @@ fn locals_live_across_suspend_points<'tcx>(
|
||||
body,
|
||||
&saved_locals,
|
||||
always_live_locals.clone(),
|
||||
requires_storage_results,
|
||||
requires_storage_cursor.into_results(),
|
||||
);
|
||||
|
||||
LivenessInfo {
|
||||
@ -828,7 +828,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
|
||||
body: &'mir Body<'tcx>,
|
||||
saved_locals: &CoroutineSavedLocals,
|
||||
always_live_locals: BitSet<Local>,
|
||||
mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'_, 'mir, 'tcx>>,
|
||||
mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
|
||||
) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> {
|
||||
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
|
||||
|
||||
|
@ -4,9 +4,8 @@ use rustc_ast::*;
|
||||
use rustc_expand::expand::AstFragment;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::definitions::*;
|
||||
use rustc_span::hygiene::LocalExpnId;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(crate) fn collect_definitions(
|
||||
@ -30,16 +29,19 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
|
||||
fn create_def(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
data: DefPathData,
|
||||
name: Symbol,
|
||||
def_kind: DefKind,
|
||||
span: Span,
|
||||
) -> LocalDefId {
|
||||
let parent_def = self.parent_def;
|
||||
debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def);
|
||||
debug!(
|
||||
"create_def(node_id={:?}, def_kind={:?}, parent_def={:?})",
|
||||
node_id, def_kind, parent_def
|
||||
);
|
||||
self.resolver.create_def(
|
||||
parent_def,
|
||||
node_id,
|
||||
data,
|
||||
name,
|
||||
def_kind,
|
||||
self.expansion.to_expn_id(),
|
||||
span.with_parent(None),
|
||||
@ -76,8 +78,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
|
||||
self.visit_macro_invoc(field.id);
|
||||
} else {
|
||||
let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
|
||||
let def =
|
||||
self.create_def(field.id, DefPathData::ValueNs(name), DefKind::Field, field.span);
|
||||
let def = self.create_def(field.id, name, DefKind::Field, field.span);
|
||||
self.with_parent(def, |this| visit::walk_field_def(this, field));
|
||||
}
|
||||
}
|
||||
@ -97,40 +98,36 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
// Pick the def data. This need not be unique, but the more
|
||||
// information we encapsulate into, the better
|
||||
let mut opt_macro_data = None;
|
||||
let ty_data = DefPathData::TypeNs(i.ident.name);
|
||||
let value_data = DefPathData::ValueNs(i.ident.name);
|
||||
let (def_data, def_kind) = match &i.kind {
|
||||
ItemKind::Impl(i) => {
|
||||
(DefPathData::Impl, DefKind::Impl { of_trait: i.of_trait.is_some() })
|
||||
}
|
||||
ItemKind::ForeignMod(..) => (DefPathData::ForeignMod, DefKind::ForeignMod),
|
||||
ItemKind::Mod(..) => (ty_data, DefKind::Mod),
|
||||
ItemKind::Trait(..) => (ty_data, DefKind::Trait),
|
||||
ItemKind::TraitAlias(..) => (ty_data, DefKind::TraitAlias),
|
||||
ItemKind::Enum(..) => (ty_data, DefKind::Enum),
|
||||
ItemKind::Struct(..) => (ty_data, DefKind::Struct),
|
||||
ItemKind::Union(..) => (ty_data, DefKind::Union),
|
||||
ItemKind::ExternCrate(..) => (ty_data, DefKind::ExternCrate),
|
||||
ItemKind::TyAlias(..) => (ty_data, DefKind::TyAlias),
|
||||
ItemKind::Static(s) => (value_data, DefKind::Static(s.mutability)),
|
||||
ItemKind::Const(..) => (value_data, DefKind::Const),
|
||||
ItemKind::Fn(..) => (value_data, DefKind::Fn),
|
||||
let def_kind = match &i.kind {
|
||||
ItemKind::Impl(i) => DefKind::Impl { of_trait: i.of_trait.is_some() },
|
||||
ItemKind::ForeignMod(..) => DefKind::ForeignMod,
|
||||
ItemKind::Mod(..) => DefKind::Mod,
|
||||
ItemKind::Trait(..) => DefKind::Trait,
|
||||
ItemKind::TraitAlias(..) => DefKind::TraitAlias,
|
||||
ItemKind::Enum(..) => DefKind::Enum,
|
||||
ItemKind::Struct(..) => DefKind::Struct,
|
||||
ItemKind::Union(..) => DefKind::Union,
|
||||
ItemKind::ExternCrate(..) => DefKind::ExternCrate,
|
||||
ItemKind::TyAlias(..) => DefKind::TyAlias,
|
||||
ItemKind::Static(s) => DefKind::Static(s.mutability),
|
||||
ItemKind::Const(..) => DefKind::Const,
|
||||
ItemKind::Fn(..) => DefKind::Fn,
|
||||
ItemKind::MacroDef(..) => {
|
||||
let macro_data = self.resolver.compile_macro(i, self.resolver.tcx.sess.edition());
|
||||
let macro_kind = macro_data.ext.macro_kind();
|
||||
opt_macro_data = Some(macro_data);
|
||||
(DefPathData::MacroNs(i.ident.name), DefKind::Macro(macro_kind))
|
||||
DefKind::Macro(macro_kind)
|
||||
}
|
||||
ItemKind::MacCall(..) => {
|
||||
visit::walk_item(self, i);
|
||||
return self.visit_macro_invoc(i.id);
|
||||
}
|
||||
ItemKind::GlobalAsm(..) => (DefPathData::GlobalAsm, DefKind::GlobalAsm),
|
||||
ItemKind::GlobalAsm(..) => DefKind::GlobalAsm,
|
||||
ItemKind::Use(..) => {
|
||||
return visit::walk_item(self, i);
|
||||
}
|
||||
};
|
||||
let def_id = self.create_def(i.id, def_data, def_kind, i.span);
|
||||
let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span);
|
||||
|
||||
if let Some(macro_data) = opt_macro_data {
|
||||
self.resolver.macro_map.insert(def_id.to_def_id(), macro_data);
|
||||
@ -144,7 +141,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(struct_def) {
|
||||
this.create_def(
|
||||
ctor_node_id,
|
||||
DefPathData::Ctor,
|
||||
kw::Empty,
|
||||
DefKind::Ctor(CtorOf::Struct, ctor_kind),
|
||||
i.span,
|
||||
);
|
||||
@ -174,12 +171,8 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
// then the closure_def will never be used, and we should avoid generating a
|
||||
// def-id for it.
|
||||
if let Some(body) = body {
|
||||
let closure_def = self.create_def(
|
||||
closure_id,
|
||||
DefPathData::ClosureExpr,
|
||||
DefKind::Closure,
|
||||
span,
|
||||
);
|
||||
let closure_def =
|
||||
self.create_def(closure_id, kw::Empty, DefKind::Closure, span);
|
||||
self.with_parent(closure_def, |this| this.visit_block(body));
|
||||
}
|
||||
return;
|
||||
@ -190,21 +183,19 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
|
||||
self.create_def(id, DefPathData::Use, DefKind::Use, use_tree.span);
|
||||
self.create_def(id, kw::Empty, DefKind::Use, use_tree.span);
|
||||
visit::walk_use_tree(self, use_tree, id);
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
|
||||
let (def_data, def_kind) = match fi.kind {
|
||||
ForeignItemKind::Static(_, mt, _) => {
|
||||
(DefPathData::ValueNs(fi.ident.name), DefKind::Static(mt))
|
||||
}
|
||||
ForeignItemKind::Fn(_) => (DefPathData::ValueNs(fi.ident.name), DefKind::Fn),
|
||||
ForeignItemKind::TyAlias(_) => (DefPathData::TypeNs(fi.ident.name), DefKind::ForeignTy),
|
||||
let def_kind = match fi.kind {
|
||||
ForeignItemKind::Static(_, mt, _) => DefKind::Static(mt),
|
||||
ForeignItemKind::Fn(_) => DefKind::Fn,
|
||||
ForeignItemKind::TyAlias(_) => DefKind::ForeignTy,
|
||||
ForeignItemKind::MacCall(_) => return self.visit_macro_invoc(fi.id),
|
||||
};
|
||||
|
||||
let def = self.create_def(fi.id, def_data, def_kind, fi.span);
|
||||
let def = self.create_def(fi.id, fi.ident.name, def_kind, fi.span);
|
||||
|
||||
self.with_parent(def, |this| visit::walk_foreign_item(this, fi));
|
||||
}
|
||||
@ -213,13 +204,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
if v.is_placeholder {
|
||||
return self.visit_macro_invoc(v.id);
|
||||
}
|
||||
let def =
|
||||
self.create_def(v.id, DefPathData::TypeNs(v.ident.name), DefKind::Variant, v.span);
|
||||
let def = self.create_def(v.id, v.ident.name, DefKind::Variant, v.span);
|
||||
self.with_parent(def, |this| {
|
||||
if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(&v.data) {
|
||||
this.create_def(
|
||||
ctor_node_id,
|
||||
DefPathData::Ctor,
|
||||
kw::Empty,
|
||||
DefKind::Ctor(CtorOf::Variant, ctor_kind),
|
||||
v.span,
|
||||
);
|
||||
@ -242,15 +232,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
self.visit_macro_invoc(param.id);
|
||||
return;
|
||||
}
|
||||
let name = param.ident.name;
|
||||
let (def_path_data, def_kind) = match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
(DefPathData::LifetimeNs(name), DefKind::LifetimeParam)
|
||||
}
|
||||
GenericParamKind::Type { .. } => (DefPathData::TypeNs(name), DefKind::TyParam),
|
||||
GenericParamKind::Const { .. } => (DefPathData::ValueNs(name), DefKind::ConstParam),
|
||||
let def_kind = match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam,
|
||||
GenericParamKind::Type { .. } => DefKind::TyParam,
|
||||
GenericParamKind::Const { .. } => DefKind::ConstParam,
|
||||
};
|
||||
self.create_def(param.id, def_path_data, def_kind, param.ident.span);
|
||||
self.create_def(param.id, param.ident.name, def_kind, param.ident.span);
|
||||
|
||||
// impl-Trait can happen inside generic parameters, like
|
||||
// ```
|
||||
@ -264,14 +251,14 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) {
|
||||
let (def_data, def_kind) = match &i.kind {
|
||||
AssocItemKind::Fn(..) => (DefPathData::ValueNs(i.ident.name), DefKind::AssocFn),
|
||||
AssocItemKind::Const(..) => (DefPathData::ValueNs(i.ident.name), DefKind::AssocConst),
|
||||
AssocItemKind::Type(..) => (DefPathData::TypeNs(i.ident.name), DefKind::AssocTy),
|
||||
let def_kind = match &i.kind {
|
||||
AssocItemKind::Fn(..) => DefKind::AssocFn,
|
||||
AssocItemKind::Const(..) => DefKind::AssocConst,
|
||||
AssocItemKind::Type(..) => DefKind::AssocTy,
|
||||
AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
|
||||
};
|
||||
|
||||
let def = self.create_def(i.id, def_data, def_kind, i.span);
|
||||
let def = self.create_def(i.id, i.ident.name, def_kind, i.span);
|
||||
self.with_parent(def, |this| visit::walk_assoc_item(this, i, ctxt));
|
||||
}
|
||||
|
||||
@ -283,12 +270,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_anon_const(&mut self, constant: &'a AnonConst) {
|
||||
let def = self.create_def(
|
||||
constant.id,
|
||||
DefPathData::AnonConst,
|
||||
DefKind::AnonConst,
|
||||
constant.value.span,
|
||||
);
|
||||
let def = self.create_def(constant.id, kw::Empty, DefKind::AnonConst, constant.value.span);
|
||||
self.with_parent(def, |this| visit::walk_anon_const(this, constant));
|
||||
}
|
||||
|
||||
@ -298,25 +280,21 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
ExprKind::Closure(ref closure) => {
|
||||
// Async closures desugar to closures inside of closures, so
|
||||
// we must create two defs.
|
||||
let closure_def =
|
||||
self.create_def(expr.id, DefPathData::ClosureExpr, DefKind::Closure, expr.span);
|
||||
let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span);
|
||||
match closure.asyncness {
|
||||
Async::Yes { closure_id, .. } => self.create_def(
|
||||
closure_id,
|
||||
DefPathData::ClosureExpr,
|
||||
DefKind::Closure,
|
||||
expr.span,
|
||||
),
|
||||
Async::Yes { closure_id, .. } => {
|
||||
self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span)
|
||||
}
|
||||
Async::No => closure_def,
|
||||
}
|
||||
}
|
||||
ExprKind::Gen(_, _, _) => {
|
||||
self.create_def(expr.id, DefPathData::ClosureExpr, DefKind::Closure, expr.span)
|
||||
self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
|
||||
}
|
||||
ExprKind::ConstBlock(ref constant) => {
|
||||
let def = self.create_def(
|
||||
constant.id,
|
||||
DefPathData::AnonConst,
|
||||
kw::Empty,
|
||||
DefKind::InlineConst,
|
||||
constant.value.span,
|
||||
);
|
||||
|
@ -45,7 +45,6 @@ use rustc_hir::def::NonMacroAttrKind;
|
||||
use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes, PerNS};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, LocalDefIdSet};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::{PrimTy, TraitCandidate};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_metadata::creader::{CStore, CrateLoader};
|
||||
@ -1212,11 +1211,12 @@ impl<'tcx> Resolver<'_, 'tcx> {
|
||||
&mut self,
|
||||
parent: LocalDefId,
|
||||
node_id: ast::NodeId,
|
||||
data: DefPathData,
|
||||
name: Symbol,
|
||||
def_kind: DefKind,
|
||||
expn_id: ExpnId,
|
||||
span: Span,
|
||||
) -> LocalDefId {
|
||||
let data = def_kind.def_path_data(name);
|
||||
assert!(
|
||||
!self.node_id_to_def_id.contains_key(&node_id),
|
||||
"adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
|
||||
|
@ -379,14 +379,13 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
|
||||
hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
|
||||
hir::definitions::DefPathData::TypeNs(..) => "t",
|
||||
hir::definitions::DefPathData::ValueNs(..) => "v",
|
||||
hir::definitions::DefPathData::ClosureExpr => "C",
|
||||
hir::definitions::DefPathData::Closure => "C",
|
||||
hir::definitions::DefPathData::Ctor => "c",
|
||||
hir::definitions::DefPathData::AnonConst => "k",
|
||||
hir::definitions::DefPathData::ImplTrait => "i",
|
||||
hir::definitions::DefPathData::OpaqueTy => "i",
|
||||
hir::definitions::DefPathData::CrateRoot
|
||||
| hir::definitions::DefPathData::Use
|
||||
| hir::definitions::DefPathData::GlobalAsm
|
||||
| hir::definitions::DefPathData::ImplTraitAssocTy
|
||||
| hir::definitions::DefPathData::MacroNs(..)
|
||||
| hir::definitions::DefPathData::LifetimeNs(..) => {
|
||||
bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
|
||||
|
@ -770,17 +770,16 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
||||
// Uppercase categories are more stable than lowercase ones.
|
||||
DefPathData::TypeNs(_) => 't',
|
||||
DefPathData::ValueNs(_) => 'v',
|
||||
DefPathData::ClosureExpr => 'C',
|
||||
DefPathData::Closure => 'C',
|
||||
DefPathData::Ctor => 'c',
|
||||
DefPathData::AnonConst => 'k',
|
||||
DefPathData::ImplTrait => 'i',
|
||||
DefPathData::OpaqueTy => 'i',
|
||||
|
||||
// These should never show up as `path_append` arguments.
|
||||
DefPathData::CrateRoot
|
||||
| DefPathData::Use
|
||||
| DefPathData::GlobalAsm
|
||||
| DefPathData::Impl
|
||||
| DefPathData::ImplTraitAssocTy
|
||||
| DefPathData::MacroNs(_)
|
||||
| DefPathData::LifetimeNs(_) => {
|
||||
bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data)
|
||||
|
@ -55,3 +55,6 @@ trait_selection_trait_has_no_impls = this trait has no implementations, consider
|
||||
|
||||
trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead
|
||||
trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated}
|
||||
|
||||
trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}`
|
||||
.help = expect either a generic argument name or {"`{Self}`"} as format argument
|
||||
|
@ -321,7 +321,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OnUnimplementedFormatString(Symbol, Span);
|
||||
pub struct OnUnimplementedFormatString {
|
||||
symbol: Symbol,
|
||||
span: Span,
|
||||
is_diagnostic_namespace_variant: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OnUnimplementedDirective {
|
||||
@ -401,6 +405,14 @@ impl IgnoredDiagnosticOption {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
|
||||
#[help]
|
||||
pub struct UnknownFormatParameterForOnUnimplementedAttr {
|
||||
argument_name: Symbol,
|
||||
trait_name: Symbol,
|
||||
}
|
||||
|
||||
impl<'tcx> OnUnimplementedDirective {
|
||||
fn parse(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
@ -414,8 +426,14 @@ impl<'tcx> OnUnimplementedDirective {
|
||||
let mut item_iter = items.iter();
|
||||
|
||||
let parse_value = |value_str, value_span| {
|
||||
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span)
|
||||
.map(Some)
|
||||
OnUnimplementedFormatString::try_parse(
|
||||
tcx,
|
||||
item_def_id,
|
||||
value_str,
|
||||
value_span,
|
||||
is_diagnostic_namespace_variant,
|
||||
)
|
||||
.map(Some)
|
||||
};
|
||||
|
||||
let condition = if is_root {
|
||||
@ -552,15 +570,15 @@ impl<'tcx> OnUnimplementedDirective {
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
tcx,
|
||||
item_def_id,
|
||||
directive.message.as_ref().map(|f| f.1),
|
||||
aggr.message.as_ref().map(|f| f.1),
|
||||
directive.message.as_ref().map(|f| f.span),
|
||||
aggr.message.as_ref().map(|f| f.span),
|
||||
"message",
|
||||
);
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
tcx,
|
||||
item_def_id,
|
||||
directive.label.as_ref().map(|f| f.1),
|
||||
aggr.label.as_ref().map(|f| f.1),
|
||||
directive.label.as_ref().map(|f| f.span),
|
||||
aggr.label.as_ref().map(|f| f.span),
|
||||
"label",
|
||||
);
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
@ -573,8 +591,8 @@ impl<'tcx> OnUnimplementedDirective {
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
tcx,
|
||||
item_def_id,
|
||||
directive.parent_label.as_ref().map(|f| f.1),
|
||||
aggr.parent_label.as_ref().map(|f| f.1),
|
||||
directive.parent_label.as_ref().map(|f| f.span),
|
||||
aggr.parent_label.as_ref().map(|f| f.span),
|
||||
"parent_label",
|
||||
);
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
@ -634,7 +652,7 @@ impl<'tcx> OnUnimplementedDirective {
|
||||
item_def_id,
|
||||
value,
|
||||
attr.span,
|
||||
attr.span,
|
||||
is_diagnostic_namespace_variant,
|
||||
)?),
|
||||
notes: Vec::new(),
|
||||
parent_label: None,
|
||||
@ -713,7 +731,12 @@ impl<'tcx> OnUnimplementedDirective {
|
||||
// `with_no_visible_paths` is also used when generating the options,
|
||||
// so we need to match it here.
|
||||
ty::print::with_no_visible_paths!(
|
||||
OnUnimplementedFormatString(v, cfg.span).format(
|
||||
OnUnimplementedFormatString {
|
||||
symbol: v,
|
||||
span: cfg.span,
|
||||
is_diagnostic_namespace_variant: false
|
||||
}
|
||||
.format(
|
||||
tcx,
|
||||
trait_ref,
|
||||
&options_map
|
||||
@ -761,20 +784,19 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: DefId,
|
||||
from: Symbol,
|
||||
err_sp: Span,
|
||||
value_span: Span,
|
||||
is_diagnostic_namespace_variant: bool,
|
||||
) -> Result<Self, ErrorGuaranteed> {
|
||||
let result = OnUnimplementedFormatString(from, value_span);
|
||||
result.verify(tcx, item_def_id, err_sp)?;
|
||||
let result = OnUnimplementedFormatString {
|
||||
symbol: from,
|
||||
span: value_span,
|
||||
is_diagnostic_namespace_variant,
|
||||
};
|
||||
result.verify(tcx, item_def_id)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn verify(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: DefId,
|
||||
span: Span,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
|
||||
let trait_def_id = if tcx.is_trait(item_def_id) {
|
||||
item_def_id
|
||||
} else {
|
||||
@ -783,7 +805,7 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||
};
|
||||
let trait_name = tcx.item_name(trait_def_id);
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
let s = self.0.as_str();
|
||||
let s = self.symbol.as_str();
|
||||
let parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||
let mut result = Ok(());
|
||||
for token in parser {
|
||||
@ -793,24 +815,40 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||
Position::ArgumentNamed(s) => {
|
||||
match Symbol::intern(s) {
|
||||
// `{ThisTraitsName}` is allowed
|
||||
s if s == trait_name => (),
|
||||
s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (),
|
||||
s if s == trait_name && !self.is_diagnostic_namespace_variant => (),
|
||||
s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
|
||||
&& !self.is_diagnostic_namespace_variant =>
|
||||
{
|
||||
()
|
||||
}
|
||||
// So is `{A}` if A is a type parameter
|
||||
s if generics.params.iter().any(|param| param.name == s) => (),
|
||||
s => {
|
||||
result = Err(struct_span_err!(
|
||||
tcx.sess,
|
||||
span,
|
||||
E0230,
|
||||
"there is no parameter `{}` on {}",
|
||||
s,
|
||||
if trait_def_id == item_def_id {
|
||||
format!("trait `{trait_name}`")
|
||||
} else {
|
||||
"impl".to_string()
|
||||
}
|
||||
)
|
||||
.emit());
|
||||
if self.is_diagnostic_namespace_variant {
|
||||
tcx.emit_spanned_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
|
||||
self.span,
|
||||
UnknownFormatParameterForOnUnimplementedAttr {
|
||||
argument_name: s,
|
||||
trait_name,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
result = Err(struct_span_err!(
|
||||
tcx.sess,
|
||||
self.span,
|
||||
E0230,
|
||||
"there is no parameter `{}` on {}",
|
||||
s,
|
||||
if trait_def_id == item_def_id {
|
||||
format!("trait `{trait_name}`")
|
||||
} else {
|
||||
"impl".to_string()
|
||||
}
|
||||
)
|
||||
.emit());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -818,7 +856,7 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
|
||||
let reported = struct_span_err!(
|
||||
tcx.sess,
|
||||
span,
|
||||
self.span,
|
||||
E0231,
|
||||
"only named substitution parameters are allowed"
|
||||
)
|
||||
@ -857,37 +895,42 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||
.collect::<FxHashMap<Symbol, String>>();
|
||||
let empty_string = String::new();
|
||||
|
||||
let s = self.0.as_str();
|
||||
let s = self.symbol.as_str();
|
||||
let parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||
let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
|
||||
parser
|
||||
.map(|p| match p {
|
||||
Piece::String(s) => s,
|
||||
Piece::String(s) => s.to_owned(),
|
||||
Piece::NextArgument(a) => match a.position {
|
||||
Position::ArgumentNamed(s) => {
|
||||
let s = Symbol::intern(s);
|
||||
Position::ArgumentNamed(arg) => {
|
||||
let s = Symbol::intern(arg);
|
||||
match generic_map.get(&s) {
|
||||
Some(val) => val,
|
||||
None if s == name => &trait_str,
|
||||
Some(val) => val.to_string(),
|
||||
None if self.is_diagnostic_namespace_variant => {
|
||||
format!("{{{arg}}}")
|
||||
}
|
||||
None if s == name => trait_str.clone(),
|
||||
None => {
|
||||
if let Some(val) = options.get(&s) {
|
||||
val
|
||||
val.clone()
|
||||
} else if s == sym::from_desugaring {
|
||||
// don't break messages using these two arguments incorrectly
|
||||
&empty_string
|
||||
} else if s == sym::ItemContext {
|
||||
item_context
|
||||
String::new()
|
||||
} else if s == sym::ItemContext
|
||||
&& !self.is_diagnostic_namespace_variant
|
||||
{
|
||||
item_context.clone()
|
||||
} else if s == sym::integral {
|
||||
"{integral}"
|
||||
String::from("{integral}")
|
||||
} else if s == sym::integer_ {
|
||||
"{integer}"
|
||||
String::from("{integer}")
|
||||
} else if s == sym::float {
|
||||
"{float}"
|
||||
String::from("{float}")
|
||||
} else {
|
||||
bug!(
|
||||
"broken on_unimplemented {:?} for {:?}: \
|
||||
no argument matching {:?}",
|
||||
self.0,
|
||||
self.symbol,
|
||||
trait_ref,
|
||||
s
|
||||
)
|
||||
@ -895,7 +938,7 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
|
||||
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
|
@ -424,11 +424,23 @@ fn fn_abi_sanity_check<'tcx>(
|
||||
}
|
||||
PassMode::Indirect { meta_attrs: None, .. } => {
|
||||
// No metadata, must be sized.
|
||||
// Conceptually, unsized arguments must be copied around, which requires dynamically
|
||||
// determining their size, which we cannot do without metadata. Consult
|
||||
// t-opsem before removing this check.
|
||||
assert!(arg.layout.is_sized());
|
||||
}
|
||||
PassMode::Indirect { meta_attrs: Some(_), on_stack, .. } => {
|
||||
// With metadata. Must be unsized and not on the stack.
|
||||
assert!(arg.layout.is_unsized() && !on_stack);
|
||||
// Also, must not be `extern` type.
|
||||
let tail = cx.tcx.struct_tail_with_normalize(arg.layout.ty, |ty| ty, || {});
|
||||
if matches!(tail.kind(), ty::Foreign(..)) {
|
||||
// These types do not have metadata, so having `meta_attrs` is bogus.
|
||||
// Conceptually, unsized arguments must be copied around, which requires dynamically
|
||||
// determining their size. Therefore, we cannot allow `extern` types here. Consult
|
||||
// t-opsem before removing this check.
|
||||
panic!("unsized arguments must not be `extern` types");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, GenericArgs, ImplTraitInTraitData, Ty, TyCtxt};
|
||||
@ -254,8 +253,7 @@ fn associated_type_for_impl_trait_in_trait(
|
||||
assert_eq!(tcx.def_kind(trait_def_id), DefKind::Trait);
|
||||
|
||||
let span = tcx.def_span(opaque_ty_def_id);
|
||||
let trait_assoc_ty =
|
||||
tcx.at(span).create_def(trait_def_id, DefPathData::ImplTraitAssocTy, DefKind::AssocTy);
|
||||
let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy);
|
||||
|
||||
let local_def_id = trait_assoc_ty.def_id();
|
||||
let def_id = local_def_id.to_def_id();
|
||||
@ -356,8 +354,7 @@ fn associated_type_for_impl_trait_in_impl(
|
||||
hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn_def_id),
|
||||
hir::FnRetTy::Return(ty) => ty.span,
|
||||
};
|
||||
let impl_assoc_ty =
|
||||
tcx.at(span).create_def(impl_local_def_id, DefPathData::ImplTraitAssocTy, DefKind::AssocTy);
|
||||
let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, kw::Empty, DefKind::AssocTy);
|
||||
|
||||
let local_def_id = impl_assoc_ty.def_id();
|
||||
let def_id = local_def_id.to_def_id();
|
||||
|
@ -233,7 +233,10 @@ unsafe impl<T> SliceIndex<[T]> for usize {
|
||||
// cannot be longer than `isize::MAX`. They also guarantee that
|
||||
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
||||
// so the call to `add` is safe.
|
||||
unsafe { slice.as_ptr().add(self) }
|
||||
unsafe {
|
||||
crate::intrinsics::assume(self < slice.len());
|
||||
slice.as_ptr().add(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1045,11 +1045,11 @@ impl<T> [T] {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let slice = ['r', 'u', 's', 't'];
|
||||
/// let mut iter = slice.windows(2);
|
||||
/// assert_eq!(iter.next().unwrap(), &['r', 'u']);
|
||||
/// assert_eq!(iter.next().unwrap(), &['u', 's']);
|
||||
/// assert_eq!(iter.next().unwrap(), &['s', 't']);
|
||||
/// let slice = ['l', 'o', 'r', 'e', 'm'];
|
||||
/// let mut iter = slice.windows(3);
|
||||
/// assert_eq!(iter.next().unwrap(), &['l', 'o', 'r']);
|
||||
/// assert_eq!(iter.next().unwrap(), &['o', 'r', 'e']);
|
||||
/// assert_eq!(iter.next().unwrap(), &['r', 'e', 'm']);
|
||||
/// assert!(iter.next().is_none());
|
||||
/// ```
|
||||
///
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(portable_simd, strict_provenance)]
|
||||
#![feature(portable_simd, strict_provenance, exposed_provenance)]
|
||||
|
||||
use core_simd::simd::{
|
||||
ptr::{SimdConstPtr, SimdMutPtr},
|
||||
|
@ -38,3 +38,39 @@ impl<'a> fmt::Display for Escape<'a> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper struct which will emit the HTML-escaped version of the contained
|
||||
/// string when passed to a format string.
|
||||
///
|
||||
/// This is only safe to use for text nodes. If you need your output to be
|
||||
/// safely contained in an attribute, use [`Escape`]. If you don't know the
|
||||
/// difference, use [`Escape`].
|
||||
pub(crate) struct EscapeBodyText<'a>(pub &'a str);
|
||||
|
||||
impl<'a> fmt::Display for EscapeBodyText<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Because the internet is always right, turns out there's not that many
|
||||
// characters to escape: http://stackoverflow.com/questions/7381974
|
||||
let EscapeBodyText(s) = *self;
|
||||
let pile_o_bits = s;
|
||||
let mut last = 0;
|
||||
for (i, ch) in s.char_indices() {
|
||||
let s = match ch {
|
||||
'>' => ">",
|
||||
'<' => "<",
|
||||
'&' => "&",
|
||||
_ => continue,
|
||||
};
|
||||
fmt.write_str(&pile_o_bits[last..i])?;
|
||||
fmt.write_str(s)?;
|
||||
// NOTE: we only expect single byte characters here - which is fine as long as we
|
||||
// only match single byte characters
|
||||
last = i + 1;
|
||||
}
|
||||
|
||||
if last < s.len() {
|
||||
fmt.write_str(&pile_o_bits[last..])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
//! Use the `render_with_highlighting` to highlight some rust code.
|
||||
|
||||
use crate::clean::PrimitiveType;
|
||||
use crate::html::escape::Escape;
|
||||
use crate::html::escape::EscapeBodyText;
|
||||
use crate::html::render::{Context, LinkFromSrc};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
@ -189,7 +189,7 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> {
|
||||
&& can_merge(current_class, Some(*parent_class), "")
|
||||
{
|
||||
for (text, class) in self.pending_elems.iter() {
|
||||
string(self.out, Escape(text), *class, &self.href_context, false);
|
||||
string(self.out, EscapeBodyText(text), *class, &self.href_context, false);
|
||||
}
|
||||
} else {
|
||||
// We only want to "open" the tag ourselves if we have more than one pending and if the
|
||||
@ -202,7 +202,13 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> {
|
||||
None
|
||||
};
|
||||
for (text, class) in self.pending_elems.iter() {
|
||||
string(self.out, Escape(text), *class, &self.href_context, close_tag.is_none());
|
||||
string(
|
||||
self.out,
|
||||
EscapeBodyText(text),
|
||||
*class,
|
||||
&self.href_context,
|
||||
close_tag.is_none(),
|
||||
);
|
||||
}
|
||||
if let Some(close_tag) = close_tag {
|
||||
exit_span(self.out, close_tag);
|
||||
|
@ -1,3 +1,3 @@
|
||||
<span class="kw">pub fn </span>foo() {
|
||||
<span class="macro">println!</span>(<span class="string">"foo"</span>);
|
||||
<span class="macro">println!</span>(<span class="string">"foo"</span>);
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
.lifetime { color: #B76514; }
|
||||
.question-mark { color: #ff9011; }
|
||||
</style>
|
||||
<pre><code><span class="attr">#![crate_type = <span class="string">"lib"</span>]
|
||||
<pre><code><span class="attr">#![crate_type = <span class="string">"lib"</span>]
|
||||
|
||||
</span><span class="kw">use </span>std::path::{Path, PathBuf};
|
||||
|
||||
<span class="attr">#[cfg(target_os = <span class="string">"linux"</span>)]
|
||||
#[cfg(target_os = <span class="string">"windows"</span>)]
|
||||
<span class="attr">#[cfg(target_os = <span class="string">"linux"</span>)]
|
||||
#[cfg(target_os = <span class="string">"windows"</span>)]
|
||||
</span><span class="kw">fn </span>main() -> () {
|
||||
<span class="kw">let </span>foo = <span class="bool-val">true </span>&& <span class="bool-val">false </span>|| <span class="bool-val">true</span>;
|
||||
<span class="kw">let _</span>: <span class="kw-2">*const </span>() = <span class="number">0</span>;
|
||||
@ -22,7 +22,7 @@
|
||||
<span class="kw">let _ </span>= <span class="kw-2">*</span>foo;
|
||||
<span class="macro">mac!</span>(foo, <span class="kw-2">&mut </span>bar);
|
||||
<span class="macro">assert!</span>(<span class="self">self</span>.length < N && index <= <span class="self">self</span>.length);
|
||||
::std::env::var(<span class="string">"gateau"</span>).is_ok();
|
||||
::std::env::var(<span class="string">"gateau"</span>).is_ok();
|
||||
<span class="attr">#[rustfmt::skip]
|
||||
</span><span class="kw">let </span>s:std::path::PathBuf = std::path::PathBuf::new();
|
||||
<span class="kw">let </span><span class="kw-2">mut </span>s = String::new();
|
||||
|
@ -1737,7 +1737,14 @@ fn item_variants(
|
||||
w.write_str("</h3></section>");
|
||||
|
||||
let heading_and_fields = match &variant_data.kind {
|
||||
clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
|
||||
clean::VariantKind::Struct(s) => {
|
||||
// If there is no field to display, no need to add the heading.
|
||||
if s.fields.iter().any(|f| !f.is_doc_hidden()) {
|
||||
Some(("Fields", &s.fields))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
clean::VariantKind::Tuple(fields) => {
|
||||
// Documentation on tuple variant fields is rare, so to reduce noise we only emit
|
||||
// the section if at least one field is documented.
|
||||
|
@ -140,7 +140,7 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
|
||||
let status = Command::new("git")
|
||||
.arg("fetch")
|
||||
.arg(test.repo)
|
||||
.arg("master")
|
||||
.arg(test.sha)
|
||||
.arg(&format!("--depth={}", depth))
|
||||
.current_dir(&out_dir)
|
||||
.status()
|
||||
|
@ -1 +1 @@
|
||||
c9808f87028e16d134438787cab3d4cc16d05fe2
|
||||
317d14a56cb8c748bf0e2f2afff89c2249ab4423
|
||||
|
@ -1,11 +1,10 @@
|
||||
//@compile-flags: -Zmiri-strict-provenance
|
||||
//@error-in-other-file: /retag .* tag does not exist in the borrow stack/
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let a = [1, 2, 3];
|
||||
let s = &a[0..0];
|
||||
assert_eq!(s.len(), 0);
|
||||
assert_eq!(*s.get_unchecked(1), 2);
|
||||
assert_eq!(*s.as_ptr().add(1), 2); //~ ERROR: /retag .* tag does not exist in the borrow stack/
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,22 @@
|
||||
error: Undefined Behavior: trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
|
||||
--> RUSTLIB/core/src/slice/mod.rs:LL:CC
|
||||
--> $DIR/zst_slice.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { &*index.get_unchecked(self) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
|
||||
| this error occurs as part of retag at ALLOC[0x4..0x8]
|
||||
LL | assert_eq!(*s.as_ptr().add(1), 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
|
||||
| this error occurs as part of retag at ALLOC[0x4..0x8]
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||
help: <TAG> would have been created here, but this is a zero-size retag ([0x0..0x0]) so the tag in question does not exist anywhere
|
||||
--> $DIR/zst_slice.rs:LL:CC
|
||||
|
|
||||
LL | assert_eq!(*s.get_unchecked(1), 2);
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
LL | assert_eq!(*s.as_ptr().add(1), 2);
|
||||
| ^^^^^^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `core::slice::<impl [i32]>::get_unchecked::<usize>` at RUSTLIB/core/src/slice/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/zst_slice.rs:LL:CC
|
||||
|
|
||||
LL | assert_eq!(*s.get_unchecked(1), 2);
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
= note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows
|
||||
fn main() {
|
||||
let v: Vec<u8> = Vec::with_capacity(10);
|
||||
let undef = unsafe { *v.get_unchecked(5) }; //~ ERROR: uninitialized
|
||||
let undef = unsafe { *v.as_ptr().add(5) }; //~ ERROR: uninitialized
|
||||
let x = undef + 1;
|
||||
panic!("this should never print: {}", x);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
|
||||
--> $DIR/uninit_byte_read.rs:LL:CC
|
||||
|
|
||||
LL | let undef = unsafe { *v.get_unchecked(5) };
|
||||
| ^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
|
||||
LL | let undef = unsafe { *v.as_ptr().add(5) };
|
||||
| ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
@ -20,8 +20,8 @@ use NaNKind::*;
|
||||
#[track_caller]
|
||||
fn check_all_outcomes<T: Eq + Hash + fmt::Display>(expected: HashSet<T>, generate: impl Fn() -> T) {
|
||||
let mut seen = HashSet::new();
|
||||
// Let's give it 8x as many tries as we are expecting values.
|
||||
let tries = expected.len() * 8;
|
||||
// Let's give it sixteen times as many tries as we are expecting values.
|
||||
let tries = expected.len() * 16;
|
||||
for _ in 0..tries {
|
||||
let val = generate();
|
||||
assert!(expected.contains(&val), "got an unexpected value: {val}");
|
||||
|
@ -0,0 +1,37 @@
|
||||
#![feature(layout_for_ptr)]
|
||||
use std::mem;
|
||||
|
||||
#[repr(packed, C)]
|
||||
struct PackedSized {
|
||||
f: u8,
|
||||
d: [u32; 4],
|
||||
}
|
||||
|
||||
#[repr(packed, C)]
|
||||
struct PackedUnsized {
|
||||
f: u8,
|
||||
d: [u32],
|
||||
}
|
||||
|
||||
impl PackedSized {
|
||||
fn unsize(&self) -> &PackedUnsized {
|
||||
// We can't unsize via a generic type since then we get the error
|
||||
// that packed structs with unsized tail don't work if the tail
|
||||
// might need dropping.
|
||||
let len = 4usize;
|
||||
unsafe { mem::transmute((self, len)) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
|
||||
let p = p.unsize() as *const PackedUnsized;
|
||||
// Make sure the size computation does *not* think there is
|
||||
// any padding in front of the `d` field.
|
||||
assert_eq!(mem::size_of_val_raw(p), 1 + 4 * 4);
|
||||
// And likewise for the offset computation.
|
||||
let d = std::ptr::addr_of!((*p).d);
|
||||
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
#![feature(layout_for_ptr)]
|
||||
use std::mem;
|
||||
|
||||
#[repr(packed(4))]
|
||||
struct Slice([u32]);
|
||||
|
||||
#[repr(packed(2), C)]
|
||||
struct PackedSized {
|
||||
f: u8,
|
||||
d: [u32; 4],
|
||||
}
|
||||
|
||||
#[repr(packed(2), C)]
|
||||
struct PackedUnsized {
|
||||
f: u8,
|
||||
d: Slice,
|
||||
}
|
||||
|
||||
impl PackedSized {
|
||||
fn unsize(&self) -> &PackedUnsized {
|
||||
// We can't unsize via a generic type since then we get the error
|
||||
// that packed structs with unsized tail don't work if the tail
|
||||
// might need dropping.
|
||||
let len = 4usize;
|
||||
unsafe { mem::transmute((self, len)) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
|
||||
let p = p.unsize() as *const PackedUnsized;
|
||||
// Make sure the size computation correctly adds exact 1 byte of padding
|
||||
// in front of the `d` field.
|
||||
assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4 * 4);
|
||||
// And likewise for the offset computation.
|
||||
let d = std::ptr::addr_of!((*p).d);
|
||||
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
|
||||
}
|
||||
}
|
@ -2000,9 +2000,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.8"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db"
|
||||
checksum = "d0c5a71827ac326072b6405552093e2ad2accd25a32fd78d4edc82d98c7f2409"
|
||||
|
||||
[[package]]
|
||||
name = "tt"
|
||||
|
@ -116,7 +116,7 @@ text-size = "1.1.1"
|
||||
rayon = "1.8.0"
|
||||
serde = { version = "1.0.192", features = ["derive"] }
|
||||
serde_json = "1.0.108"
|
||||
triomphe = { version = "0.1.8", default-features = false, features = ["std"] }
|
||||
triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
|
||||
# can't upgrade due to dashmap depending on 0.12.3 currently
|
||||
hashbrown = { version = "0.12.3", features = [
|
||||
"inline-more",
|
||||
|
@ -38,7 +38,6 @@ mod tests;
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::Index,
|
||||
};
|
||||
|
||||
@ -340,34 +339,37 @@ pub trait ItemTreeNode: Clone {
|
||||
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
|
||||
}
|
||||
|
||||
pub struct FileItemTreeId<N: ItemTreeNode> {
|
||||
index: Idx<N>,
|
||||
_p: PhantomData<N>,
|
||||
pub struct FileItemTreeId<N: ItemTreeNode>(Idx<N>);
|
||||
|
||||
impl<N: ItemTreeNode> FileItemTreeId<N> {
|
||||
pub fn index(&self) -> Idx<N> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { index: self.index, _p: PhantomData }
|
||||
Self(self.0)
|
||||
}
|
||||
}
|
||||
impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
|
||||
|
||||
impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
|
||||
fn eq(&self, other: &FileItemTreeId<N>) -> bool {
|
||||
self.index == other.index
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
|
||||
|
||||
impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.index.hash(state)
|
||||
self.0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.index.fmt(f)
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,7 +550,7 @@ impl Index<RawVisibilityId> for ItemTree {
|
||||
impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
|
||||
type Output = N;
|
||||
fn index(&self, id: FileItemTreeId<N>) -> &N {
|
||||
N::lookup(self, id.index)
|
||||
N::lookup(self, id.index())
|
||||
}
|
||||
}
|
||||
|
||||
@ -925,23 +927,23 @@ impl ModItem {
|
||||
|
||||
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
|
||||
match self {
|
||||
ModItem::Use(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::ExternBlock(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Function(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Struct(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Union(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Enum(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Const(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Static(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Trait(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::TraitAlias(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Impl(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Mod(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::MacroRules(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::MacroDef(it) => tree[it.index].ast_id().upcast(),
|
||||
ModItem::Use(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::ExternCrate(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::ExternBlock(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Function(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Struct(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Union(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Enum(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Const(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Static(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Trait(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::TraitAlias(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Impl(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::TypeAlias(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Mod(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::MacroCall(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::MacroRules(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::MacroDef(it) => tree[it.index()].ast_id().upcast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
use super::*;
|
||||
|
||||
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
|
||||
FileItemTreeId { index, _p: PhantomData }
|
||||
FileItemTreeId(index)
|
||||
}
|
||||
|
||||
pub(super) struct Ctx<'a> {
|
||||
|
@ -1152,20 +1152,15 @@ impl<'a> InferenceContext<'a> {
|
||||
(ty, variant)
|
||||
}
|
||||
TypeNs::TypeAliasId(it) => {
|
||||
let container = it.lookup(self.db.upcast()).container;
|
||||
let parent_subst = match container {
|
||||
ItemContainerId::TraitId(id) => {
|
||||
let subst = TyBuilder::subst_for_def(self.db, id, None)
|
||||
.fill_with_inference_vars(&mut self.table)
|
||||
.build();
|
||||
Some(subst)
|
||||
}
|
||||
// Type aliases do not exist in impls.
|
||||
_ => None,
|
||||
let resolved_seg = match unresolved {
|
||||
None => path.segments().last().unwrap(),
|
||||
Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(),
|
||||
};
|
||||
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
|
||||
.fill_with_inference_vars(&mut self.table)
|
||||
.build();
|
||||
let substs =
|
||||
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
|
||||
let ty = self.db.ty(it.into());
|
||||
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
|
||||
|
||||
self.resolve_variant_on_alias(ty, unresolved, mod_path)
|
||||
}
|
||||
TypeNs::AdtSelfType(_) => {
|
||||
|
@ -768,7 +768,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn substs_from_path_segment(
|
||||
pub(super) fn substs_from_path_segment(
|
||||
&self,
|
||||
segment: PathSegment<'_>,
|
||||
def: Option<GenericDefId>,
|
||||
|
@ -269,6 +269,10 @@ impl ProjectionStore {
|
||||
impl ProjectionId {
|
||||
pub const EMPTY: ProjectionId = ProjectionId(0);
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self == ProjectionId::EMPTY
|
||||
}
|
||||
|
||||
pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
|
||||
store.id_to_proj.get(&self).unwrap()
|
||||
}
|
||||
@ -1069,6 +1073,10 @@ pub struct MirBody {
|
||||
}
|
||||
|
||||
impl MirBody {
|
||||
pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
|
||||
self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
|
||||
}
|
||||
|
||||
fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
|
||||
fn for_operand(
|
||||
op: &mut Operand,
|
||||
@ -1188,3 +1196,9 @@ pub enum MirSpan {
|
||||
}
|
||||
|
||||
impl_from!(ExprId, PatId for MirSpan);
|
||||
|
||||
impl From<&ExprId> for MirSpan {
|
||||
fn from(value: &ExprId) -> Self {
|
||||
(*value).into()
|
||||
}
|
||||
}
|
||||
|
@ -105,9 +105,14 @@ pub enum MirLowerError {
|
||||
/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
|
||||
struct DropScopeToken;
|
||||
impl DropScopeToken {
|
||||
fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId {
|
||||
fn pop_and_drop(
|
||||
self,
|
||||
ctx: &mut MirLowerCtx<'_>,
|
||||
current: BasicBlockId,
|
||||
span: MirSpan,
|
||||
) -> BasicBlockId {
|
||||
std::mem::forget(self);
|
||||
ctx.pop_drop_scope_internal(current)
|
||||
ctx.pop_drop_scope_internal(current, span)
|
||||
}
|
||||
|
||||
/// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
|
||||
@ -582,7 +587,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
|
||||
let scope = this.push_drop_scope();
|
||||
if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
|
||||
current = scope.pop_and_drop(this, current);
|
||||
current = scope.pop_and_drop(this, current, body.into());
|
||||
this.set_goto(current, begin, expr_id.into());
|
||||
} else {
|
||||
scope.pop_assume_dropped(this);
|
||||
@ -720,7 +725,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
.ok_or(MirLowerError::ContinueWithoutLoop)?,
|
||||
};
|
||||
let begin = loop_data.begin;
|
||||
current = self.drop_until_scope(loop_data.drop_scope_index, current);
|
||||
current =
|
||||
self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into());
|
||||
self.set_goto(current, begin, expr_id.into());
|
||||
Ok(None)
|
||||
}
|
||||
@ -759,7 +765,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
self.current_loop_blocks.as_ref().unwrap().drop_scope_index,
|
||||
),
|
||||
};
|
||||
current = self.drop_until_scope(drop_scope, current);
|
||||
current = self.drop_until_scope(drop_scope, current, expr_id.into());
|
||||
self.set_goto(current, end, expr_id.into());
|
||||
Ok(None)
|
||||
}
|
||||
@ -773,7 +779,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
current = self.drop_until_scope(0, current);
|
||||
current = self.drop_until_scope(0, current, expr_id.into());
|
||||
self.set_terminator(current, TerminatorKind::Return, expr_id.into());
|
||||
Ok(None)
|
||||
}
|
||||
@ -1782,7 +1788,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_fake_read(c, p, expr.into());
|
||||
current = scope2.pop_and_drop(self, c);
|
||||
current = scope2.pop_and_drop(self, c, expr.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1793,7 +1799,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
};
|
||||
current = c;
|
||||
}
|
||||
current = scope.pop_and_drop(self, current);
|
||||
current = scope.pop_and_drop(self, current, span);
|
||||
Ok(Some(current))
|
||||
}
|
||||
|
||||
@ -1873,9 +1879,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId {
|
||||
fn drop_until_scope(
|
||||
&mut self,
|
||||
scope_index: usize,
|
||||
mut current: BasicBlockId,
|
||||
span: MirSpan,
|
||||
) -> BasicBlockId {
|
||||
for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
|
||||
self.emit_drop_and_storage_dead_for_scope(scope, &mut current);
|
||||
self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span);
|
||||
}
|
||||
current
|
||||
}
|
||||
@ -1891,17 +1902,22 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
}
|
||||
|
||||
/// Don't call directly
|
||||
fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId {
|
||||
fn pop_drop_scope_internal(
|
||||
&mut self,
|
||||
mut current: BasicBlockId,
|
||||
span: MirSpan,
|
||||
) -> BasicBlockId {
|
||||
let scope = self.drop_scopes.pop().unwrap();
|
||||
self.emit_drop_and_storage_dead_for_scope(&scope, &mut current);
|
||||
self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span);
|
||||
current
|
||||
}
|
||||
|
||||
fn pop_drop_scope_assert_finished(
|
||||
&mut self,
|
||||
mut current: BasicBlockId,
|
||||
span: MirSpan,
|
||||
) -> Result<BasicBlockId> {
|
||||
current = self.pop_drop_scope_internal(current);
|
||||
current = self.pop_drop_scope_internal(current, span);
|
||||
if !self.drop_scopes.is_empty() {
|
||||
implementation_error!("Mismatched count between drop scope push and pops");
|
||||
}
|
||||
@ -1912,6 +1928,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
&mut self,
|
||||
scope: &DropScope,
|
||||
current: &mut Idx<BasicBlock>,
|
||||
span: MirSpan,
|
||||
) {
|
||||
for &l in scope.locals.iter().rev() {
|
||||
if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
|
||||
@ -1919,13 +1936,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
self.set_terminator(
|
||||
prev,
|
||||
TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
|
||||
MirSpan::Unknown,
|
||||
span,
|
||||
);
|
||||
}
|
||||
self.push_statement(
|
||||
*current,
|
||||
StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
|
||||
);
|
||||
self.push_statement(*current, StatementKind::StorageDead(l).with_span(span));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2002,7 +2016,7 @@ pub fn mir_body_for_closure_query(
|
||||
|_| true,
|
||||
)?;
|
||||
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
|
||||
let current = ctx.pop_drop_scope_assert_finished(current)?;
|
||||
let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
|
||||
ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
|
||||
}
|
||||
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
|
||||
@ -2146,7 +2160,7 @@ pub fn lower_to_mir(
|
||||
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
|
||||
};
|
||||
if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
|
||||
let current = ctx.pop_drop_scope_assert_finished(current)?;
|
||||
let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
|
||||
ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
|
||||
}
|
||||
Ok(ctx.result)
|
||||
|
@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
let indent = mem::take(&mut self.indent);
|
||||
let mut ctx = MirPrettyCtx {
|
||||
body: &body,
|
||||
local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(),
|
||||
local_to_binding: body.local_to_binding_map(),
|
||||
result,
|
||||
indent,
|
||||
..*self
|
||||
@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
|
||||
fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
|
||||
let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect();
|
||||
let local_to_binding = body.local_to_binding_map();
|
||||
MirPrettyCtx {
|
||||
body,
|
||||
db,
|
||||
|
@ -1129,3 +1129,27 @@ fn foo() {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_alias() {
|
||||
check_types(
|
||||
r#"
|
||||
type Wrap<T> = T;
|
||||
|
||||
enum X {
|
||||
A { cool: u32, stuff: u32 },
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let wrapped = Wrap::<X>::A {
|
||||
cool: 100,
|
||||
stuff: 100,
|
||||
};
|
||||
|
||||
if let Wrap::<X>::A { cool, ..} = &wrapped {}
|
||||
//^^^^ &u32
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ use hir_ty::{
|
||||
known_const_to_ast,
|
||||
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
|
||||
method_resolution::{self, TyFingerprint},
|
||||
mir::{self, interpret_mir},
|
||||
mir::interpret_mir,
|
||||
primitive::UintTy,
|
||||
traits::FnTrait,
|
||||
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
|
||||
@ -129,9 +129,10 @@ pub use {
|
||||
hir_ty::{
|
||||
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
|
||||
layout::LayoutError,
|
||||
mir::MirEvalError,
|
||||
PointerCast, Safety,
|
||||
},
|
||||
// FIXME: Properly encapsulate mir
|
||||
hir_ty::{mir, Interner as ChalkTyInterner},
|
||||
};
|
||||
|
||||
// These are negative re-exports: pub using these names is forbidden, they
|
||||
@ -1914,17 +1915,20 @@ impl DefWithBody {
|
||||
if let ast::Expr::MatchExpr(match_expr) =
|
||||
&source_ptr.value.to_node(&root)
|
||||
{
|
||||
if let Some(scrut_expr) = match_expr.expr() {
|
||||
acc.push(
|
||||
MissingMatchArms {
|
||||
scrutinee_expr: InFile::new(
|
||||
source_ptr.file_id,
|
||||
AstPtr::new(&scrut_expr),
|
||||
),
|
||||
uncovered_patterns,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
match match_expr.expr() {
|
||||
Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
|
||||
acc.push(
|
||||
MissingMatchArms {
|
||||
scrutinee_expr: InFile::new(
|
||||
source_ptr.file_id,
|
||||
AstPtr::new(&scrut_expr),
|
||||
),
|
||||
uncovered_patterns,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use syntax::{ast, AstNode};
|
||||
use syntax::{ast, AstNode, SyntaxKind, T};
|
||||
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
@ -39,7 +39,19 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
||||
AssistId("remove_parentheses", AssistKind::Refactor),
|
||||
"Remove redundant parentheses",
|
||||
target,
|
||||
|builder| builder.replace_ast(parens.into(), expr),
|
||||
|builder| {
|
||||
let prev_token = parens.syntax().first_token().and_then(|it| it.prev_token());
|
||||
let need_to_add_ws = match prev_token {
|
||||
Some(it) => {
|
||||
let tokens = vec![T![&], T![!], T!['('], T!['['], T!['{']];
|
||||
it.kind() != SyntaxKind::WHITESPACE && !tokens.contains(&it.kind())
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
let expr = if need_to_add_ws { format!(" {}", expr) } else { expr.to_string() };
|
||||
|
||||
builder.replace(parens.syntax().text_range(), expr)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -49,6 +61,15 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn remove_parens_space() {
|
||||
check_assist(
|
||||
remove_parentheses,
|
||||
r#"fn f() { match$0(true) {} }"#,
|
||||
r#"fn f() { match true {} }"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_parens_simple() {
|
||||
check_assist(remove_parentheses, r#"fn f() { $0(2) + 2; }"#, r#"fn f() { 2 + 2; }"#);
|
||||
@ -94,8 +115,8 @@ mod tests {
|
||||
check_assist(remove_parentheses, r#"fn f() { f(($02 + 2)); }"#, r#"fn f() { f(2 + 2); }"#);
|
||||
check_assist(
|
||||
remove_parentheses,
|
||||
r#"fn f() { (1<2)&&$0(3>4); }"#,
|
||||
r#"fn f() { (1<2)&&3>4; }"#,
|
||||
r#"fn f() { (1<2) &&$0(3>4); }"#,
|
||||
r#"fn f() { (1<2) && 3>4; }"#,
|
||||
);
|
||||
}
|
||||
|
||||
@ -164,8 +185,8 @@ mod tests {
|
||||
fn remove_parens_weird_places() {
|
||||
check_assist(
|
||||
remove_parentheses,
|
||||
r#"fn f() { match () { _=>$0(()) } }"#,
|
||||
r#"fn f() { match () { _=>() } }"#,
|
||||
r#"fn f() { match () { _ =>$0(()) } }"#,
|
||||
r#"fn f() { match () { _ => () } }"#,
|
||||
);
|
||||
|
||||
check_assist(
|
||||
|
@ -26,17 +26,17 @@ pub(crate) fn complete_dot(
|
||||
item.add_to(acc, ctx.db);
|
||||
}
|
||||
|
||||
if let DotAccessKind::Method { .. } = dot_access.kind {
|
||||
cov_mark::hit!(test_no_struct_field_completion_for_method_call);
|
||||
} else {
|
||||
complete_fields(
|
||||
acc,
|
||||
ctx,
|
||||
receiver_ty,
|
||||
|acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty),
|
||||
|acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty),
|
||||
);
|
||||
}
|
||||
let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
|
||||
|
||||
complete_fields(
|
||||
acc,
|
||||
ctx,
|
||||
receiver_ty,
|
||||
|acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty),
|
||||
|acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty),
|
||||
is_field_access,
|
||||
);
|
||||
|
||||
complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None));
|
||||
}
|
||||
|
||||
@ -82,6 +82,7 @@ pub(crate) fn complete_undotted_self(
|
||||
)
|
||||
},
|
||||
|acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty),
|
||||
true,
|
||||
);
|
||||
complete_methods(ctx, &ty, |func| {
|
||||
acc.add_method(
|
||||
@ -104,18 +105,23 @@ fn complete_fields(
|
||||
receiver: &hir::Type,
|
||||
mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type),
|
||||
mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type),
|
||||
is_field_access: bool,
|
||||
) {
|
||||
let mut seen_names = FxHashSet::default();
|
||||
for receiver in receiver.autoderef(ctx.db) {
|
||||
for (field, ty) in receiver.fields(ctx.db) {
|
||||
if seen_names.insert(field.name(ctx.db)) {
|
||||
if seen_names.insert(field.name(ctx.db))
|
||||
&& (is_field_access || ty.is_fn() || ty.is_closure())
|
||||
{
|
||||
named_field(acc, field, ty);
|
||||
}
|
||||
}
|
||||
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
|
||||
// Tuples are always the last type in a deref chain, so just check if the name is
|
||||
// already seen without inserting into the hashset.
|
||||
if !seen_names.contains(&hir::Name::new_tuple_field(i)) {
|
||||
if !seen_names.contains(&hir::Name::new_tuple_field(i))
|
||||
&& (is_field_access || ty.is_fn() || ty.is_closure())
|
||||
{
|
||||
// Tuple fields are always public (tuple struct fields are handled above).
|
||||
tuple_index(acc, i, ty);
|
||||
}
|
||||
@ -250,7 +256,6 @@ impl A {
|
||||
|
||||
#[test]
|
||||
fn test_no_struct_field_completion_for_method_call() {
|
||||
cov_mark::check!(test_no_struct_field_completion_for_method_call);
|
||||
check(
|
||||
r#"
|
||||
struct A { the_field: u32 }
|
||||
@ -1172,4 +1177,63 @@ impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_function_field_completion() {
|
||||
check(
|
||||
r#"
|
||||
struct S { va_field: u32, fn_field: fn() }
|
||||
fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() }
|
||||
"#,
|
||||
expect![[r#"
|
||||
fd fn_field fn()
|
||||
"#]],
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"fn_field",
|
||||
r#"
|
||||
struct S { va_field: u32, fn_field: fn() }
|
||||
fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() }
|
||||
"#,
|
||||
r#"
|
||||
struct S { va_field: u32, fn_field: fn() }
|
||||
fn foo() { (S { va_field: 0, fn_field: || {} }.fn_field)() }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_function_field_completion() {
|
||||
check(
|
||||
r#"
|
||||
struct B(u32, fn())
|
||||
fn foo() {
|
||||
let b = B(0, || {});
|
||||
b.$0()
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fd 1 fn()
|
||||
"#]],
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"1",
|
||||
r#"
|
||||
struct B(u32, fn())
|
||||
fn foo() {
|
||||
let b = B(0, || {});
|
||||
b.$0()
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct B(u32, fn())
|
||||
fn foo() {
|
||||
let b = B(0, || {});
|
||||
(b.1)()
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,10 @@ use ide_db::{
|
||||
RootDatabase, SnippetCap, SymbolKind,
|
||||
};
|
||||
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{
|
||||
context::{DotAccess, PathCompletionCtx, PathKind, PatternContext},
|
||||
context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
|
||||
item::{Builder, CompletionRelevanceTypeMatch},
|
||||
render::{
|
||||
function::render_fn,
|
||||
@ -147,7 +148,42 @@ pub(crate) fn render_field(
|
||||
.set_documentation(field.docs(db))
|
||||
.set_deprecated(is_deprecated)
|
||||
.lookup_by(name);
|
||||
item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
|
||||
|
||||
let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
|
||||
if !is_field_access || ty.is_fn() || ty.is_closure() {
|
||||
let mut builder = TextEdit::builder();
|
||||
// Using TextEdit, insert '(' before the struct name and ')' before the
|
||||
// dot access, then comes the field name and optionally insert function
|
||||
// call parens.
|
||||
|
||||
builder.replace(
|
||||
ctx.source_range(),
|
||||
field_with_receiver(db, receiver.as_ref(), &escaped_name).into(),
|
||||
);
|
||||
|
||||
let expected_fn_type =
|
||||
ctx.completion.expected_type.as_ref().is_some_and(|ty| ty.is_fn() || ty.is_closure());
|
||||
|
||||
if !expected_fn_type {
|
||||
if let Some(receiver) = &dot_access.receiver {
|
||||
if let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) {
|
||||
builder.insert(receiver.syntax().text_range().start(), "(".to_string());
|
||||
builder.insert(ctx.source_range().end(), ")".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let is_parens_needed =
|
||||
!matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
|
||||
|
||||
if is_parens_needed {
|
||||
builder.insert(ctx.source_range().end(), "()".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
item.text_edit(builder.finish());
|
||||
} else {
|
||||
item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
|
||||
}
|
||||
if let Some(receiver) = &dot_access.receiver {
|
||||
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
|
||||
if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
|
||||
@ -1600,7 +1636,7 @@ fn main() {
|
||||
fn struct_field_method_ref() {
|
||||
check_kinds(
|
||||
r#"
|
||||
struct Foo { bar: u32 }
|
||||
struct Foo { bar: u32, qux: fn() }
|
||||
impl Foo { fn baz(&self) -> u32 { 0 } }
|
||||
|
||||
fn foo(f: Foo) { let _: &u32 = f.b$0 }
|
||||
@ -1610,30 +1646,92 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
|
||||
[
|
||||
CompletionItem {
|
||||
label: "baz()",
|
||||
source_range: 98..99,
|
||||
delete: 98..99,
|
||||
source_range: 109..110,
|
||||
delete: 109..110,
|
||||
insert: "baz()$0",
|
||||
kind: Method,
|
||||
lookup: "baz",
|
||||
detail: "fn(&self) -> u32",
|
||||
ref_match: "&@96",
|
||||
ref_match: "&@107",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "bar",
|
||||
source_range: 98..99,
|
||||
delete: 98..99,
|
||||
source_range: 109..110,
|
||||
delete: 109..110,
|
||||
insert: "bar",
|
||||
kind: SymbolKind(
|
||||
Field,
|
||||
),
|
||||
detail: "u32",
|
||||
ref_match: "&@96",
|
||||
ref_match: "&@107",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "qux",
|
||||
source_range: 109..110,
|
||||
text_edit: TextEdit {
|
||||
indels: [
|
||||
Indel {
|
||||
insert: "(",
|
||||
delete: 107..107,
|
||||
},
|
||||
Indel {
|
||||
insert: "qux)()",
|
||||
delete: 109..110,
|
||||
},
|
||||
],
|
||||
},
|
||||
kind: SymbolKind(
|
||||
Field,
|
||||
),
|
||||
detail: "fn()",
|
||||
},
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expected_fn_type_ref() {
|
||||
check_kinds(
|
||||
r#"
|
||||
struct S { field: fn() }
|
||||
|
||||
fn foo() {
|
||||
let foo: fn() = S { fields: || {}}.fi$0;
|
||||
}
|
||||
"#,
|
||||
&[CompletionItemKind::SymbolKind(SymbolKind::Field)],
|
||||
expect![[r#"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "field",
|
||||
source_range: 76..78,
|
||||
delete: 76..78,
|
||||
insert: "field",
|
||||
kind: SymbolKind(
|
||||
Field,
|
||||
),
|
||||
detail: "fn()",
|
||||
relevance: CompletionRelevance {
|
||||
exact_name_match: false,
|
||||
type_match: Some(
|
||||
Exact,
|
||||
),
|
||||
is_local: false,
|
||||
is_item_from_trait: false,
|
||||
is_name_already_imported: false,
|
||||
requires_import: false,
|
||||
is_op_method: false,
|
||||
is_private_editable: false,
|
||||
postfix_match: None,
|
||||
is_definite: false,
|
||||
},
|
||||
},
|
||||
]
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qualified_path_ref() {
|
||||
check_kinds(
|
||||
|
@ -354,6 +354,35 @@ fn outer(Foo { bar$0 }: Foo) {}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_in_record_field_pat_with_generic_type_alias() {
|
||||
check_empty(
|
||||
r#"
|
||||
type Wrap<T> = T;
|
||||
|
||||
enum X {
|
||||
A { cool: u32, stuff: u32 },
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let wrapped = Wrap::<X>::A {
|
||||
cool: 100,
|
||||
stuff: 100,
|
||||
};
|
||||
|
||||
if let Wrap::<X>::A { $0 } = &wrapped {};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fd cool u32
|
||||
fd stuff u32
|
||||
kw mut
|
||||
kw ref
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_in_fn_param() {
|
||||
check_empty(
|
||||
|
@ -17,7 +17,10 @@ pub(crate) fn missing_match_arms(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
use crate::{
|
||||
tests::{check_diagnostics, check_diagnostics_with_config},
|
||||
DiagnosticsConfig,
|
||||
};
|
||||
|
||||
#[track_caller]
|
||||
fn check_diagnostics_no_bails(ra_fixture: &str) {
|
||||
@ -25,6 +28,20 @@ mod tests {
|
||||
crate::tests::check_diagnostics(ra_fixture)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_body() {
|
||||
let mut config = DiagnosticsConfig::test_sample();
|
||||
config.disabled.insert("syntax-error".to_string());
|
||||
check_diagnostics_with_config(
|
||||
config,
|
||||
r#"
|
||||
fn main() {
|
||||
match 0;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_tuple() {
|
||||
check_diagnostics_no_bails(
|
||||
|
@ -31,6 +31,7 @@ mod discriminant;
|
||||
mod fn_lifetime_fn;
|
||||
mod implicit_static;
|
||||
mod param_name;
|
||||
mod implicit_drop;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct InlayHintsConfig {
|
||||
@ -45,6 +46,7 @@ pub struct InlayHintsConfig {
|
||||
pub closure_return_type_hints: ClosureReturnTypeHints,
|
||||
pub closure_capture_hints: bool,
|
||||
pub binding_mode_hints: bool,
|
||||
pub implicit_drop_hints: bool,
|
||||
pub lifetime_elision_hints: LifetimeElisionHints,
|
||||
pub param_names_for_lifetime_elision_hints: bool,
|
||||
pub hide_named_constructor_hints: bool,
|
||||
@ -124,6 +126,7 @@ pub enum InlayKind {
|
||||
Lifetime,
|
||||
Parameter,
|
||||
Type,
|
||||
Drop,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -503,7 +506,10 @@ fn hints(
|
||||
ast::Item(it) => match it {
|
||||
// FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
|
||||
ast::Item::Impl(_) => None,
|
||||
ast::Item::Fn(it) => fn_lifetime_fn::hints(hints, config, it),
|
||||
ast::Item::Fn(it) => {
|
||||
implicit_drop::hints(hints, sema, config, &it);
|
||||
fn_lifetime_fn::hints(hints, config, it)
|
||||
},
|
||||
// static type elisions
|
||||
ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)),
|
||||
ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)),
|
||||
@ -591,6 +597,7 @@ mod tests {
|
||||
max_length: None,
|
||||
closing_brace_hints_min_lines: None,
|
||||
fields_to_resolve: InlayFieldsToResolve::empty(),
|
||||
implicit_drop_hints: false,
|
||||
};
|
||||
pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
|
||||
type_hints: true,
|
||||
|
@ -0,0 +1,204 @@
|
||||
//! Implementation of "implicit drop" inlay hints:
|
||||
//! ```no_run
|
||||
//! fn main() {
|
||||
//! let x = vec![2];
|
||||
//! if some_condition() {
|
||||
//! /* drop(x) */return;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
use hir::{
|
||||
db::{DefDatabase as _, HirDatabase as _},
|
||||
mir::{MirSpan, TerminatorKind},
|
||||
ChalkTyInterner, DefWithBody, Semantics,
|
||||
};
|
||||
use ide_db::{base_db::FileRange, RootDatabase};
|
||||
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
match_ast,
|
||||
};
|
||||
|
||||
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
|
||||
|
||||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &InlayHintsConfig,
|
||||
def: &ast::Fn,
|
||||
) -> Option<()> {
|
||||
if !config.implicit_drop_hints {
|
||||
return None;
|
||||
}
|
||||
|
||||
let def = sema.to_def(def)?;
|
||||
let def: DefWithBody = def.into();
|
||||
|
||||
let source_map = sema.db.body_with_source_map(def.into()).1;
|
||||
|
||||
let hir = sema.db.body(def.into());
|
||||
let mir = sema.db.mir_body(def.into()).ok()?;
|
||||
|
||||
let local_to_binding = mir.local_to_binding_map();
|
||||
|
||||
for (_, bb) in mir.basic_blocks.iter() {
|
||||
let terminator = bb.terminator.as_ref()?;
|
||||
if let TerminatorKind::Drop { place, .. } = terminator.kind {
|
||||
if !place.projection.is_empty() {
|
||||
continue; // Ignore complex cases for now
|
||||
}
|
||||
if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() {
|
||||
continue; // Arguably only ADTs have significant drop impls
|
||||
}
|
||||
let Some(binding) = local_to_binding.get(place.local) else {
|
||||
continue; // Ignore temporary values
|
||||
};
|
||||
let range = match terminator.span {
|
||||
MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
|
||||
Ok(s) => {
|
||||
let root = &s.file_syntax(sema.db);
|
||||
let expr = s.value.to_node(root);
|
||||
let expr = expr.syntax();
|
||||
match_ast! {
|
||||
match expr {
|
||||
ast::BlockExpr(x) => x.stmt_list().and_then(|x| x.r_curly_token()).map(|x| x.text_range()).unwrap_or_else(|| expr.text_range()),
|
||||
_ => expr.text_range(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => continue,
|
||||
},
|
||||
MirSpan::PatId(p) => match source_map.pat_syntax(p) {
|
||||
Ok(s) => s.value.text_range(),
|
||||
Err(_) => continue,
|
||||
},
|
||||
MirSpan::Unknown => continue,
|
||||
};
|
||||
let binding = &hir.bindings[*binding];
|
||||
let binding_source = binding
|
||||
.definitions
|
||||
.first()
|
||||
.and_then(|d| source_map.pat_syntax(*d).ok())
|
||||
.and_then(|d| {
|
||||
Some(FileRange { file_id: d.file_id.file_id()?, range: d.value.text_range() })
|
||||
});
|
||||
let name = binding.name.to_smol_str();
|
||||
if name.starts_with("<ra@") {
|
||||
continue; // Ignore desugared variables
|
||||
}
|
||||
let mut label = InlayHintLabel::simple(
|
||||
name,
|
||||
Some(crate::InlayTooltip::String("moz".into())),
|
||||
binding_source,
|
||||
);
|
||||
label.prepend_str("drop(");
|
||||
label.append_str(")");
|
||||
acc.push(InlayHint {
|
||||
range,
|
||||
position: InlayHintPosition::Before,
|
||||
pad_left: true,
|
||||
pad_right: true,
|
||||
kind: InlayKind::Drop,
|
||||
needs_resolve: label.needs_resolve(),
|
||||
label,
|
||||
text_edit: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
|
||||
InlayHintsConfig,
|
||||
};
|
||||
|
||||
const ONLY_DROP_CONFIG: InlayHintsConfig =
|
||||
InlayHintsConfig { implicit_drop_hints: true, ..DISABLED_CONFIG };
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
check_with_config(
|
||||
ONLY_DROP_CONFIG,
|
||||
r#"
|
||||
struct X;
|
||||
fn f() {
|
||||
let x = X;
|
||||
if 2 == 5 {
|
||||
return;
|
||||
//^^^^^^ drop(x)
|
||||
}
|
||||
}
|
||||
//^ drop(x)
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_hint_for_copy_types_and_mutable_references() {
|
||||
// `T: Copy` and `T = &mut U` types do nothing on drop, so we should hide drop inlay hint for them.
|
||||
check_with_config(
|
||||
ONLY_DROP_CONFIG,
|
||||
r#"
|
||||
//- minicore: copy, derive
|
||||
|
||||
struct X(i32, i32);
|
||||
#[derive(Clone, Copy)]
|
||||
struct Y(i32, i32);
|
||||
fn f() {
|
||||
let a = 2;
|
||||
let b = a + 4;
|
||||
let mut x = X(a, b);
|
||||
let mut y = Y(a, b);
|
||||
let mx = &mut x;
|
||||
let my = &mut y;
|
||||
let c = a + b;
|
||||
}
|
||||
//^ drop(x)
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_operator() {
|
||||
// We currently show drop inlay hint for every `?` operator that may potentialy drop something. We probably need to
|
||||
// make it configurable as it doesn't seem very useful.
|
||||
check_with_config(
|
||||
ONLY_DROP_CONFIG,
|
||||
r#"
|
||||
//- minicore: copy, try, option
|
||||
|
||||
struct X;
|
||||
fn f() -> Option<()> {
|
||||
let x = X;
|
||||
let t_opt = Some(2);
|
||||
let t = t_opt?;
|
||||
//^^^^^^ drop(x)
|
||||
Some(())
|
||||
}
|
||||
//^ drop(x)
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_let() {
|
||||
check_with_config(
|
||||
ONLY_DROP_CONFIG,
|
||||
r#"
|
||||
struct X;
|
||||
fn f() {
|
||||
let x = X;
|
||||
if let X = x {
|
||||
let y = X;
|
||||
}
|
||||
//^ drop(y)
|
||||
}
|
||||
//^ drop(x)
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -118,6 +118,7 @@ impl StaticIndex<'_> {
|
||||
adjustment_hints: crate::AdjustmentHints::Never,
|
||||
adjustment_hints_mode: AdjustmentHintsMode::Prefix,
|
||||
adjustment_hints_hide_outside_unsafe: false,
|
||||
implicit_drop_hints: false,
|
||||
hide_named_constructor_hints: false,
|
||||
hide_closure_initialization_hints: false,
|
||||
closure_style: hir::ClosureStyle::ImplFn,
|
||||
|
@ -376,6 +376,16 @@ fn error_block(p: &mut Parser<'_>, message: &str) {
|
||||
m.complete(p, ERROR);
|
||||
}
|
||||
|
||||
// test_err top_level_let
|
||||
// let ref foo: fn() = 1 + 3;
|
||||
fn error_let_stmt(p: &mut Parser<'_>, message: &str) {
|
||||
assert!(p.at(T![let]));
|
||||
let m = p.start();
|
||||
p.error(message);
|
||||
expressions::let_stmt(p, expressions::Semicolon::Optional);
|
||||
m.complete(p, ERROR);
|
||||
}
|
||||
|
||||
/// The `parser` passed this is required to at least consume one token if it returns `true`.
|
||||
/// If the `parser` returns false, parsing will stop.
|
||||
fn delimited(
|
||||
|
@ -59,7 +59,8 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
|
||||
attributes::outer_attrs(p);
|
||||
|
||||
if p.at(T![let]) {
|
||||
let_stmt(p, m, semicolon);
|
||||
let_stmt(p, semicolon);
|
||||
m.complete(p, LET_STMT);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -109,54 +110,53 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
|
||||
m.complete(p, EXPR_STMT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test let_stmt
|
||||
// fn f() { let x: i32 = 92; }
|
||||
fn let_stmt(p: &mut Parser<'_>, m: Marker, with_semi: Semicolon) {
|
||||
p.bump(T![let]);
|
||||
patterns::pattern(p);
|
||||
if p.at(T![:]) {
|
||||
// test let_stmt_ascription
|
||||
// fn f() { let x: i32; }
|
||||
types::ascription(p);
|
||||
}
|
||||
// test let_stmt
|
||||
// fn f() { let x: i32 = 92; }
|
||||
pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
|
||||
p.bump(T![let]);
|
||||
patterns::pattern(p);
|
||||
if p.at(T![:]) {
|
||||
// test let_stmt_ascription
|
||||
// fn f() { let x: i32; }
|
||||
types::ascription(p);
|
||||
}
|
||||
|
||||
let mut expr_after_eq: Option<CompletedMarker> = None;
|
||||
if p.eat(T![=]) {
|
||||
// test let_stmt_init
|
||||
// fn f() { let x = 92; }
|
||||
expr_after_eq = expressions::expr(p);
|
||||
}
|
||||
let mut expr_after_eq: Option<CompletedMarker> = None;
|
||||
if p.eat(T![=]) {
|
||||
// test let_stmt_init
|
||||
// fn f() { let x = 92; }
|
||||
expr_after_eq = expressions::expr(p);
|
||||
}
|
||||
|
||||
if p.at(T![else]) {
|
||||
// test_err let_else_right_curly_brace
|
||||
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
|
||||
if let Some(expr) = expr_after_eq {
|
||||
if BlockLike::is_blocklike(expr.kind()) {
|
||||
p.error(
|
||||
"right curly brace `}` before `else` in a `let...else` statement not allowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// test let_else
|
||||
// fn f() { let Some(x) = opt else { return }; }
|
||||
let m = p.start();
|
||||
p.bump(T![else]);
|
||||
block_expr(p);
|
||||
m.complete(p, LET_ELSE);
|
||||
}
|
||||
|
||||
match with_semi {
|
||||
Semicolon::Forbidden => (),
|
||||
Semicolon::Optional => {
|
||||
p.eat(T![;]);
|
||||
}
|
||||
Semicolon::Required => {
|
||||
p.expect(T![;]);
|
||||
if p.at(T![else]) {
|
||||
// test_err let_else_right_curly_brace
|
||||
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
|
||||
if let Some(expr) = expr_after_eq {
|
||||
if BlockLike::is_blocklike(expr.kind()) {
|
||||
p.error(
|
||||
"right curly brace `}` before `else` in a `let...else` statement not allowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
m.complete(p, LET_STMT);
|
||||
|
||||
// test let_else
|
||||
// fn f() { let Some(x) = opt else { return }; }
|
||||
let m = p.start();
|
||||
p.bump(T![else]);
|
||||
block_expr(p);
|
||||
m.complete(p, LET_ELSE);
|
||||
}
|
||||
|
||||
match with_semi {
|
||||
Semicolon::Forbidden => (),
|
||||
Semicolon::Optional => {
|
||||
p.eat(T![;]);
|
||||
}
|
||||
Semicolon::Required => {
|
||||
p.expect(T![;]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -693,6 +693,17 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
|
||||
// We permit `.. }` on the left-hand side of a destructuring assignment.
|
||||
if !p.at(T!['}']) {
|
||||
expr(p);
|
||||
|
||||
if p.at(T![,]) {
|
||||
// test_err comma_after_functional_update_syntax
|
||||
// fn foo() {
|
||||
// S { ..x, };
|
||||
// S { ..x, a: 0 }
|
||||
// }
|
||||
|
||||
// Do not bump, so we can support additional fields after this comma.
|
||||
p.error("cannot use a comma after the base struct");
|
||||
}
|
||||
}
|
||||
}
|
||||
T!['{'] => {
|
||||
|
@ -79,6 +79,7 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) {
|
||||
e.complete(p, ERROR);
|
||||
}
|
||||
EOF | T!['}'] => p.error("expected an item"),
|
||||
T![let] => error_let_stmt(p, "expected an item"),
|
||||
_ => p.err_and_bump("expected an item"),
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
SOURCE_FILE
|
||||
FN
|
||||
FN_KW "fn"
|
||||
WHITESPACE " "
|
||||
NAME
|
||||
IDENT "foo"
|
||||
PARAM_LIST
|
||||
L_PAREN "("
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
BLOCK_EXPR
|
||||
STMT_LIST
|
||||
L_CURLY "{"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
RECORD_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "S"
|
||||
WHITESPACE " "
|
||||
RECORD_EXPR_FIELD_LIST
|
||||
L_CURLY "{"
|
||||
WHITESPACE " "
|
||||
DOT2 ".."
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "x"
|
||||
COMMA ","
|
||||
WHITESPACE " "
|
||||
R_CURLY "}"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
RECORD_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "S"
|
||||
WHITESPACE " "
|
||||
RECORD_EXPR_FIELD_LIST
|
||||
L_CURLY "{"
|
||||
WHITESPACE " "
|
||||
DOT2 ".."
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "x"
|
||||
COMMA ","
|
||||
WHITESPACE " "
|
||||
RECORD_EXPR_FIELD
|
||||
NAME_REF
|
||||
IDENT "a"
|
||||
COLON ":"
|
||||
WHITESPACE " "
|
||||
LITERAL
|
||||
INT_NUMBER "0"
|
||||
WHITESPACE " "
|
||||
R_CURLY "}"
|
||||
WHITESPACE "\n"
|
||||
R_CURLY "}"
|
||||
WHITESPACE "\n"
|
||||
error 22: cannot use a comma after the base struct
|
||||
error 38: cannot use a comma after the base struct
|
@ -0,0 +1,4 @@
|
||||
fn foo() {
|
||||
S { ..x, };
|
||||
S { ..x, a: 0 }
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
SOURCE_FILE
|
||||
ERROR
|
||||
LET_KW "let"
|
||||
WHITESPACE " "
|
||||
IDENT_PAT
|
||||
REF_KW "ref"
|
||||
WHITESPACE " "
|
||||
NAME
|
||||
IDENT "foo"
|
||||
COLON ":"
|
||||
WHITESPACE " "
|
||||
FN_PTR_TYPE
|
||||
FN_KW "fn"
|
||||
PARAM_LIST
|
||||
L_PAREN "("
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
EQ "="
|
||||
WHITESPACE " "
|
||||
BIN_EXPR
|
||||
LITERAL
|
||||
INT_NUMBER "1"
|
||||
WHITESPACE " "
|
||||
PLUS "+"
|
||||
WHITESPACE " "
|
||||
LITERAL
|
||||
INT_NUMBER "3"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n"
|
||||
error 0: expected an item
|
@ -0,0 +1 @@
|
||||
let ref foo: fn() = 1 + 3;
|
@ -783,6 +783,7 @@ impl flags::AnalysisStats {
|
||||
closure_return_type_hints: ide::ClosureReturnTypeHints::Always,
|
||||
closure_capture_hints: true,
|
||||
binding_mode_hints: true,
|
||||
implicit_drop_hints: true,
|
||||
lifetime_elision_hints: ide::LifetimeElisionHints::Always,
|
||||
param_names_for_lifetime_elision_hints: true,
|
||||
hide_named_constructor_hints: false,
|
||||
|
@ -381,6 +381,8 @@ config_data! {
|
||||
inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false",
|
||||
/// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
|
||||
inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"",
|
||||
/// Whether to show implicit drop hints.
|
||||
inlayHints_implicitDrops_enable: bool = "false",
|
||||
/// Whether to show inlay type hints for elided lifetimes in function signatures.
|
||||
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
|
||||
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
|
||||
@ -1391,6 +1393,7 @@ impl Config {
|
||||
type_hints: self.data.inlayHints_typeHints_enable,
|
||||
parameter_hints: self.data.inlayHints_parameterHints_enable,
|
||||
chaining_hints: self.data.inlayHints_chainingHints_enable,
|
||||
implicit_drop_hints: self.data.inlayHints_implicitDrops_enable,
|
||||
discriminant_hints: match self.data.inlayHints_discriminantHints_enable {
|
||||
DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
|
||||
DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,
|
||||
|
@ -22,6 +22,7 @@ use ide_db::{
|
||||
base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, ProcMacros},
|
||||
FxHashMap,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use load_cargo::{load_proc_macro, ProjectFolders};
|
||||
use proc_macro_api::ProcMacroServer;
|
||||
use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
|
||||
@ -227,16 +228,12 @@ impl GlobalState {
|
||||
let mut i = 0;
|
||||
while i < workspaces.len() {
|
||||
if let Ok(w) = &workspaces[i] {
|
||||
let dupes: Vec<_> = workspaces
|
||||
let dupes: Vec<_> = workspaces[i + 1..]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.skip(i + 1)
|
||||
.filter_map(|(i, it)| {
|
||||
it.as_ref().ok().filter(|ws| ws.eq_ignore_build_data(w)).map(|_| i)
|
||||
})
|
||||
.positions(|it| it.as_ref().is_ok_and(|ws| ws.eq_ignore_build_data(w)))
|
||||
.collect();
|
||||
dupes.into_iter().rev().for_each(|d| {
|
||||
_ = workspaces.remove(d);
|
||||
_ = workspaces.remove(d + i + 1);
|
||||
});
|
||||
}
|
||||
i += 1;
|
||||
|
@ -1054,6 +1054,10 @@ pub mod option {
|
||||
Some(T),
|
||||
}
|
||||
|
||||
// region:copy
|
||||
impl<T: Copy> Copy for Option<T> {}
|
||||
// endregion:copy
|
||||
|
||||
impl<T> Option<T> {
|
||||
pub const fn unwrap(self) -> T {
|
||||
match self {
|
||||
|
@ -564,6 +564,11 @@ Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
|
||||
--
|
||||
Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
|
||||
--
|
||||
[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`)::
|
||||
+
|
||||
--
|
||||
Whether to show implicit drop hints.
|
||||
--
|
||||
[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
|
||||
+
|
||||
--
|
||||
|
@ -1264,6 +1264,11 @@
|
||||
"Show prefix or postfix depending on which uses less parenthesis, preferring postfix."
|
||||
]
|
||||
},
|
||||
"rust-analyzer.inlayHints.implicitDrops.enable": {
|
||||
"markdownDescription": "Whether to show implicit drop hints.",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
|
||||
"markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
|
||||
"default": "never",
|
||||
|
@ -3,7 +3,7 @@ import * as vscode from "vscode";
|
||||
import * as path from "path";
|
||||
import type * as ra from "./lsp_ext";
|
||||
|
||||
import { Cargo, getRustcId, getSysroot } from "./toolchain";
|
||||
import { Cargo, type ExecutableInfo, getRustcId, getSysroot } from "./toolchain";
|
||||
import type { Ctx } from "./ctx";
|
||||
import { prepareEnv } from "./run";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
@ -12,6 +12,7 @@ const debugOutput = vscode.window.createOutputChannel("Debug");
|
||||
type DebugConfigProvider = (
|
||||
config: ra.Runnable,
|
||||
executable: string,
|
||||
cargoWorkspace: string,
|
||||
env: Record<string, string>,
|
||||
sourceFileMap?: Record<string, string>,
|
||||
) => vscode.DebugConfiguration;
|
||||
@ -130,7 +131,7 @@ async function getDebugConfiguration(
|
||||
}
|
||||
|
||||
const env = prepareEnv(runnable, ctx.config.runnablesExtraEnv);
|
||||
const executable = await getDebugExecutable(runnable, env);
|
||||
const { executable, workspace: cargoWorkspace } = await getDebugExecutableInfo(runnable, env);
|
||||
let sourceFileMap = debugOptions.sourceFileMap;
|
||||
if (sourceFileMap === "auto") {
|
||||
// let's try to use the default toolchain
|
||||
@ -142,7 +143,13 @@ async function getDebugConfiguration(
|
||||
}
|
||||
|
||||
const provider = unwrapUndefinable(knownEngines[debugEngine.id]);
|
||||
const debugConfig = provider(runnable, simplifyPath(executable), env, sourceFileMap);
|
||||
const debugConfig = provider(
|
||||
runnable,
|
||||
simplifyPath(executable),
|
||||
cargoWorkspace,
|
||||
env,
|
||||
sourceFileMap,
|
||||
);
|
||||
if (debugConfig.type in debugOptions.engineSettings) {
|
||||
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
|
||||
for (var key in settingsMap) {
|
||||
@ -164,20 +171,21 @@ async function getDebugConfiguration(
|
||||
return debugConfig;
|
||||
}
|
||||
|
||||
async function getDebugExecutable(
|
||||
async function getDebugExecutableInfo(
|
||||
runnable: ra.Runnable,
|
||||
env: Record<string, string>,
|
||||
): Promise<string> {
|
||||
): Promise<ExecutableInfo> {
|
||||
const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput, env);
|
||||
const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
|
||||
const executableInfo = await cargo.executableInfoFromArgs(runnable.args.cargoArgs);
|
||||
|
||||
// if we are here, there were no compilation errors.
|
||||
return executable;
|
||||
return executableInfo;
|
||||
}
|
||||
|
||||
function getLldbDebugConfig(
|
||||
runnable: ra.Runnable,
|
||||
executable: string,
|
||||
cargoWorkspace: string,
|
||||
env: Record<string, string>,
|
||||
sourceFileMap?: Record<string, string>,
|
||||
): vscode.DebugConfiguration {
|
||||
@ -187,7 +195,7 @@ function getLldbDebugConfig(
|
||||
name: runnable.label,
|
||||
program: executable,
|
||||
args: runnable.args.executableArgs,
|
||||
cwd: runnable.args.workspaceRoot,
|
||||
cwd: cargoWorkspace || runnable.args.workspaceRoot,
|
||||
sourceMap: sourceFileMap,
|
||||
sourceLanguages: ["rust"],
|
||||
env,
|
||||
@ -197,6 +205,7 @@ function getLldbDebugConfig(
|
||||
function getCppvsDebugConfig(
|
||||
runnable: ra.Runnable,
|
||||
executable: string,
|
||||
cargoWorkspace: string,
|
||||
env: Record<string, string>,
|
||||
sourceFileMap?: Record<string, string>,
|
||||
): vscode.DebugConfiguration {
|
||||
@ -206,7 +215,7 @@ function getCppvsDebugConfig(
|
||||
name: runnable.label,
|
||||
program: executable,
|
||||
args: runnable.args.executableArgs,
|
||||
cwd: runnable.args.workspaceRoot,
|
||||
cwd: cargoWorkspace || runnable.args.workspaceRoot,
|
||||
sourceFileMap,
|
||||
env,
|
||||
};
|
||||
|
@ -9,11 +9,17 @@ import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
interface CompilationArtifact {
|
||||
fileName: string;
|
||||
workspace: string;
|
||||
name: string;
|
||||
kind: string;
|
||||
isTest: boolean;
|
||||
}
|
||||
|
||||
export interface ExecutableInfo {
|
||||
executable: string;
|
||||
workspace: string;
|
||||
}
|
||||
|
||||
export interface ArtifactSpec {
|
||||
cargoArgs: string[];
|
||||
filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
|
||||
@ -68,6 +74,7 @@ export class Cargo {
|
||||
artifacts.push({
|
||||
fileName: message.executable,
|
||||
name: message.target.name,
|
||||
workspace: message.manifest_path.replace(/\/Cargo\.toml$/, ""),
|
||||
kind: message.target.kind[0],
|
||||
isTest: message.profile.test,
|
||||
});
|
||||
@ -86,7 +93,7 @@ export class Cargo {
|
||||
return spec.filter?.(artifacts) ?? artifacts;
|
||||
}
|
||||
|
||||
async executableFromArgs(args: readonly string[]): Promise<string> {
|
||||
async executableInfoFromArgs(args: readonly string[]): Promise<ExecutableInfo> {
|
||||
const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
|
||||
|
||||
if (artifacts.length === 0) {
|
||||
@ -96,7 +103,10 @@ export class Cargo {
|
||||
}
|
||||
|
||||
const artifact = unwrapUndefinable(artifacts[0]);
|
||||
return artifact.fileName;
|
||||
return {
|
||||
executable: artifact.fileName,
|
||||
workspace: artifact.workspace,
|
||||
};
|
||||
}
|
||||
|
||||
private async runCargo(
|
||||
|
13
tests/codegen/issues/issue-116878.rs
Normal file
13
tests/codegen/issues/issue-116878.rs
Normal file
@ -0,0 +1,13 @@
|
||||
// no-system-llvm
|
||||
// compile-flags: -O
|
||||
// ignore-debug: the debug assertions get in the way
|
||||
#![crate_type = "lib"]
|
||||
|
||||
/// Make sure no bounds checks are emitted after a `get_unchecked`.
|
||||
// CHECK-LABEL: @unchecked_slice_no_bounds_check
|
||||
#[no_mangle]
|
||||
pub unsafe fn unchecked_slice_no_bounds_check(s: &[u8]) -> u8 {
|
||||
let a = *s.get_unchecked(1);
|
||||
// CHECK-NOT: panic_bounds_check
|
||||
a + s[0]
|
||||
}
|
18
tests/rustdoc/enum-variant-fields-heading.rs
Normal file
18
tests/rustdoc/enum-variant-fields-heading.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// This is a regression test for <https://github.com/rust-lang/rust/issues/118195>.
|
||||
// It ensures that the "Fields" heading is not generated if no field is displayed.
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
// @has 'foo/enum.Foo.html'
|
||||
// @has - '//*[@id="variant.A"]' 'A'
|
||||
// @count - '//*[@id="variant.A.fields"]' 0
|
||||
// @has - '//*[@id="variant.B"]' 'B'
|
||||
// @count - '//*[@id="variant.B.fields"]' 0
|
||||
// @snapshot variants - '//*[@id="main-content"]/*[@class="variants"]'
|
||||
|
||||
pub enum Foo {
|
||||
/// A variant with no fields
|
||||
A {},
|
||||
/// A variant with hidden fields
|
||||
B { #[doc(hidden)] a: u8 },
|
||||
}
|
3
tests/rustdoc/enum-variant-fields-heading.variants.html
Normal file
3
tests/rustdoc/enum-variant-fields-heading.variants.html
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="variants"><section id="variant.A" class="variant"><a href="#variant.A" class="anchor">§</a><h3 class="code-header">A</h3></section><div class="docblock"><p>A variant with no fields</p>
|
||||
</div><section id="variant.B" class="variant"><a href="#variant.B" class="anchor">§</a><h3 class="code-header">B</h3></section><div class="docblock"><p>A variant with hidden fields</p>
|
||||
</div></div>
|
25
tests/ui/coherence/occurs-check/associated-type.next.stderr
Normal file
25
tests/ui/coherence/occurs-check/associated-type.next.stderr
Normal file
@ -0,0 +1,25 @@
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())`
|
||||
--> $DIR/associated-type.rs:31:1
|
||||
|
|
||||
LL | impl<T> Overlap<T> for T {
|
||||
| ------------------------ first implementation here
|
||||
...
|
||||
LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
|
||||
LL | |
|
||||
LL | | where
|
||||
LL | | for<'a> *const T: ToUnit<'a>,
|
||||
| |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), ())`
|
||||
|
|
||||
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
25
tests/ui/coherence/occurs-check/associated-type.old.stderr
Normal file
25
tests/ui/coherence/occurs-check/associated-type.old.stderr
Normal file
@ -0,0 +1,25 @@
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
|
||||
error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)`
|
||||
--> $DIR/associated-type.rs:31:1
|
||||
|
|
||||
LL | impl<T> Overlap<T> for T {
|
||||
| ------------------------ first implementation here
|
||||
...
|
||||
LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
|
||||
LL | |
|
||||
LL | | where
|
||||
LL | | for<'a> *const T: ToUnit<'a>,
|
||||
| |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), _)`
|
||||
|
|
||||
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
45
tests/ui/coherence/occurs-check/associated-type.rs
Normal file
45
tests/ui/coherence/occurs-check/associated-type.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// revisions: old next
|
||||
//[next] compile-flags: -Ztrait-solver=next
|
||||
|
||||
// A regression test for #105787
|
||||
|
||||
// Using the higher ranked projection hack to prevent us from replacing the projection
|
||||
// with an inference variable.
|
||||
trait ToUnit<'a> {
|
||||
type Unit;
|
||||
}
|
||||
|
||||
struct LocalTy;
|
||||
impl<'a> ToUnit<'a> for *const LocalTy {
|
||||
type Unit = ();
|
||||
}
|
||||
|
||||
impl<'a, T: Copy + ?Sized> ToUnit<'a> for *const T {
|
||||
type Unit = ();
|
||||
}
|
||||
|
||||
trait Overlap<T> {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
type Assoc<'a, T> = <*const T as ToUnit<'a>>::Unit;
|
||||
|
||||
impl<T> Overlap<T> for T {
|
||||
type Assoc = usize;
|
||||
}
|
||||
|
||||
impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
|
||||
//~^ ERROR conflicting implementations of trait
|
||||
where
|
||||
for<'a> *const T: ToUnit<'a>,
|
||||
{
|
||||
type Assoc = Box<usize>;
|
||||
}
|
||||
|
||||
fn foo<T: Overlap<U>, U>(x: T::Assoc) -> T::Assoc {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo::<for<'a> fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize);
|
||||
}
|
12
tests/ui/coherence/occurs-check/opaques.next.stderr
Normal file
12
tests/ui/coherence/occurs-check/opaques.next.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
error[E0119]: conflicting implementations of trait `Trait<Alias<_>>` for type `Alias<_>`
|
||||
--> $DIR/opaques.rs:29:1
|
||||
|
|
||||
LL | impl<T> Trait<T> for T {
|
||||
| ---------------------- first implementation here
|
||||
...
|
||||
LL | impl<T> Trait<T> for defining_scope::Alias<T> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Alias<_>`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
37
tests/ui/coherence/occurs-check/opaques.rs
Normal file
37
tests/ui/coherence/occurs-check/opaques.rs
Normal file
@ -0,0 +1,37 @@
|
||||
//revisions: old next
|
||||
//[next] compile-flags: -Ztrait-solver=next
|
||||
|
||||
// A regression test for #105787
|
||||
|
||||
//[old] known-bug: #105787
|
||||
//[old] check-pass
|
||||
#![feature(type_alias_impl_trait)]
|
||||
mod defining_scope {
|
||||
use super::*;
|
||||
pub type Alias<T> = impl Sized;
|
||||
|
||||
pub fn cast<T>(x: Container<Alias<T>, T>) -> Container<T, T> {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
struct Container<T: Trait<U>, U> {
|
||||
x: <T as Trait<U>>::Assoc,
|
||||
}
|
||||
|
||||
trait Trait<T> {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl<T> Trait<T> for T {
|
||||
type Assoc = Box<u32>;
|
||||
}
|
||||
impl<T> Trait<T> for defining_scope::Alias<T> {
|
||||
//[next]~^ ERROR conflicting implementations of trait
|
||||
type Assoc = usize;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x: Box<u32> = defining_scope::cast::<()>(Container { x: 0 }).x;
|
||||
println!("{}", *x);
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
#![feature(diagnostic_namespace)]
|
||||
|
||||
#[diagnostic::on_unimplemented(
|
||||
on(_Self = "&str"),
|
||||
//~^WARN malformed `on_unimplemented` attribute
|
||||
//~|WARN malformed `on_unimplemented` attribute
|
||||
message = "trait has `{Self}` and `{T}` as params",
|
||||
label = "trait has `{Self}` and `{T}` as params",
|
||||
note = "trait has `{Self}` and `{T}` as params",
|
||||
parent_label = "in this scope",
|
||||
//~^WARN malformed `on_unimplemented` attribute
|
||||
//~|WARN malformed `on_unimplemented` attribute
|
||||
append_const_msg
|
||||
//~^WARN malformed `on_unimplemented` attribute
|
||||
//~|WARN malformed `on_unimplemented` attribute
|
||||
)]
|
||||
trait Foo<T> {}
|
||||
|
||||
#[diagnostic::on_unimplemented = "Message"]
|
||||
//~^WARN malformed `on_unimplemented` attribute
|
||||
//~|WARN malformed `on_unimplemented` attribute
|
||||
trait Bar {}
|
||||
|
||||
#[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
|
||||
//~^WARN #[diagnostic::on_unimplemented]` can only be applied to trait definitions
|
||||
impl Bar for i32 {}
|
||||
|
||||
// cannot use special rustc_on_unimplement symbols
|
||||
// in the format string
|
||||
#[diagnostic::on_unimplemented(
|
||||
message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
//~^WARN there is no parameter `from_desugaring` on trait `Baz`
|
||||
//~|WARN there is no parameter `from_desugaring` on trait `Baz`
|
||||
//~|WARN there is no parameter `direct` on trait `Baz`
|
||||
//~|WARN there is no parameter `direct` on trait `Baz`
|
||||
//~|WARN there is no parameter `cause` on trait `Baz`
|
||||
//~|WARN there is no parameter `cause` on trait `Baz`
|
||||
//~|WARN there is no parameter `integral` on trait `Baz`
|
||||
//~|WARN there is no parameter `integral` on trait `Baz`
|
||||
//~|WARN there is no parameter `integer` on trait `Baz`
|
||||
//~|WARN there is no parameter `integer` on trait `Baz`
|
||||
label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
//~^WARN there is no parameter `float` on trait `Baz`
|
||||
//~|WARN there is no parameter `float` on trait `Baz`
|
||||
//~|WARN there is no parameter `_Self` on trait `Baz`
|
||||
//~|WARN there is no parameter `_Self` on trait `Baz`
|
||||
//~|WARN there is no parameter `crate_local` on trait `Baz`
|
||||
//~|WARN there is no parameter `crate_local` on trait `Baz`
|
||||
//~|WARN there is no parameter `Trait` on trait `Baz`
|
||||
//~|WARN there is no parameter `Trait` on trait `Baz`
|
||||
//~|WARN there is no parameter `ItemContext` on trait `Baz`
|
||||
//~|WARN there is no parameter `ItemContext` on trait `Baz`
|
||||
)]
|
||||
trait Baz {}
|
||||
|
||||
fn takes_foo(_: impl Foo<i32>) {}
|
||||
fn takes_bar(_: impl Bar) {}
|
||||
fn takes_baz(_: impl Baz) {}
|
||||
|
||||
fn main() {
|
||||
takes_foo(());
|
||||
//~^ERROR trait has `()` and `i32` as params
|
||||
takes_bar(());
|
||||
//~^ERROR the trait bound `(): Bar` is not satisfied
|
||||
takes_baz(());
|
||||
//~^ERROR {from_desugaring}{direct}{cause}{integral}{integer}
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:24:1
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:4:5
|
||||
|
|
||||
LL | on(_Self = "&str"),
|
||||
| ^^^^^^^^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:10:5
|
||||
|
|
||||
LL | parent_label = "in this scope",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5
|
||||
|
|
||||
LL | append_const_msg
|
||||
| ^^^^^^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:32
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented = "Message"]
|
||||
| ^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
|
||||
warning: there is no parameter `from_desugaring` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `direct` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `cause` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `integral` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `integer` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `float` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `_Self` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `crate_local` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `Trait` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: there is no parameter `ItemContext` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:4:5
|
||||
|
|
||||
LL | on(_Self = "&str"),
|
||||
| ^^^^^^^^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:10:5
|
||||
|
|
||||
LL | parent_label = "in this scope",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5
|
||||
|
|
||||
LL | append_const_msg
|
||||
| ^^^^^^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0277]: trait has `()` and `i32` as params
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:61:15
|
||||
|
|
||||
LL | takes_foo(());
|
||||
| --------- ^^ trait has `()` and `i32` as params
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Foo<i32>` is not implemented for `()`
|
||||
= note: trait has `()` and `i32` as params
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:17:1
|
||||
|
|
||||
LL | trait Foo<T> {}
|
||||
| ^^^^^^^^^^^^
|
||||
note: required by a bound in `takes_foo`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:56:22
|
||||
|
|
||||
LL | fn takes_foo(_: impl Foo<i32>) {}
|
||||
| ^^^^^^^^ required by this bound in `takes_foo`
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:32
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented = "Message"]
|
||||
| ^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0277]: the trait bound `(): Bar` is not satisfied
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:63:15
|
||||
|
|
||||
LL | takes_bar(());
|
||||
| --------- ^^ the trait `Bar` is not implemented for `()`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Bar` is implemented for `i32`
|
||||
note: required by a bound in `takes_bar`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:57:22
|
||||
|
|
||||
LL | fn takes_bar(_: impl Bar) {}
|
||||
| ^^^ required by this bound in `takes_bar`
|
||||
|
||||
warning: there is no parameter `from_desugaring` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `direct` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `cause` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `integral` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `integer` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
|
||||
|
|
||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `float` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `_Self` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `crate_local` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `Trait` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: there is no parameter `ItemContext` on trait `Baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
|
||||
|
|
||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15
|
||||
|
|
||||
LL | takes_baz(());
|
||||
| --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Baz` is not implemented for `()`
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:1
|
||||
|
|
||||
LL | trait Baz {}
|
||||
| ^^^^^^^^^
|
||||
note: required by a bound in `takes_baz`
|
||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:22
|
||||
|
|
||||
LL | fn takes_baz(_: impl Baz) {}
|
||||
| ^^^ required by this bound in `takes_baz`
|
||||
|
||||
error: aborting due to 3 previous errors; 29 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user