mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-16 22:16:53 +00:00
Auto merge of #112164 - Dylan-DPC:rollup-93zj7jw, r=Dylan-DPC
Rollup of 7 pull requests Successful merges: - #108459 (rustdoc: Fix LinkReplacer link matching) - #111318 (Add a distinct `OperandValue::ZeroSized` variant for ZSTs) - #111892 (rustdoc: add interaction delays for tooltip popovers) - #111980 (Preserve substs in opaques recorded in typeck results) - #112024 (Don't suggest break through nested items) - #112128 (Don't compute inlining status of mono items in advance.) - #112141 (remove reference to Into in ? operator core/std docs, fix #111655) Failed merges: - #112071 (Group rfcs tests) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
fabf929863
@ -279,8 +279,18 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||
// HACK This bubble is required for this tests to pass:
|
||||
// nested-return-type2-tait2.rs
|
||||
// nested-return-type2-tait3.rs
|
||||
let infcx =
|
||||
self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).build();
|
||||
// FIXME(-Ztrait-solver=next): We probably should use `DefiningAnchor::Error`
|
||||
// and prepopulate this `InferCtxt` with known opaque values, rather than
|
||||
// using the `Bind` anchor here. For now it's fine.
|
||||
let infcx = self
|
||||
.tcx
|
||||
.infer_ctxt()
|
||||
.with_opaque_type_inference(if self.tcx.trait_solver_next() {
|
||||
DefiningAnchor::Bind(def_id)
|
||||
} else {
|
||||
DefiningAnchor::Bubble
|
||||
})
|
||||
.build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
// Require the hidden type to be well-formed with only the generics of the opaque type.
|
||||
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
|
||||
|
@ -188,9 +188,6 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
||||
|
||||
// FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
|
||||
// predefined opaques in the typeck root.
|
||||
// FIXME(-Ztrait-solver=next): This is also totally wrong for TAITs, since
|
||||
// the HIR typeck map defining usages back to their definition params,
|
||||
// they won't actually match up with the usages in this body...
|
||||
if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
|
||||
checker.register_predefined_opaques_in_new_solver();
|
||||
}
|
||||
@ -1042,10 +1039,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
.typeck(self.body.source.def_id().expect_local())
|
||||
.concrete_opaque_types
|
||||
.iter()
|
||||
.map(|(&def_id, &hidden_ty)| {
|
||||
let substs = ty::InternalSubsts::identity_for_item(self.infcx.tcx, def_id);
|
||||
(ty::OpaqueTypeKey { def_id, substs }, hidden_ty)
|
||||
})
|
||||
.map(|(k, v)| (*k, *v))
|
||||
.collect();
|
||||
|
||||
let renumbered_opaques = self.infcx.tcx.fold_regions(opaques, |_, _| {
|
||||
|
@ -758,7 +758,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
|
||||
|
||||
if place.layout.is_zst() {
|
||||
return OperandRef::new_zst(self, place.layout);
|
||||
return OperandRef::zero_sized(place.layout);
|
||||
}
|
||||
|
||||
fn scalar_load_metadata<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, load: RValue<'gcc>, scalar: &abi::Scalar) {
|
||||
|
@ -159,8 +159,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
||||
fn is_gcc_immediate(&self) -> bool {
|
||||
match self.abi {
|
||||
Abi::Scalar(_) | Abi::Vector { .. } => true,
|
||||
Abi::ScalarPair(..) => false,
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
|
||||
Abi::ScalarPair(..) | Abi::Uninhabited | Abi::Aggregate { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,7 +486,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
|
||||
|
||||
if place.layout.is_zst() {
|
||||
return OperandRef::new_zst(self, place.layout);
|
||||
return OperandRef::zero_sized(place.layout);
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(bx))]
|
||||
|
@ -198,8 +198,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
||||
fn is_llvm_immediate(&self) -> bool {
|
||||
match self.abi {
|
||||
Abi::Scalar(_) | Abi::Vector { .. } => true,
|
||||
Abi::ScalarPair(..) => false,
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
|
||||
Abi::ScalarPair(..) | Abi::Uninhabited | Abi::Aggregate { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +295,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
let (base, info) = match bx.load_operand(src).val {
|
||||
OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
|
||||
OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None),
|
||||
OperandValue::Ref(..) => bug!(),
|
||||
OperandValue::Ref(..) | OperandValue::ZeroSized => bug!(),
|
||||
};
|
||||
OperandValue::Pair(base, info).store(bx, dst);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::operand::OperandRef;
|
||||
use super::operand::OperandValue::{Immediate, Pair, Ref};
|
||||
use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized};
|
||||
use super::place::PlaceRef;
|
||||
use super::{CachedLlbb, FunctionCx, LocalRef};
|
||||
|
||||
@ -427,6 +427,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
assert_eq!(align, op.layout.align.abi, "return place is unaligned!");
|
||||
llval
|
||||
}
|
||||
ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"),
|
||||
};
|
||||
let ty = bx.cast_backend_type(cast_ty);
|
||||
let addr = bx.pointercast(llslot, bx.type_ptr_to(ty));
|
||||
@ -1386,6 +1387,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
(llval, align, true)
|
||||
}
|
||||
}
|
||||
ZeroSized => match arg.mode {
|
||||
PassMode::Indirect { .. } => {
|
||||
// Though `extern "Rust"` doesn't pass ZSTs, some ABIs pass
|
||||
// a pointer for `repr(C)` structs even when empty, so get
|
||||
// one from an `alloca` (which can be left uninitialized).
|
||||
let scratch = PlaceRef::alloca(bx, arg.layout);
|
||||
(scratch.llval, scratch.align, true)
|
||||
}
|
||||
_ => bug!("ZST {op:?} wasn't ignored, but was passed with abi {arg:?}"),
|
||||
},
|
||||
};
|
||||
|
||||
if by_ref && !arg.is_indirect() {
|
||||
|
@ -352,6 +352,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
bx.set_var_name(a, &(name.clone() + ".0"));
|
||||
bx.set_var_name(b, &(name.clone() + ".1"));
|
||||
}
|
||||
OperandValue::ZeroSized => {
|
||||
// These never have a value to talk about
|
||||
}
|
||||
},
|
||||
LocalRef::PendingOperand => {}
|
||||
}
|
||||
|
@ -129,16 +129,13 @@ enum LocalRef<'tcx, V> {
|
||||
PendingOperand,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
|
||||
fn new_operand<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||
bx: &mut Bx,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> LocalRef<'tcx, V> {
|
||||
impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> {
|
||||
fn new_operand(layout: TyAndLayout<'tcx>) -> LocalRef<'tcx, V> {
|
||||
if layout.is_zst() {
|
||||
// Zero-size temporaries aren't always initialized, which
|
||||
// doesn't matter because they don't contain data, but
|
||||
// we need something in the operand.
|
||||
LocalRef::Operand(OperandRef::new_zst(bx, layout))
|
||||
LocalRef::Operand(OperandRef::zero_sized(layout))
|
||||
} else {
|
||||
LocalRef::PendingOperand
|
||||
}
|
||||
@ -249,7 +246,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
}
|
||||
} else {
|
||||
debug!("alloc: {:?} -> operand", local);
|
||||
LocalRef::new_operand(&mut start_bx, layout)
|
||||
LocalRef::new_operand(layout)
|
||||
}
|
||||
};
|
||||
|
||||
@ -355,7 +352,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
let local = |op| LocalRef::Operand(op);
|
||||
match arg.mode {
|
||||
PassMode::Ignore => {
|
||||
return local(OperandRef::new_zst(bx, arg.layout));
|
||||
return local(OperandRef::zero_sized(arg.layout));
|
||||
}
|
||||
PassMode::Direct(_) => {
|
||||
let llarg = bx.get_param(llarg_idx);
|
||||
|
@ -45,6 +45,14 @@ pub enum OperandValue<V> {
|
||||
/// as returned by [`LayoutTypeMethods::scalar_pair_element_backend_type`]
|
||||
/// with `immediate: true`.
|
||||
Pair(V, V),
|
||||
/// A value taking no bytes, and which therefore needs no LLVM value at all.
|
||||
///
|
||||
/// If you ever need a `V` to pass to something, get a fresh poison value
|
||||
/// from [`ConstMethods::const_poison`].
|
||||
///
|
||||
/// An `OperandValue` *must* be this variant for any type for which
|
||||
/// `is_zst` on its `Layout` returns `true`.
|
||||
ZeroSized,
|
||||
}
|
||||
|
||||
/// An `OperandRef` is an "SSA" reference to a Rust value, along with
|
||||
@ -71,15 +79,9 @@ impl<V: CodegenObject> fmt::Debug for OperandRef<'_, V> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
pub fn new_zst<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||
bx: &mut Bx,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> OperandRef<'tcx, V> {
|
||||
pub fn zero_sized(layout: TyAndLayout<'tcx>) -> OperandRef<'tcx, V> {
|
||||
assert!(layout.is_zst());
|
||||
OperandRef {
|
||||
val: OperandValue::Immediate(bx.const_poison(bx.immediate_backend_type(layout))),
|
||||
layout,
|
||||
}
|
||||
OperandRef { val: OperandValue::ZeroSized, layout }
|
||||
}
|
||||
|
||||
pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||
@ -97,7 +99,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout));
|
||||
OperandValue::Immediate(llval)
|
||||
}
|
||||
ConstValue::ZeroSized => return OperandRef::new_zst(bx, layout),
|
||||
ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
|
||||
ConstValue::Slice { data, start, end } => {
|
||||
let Abi::ScalarPair(a_scalar, _) = layout.abi else {
|
||||
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
|
||||
@ -178,7 +180,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
);
|
||||
OperandRef { val: OperandValue::Pair(a_val, b_val), layout }
|
||||
}
|
||||
_ if layout.is_zst() => OperandRef::new_zst(bx, layout),
|
||||
_ if layout.is_zst() => OperandRef::zero_sized(layout),
|
||||
_ => {
|
||||
// Neither a scalar nor scalar pair. Load from a place
|
||||
let init = bx.const_data_from_alloc(alloc);
|
||||
@ -216,6 +218,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
OperandValue::Immediate(llptr) => (llptr, None),
|
||||
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
|
||||
OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self),
|
||||
OperandValue::ZeroSized => bug!("Deref of ZST operand {:?}", self),
|
||||
};
|
||||
let layout = cx.layout_of(projected_ty);
|
||||
PlaceRef { llval: llptr, llextra, layout, align: layout.align.abi }
|
||||
@ -273,9 +276,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
|
||||
let mut val = match (self.val, self.layout.abi) {
|
||||
// If the field is ZST, it has no data.
|
||||
_ if field.is_zst() => {
|
||||
return OperandRef::new_zst(bx, field);
|
||||
}
|
||||
_ if field.is_zst() => OperandValue::ZeroSized,
|
||||
|
||||
// Newtype of a scalar, scalar pair or vector.
|
||||
(OperandValue::Immediate(_) | OperandValue::Pair(..), _)
|
||||
@ -306,6 +307,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
};
|
||||
|
||||
match (&mut val, field.abi) {
|
||||
(OperandValue::ZeroSized, _) => {}
|
||||
(
|
||||
OperandValue::Immediate(llval),
|
||||
Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. },
|
||||
@ -359,8 +361,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
|
||||
/// Returns an `OperandValue` that's generally UB to use in any way.
|
||||
///
|
||||
/// Depending on the `layout`, returns an `Immediate` or `Pair` containing
|
||||
/// poison value(s), or a `Ref` containing a poison pointer.
|
||||
/// Depending on the `layout`, returns `ZeroSized` for ZSTs, an `Immediate` or
|
||||
/// `Pair` containing poison value(s), or a `Ref` containing a poison pointer.
|
||||
///
|
||||
/// Supports sized types only.
|
||||
pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||
@ -368,7 +370,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> OperandValue<V> {
|
||||
assert!(layout.is_sized());
|
||||
if bx.cx().is_backend_immediate(layout) {
|
||||
if layout.is_zst() {
|
||||
OperandValue::ZeroSized
|
||||
} else if bx.cx().is_backend_immediate(layout) {
|
||||
let ibty = bx.cx().immediate_backend_type(layout);
|
||||
OperandValue::Immediate(bx.const_poison(ibty))
|
||||
} else if bx.cx().is_backend_scalar_pair(layout) {
|
||||
@ -421,12 +425,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
|
||||
flags: MemFlags,
|
||||
) {
|
||||
debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest);
|
||||
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized
|
||||
// value is through `undef`, and store itself is useless.
|
||||
if dest.layout.is_zst() {
|
||||
return;
|
||||
}
|
||||
match self {
|
||||
OperandValue::ZeroSized => {
|
||||
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized
|
||||
// value is through `undef`/`poison`, and the store itself is useless.
|
||||
}
|
||||
OperandValue::Ref(r, None, source_align) => {
|
||||
if flags.contains(MemFlags::NONTEMPORAL) {
|
||||
// HACK(nox): This is inefficient but there is no nontemporal memcpy.
|
||||
@ -527,7 +530,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
// checks in `codegen_consume` and `extract_field`.
|
||||
let elem = o.layout.field(bx.cx(), 0);
|
||||
if elem.is_zst() {
|
||||
o = OperandRef::new_zst(bx, elem);
|
||||
o = OperandRef::zero_sized(elem);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
@ -561,7 +564,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
|
||||
// ZSTs don't require any actual memory access.
|
||||
if layout.is_zst() {
|
||||
return OperandRef::new_zst(bx, layout);
|
||||
return OperandRef::zero_sized(layout);
|
||||
}
|
||||
|
||||
if let Some(o) = self.maybe_codegen_consume_direct(bx, place_ref) {
|
||||
|
@ -70,6 +70,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
OperandValue::Ref(_, Some(_), _) => {
|
||||
bug!("unsized coercion on an unsized rvalue");
|
||||
}
|
||||
OperandValue::ZeroSized => {
|
||||
bug!("unsized coercion on a ZST rvalue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,11 +168,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
}
|
||||
|
||||
match src.val {
|
||||
OperandValue::Ref(..) => {
|
||||
OperandValue::Ref(..) | OperandValue::ZeroSized => {
|
||||
span_bug!(
|
||||
self.mir.span,
|
||||
"Operand path should have handled transmute \
|
||||
from `Ref` {src:?} to place {dst:?}"
|
||||
from {src:?} to place {dst:?}"
|
||||
);
|
||||
}
|
||||
OperandValue::Immediate(..) | OperandValue::Pair(..) => {
|
||||
@ -220,17 +223,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
let fake_place = PlaceRef::new_sized_aligned(cast_ptr, cast, align);
|
||||
Some(bx.load_operand(fake_place).val)
|
||||
}
|
||||
OperandValue::ZeroSized => {
|
||||
let OperandValueKind::ZeroSized = operand_kind else {
|
||||
bug!("Found {operand_kind:?} for operand {operand:?}");
|
||||
};
|
||||
if let OperandValueKind::ZeroSized = cast_kind {
|
||||
Some(OperandValue::ZeroSized)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
OperandValue::Immediate(imm) => {
|
||||
let OperandValueKind::Immediate(in_scalar) = operand_kind else {
|
||||
bug!("Found {operand_kind:?} for operand {operand:?}");
|
||||
};
|
||||
if let OperandValueKind::Immediate(out_scalar) = cast_kind {
|
||||
match (in_scalar, out_scalar) {
|
||||
(ScalarOrZst::Zst, ScalarOrZst::Zst) => {
|
||||
Some(OperandRef::new_zst(bx, cast).val)
|
||||
}
|
||||
(ScalarOrZst::Scalar(in_scalar), ScalarOrZst::Scalar(out_scalar))
|
||||
if in_scalar.size(self.cx) == out_scalar.size(self.cx) =>
|
||||
if let OperandValueKind::Immediate(out_scalar) = cast_kind
|
||||
&& in_scalar.size(self.cx) == out_scalar.size(self.cx)
|
||||
{
|
||||
let operand_bty = bx.backend_type(operand.layout);
|
||||
let cast_bty = bx.backend_type(cast);
|
||||
@ -242,9 +250,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
out_scalar,
|
||||
cast_bty,
|
||||
)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -457,6 +462,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
OperandValue::Ref(..) => {
|
||||
bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand);
|
||||
}
|
||||
OperandValue::ZeroSized => {
|
||||
bug!("zero-sized operand {:?} in `codegen_rvalue_operand`", operand);
|
||||
}
|
||||
};
|
||||
let (lldata, llextra) =
|
||||
base::unsize_ptr(bx, lldata, operand.layout.ty, cast.ty, llextra);
|
||||
@ -490,6 +498,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
OperandValue::Ref(_, _, _) => todo!(),
|
||||
OperandValue::Immediate(v) => (v, None),
|
||||
OperandValue::Pair(v, l) => (v, Some(l)),
|
||||
OperandValue::ZeroSized => bug!("ZST -- which is not PointerLike -- in DynStar"),
|
||||
};
|
||||
let (lldata, llextra) =
|
||||
base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra);
|
||||
@ -718,7 +727,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
// According to `rvalue_creates_operand`, only ZST
|
||||
// aggregate rvalues are allowed to be operands.
|
||||
let ty = rvalue.ty(self.mir, self.cx.tcx());
|
||||
OperandRef::new_zst(bx, self.cx.layout_of(self.monomorphize(ty)))
|
||||
OperandRef::zero_sized(self.cx.layout_of(self.monomorphize(ty)))
|
||||
}
|
||||
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
|
||||
let operand = self.codegen_operand(bx, operand);
|
||||
@ -936,6 +945,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
// Can always load from a pointer as needed
|
||||
(OperandValueKind::Ref, _) => true,
|
||||
|
||||
// ZST-to-ZST is the easiest thing ever
|
||||
(OperandValueKind::ZeroSized, OperandValueKind::ZeroSized) => true,
|
||||
|
||||
// But if only one of them is a ZST the sizes can't match
|
||||
(OperandValueKind::ZeroSized, _) | (_, OperandValueKind::ZeroSized) => false,
|
||||
|
||||
// Need to generate an `alloc` to get a pointer from an immediate
|
||||
(OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false,
|
||||
|
||||
@ -979,12 +994,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
|
||||
/// Gets which variant of [`OperandValue`] is expected for a particular type.
|
||||
fn value_kind(&self, layout: TyAndLayout<'tcx>) -> OperandValueKind {
|
||||
if self.cx.is_backend_immediate(layout) {
|
||||
if layout.is_zst() {
|
||||
OperandValueKind::ZeroSized
|
||||
} else if self.cx.is_backend_immediate(layout) {
|
||||
debug_assert!(!self.cx.is_backend_scalar_pair(layout));
|
||||
OperandValueKind::Immediate(match layout.abi {
|
||||
abi::Abi::Scalar(s) => ScalarOrZst::Scalar(s),
|
||||
abi::Abi::Vector { element, .. } => ScalarOrZst::Scalar(element),
|
||||
_ if layout.is_zst() => ScalarOrZst::Zst,
|
||||
abi::Abi::Scalar(s) => s,
|
||||
abi::Abi::Vector { element, .. } => element,
|
||||
x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"),
|
||||
})
|
||||
} else if self.cx.is_backend_scalar_pair(layout) {
|
||||
@ -1007,21 +1023,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum OperandValueKind {
|
||||
Ref,
|
||||
Immediate(ScalarOrZst),
|
||||
Immediate(abi::Scalar),
|
||||
Pair(abi::Scalar, abi::Scalar),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum ScalarOrZst {
|
||||
Zst,
|
||||
Scalar(abi::Scalar),
|
||||
}
|
||||
|
||||
impl ScalarOrZst {
|
||||
pub fn size(self, cx: &impl abi::HasDataLayout) -> abi::Size {
|
||||
match self {
|
||||
ScalarOrZst::Zst => abi::Size::ZERO,
|
||||
ScalarOrZst::Scalar(s) => s.size(cx),
|
||||
}
|
||||
}
|
||||
ZeroSized,
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use rustc_errors::StashKey;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem};
|
||||
@ -59,7 +60,20 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
|
||||
}
|
||||
}
|
||||
|
||||
let Some(hidden) = locator.found else {
|
||||
if let Some(hidden) = locator.found {
|
||||
// Only check against typeck if we didn't already error
|
||||
if !hidden.ty.references_error() {
|
||||
for concrete_type in locator.typeck_types {
|
||||
if concrete_type.ty != tcx.erase_regions(hidden.ty)
|
||||
&& !(concrete_type, hidden).references_error()
|
||||
{
|
||||
hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hidden.ty
|
||||
} else {
|
||||
let reported = tcx.sess.emit_err(UnconstrainedOpaqueType {
|
||||
span: tcx.def_span(def_id),
|
||||
name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
|
||||
@ -70,21 +84,8 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
|
||||
_ => "item",
|
||||
},
|
||||
});
|
||||
return tcx.ty_error(reported);
|
||||
};
|
||||
|
||||
// Only check against typeck if we didn't already error
|
||||
if !hidden.ty.references_error() {
|
||||
for concrete_type in locator.typeck_types {
|
||||
if concrete_type.ty != tcx.erase_regions(hidden.ty)
|
||||
&& !(concrete_type, hidden).references_error()
|
||||
{
|
||||
hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
|
||||
}
|
||||
}
|
||||
tcx.ty_error(reported)
|
||||
}
|
||||
|
||||
hidden.ty
|
||||
}
|
||||
|
||||
struct TaitConstraintLocator<'tcx> {
|
||||
@ -130,13 +131,28 @@ impl TaitConstraintLocator<'_> {
|
||||
self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error(guar) });
|
||||
return;
|
||||
}
|
||||
let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else {
|
||||
|
||||
let mut constrained = false;
|
||||
for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
|
||||
if opaque_type_key.def_id != self.def_id {
|
||||
continue;
|
||||
}
|
||||
constrained = true;
|
||||
let concrete_type =
|
||||
self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
|
||||
opaque_type_key,
|
||||
self.tcx,
|
||||
true,
|
||||
));
|
||||
if self.typeck_types.iter().all(|prev| prev.ty != concrete_type.ty) {
|
||||
self.typeck_types.push(concrete_type);
|
||||
}
|
||||
}
|
||||
|
||||
if !constrained {
|
||||
debug!("no constraints in typeck results");
|
||||
return;
|
||||
};
|
||||
if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) {
|
||||
self.typeck_types.push(typeck_hidden_ty);
|
||||
}
|
||||
|
||||
// Use borrowck to get the type with unerased regions.
|
||||
let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types;
|
||||
@ -190,17 +206,45 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn find_opaque_ty_constraints_for_rpit(
|
||||
tcx: TyCtxt<'_>,
|
||||
pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
owner_def_id: LocalDefId,
|
||||
) -> Ty<'_> {
|
||||
let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
|
||||
let tables = tcx.typeck(owner_def_id);
|
||||
|
||||
if let Some(concrete) = concrete {
|
||||
// Check that all of the opaques we inferred during HIR are compatible.
|
||||
// FIXME: We explicitly don't check that the types inferred during HIR
|
||||
// typeck are compatible with the one that we infer during borrowck,
|
||||
// because that one actually sometimes has consts evaluated eagerly so
|
||||
// using strict type equality will fail.
|
||||
let mut hir_opaque_ty: Option<ty::OpaqueHiddenType<'tcx>> = None;
|
||||
if tables.tainted_by_errors.is_none() {
|
||||
for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
|
||||
if opaque_type_key.def_id != def_id {
|
||||
continue;
|
||||
}
|
||||
let concrete_type = tcx.erase_regions(
|
||||
hidden_type.remap_generic_params_to_declaration_params(opaque_type_key, tcx, true),
|
||||
);
|
||||
if let Some(prev) = &mut hir_opaque_ty {
|
||||
if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() {
|
||||
prev.report_mismatch(&concrete_type, def_id, tcx).stash(
|
||||
tcx.def_span(opaque_type_key.def_id),
|
||||
StashKey::OpaqueHiddenTypeMismatch,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
hir_opaque_ty = Some(concrete_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
|
||||
if let Some(mir_opaque_ty) = mir_opaque_ty {
|
||||
let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id);
|
||||
debug!(?scope);
|
||||
let mut locator = RpitConstraintChecker { def_id, tcx, found: concrete };
|
||||
let mut locator = RpitConstraintChecker { def_id, tcx, found: mir_opaque_ty };
|
||||
|
||||
match tcx.hir().get(scope) {
|
||||
Node::Item(it) => intravisit::walk_item(&mut locator, it),
|
||||
@ -208,17 +252,18 @@ pub(super) fn find_opaque_ty_constraints_for_rpit(
|
||||
Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it),
|
||||
other => bug!("{:?} is not a valid scope for an opaque type item", other),
|
||||
}
|
||||
}
|
||||
|
||||
concrete.map(|concrete| concrete.ty).unwrap_or_else(|| {
|
||||
let table = tcx.typeck(owner_def_id);
|
||||
if let Some(guar) = table.tainted_by_errors {
|
||||
// Some error in the
|
||||
// owner fn prevented us from populating
|
||||
mir_opaque_ty.ty
|
||||
} else {
|
||||
if let Some(guar) = tables.tainted_by_errors {
|
||||
// Some error in the owner fn prevented us from populating
|
||||
// the `concrete_opaque_types` table.
|
||||
tcx.ty_error(guar)
|
||||
} else {
|
||||
table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| {
|
||||
// Fall back to the RPIT we inferred during HIR typeck
|
||||
if let Some(hir_opaque_ty) = hir_opaque_ty {
|
||||
hir_opaque_ty.ty
|
||||
} else {
|
||||
// We failed to resolve the opaque type or it
|
||||
// resolves to itself. We interpret this as the
|
||||
// no values of the hidden type ever being constructed,
|
||||
@ -226,9 +271,9 @@ pub(super) fn find_opaque_ty_constraints_for_rpit(
|
||||
// For backwards compatibility reasons, we fall back to
|
||||
// `()` until we the diverging default is changed.
|
||||
tcx.mk_diverging_default()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct RpitConstraintChecker<'tcx> {
|
||||
|
@ -874,7 +874,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let found = self.resolve_vars_with_obligations(found);
|
||||
|
||||
let in_loop = self.is_loop(id)
|
||||
|| self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
|
||||
|| self
|
||||
.tcx
|
||||
.hir()
|
||||
.parent_iter(id)
|
||||
.take_while(|(_, node)| {
|
||||
// look at parents until we find the first body owner
|
||||
node.body_id().is_none()
|
||||
})
|
||||
.any(|(parent_id, _)| self.is_loop(parent_id));
|
||||
|
||||
let in_local_statement = self.is_local_statement(id)
|
||||
|| self
|
||||
|
@ -583,19 +583,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let hidden_type =
|
||||
self.tcx().erase_regions(hidden_type.remap_generic_params_to_declaration_params(
|
||||
opaque_type_key,
|
||||
self.tcx(),
|
||||
true,
|
||||
));
|
||||
|
||||
// Here we only detect impl trait definition conflicts when they
|
||||
// are equal modulo regions.
|
||||
if let Some(last_opaque_ty) = self
|
||||
.typeck_results
|
||||
.concrete_opaque_types
|
||||
.insert(opaque_type_key.def_id, hidden_type)
|
||||
.insert(opaque_type_key, hidden_type)
|
||||
&& last_opaque_ty.ty != hidden_type.ty
|
||||
{
|
||||
assert!(!self.tcx().trait_solver_next());
|
||||
hidden_type
|
||||
.report_mismatch(&last_opaque_ty, opaque_type_key.def_id, self.tcx())
|
||||
.stash(
|
||||
|
@ -155,11 +155,7 @@ pub struct TypeckResults<'tcx> {
|
||||
/// We also store the type here, so that the compiler can use it as a hint
|
||||
/// for figuring out hidden types, even if they are only set in dead code
|
||||
/// (which doesn't show up in MIR).
|
||||
///
|
||||
/// These types are mapped back to the opaque's identity substitutions
|
||||
/// (with erased regions), which is why we don't associated substs with any
|
||||
/// of these usages.
|
||||
pub concrete_opaque_types: FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>,
|
||||
pub concrete_opaque_types: FxIndexMap<ty::OpaqueTypeKey<'tcx>, ty::OpaqueHiddenType<'tcx>>,
|
||||
|
||||
/// Tracks the minimum captures required for a closure;
|
||||
/// see `MinCaptureInformationMap` for more details.
|
||||
|
@ -179,7 +179,6 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_middle::mir::interpret::{AllocId, ConstValue};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar};
|
||||
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
|
||||
@ -220,78 +219,29 @@ pub struct InliningMap<'tcx> {
|
||||
// The range selects elements within the `targets` vecs.
|
||||
index: FxHashMap<MonoItem<'tcx>, Range<usize>>,
|
||||
targets: Vec<MonoItem<'tcx>>,
|
||||
|
||||
// Contains one bit per mono item in the `targets` field. That bit
|
||||
// is true if that mono item needs to be inlined into every CGU.
|
||||
inlines: GrowableBitSet<usize>,
|
||||
}
|
||||
|
||||
/// Struct to store mono items in each collecting and if they should
|
||||
/// be inlined. We call `instantiation_mode` to get their inlining
|
||||
/// status when inserting new elements, which avoids calling it in
|
||||
/// `inlining_map.lock_mut()`. See the `collect_items_rec` implementation
|
||||
/// below.
|
||||
struct MonoItems<'tcx> {
|
||||
// If this is false, we do not need to compute whether items
|
||||
// will need to be inlined.
|
||||
compute_inlining: bool,
|
||||
|
||||
// The TyCtxt used to determine whether the a item should
|
||||
// be inlined.
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
||||
// The collected mono items. The bool field in each element
|
||||
// indicates whether this element should be inlined.
|
||||
items: Vec<(Spanned<MonoItem<'tcx>>, bool /*inlined*/)>,
|
||||
}
|
||||
|
||||
impl<'tcx> MonoItems<'tcx> {
|
||||
#[inline]
|
||||
fn push(&mut self, item: Spanned<MonoItem<'tcx>>) {
|
||||
self.extend([item]);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = Spanned<MonoItem<'tcx>>>>(&mut self, iter: T) {
|
||||
self.items.extend(iter.into_iter().map(|mono_item| {
|
||||
let inlined = if !self.compute_inlining {
|
||||
false
|
||||
} else {
|
||||
mono_item.node.instantiation_mode(self.tcx) == InstantiationMode::LocalCopy
|
||||
};
|
||||
(mono_item, inlined)
|
||||
}))
|
||||
}
|
||||
}
|
||||
type MonoItems<'tcx> = Vec<Spanned<MonoItem<'tcx>>>;
|
||||
|
||||
impl<'tcx> InliningMap<'tcx> {
|
||||
fn new() -> InliningMap<'tcx> {
|
||||
InliningMap {
|
||||
index: FxHashMap::default(),
|
||||
targets: Vec::new(),
|
||||
inlines: GrowableBitSet::with_capacity(1024),
|
||||
}
|
||||
InliningMap { index: FxHashMap::default(), targets: Vec::new() }
|
||||
}
|
||||
|
||||
fn record_accesses<'a>(
|
||||
&mut self,
|
||||
source: MonoItem<'tcx>,
|
||||
new_targets: &'a [(Spanned<MonoItem<'tcx>>, bool)],
|
||||
new_targets: &'a [Spanned<MonoItem<'tcx>>],
|
||||
) where
|
||||
'tcx: 'a,
|
||||
{
|
||||
let start_index = self.targets.len();
|
||||
let new_items_count = new_targets.len();
|
||||
let new_items_count_total = new_items_count + self.targets.len();
|
||||
|
||||
self.targets.reserve(new_items_count);
|
||||
self.inlines.ensure(new_items_count_total);
|
||||
|
||||
for (i, (Spanned { node: mono_item, .. }, inlined)) in new_targets.into_iter().enumerate() {
|
||||
for Spanned { node: mono_item, .. } in new_targets.into_iter() {
|
||||
self.targets.push(*mono_item);
|
||||
if *inlined {
|
||||
self.inlines.insert(i + start_index);
|
||||
}
|
||||
}
|
||||
|
||||
let end_index = self.targets.len();
|
||||
@ -300,13 +250,14 @@ impl<'tcx> InliningMap<'tcx> {
|
||||
|
||||
/// Internally iterate over all items referenced by `source` which will be
|
||||
/// made available for inlining.
|
||||
pub fn with_inlining_candidates<F>(&self, source: MonoItem<'tcx>, mut f: F)
|
||||
pub fn with_inlining_candidates<F>(&self, tcx: TyCtxt<'tcx>, source: MonoItem<'tcx>, mut f: F)
|
||||
where
|
||||
F: FnMut(MonoItem<'tcx>),
|
||||
{
|
||||
if let Some(range) = self.index.get(&source) {
|
||||
for (i, candidate) in self.targets[range.clone()].iter().enumerate() {
|
||||
if self.inlines.contains(range.start + i) {
|
||||
for candidate in self.targets[range.clone()].iter() {
|
||||
let is_inlined = candidate.instantiation_mode(tcx) == InstantiationMode::LocalCopy;
|
||||
if is_inlined {
|
||||
f(*candidate);
|
||||
}
|
||||
}
|
||||
@ -367,7 +318,7 @@ pub fn collect_crate_mono_items(
|
||||
#[instrument(skip(tcx, mode), level = "debug")]
|
||||
fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<'_>> {
|
||||
debug!("collecting roots");
|
||||
let mut roots = MonoItems { compute_inlining: false, tcx, items: Vec::new() };
|
||||
let mut roots = Vec::new();
|
||||
|
||||
{
|
||||
let entry_fn = tcx.entry_fn(());
|
||||
@ -393,9 +344,8 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<
|
||||
// whose predicates hold. Luckily, items that aren't instantiable
|
||||
// can't actually be used, so we can just skip codegenning them.
|
||||
roots
|
||||
.items
|
||||
.into_iter()
|
||||
.filter_map(|(Spanned { node: mono_item, .. }, _)| {
|
||||
.filter_map(|Spanned { node: mono_item, .. }| {
|
||||
mono_item.is_instantiable(tcx).then_some(mono_item)
|
||||
})
|
||||
.collect()
|
||||
@ -417,7 +367,7 @@ fn collect_items_rec<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
let mut neighbors = MonoItems { compute_inlining: true, tcx, items: Vec::new() };
|
||||
let mut neighbors = Vec::new();
|
||||
let recursion_depth_reset;
|
||||
|
||||
//
|
||||
@ -542,9 +492,9 @@ fn collect_items_rec<'tcx>(
|
||||
formatted_item,
|
||||
});
|
||||
}
|
||||
inlining_map.lock_mut().record_accesses(starting_point.node, &neighbors.items);
|
||||
inlining_map.lock_mut().record_accesses(starting_point.node, &neighbors);
|
||||
|
||||
for (neighbour, _) in neighbors.items {
|
||||
for neighbour in neighbors {
|
||||
collect_items_rec(tcx, neighbour, visited, recursion_depths, recursion_limit, inlining_map);
|
||||
}
|
||||
|
||||
|
@ -424,7 +424,7 @@ fn place_inlined_mono_items<'tcx>(
|
||||
// Collect all items that need to be available in this codegen unit.
|
||||
let mut reachable = FxHashSet::default();
|
||||
for root in old_codegen_unit.items().keys() {
|
||||
follow_inlining(*root, cx.inlining_map, &mut reachable);
|
||||
follow_inlining(cx.tcx, *root, cx.inlining_map, &mut reachable);
|
||||
}
|
||||
|
||||
let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name());
|
||||
@ -478,6 +478,7 @@ fn place_inlined_mono_items<'tcx>(
|
||||
return mono_item_placements;
|
||||
|
||||
fn follow_inlining<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mono_item: MonoItem<'tcx>,
|
||||
inlining_map: &InliningMap<'tcx>,
|
||||
visited: &mut FxHashSet<MonoItem<'tcx>>,
|
||||
@ -486,8 +487,8 @@ fn place_inlined_mono_items<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
inlining_map.with_inlining_candidates(mono_item, |target| {
|
||||
follow_inlining(target, inlining_map, visited);
|
||||
inlining_map.with_inlining_candidates(tcx, mono_item, |target| {
|
||||
follow_inlining(tcx, target, inlining_map, visited);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -495,8 +495,7 @@ pub trait Into<T>: Sized {
|
||||
/// By converting underlying error types to our own custom error type that encapsulates the
|
||||
/// underlying error type, we can return a single error type without losing information on the
|
||||
/// underlying cause. The '?' operator automatically converts the underlying error type to our
|
||||
/// custom error type by calling `Into<CliError>::into` which is automatically provided when
|
||||
/// implementing `From`. The compiler then infers which implementation of `Into` should be used.
|
||||
/// custom error type with `From::from`.
|
||||
///
|
||||
/// ```
|
||||
/// use std::fs;
|
||||
|
@ -381,7 +381,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||
Some(Event::Code(text)) => {
|
||||
trace!("saw code {}", text);
|
||||
if let Some(link) = self.shortcut_link {
|
||||
trace!("original text was {}", link.original_text);
|
||||
// NOTE: this only replaces if the code block is the *entire* text.
|
||||
// If only part of the link has code highlighting, the disambiguator will not be removed.
|
||||
// e.g. [fn@`f`]
|
||||
@ -390,8 +389,11 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||
// So we could never be sure we weren't replacing too much:
|
||||
// [fn@my_`f`unc] is treated the same as [my_func()] in that pass.
|
||||
//
|
||||
// NOTE: &[1..len() - 1] is to strip the backticks
|
||||
if **text == link.original_text[1..link.original_text.len() - 1] {
|
||||
// NOTE: .get(1..len() - 1) is to strip the backticks
|
||||
if let Some(link) = self.links.iter().find(|l| {
|
||||
l.href == link.href
|
||||
&& Some(&**text) == l.original_text.get(1..l.original_text.len() - 1)
|
||||
}) {
|
||||
debug!("replacing {} with {}", text, link.new_text);
|
||||
*text = CowStr::Borrowed(&link.new_text);
|
||||
}
|
||||
@ -402,9 +404,12 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||
Some(Event::Text(text)) => {
|
||||
trace!("saw text {}", text);
|
||||
if let Some(link) = self.shortcut_link {
|
||||
trace!("original text was {}", link.original_text);
|
||||
// NOTE: same limitations as `Event::Code`
|
||||
if **text == *link.original_text {
|
||||
if let Some(link) = self
|
||||
.links
|
||||
.iter()
|
||||
.find(|l| l.href == link.href && **text == *l.original_text)
|
||||
{
|
||||
debug!("replacing {} with {}", text, link.new_text);
|
||||
*text = CowStr::Borrowed(&link.new_text);
|
||||
}
|
||||
|
@ -1179,6 +1179,10 @@ a.test-arrow:hover {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.code-header a.tooltip:hover {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
/* placeholder thunk so that the mouse can easily travel from "(i)" to popover
|
||||
the resulting "hover tunnel" is a stepped triangle, approximating
|
||||
https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown */
|
||||
@ -1191,6 +1195,14 @@ a.tooltip:hover::after {
|
||||
content: "\00a0";
|
||||
}
|
||||
|
||||
/* This animation is layered onto the mistake-proofing delay for dismissing
|
||||
a hovered tooltip, to ensure it feels responsive even with the delay.
|
||||
*/
|
||||
.fade-out {
|
||||
opacity: 0;
|
||||
transition: opacity 0.45s cubic-bezier(0, 0, 0.1, 1.0);
|
||||
}
|
||||
|
||||
.popover.tooltip .content {
|
||||
margin: 0.25em 0.5em;
|
||||
}
|
||||
|
@ -4,6 +4,13 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// The amount of time that the cursor must remain still over a hover target before
|
||||
// revealing a tooltip.
|
||||
//
|
||||
// https://www.nngroup.com/articles/timing-exposing-content/
|
||||
window.RUSTDOC_TOOLTIP_HOVER_MS = 300;
|
||||
window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS = 450;
|
||||
|
||||
// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
|
||||
// for a resource under the root-path, with the resource-suffix.
|
||||
function resourcePath(basename, extension) {
|
||||
@ -772,6 +779,13 @@ function preLoadCss(cssUrl) {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Show a tooltip immediately.
|
||||
*
|
||||
* @param {DOMElement} e - The tooltip's anchor point. The DOM is consulted to figure
|
||||
* out what the tooltip should contain, and where it should be
|
||||
* positioned.
|
||||
*/
|
||||
function showTooltip(e) {
|
||||
const notable_ty = e.getAttribute("data-notable-ty");
|
||||
if (!window.NOTABLE_TRAITS && notable_ty) {
|
||||
@ -782,8 +796,10 @@ function preLoadCss(cssUrl) {
|
||||
throw new Error("showTooltip() called with notable without any notable traits!");
|
||||
}
|
||||
}
|
||||
// Make this function idempotent. If the tooltip is already shown, avoid doing extra work
|
||||
// and leave it alone.
|
||||
if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) {
|
||||
// Make this function idempotent.
|
||||
clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);
|
||||
return;
|
||||
}
|
||||
window.hideAllModals(false);
|
||||
@ -791,11 +807,18 @@ function preLoadCss(cssUrl) {
|
||||
if (notable_ty) {
|
||||
wrapper.innerHTML = "<div class=\"content\">" +
|
||||
window.NOTABLE_TRAITS[notable_ty] + "</div>";
|
||||
} else if (e.getAttribute("title") !== undefined) {
|
||||
const titleContent = document.createElement("div");
|
||||
titleContent.className = "content";
|
||||
titleContent.appendChild(document.createTextNode(e.getAttribute("title")));
|
||||
wrapper.appendChild(titleContent);
|
||||
} else {
|
||||
// Replace any `title` attribute with `data-title` to avoid double tooltips.
|
||||
if (e.getAttribute("title") !== null) {
|
||||
e.setAttribute("data-title", e.getAttribute("title"));
|
||||
e.removeAttribute("title");
|
||||
}
|
||||
if (e.getAttribute("data-title") !== null) {
|
||||
const titleContent = document.createElement("div");
|
||||
titleContent.className = "content";
|
||||
titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));
|
||||
wrapper.appendChild(titleContent);
|
||||
}
|
||||
}
|
||||
wrapper.className = "tooltip popover";
|
||||
const focusCatcher = document.createElement("div");
|
||||
@ -824,17 +847,77 @@ function preLoadCss(cssUrl) {
|
||||
wrapper.style.visibility = "";
|
||||
window.CURRENT_TOOLTIP_ELEMENT = wrapper;
|
||||
window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e;
|
||||
clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);
|
||||
wrapper.onpointerenter = function(ev) {
|
||||
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
|
||||
if (ev.pointerType !== "mouse") {
|
||||
return;
|
||||
}
|
||||
clearTooltipHoverTimeout(e);
|
||||
};
|
||||
wrapper.onpointerleave = function(ev) {
|
||||
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
|
||||
if (ev.pointerType !== "mouse") {
|
||||
return;
|
||||
}
|
||||
if (!e.TOOLTIP_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) {
|
||||
hideTooltip(true);
|
||||
if (!e.TOOLTIP_FORCE_VISIBLE && !elemIsInParent(ev.relatedTarget, e)) {
|
||||
// See "Tooltip pointer leave gesture" below.
|
||||
setTooltipHoverTimeout(e, false);
|
||||
addClass(wrapper, "fade-out");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide the tooltip after a timeout. If a timeout was already set before this function
|
||||
* was called, that timeout gets cleared. If the tooltip is already in the requested state,
|
||||
* this function will still clear any pending timeout, but otherwise do nothing.
|
||||
*
|
||||
* @param {DOMElement} element - The tooltip's anchor point. The DOM is consulted to figure
|
||||
* out what the tooltip should contain, and where it should be
|
||||
* positioned.
|
||||
* @param {boolean} show - If true, the tooltip will be made visible. If false, it will
|
||||
* be hidden.
|
||||
*/
|
||||
function setTooltipHoverTimeout(element, show) {
|
||||
clearTooltipHoverTimeout(element);
|
||||
if (!show && !window.CURRENT_TOOLTIP_ELEMENT) {
|
||||
// To "hide" an already hidden element, just cancel its timeout.
|
||||
return;
|
||||
}
|
||||
if (show && window.CURRENT_TOOLTIP_ELEMENT) {
|
||||
// To "show" an already visible element, just cancel its timeout.
|
||||
return;
|
||||
}
|
||||
if (window.CURRENT_TOOLTIP_ELEMENT &&
|
||||
window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE !== element) {
|
||||
// Don't do anything if another tooltip is already visible.
|
||||
return;
|
||||
}
|
||||
element.TOOLTIP_HOVER_TIMEOUT = setTimeout(() => {
|
||||
if (show) {
|
||||
showTooltip(element);
|
||||
} else if (!element.TOOLTIP_FORCE_VISIBLE) {
|
||||
hideTooltip(false);
|
||||
}
|
||||
}, show ? window.RUSTDOC_TOOLTIP_HOVER_MS : window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a show/hide timeout was set by `setTooltipHoverTimeout`, cancel it. If none exists,
|
||||
* do nothing.
|
||||
*
|
||||
* @param {DOMElement} element - The tooltip's anchor point,
|
||||
* as passed to `setTooltipHoverTimeout`.
|
||||
*/
|
||||
function clearTooltipHoverTimeout(element) {
|
||||
if (element.TOOLTIP_HOVER_TIMEOUT !== undefined) {
|
||||
removeClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out");
|
||||
clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);
|
||||
delete element.TOOLTIP_HOVER_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
function tooltipBlurHandler(event) {
|
||||
if (window.CURRENT_TOOLTIP_ELEMENT &&
|
||||
!elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT) &&
|
||||
@ -854,6 +937,12 @@ function preLoadCss(cssUrl) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the current tooltip immediately.
|
||||
*
|
||||
* @param {boolean} focus - If set to `true`, move keyboard focus to the tooltip anchor point.
|
||||
* If set to `false`, leave keyboard focus alone.
|
||||
*/
|
||||
function hideTooltip(focus) {
|
||||
if (window.CURRENT_TOOLTIP_ELEMENT) {
|
||||
if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) {
|
||||
@ -864,6 +953,7 @@ function preLoadCss(cssUrl) {
|
||||
}
|
||||
const body = document.getElementsByTagName("body")[0];
|
||||
body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);
|
||||
clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);
|
||||
window.CURRENT_TOOLTIP_ELEMENT = null;
|
||||
}
|
||||
}
|
||||
@ -886,7 +976,14 @@ function preLoadCss(cssUrl) {
|
||||
if (ev.pointerType !== "mouse") {
|
||||
return;
|
||||
}
|
||||
showTooltip(this);
|
||||
setTooltipHoverTimeout(this, true);
|
||||
};
|
||||
e.onpointermove = function(ev) {
|
||||
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
|
||||
if (ev.pointerType !== "mouse") {
|
||||
return;
|
||||
}
|
||||
setTooltipHoverTimeout(this, true);
|
||||
};
|
||||
e.onpointerleave = function(ev) {
|
||||
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
|
||||
@ -895,7 +992,38 @@ function preLoadCss(cssUrl) {
|
||||
}
|
||||
if (!this.TOOLTIP_FORCE_VISIBLE &&
|
||||
!elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) {
|
||||
hideTooltip(true);
|
||||
// Tooltip pointer leave gesture:
|
||||
//
|
||||
// Designing a good hover microinteraction is a matter of guessing user
|
||||
// intent from what are, literally, vague gestures. In this case, guessing if
|
||||
// hovering in or out of the tooltip base is intentional or not.
|
||||
//
|
||||
// To figure this out, a few different techniques are used:
|
||||
//
|
||||
// * When the mouse pointer enters a tooltip anchor point, its hitbox is grown
|
||||
// on the bottom, where the popover is/will appear. Search "hover tunnel" in
|
||||
// rustdoc.css for the implementation.
|
||||
// * There's a delay when the mouse pointer enters the popover base anchor, in
|
||||
// case the mouse pointer was just passing through and the user didn't want
|
||||
// to open it.
|
||||
// * Similarly, a delay is added when exiting the anchor, or the popover
|
||||
// itself, before hiding it.
|
||||
// * A fade-out animation is layered onto the pointer exit delay to immediately
|
||||
// inform the user that they successfully dismissed the popover, while still
|
||||
// providing a way for them to cancel it if it was a mistake and they still
|
||||
// wanted to interact with it.
|
||||
// * No animation is used for revealing it, because we don't want people to try
|
||||
// to interact with an element while it's in the middle of fading in: either
|
||||
// they're allowed to interact with it while it's fading in, meaning it can't
|
||||
// serve as mistake-proofing for the popover, or they can't, but
|
||||
// they might try and be frustrated.
|
||||
//
|
||||
// See also:
|
||||
// * https://www.nngroup.com/articles/timing-exposing-content/
|
||||
// * https://www.nngroup.com/articles/tooltip-guidelines/
|
||||
// * https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown
|
||||
setTooltipHoverTimeout(e, false);
|
||||
addClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -14,10 +14,10 @@ use std::intrinsics::{transmute, transmute_unchecked};
|
||||
// Some of these need custom MIR to not get removed by MIR optimizations.
|
||||
use std::intrinsics::mir::*;
|
||||
|
||||
enum Never {}
|
||||
pub enum ZstNever {}
|
||||
|
||||
#[repr(align(2))]
|
||||
pub struct BigNever(Never, u16, Never);
|
||||
pub struct BigNever(ZstNever, u16, ZstNever);
|
||||
|
||||
#[repr(align(8))]
|
||||
pub struct Scalar64(i64);
|
||||
@ -56,11 +56,13 @@ pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] {
|
||||
transmute_unchecked(x)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @check_to_uninhabited(
|
||||
// CHECK-LABEL: @check_to_empty_array(
|
||||
#[no_mangle]
|
||||
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||
pub unsafe fn check_to_uninhabited(x: u16) -> BigNever {
|
||||
pub unsafe fn check_to_empty_array(x: [u32; 5]) -> [u32; 0] {
|
||||
// CHECK-NOT: trap
|
||||
// CHECK: call void @llvm.trap
|
||||
// CHECK-NOT: trap
|
||||
mir!{
|
||||
{
|
||||
RET = CastTransmute(x);
|
||||
@ -69,6 +71,37 @@ pub unsafe fn check_to_uninhabited(x: u16) -> BigNever {
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @check_from_empty_array(
|
||||
#[no_mangle]
|
||||
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||
pub unsafe fn check_from_empty_array(x: [u32; 0]) -> [u32; 5] {
|
||||
// CHECK-NOT: trap
|
||||
// CHECK: call void @llvm.trap
|
||||
// CHECK-NOT: trap
|
||||
mir!{
|
||||
{
|
||||
RET = CastTransmute(x);
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @check_to_uninhabited(
|
||||
#[no_mangle]
|
||||
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||
pub unsafe fn check_to_uninhabited(x: u16) {
|
||||
// CHECK-NOT: trap
|
||||
// CHECK: call void @llvm.trap
|
||||
// CHECK-NOT: trap
|
||||
mir!{
|
||||
let temp: BigNever;
|
||||
{
|
||||
temp = CastTransmute(x);
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @check_from_uninhabited(
|
||||
#[no_mangle]
|
||||
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||
@ -366,6 +399,40 @@ pub unsafe fn check_issue_109992(x: ()) -> [(); 1] {
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @check_unit_to_never(
|
||||
#[no_mangle]
|
||||
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||
pub unsafe fn check_unit_to_never(x: ()) {
|
||||
// This uses custom MIR to avoid MIR optimizations having removed ZST ops.
|
||||
|
||||
// CHECK-NOT: trap
|
||||
// CHECK: call void @llvm.trap
|
||||
// CHECK-NOT: trap
|
||||
mir!{
|
||||
let temp: ZstNever;
|
||||
{
|
||||
temp = CastTransmute(x);
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @check_unit_from_never(
|
||||
#[no_mangle]
|
||||
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||
pub unsafe fn check_unit_from_never(x: ZstNever) -> () {
|
||||
// This uses custom MIR to avoid MIR optimizations having removed ZST ops.
|
||||
|
||||
// CHECK: start
|
||||
// CHECK-NEXT: ret void
|
||||
mir!{
|
||||
{
|
||||
RET = CastTransmute(x);
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @check_maybe_uninit_pair(i16 %x.0, i64 %x.1)
|
||||
#[no_mangle]
|
||||
pub unsafe fn check_maybe_uninit_pair(
|
||||
|
@ -40,6 +40,7 @@ define-function: (
|
||||
"background-color": |background|,
|
||||
"border-color": |border|,
|
||||
})
|
||||
click: ".docblock .example-wrap.compile_fail .tooltip"
|
||||
|
||||
// should_panic block
|
||||
assert-css: (
|
||||
@ -71,6 +72,7 @@ define-function: (
|
||||
"background-color": |background|,
|
||||
"border-color": |border|,
|
||||
})
|
||||
click: ".docblock .example-wrap.should_panic .tooltip"
|
||||
|
||||
// ignore block
|
||||
assert-css: (
|
||||
|
@ -122,7 +122,7 @@ assert-count: ("//*[@class='tooltip popover']", 0)
|
||||
// Now check the colors.
|
||||
define-function: (
|
||||
"check-colors",
|
||||
(theme, header_color, content_color, type_color, trait_color),
|
||||
(theme, header_color, content_color, type_color, trait_color, link_color),
|
||||
block {
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html"
|
||||
// This is needed to ensure that the text color is computed.
|
||||
@ -133,8 +133,20 @@ define-function: (
|
||||
// We reload the page so the local storage settings are being used.
|
||||
reload:
|
||||
|
||||
assert-css: (
|
||||
"//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
|
||||
{"color": |content_color|},
|
||||
ALL,
|
||||
)
|
||||
|
||||
move-cursor-to: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
|
||||
assert-count: (".tooltip.popover", 1)
|
||||
wait-for-count: (".tooltip.popover", 1)
|
||||
|
||||
assert-css: (
|
||||
"//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
|
||||
{"color": |link_color|},
|
||||
ALL,
|
||||
)
|
||||
|
||||
assert-css: (
|
||||
".tooltip.popover h3",
|
||||
@ -163,6 +175,7 @@ call-function: (
|
||||
"check-colors",
|
||||
{
|
||||
"theme": "ayu",
|
||||
"link_color": "rgb(57, 175, 215)",
|
||||
"content_color": "rgb(230, 225, 207)",
|
||||
"header_color": "rgb(255, 255, 255)",
|
||||
"type_color": "rgb(255, 160, 165)",
|
||||
@ -174,6 +187,7 @@ call-function: (
|
||||
"check-colors",
|
||||
{
|
||||
"theme": "dark",
|
||||
"link_color": "rgb(210, 153, 29)",
|
||||
"content_color": "rgb(221, 221, 221)",
|
||||
"header_color": "rgb(221, 221, 221)",
|
||||
"type_color": "rgb(45, 191, 184)",
|
||||
@ -185,6 +199,7 @@ call-function: (
|
||||
"check-colors",
|
||||
{
|
||||
"theme": "light",
|
||||
"link_color": "rgb(56, 115, 173)",
|
||||
"content_color": "rgb(0, 0, 0)",
|
||||
"header_color": "rgb(0, 0, 0)",
|
||||
"type_color": "rgb(173, 55, 138)",
|
||||
|
37
tests/rustdoc/intra-doc/issue-108459.rs
Normal file
37
tests/rustdoc/intra-doc/issue-108459.rs
Normal file
@ -0,0 +1,37 @@
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
|
||||
pub struct S;
|
||||
pub mod char {}
|
||||
|
||||
// Ensure this doesn't ICE due to trying to slice off non-existent backticks from "S"
|
||||
|
||||
/// See [S] and [`S`]
|
||||
pub struct MyStruct1;
|
||||
|
||||
// Ensure that link texts are replaced correctly even if there are multiple links with
|
||||
// the same target but different text
|
||||
|
||||
/// See also [crate::char] and [mod@char] and [prim@char]
|
||||
// @has issue_108459/struct.MyStruct2.html '//*[@href="char/index.html"]' 'crate::char'
|
||||
// @has - '//*[@href="char/index.html"]' 'char'
|
||||
// @has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
|
||||
pub struct MyStruct2;
|
||||
|
||||
/// See also [mod@char] and [prim@char] and [crate::char]
|
||||
// @has issue_108459/struct.MyStruct3.html '//*[@href="char/index.html"]' 'crate::char'
|
||||
// @has - '//*[@href="char/index.html"]' 'char'
|
||||
// @has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
|
||||
pub struct MyStruct3;
|
||||
|
||||
// Ensure that links are correct even if there are multiple links with the same text but
|
||||
// different targets
|
||||
|
||||
/// See also [char][mod@char] and [char][prim@char]
|
||||
// @has issue_108459/struct.MyStruct4.html '//*[@href="char/index.html"]' 'char'
|
||||
// @has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
|
||||
pub struct MyStruct4;
|
||||
|
||||
/// See also [char][prim@char] and [char][crate::char]
|
||||
// @has issue_108459/struct.MyStruct5.html '//*[@href="char/index.html"]' 'char'
|
||||
// @has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
|
||||
pub struct MyStruct5;
|
@ -12,5 +12,5 @@ pub struct MyString;
|
||||
|
||||
/// See also [crate::char] and [mod@char]
|
||||
// @has prim_precedence/struct.MyString2.html '//*[@href="char/index.html"]' 'crate::char'
|
||||
// @has - '//*[@href="char/index.html"]' 'mod@char'
|
||||
// @has - '//*[@href="char/index.html"]' 'char'
|
||||
pub struct MyString2;
|
||||
|
@ -13,16 +13,9 @@ error: internal compiler error: projection clauses should be implied from elsewh
|
||||
LL | async fn foo(x: u32) -> u32 {
|
||||
| ^^^query stack during panic:
|
||||
#0 [typeck] type-checking `foo`
|
||||
#1 [thir_body] building THIR for `foo`
|
||||
#2 [check_match] match-checking `foo`
|
||||
#3 [mir_built] building MIR for `foo`
|
||||
#4 [unsafety_check_result] unsafety-checking `foo`
|
||||
#5 [mir_const] preparing `foo` for borrow checking
|
||||
#6 [mir_promoted] promoting constants in MIR for `foo`
|
||||
#7 [mir_borrowck] borrow-checking `foo`
|
||||
#8 [type_of] computing type of `foo::{opaque#0}`
|
||||
#9 [check_mod_item_types] checking item types in top-level module
|
||||
#10 [analysis] running analysis passes on this crate
|
||||
#1 [type_of] computing type of `foo::{opaque#0}`
|
||||
#2 [check_mod_item_types] checking item types in top-level module
|
||||
#3 [analysis] running analysis passes on this crate
|
||||
end of query stack
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -13,41 +13,6 @@ error[E0391]: cycle detected when computing type of `make_dyn_star::{opaque#0}`
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires borrow-checking `make_dyn_star`...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires promoting constants in MIR for `make_dyn_star`...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires preparing `make_dyn_star` for borrow checking...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires unsafety-checking `make_dyn_star`...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building MIR for `make_dyn_star`...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires match-checking `make_dyn_star`...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building THIR for `make_dyn_star`...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires type-checking `make_dyn_star`...
|
||||
--> $DIR/param-env-infer.rs:11:1
|
||||
|
|
||||
|
@ -4,41 +4,6 @@ error[E0391]: cycle detected when computing type of `cycle1::{opaque#0}`
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires borrow-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires promoting constants in MIR for `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires preparing `cycle1` for borrow checking...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires unsafety-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building MIR for `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires match-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building THIR for `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:12:1
|
||||
|
|
||||
LL | fn cycle1() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires type-checking `cycle1`...
|
||||
--> $DIR/auto-trait-leak.rs:14:5
|
||||
|
|
||||
@ -50,41 +15,6 @@ note: ...which requires computing type of `cycle2::{opaque#0}`...
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^
|
||||
note: ...which requires borrow-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires promoting constants in MIR for `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires preparing `cycle2` for borrow checking...
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires unsafety-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building MIR for `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires match-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires building THIR for `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:19:1
|
||||
|
|
||||
LL | fn cycle2() -> impl Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires type-checking `cycle2`...
|
||||
--> $DIR/auto-trait-leak.rs:20:5
|
||||
|
|
||||
|
@ -4,9 +4,9 @@ impl Trait for () {}
|
||||
fn foo<T: Trait, U: Trait>() -> impl Trait {
|
||||
//~^ WARN function cannot return without recursing [unconditional_recursion]
|
||||
let a: T = foo::<T, U>();
|
||||
//~^ ERROR concrete type differs from previous defining opaque type use
|
||||
loop {}
|
||||
let _: T = foo::<U, T>();
|
||||
//~^ ERROR concrete type differs from previous defining opaque type use
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -11,15 +11,15 @@ LL | let a: T = foo::<T, U>();
|
||||
= note: `#[warn(unconditional_recursion)]` on by default
|
||||
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
--> $DIR/multiple-defining-usages-in-body.rs:8:16
|
||||
|
|
||||
LL | let _: T = foo::<U, T>();
|
||||
| ^^^^^^^^^^^^^ expected `T`, got `U`
|
||||
|
|
||||
note: previous use here
|
||||
--> $DIR/multiple-defining-usages-in-body.rs:6:16
|
||||
|
|
||||
LL | let a: T = foo::<T, U>();
|
||||
| ^^^^^^^^^^^^^ expected `U`, got `T`
|
||||
|
|
||||
note: previous use here
|
||||
--> $DIR/multiple-defining-usages-in-body.rs:9:16
|
||||
|
|
||||
LL | let _: T = foo::<U, T>();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
55
tests/ui/loops/dont-suggest-break-thru-item.rs
Normal file
55
tests/ui/loops/dont-suggest-break-thru-item.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// edition:2021
|
||||
|
||||
#![feature(inline_const)]
|
||||
|
||||
fn closure() {
|
||||
loop {
|
||||
let closure = || {
|
||||
if true {
|
||||
Err(1)
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn async_block() {
|
||||
loop {
|
||||
let fut = async {
|
||||
if true {
|
||||
Err(1)
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_item() {
|
||||
let _ = loop {
|
||||
fn foo() -> Result<(), ()> {
|
||||
if true {
|
||||
Err(1)
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn const_block() {
|
||||
let _ = loop {
|
||||
const {
|
||||
if true {
|
||||
Err(1)
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
Err(())
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
55
tests/ui/loops/dont-suggest-break-thru-item.stderr
Normal file
55
tests/ui/loops/dont-suggest-break-thru-item.stderr
Normal file
@ -0,0 +1,55 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/dont-suggest-break-thru-item.rs:9:17
|
||||
|
|
||||
LL | / if true {
|
||||
LL | | Err(1)
|
||||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/dont-suggest-break-thru-item.rs:22:17
|
||||
|
|
||||
LL | / if true {
|
||||
LL | | Err(1)
|
||||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/dont-suggest-break-thru-item.rs:35:17
|
||||
|
|
||||
LL | / if true {
|
||||
LL | | Err(1)
|
||||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/dont-suggest-break-thru-item.rs:47:17
|
||||
|
|
||||
LL | / if true {
|
||||
LL | | Err(1)
|
||||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
19
tests/ui/traits/new-solver/dont-remap-tait-substs.rs
Normal file
19
tests/ui/traits/new-solver/dont-remap-tait-substs.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// compile-flags: -Ztrait-solver=next
|
||||
// check-pass
|
||||
|
||||
// Makes sure we don't prepopulate the MIR typeck of `define`
|
||||
// with `Foo<T, U> = T`, but instead, `Foo<B, A> = B`, so that
|
||||
// the param-env predicates actually apply.
|
||||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
type Foo<T: Send, U> = impl NeedsSend<T>;
|
||||
|
||||
trait NeedsSend<T> {}
|
||||
impl<T: Send> NeedsSend<T> for T {}
|
||||
|
||||
fn define<A, B: Send>(a: A, b: B) {
|
||||
let y: Option<Foo<B, A>> = Some(b);
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,3 +1,5 @@
|
||||
// revisions: current next
|
||||
//[next] compile-flags: -Ztrait-solver=next
|
||||
// check-pass
|
||||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
@ -8,7 +8,6 @@ type X<A, B> = impl Into<&'static A>;
|
||||
|
||||
fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) {
|
||||
//~^ ERROR the trait bound `&'static B: From<&A>` is not satisfied
|
||||
//~| ERROR concrete type differs from previous defining opaque type use
|
||||
(a, a)
|
||||
}
|
||||
|
||||
|
@ -10,15 +10,6 @@ help: consider introducing a `where` clause, but there might be an alternative b
|
||||
LL | fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) where &'static B: From<&A> {
|
||||
| ++++++++++++++++++++++++++
|
||||
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
--> $DIR/multiple-def-uses-in-one-fn.rs:9:45
|
||||
|
|
||||
LL | fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| expected `&B`, got `&A`
|
||||
| this expression supplies two conflicting concrete types for the same opaque type
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
Loading…
Reference in New Issue
Block a user