From dbef6e450799c0da7ec3c749fd7574fe8c6755da Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 2 Jun 2022 19:42:29 +0200 Subject: [PATCH] address review --- Cargo.lock | 1 - .../rustc_const_eval/src/const_eval/error.rs | 2 + .../rustc_const_eval/src/const_eval/mod.rs | 41 +++++++++---- .../src/const_eval/valtrees.rs | 28 ++++++--- .../rustc_middle/src/mir/interpret/queries.rs | 2 +- .../rustc_middle/src/mir/interpret/value.rs | 1 - compiler/rustc_middle/src/query/mod.rs | 12 ++-- compiler/rustc_middle/src/ty/mod.rs | 9 ++- compiler/rustc_middle/src/ty/print/mod.rs | 1 + compiler/rustc_middle/src/ty/print/pretty.rs | 4 +- compiler/rustc_mir_build/src/build/mod.rs | 58 +------------------ .../src/thir/pattern/const_to_pat.rs | 2 +- src/test/incremental/issue-54242.rs | 2 +- src/test/ui/consts/const-size_of-cycle.stderr | 4 +- src/test/ui/consts/issue-44415.rs | 2 +- src/test/ui/consts/issue-44415.stderr | 4 +- 16 files changed, 80 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5716ee2fc14..eafddf80080 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,7 +358,6 @@ dependencies = [ "libgit2-sys", "log", "memchr", - "num_cpus", "opener", "openssl", "os_info", diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 3bd092263c1..3eeb0138b37 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -140,6 +140,7 @@ pub fn report_as_lint( /// /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. /// (Except that for some errors, we ignore all that -- see `must_error` below.) + #[instrument(skip(self, tcx, decorate, lint_root), level = "debug")] fn struct_generic( &self, tcx: TyCtxtAt<'tcx>, @@ -190,6 +191,7 @@ fn struct_generic( decorate(err); }; + debug!("self.error: {:?}", self.error); // Special handling for certain errors match &self.error { // Don't emit a new diagnostic for these errors diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 51edf64de80..793f02a8ade 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -24,12 +24,6 @@ pub use machine::*; pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value}; -pub(crate) enum ValTreeCreationError { - NonSupportedType, - Other, -} -pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError>; - pub(crate) fn const_caller_location( tcx: TyCtxt<'_>, (file, line, col): (Symbol, u32, u32), @@ -44,6 +38,16 @@ pub(crate) fn const_caller_location( ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx)) } +// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. +const VALTREE_MAX_NODES: usize = 100000; + +pub(crate) enum ValTreeCreationError { + NodesOverflow, + NonSupportedType, + Other, +} +pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError>; + /// Evaluates a constant and turns it into a type-level constant value. pub(crate) fn eval_to_valtree<'tcx>( tcx: TyCtxt<'tcx>, @@ -62,11 +66,28 @@ pub(crate) fn eval_to_valtree<'tcx>( let place = ecx.raw_const_to_mplace(const_alloc).unwrap(); debug!(?place); - let valtree_result = const_to_valtree_inner(&ecx, &place); + let mut num_nodes = 0; + let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes); match valtree_result { Ok(valtree) => Ok(Some(valtree)), - Err(_) => Ok(None), + Err(err) => { + let did = cid.instance.def_id(); + let s = cid.display(tcx); + match err { + ValTreeCreationError::NodesOverflow => { + let msg = format!("maximum number of nodes exceeded in constant {}", &s); + let mut diag = match tcx.hir().span_if_local(did) { + Some(span) => tcx.sess.struct_span_err(span, &msg), + None => tcx.sess.struct_err(&msg), + }; + diag.emit(); + + Ok(None) + } + ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None), + } + } } } @@ -75,7 +96,7 @@ pub(crate) fn eval_to_valtree<'tcx>( pub(crate) fn try_destructure_const<'tcx>( tcx: TyCtxt<'tcx>, const_: ty::Const<'tcx>, -) -> Option> { +) -> Option> { if let ty::ConstKind::Value(valtree) = const_.val() { let branches = match valtree { ty::ValTree::Branch(b) => b, @@ -141,7 +162,7 @@ pub(crate) fn try_destructure_const<'tcx>( let fields = tcx.arena.alloc_from_iter(fields.into_iter()); - Some(mir::DestructuredConst { variant, fields }) + Some(ty::DestructuredConst { variant, fields }) } else { None } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 080133275a6..4137e351344 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,6 +1,6 @@ use super::eval_queries::{mk_eval_cx, op_to_const}; use super::machine::CompileTimeEvalContext; -use super::{ValTreeCreationError, ValTreeCreationResult}; +use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit, @@ -16,6 +16,7 @@ fn branches<'tcx>( place: &MPlaceTy<'tcx>, n: usize, variant: Option, + num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { let place = match variant { Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), @@ -27,7 +28,7 @@ fn branches<'tcx>( let mut fields = Vec::with_capacity(n); for i in 0..n { let field = ecx.mplace_field(&place, i).unwrap(); - let valtree = const_to_valtree_inner(ecx, &field)?; + let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; fields.push(Some(valtree)); } @@ -39,6 +40,11 @@ fn branches<'tcx>( .collect::>>() .expect("should have already checked for errors in ValTree creation"); + // Have to account for ZSTs here + if branches.len() == 0 { + *num_nodes += 1; + } + Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches))) } @@ -46,6 +52,7 @@ fn branches<'tcx>( fn slice_branches<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, + num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { let n = place .len(&ecx.tcx.tcx) @@ -54,7 +61,7 @@ fn slice_branches<'tcx>( let mut elems = Vec::with_capacity(n as usize); for i in 0..n { let place_elem = ecx.mplace_index(place, i).unwrap(); - let valtree = const_to_valtree_inner(ecx, &place_elem)?; + let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; elems.push(valtree); } @@ -65,12 +72,18 @@ fn slice_branches<'tcx>( pub(crate) fn const_to_valtree_inner<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, + num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { let ty = place.layout.ty; debug!("ty kind: {:?}", ty.kind()); + if *num_nodes >= VALTREE_MAX_NODES { + return Err(ValTreeCreationError::NodesOverflow); + } + match ty.kind() { ty::FnDef(..) => { + *num_nodes += 1; Ok(ty::ValTree::zst()) } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { @@ -78,6 +91,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( return Err(ValTreeCreationError::Other); }; let val = val.to_scalar().unwrap(); + *num_nodes += 1; Ok(ty::ValTree::Leaf(val.assert_int())) } @@ -94,11 +108,11 @@ pub(crate) fn const_to_valtree_inner<'tcx>( }; debug!(?derefd_place); - const_to_valtree_inner(ecx, &derefd_place) + const_to_valtree_inner(ecx, &derefd_place, num_nodes) } ty::Str | ty::Slice(_) | ty::Array(_, _) => { - slice_branches(ecx, place) + slice_branches(ecx, place, num_nodes) } // Trait objects are not allowed in type level constants, as we have no concept for // resolving their backing type, even if we can do that at const eval time. We may @@ -107,7 +121,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), ty::Tuple(elem_tys) => { - branches(ecx, place, elem_tys.len(), None) + branches(ecx, place, elem_tys.len(), None, num_nodes) } ty::Adt(def, _) => { @@ -120,7 +134,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else { return Err(ValTreeCreationError::Other); }; - branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant)) + branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) } ty::Never diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 69353232f06..9284d240037 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -194,7 +194,7 @@ fn eval_to_allocation( impl<'tcx> TyCtxt<'tcx> { /// Destructure a type-level constant ADT or array into its variant index and its field values. /// Panics if the destructuring fails, use `try_destructure_const` for fallible version. - pub fn destructure_const(self, const_: ty::Const<'tcx>) -> mir::DestructuredConst<'tcx> { + pub fn destructure_const(self, const_: ty::Const<'tcx>) -> ty::DestructuredConst<'tcx> { self.try_destructure_const(const_).unwrap() } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index f566467b7ce..146ae45e468 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -78,7 +78,6 @@ pub fn try_to_scalar_int(&self) -> Option { Some(self.try_to_scalar()?.assert_int()) } - #[inline(always)] pub fn try_to_bits(&self, size: Size) -> Option { self.try_to_scalar_int()?.to_bits(size).ok() } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 22b1ad41904..caa1d4cd34e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -967,26 +967,26 @@ query eval_to_valtree( key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>> ) -> EvalToValTreeResult<'tcx> { - desc { "evaluate type-level constant" } + desc { "evaluating type-level constant" } } /// Converts a type level constant value into `ConstValue` query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> ConstValue<'tcx> { - desc { "convert type-level constant value to mir constant value"} + desc { "converting type-level constant value to mir constant value"} } /// Destructure a constant ADT or array into its variant index and its /// field values or return `None` if constant is invalid. /// /// Use infallible `TyCtxt::destructure_const` when you know that constant is valid. - query try_destructure_const(key: ty::Const<'tcx>) -> Option> { - desc { "destructure type level constant"} + query try_destructure_const(key: ty::Const<'tcx>) -> Option> { + desc { "destructuring type level constant"} } /// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index /// and its field values. query try_destructure_mir_constant(key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>) -> Option> { - desc { "destructure mir constant"} + desc { "destructuring mir constant"} remap_env_constness } @@ -995,7 +995,7 @@ query deref_mir_constant( key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>> ) -> mir::ConstantKind<'tcx> { - desc { "deref constant" } + desc { "dereferencing mir constant" } remap_env_constness } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8da447d16fb..a53583d8673 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -42,8 +42,8 @@ use rustc_session::cstore::CrateStoreDyn; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; -use rustc_target::abi::Align; pub use subst::*; +use rustc_target::abi::{Align, VariantIdx}; pub use vtable::*; use std::fmt::Debug; @@ -2453,3 +2453,10 @@ pub struct FoundRelationships { /// _>::AssocType = ?T` pub output: bool, } + +/// The constituent parts of a type level constant of kind ADT or array. +#[derive(Copy, Clone, Debug, HashStable)] +pub struct DestructuredConst<'tcx> { + pub variant: Option, + pub fields: &'tcx [ty::Const<'tcx>], +} diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 7a096bbc4d9..7ae23d371e5 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -98,6 +98,7 @@ fn path_generic_args( // Defaults (should not be overridden): + #[instrument(skip(self), level = "debug")] fn default_print_def_path( self, def_id: DefId, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 3b11f7572a6..3028a57e11c 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1460,9 +1460,7 @@ fn pretty_print_const_valtree( return Ok(self); } // Aggregates, printed as array/tuple/struct/variant construction syntax. - (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) - if !ty.has_param_types_or_consts() => - { + (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => { let Some(contents) = self.tcx().try_destructure_const( ty::Const::from_value(self.tcx(), valtree, ty) ) else { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 51f7aa642c8..0376b53ecc5 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -1,9 +1,9 @@ use crate::build; +pub(crate) use crate::build::expr::as_constant::lit_to_mir_constant; use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; use crate::thir::constant::parse_float_into_scalar; use crate::thir::pattern::pat_from_hir; -use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -14,9 +14,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; -use rustc_middle::mir::interpret::{ - Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar, -}; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::*; use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, LocalVarId, PatKind, Thir}; use rustc_middle::ty::subst::Subst; @@ -24,7 +22,6 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_span::Symbol; -use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use super::lints; @@ -1096,57 +1093,6 @@ fn parse_float_into_constval<'tcx>( parse_float_into_scalar(num, float_ty, neg).map(ConstValue::Scalar) } -#[instrument(skip(tcx, lit_input))] -pub(crate) fn lit_to_mir_constant<'tcx>( - tcx: TyCtxt<'tcx>, - lit_input: LitToConstInput<'tcx>, -) -> Result, LitToConstError> { - let LitToConstInput { lit, ty, neg } = lit_input; - let trunc = |n| { - let param_ty = ty::ParamEnv::reveal_all().and(ty); - let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; - trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); - let result = width.truncate(n); - trace!("trunc result: {}", result); - Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) - }; - - let value = match (lit, &ty.kind()) { - (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { - let s = s.as_str(); - let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); - let allocation = tcx.intern_const_alloc(allocation); - ConstValue::Slice { data: allocation, start: 0, end: s.len() } - } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) - if matches!(inner_ty.kind(), ty::Slice(_)) => - { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); - let allocation = tcx.intern_const_alloc(allocation); - ConstValue::Slice { data: allocation, start: 0, end: data.len() } - } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { - let id = tcx.allocate_bytes(data); - ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) - } - (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { - ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) - } - (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { - trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })? - } - (ast::LitKind::Float(n, _), ty::Float(fty)) => { - parse_float_into_constval(*n, *fty, neg).ok_or(LitToConstError::Reported)? - } - (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), - (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), - (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported), - _ => return Err(LitToConstError::TypeError), - }; - - Ok(ConstantKind::Val(value, ty)) -} - /////////////////////////////////////////////////////////////////////////// // Builder methods are broken up into modules, depending on what kind // of thing is being lowered. Note that they use the `unpack` macro diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index b423c645447..845be2ab264 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -503,7 +503,7 @@ fn recur( // deref pattern. _ => { if !pointee_ty.is_sized(tcx.at(span), param_env) { - // `tcx.deref_mirconstant()` below will ICE with an unsized type + // `tcx.deref_mir_constant()` below will ICE with an unsized type // (except slices, which are handled in a separate arm above). let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty); if self.include_lint_checks { diff --git a/src/test/incremental/issue-54242.rs b/src/test/incremental/issue-54242.rs index d32df954499..806c8415259 100644 --- a/src/test/incremental/issue-54242.rs +++ b/src/test/incremental/issue-54242.rs @@ -14,7 +14,7 @@ impl Tr for str { type Arr = [u8; 8]; #[cfg(cfail)] type Arr = [u8; Self::C]; - //[cfail]~^ ERROR cycle detected when evaluate type-level constant + //[cfail]~^ ERROR cycle detected when evaluating type-level constant } fn main() {} diff --git a/src/test/ui/consts/const-size_of-cycle.stderr b/src/test/ui/consts/const-size_of-cycle.stderr index 0772b1712c5..46f02ce8a45 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -1,4 +1,4 @@ -error[E0391]: cycle detected when evaluate type-level constant +error[E0391]: cycle detected when evaluating type-level constant --> $DIR/const-size_of-cycle.rs:4:17 | LL | bytes: [u8; std::mem::size_of::()] @@ -17,7 +17,7 @@ LL | bytes: [u8; std::mem::size_of::()] = note: ...which requires computing layout of `Foo`... = note: ...which requires computing layout of `[u8; _]`... = note: ...which requires normalizing `[u8; _]`... - = note: ...which again requires evaluate type-level constant, completing the cycle + = note: ...which again requires evaluating type-level constant, completing the cycle note: cycle used when checking that `Foo` is well-formed --> $DIR/const-size_of-cycle.rs:3:1 | diff --git a/src/test/ui/consts/issue-44415.rs b/src/test/ui/consts/issue-44415.rs index 79f415e6a2c..d93b451be45 100644 --- a/src/test/ui/consts/issue-44415.rs +++ b/src/test/ui/consts/issue-44415.rs @@ -4,7 +4,7 @@ struct Foo { bytes: [u8; unsafe { intrinsics::size_of::() }], - //~^ ERROR cycle detected when evaluate type-level constant + //~^ ERROR cycle detected when evaluating type-level constant x: usize, } diff --git a/src/test/ui/consts/issue-44415.stderr b/src/test/ui/consts/issue-44415.stderr index 6641300fc9a..c085beb0ea5 100644 --- a/src/test/ui/consts/issue-44415.stderr +++ b/src/test/ui/consts/issue-44415.stderr @@ -1,4 +1,4 @@ -error[E0391]: cycle detected when evaluate type-level constant +error[E0391]: cycle detected when evaluating type-level constant --> $DIR/issue-44415.rs:6:17 | LL | bytes: [u8; unsafe { intrinsics::size_of::() }], @@ -17,7 +17,7 @@ LL | bytes: [u8; unsafe { intrinsics::size_of::() }], = note: ...which requires computing layout of `Foo`... = note: ...which requires computing layout of `[u8; _]`... = note: ...which requires normalizing `[u8; _]`... - = note: ...which again requires evaluate type-level constant, completing the cycle + = note: ...which again requires evaluating type-level constant, completing the cycle note: cycle used when checking that `Foo` is well-formed --> $DIR/issue-44415.rs:5:1 | -- GitLab