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:
bors 2023-06-01 08:06:07 +00:00
commit fabf929863
38 changed files with 639 additions and 351 deletions

View File

@ -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

View File

@ -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, |_, _| {

View File

@ -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) {

View File

@ -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,
}
}

View File

@ -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))]

View File

@ -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,
}
}

View File

@ -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);
}

View File

@ -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() {

View File

@ -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 => {}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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,
}

View File

@ -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> {

View File

@ -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

View File

@ -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(

View File

@ -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.

View File

@ -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);
}

View File

@ -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);
});
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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");
}
};
});

View File

@ -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(

View File

@ -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: (

View File

@ -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)",

View 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;

View File

@ -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;

View File

@ -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

View File

@ -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
|

View File

@ -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
|

View File

@ -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() {}

View File

@ -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

View 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() {}

View 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`.

View 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() {}

View File

@ -1,3 +1,5 @@
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
// check-pass
#![feature(type_alias_impl_trait)]

View File

@ -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)
}

View File

@ -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`.