diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 33f07c1d8fc..13e346b86bc 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -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 diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 3b896f6540c..908ff3da5ca 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -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, |_, _| { diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 869344ce92d..f9ea0f00456 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -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) { diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 5df8c1a209d..30a3fe67b85 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -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, } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4d0bcd53d15..5968e70b1cc 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -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))] diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index e264ce78f0d..a493c9c0548 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -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, } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 15c7847155d..242d209b684 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -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); } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 3f0b64b1103..e0cb26d3ba8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -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() { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index bba2800fb05..4f79c6a3d82 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -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 => {} } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 00414003202..2809ec2deb5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -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); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 4000c9540ce..31c293d7c29 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -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) { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 0255b660380..5241a5aee00 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -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, } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index f7c5b44678f..4d96a7ff4c3 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -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> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c4add4dbdfb..e19b0664461 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -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 diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 0f21fc1e662..964acc4eb77 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -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( diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index e04dbbff9a7..8cbffa14850 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -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. diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index cefa64d27ac..09025c1ee33 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -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); } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index be9c349c384..0bacb58383d 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -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); }); } } diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 38a6d1ccdb5..799085e9a83 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -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; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index b26a5c32ec6..1c27320024a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -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); } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a7d5f497756..054cfe7597e 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -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; } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index bccf675c14b..6da51ea0a55 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -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"); } }; }); diff --git a/tests/codegen/intrinsics/transmute.rs b/tests/codegen/intrinsics/transmute.rs index 664e697c2a5..fe42494000e 100644 --- a/tests/codegen/intrinsics/transmute.rs +++ b/tests/codegen/intrinsics/transmute.rs @@ -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( diff --git a/tests/rustdoc-gui/codeblock-tooltip.goml b/tests/rustdoc-gui/codeblock-tooltip.goml index e1c81ed79e4..7be5e39ba47 100644 --- a/tests/rustdoc-gui/codeblock-tooltip.goml +++ b/tests/rustdoc-gui/codeblock-tooltip.goml @@ -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: ( diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml index ecb57c274a5..371931d51fc 100644 --- a/tests/rustdoc-gui/notable-trait.goml +++ b/tests/rustdoc-gui/notable-trait.goml @@ -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)", diff --git a/tests/rustdoc/intra-doc/issue-108459.rs b/tests/rustdoc/intra-doc/issue-108459.rs new file mode 100644 index 00000000000..eb1c7a05e54 --- /dev/null +++ b/tests/rustdoc/intra-doc/issue-108459.rs @@ -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; diff --git a/tests/rustdoc/intra-doc/prim-precedence.rs b/tests/rustdoc/intra-doc/prim-precedence.rs index 25625b95277..c5a64e42a01 100644 --- a/tests/rustdoc/intra-doc/prim-precedence.rs +++ b/tests/rustdoc/intra-doc/prim-precedence.rs @@ -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; diff --git a/tests/ui/chalkify/bugs/async.stderr b/tests/ui/chalkify/bugs/async.stderr index 7e64e67f24c..e6d46b02706 100644 --- a/tests/ui/chalkify/bugs/async.stderr +++ b/tests/ui/chalkify/bugs/async.stderr @@ -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 diff --git a/tests/ui/dyn-star/param-env-infer.next.stderr b/tests/ui/dyn-star/param-env-infer.next.stderr index 64d76bb04b1..408abecc30d 100644 --- a/tests/ui/dyn-star/param-env-infer.next.stderr +++ b/tests/ui/dyn-star/param-env-infer.next.stderr @@ -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 | diff --git a/tests/ui/impl-trait/auto-trait-leak.stderr b/tests/ui/impl-trait/auto-trait-leak.stderr index aa4ee75bb75..c0c4cd5013e 100644 --- a/tests/ui/impl-trait/auto-trait-leak.stderr +++ b/tests/ui/impl-trait/auto-trait-leak.stderr @@ -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 | diff --git a/tests/ui/impl-trait/multiple-defining-usages-in-body.rs b/tests/ui/impl-trait/multiple-defining-usages-in-body.rs index c3a6f09f86d..86661153a0d 100644 --- a/tests/ui/impl-trait/multiple-defining-usages-in-body.rs +++ b/tests/ui/impl-trait/multiple-defining-usages-in-body.rs @@ -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() {} diff --git a/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr b/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr index 06991749bfa..f3c090408b4 100644 --- a/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr +++ b/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr @@ -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 diff --git a/tests/ui/loops/dont-suggest-break-thru-item.rs b/tests/ui/loops/dont-suggest-break-thru-item.rs new file mode 100644 index 00000000000..b46ba89e81d --- /dev/null +++ b/tests/ui/loops/dont-suggest-break-thru-item.rs @@ -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() {} diff --git a/tests/ui/loops/dont-suggest-break-thru-item.stderr b/tests/ui/loops/dont-suggest-break-thru-item.stderr new file mode 100644 index 00000000000..4fce4715119 --- /dev/null +++ b/tests/ui/loops/dont-suggest-break-thru-item.stderr @@ -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`. diff --git a/tests/ui/traits/new-solver/dont-remap-tait-substs.rs b/tests/ui/traits/new-solver/dont-remap-tait-substs.rs new file mode 100644 index 00000000000..028222f4e6d --- /dev/null +++ b/tests/ui/traits/new-solver/dont-remap-tait-substs.rs @@ -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() {} diff --git a/tests/ui/type-alias-impl-trait/cross_inference.rs b/tests/ui/type-alias-impl-trait/cross_inference.rs index dafaf40a69d..07f3dd1997b 100644 --- a/tests/ui/type-alias-impl-trait/cross_inference.rs +++ b/tests/ui/type-alias-impl-trait/cross_inference.rs @@ -1,3 +1,5 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next // check-pass #![feature(type_alias_impl_trait)] diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs index 9ae2c34b935..da845e86147 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs @@ -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) } diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr index 0d24d42ba62..66a6b0bbf74 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr @@ -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`.