Rollup merge of #102021 - lcnr:tyConst-fun, r=b-naber,BoxyUwU
some post-valtree cleanup r? project-const-generics cc ```@b-naber```
This commit is contained in:
commit
b149c48186
12 changed files with 58 additions and 122 deletions
|
@ -100,10 +100,10 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
val: mir::ConstantKind<'tcx>,
|
val: mir::ConstantKind<'tcx>,
|
||||||
) -> InterpResult<'tcx, mir::DestructuredMirConstant<'tcx>> {
|
) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
|
||||||
trace!("destructure_mir_constant: {:?}", val);
|
trace!("destructure_mir_constant: {:?}", val);
|
||||||
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
|
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
|
||||||
let op = ecx.mir_const_to_op(&val, None)?;
|
let op = ecx.const_to_op(&val, None)?;
|
||||||
|
|
||||||
// We go to `usize` as we cannot allocate anything bigger anyway.
|
// We go to `usize` as we cannot allocate anything bigger anyway.
|
||||||
let (field_count, variant, down) = match val.ty().kind() {
|
let (field_count, variant, down) = match val.ty().kind() {
|
||||||
|
@ -129,7 +129,7 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
|
||||||
.collect::<InterpResult<'tcx, Vec<_>>>()?;
|
.collect::<InterpResult<'tcx, Vec<_>>>()?;
|
||||||
let fields = tcx.arena.alloc_from_iter(fields_iter);
|
let fields = tcx.arena.alloc_from_iter(fields_iter);
|
||||||
|
|
||||||
Ok(mir::DestructuredMirConstant { variant, fields })
|
Ok(mir::DestructuredConstant { variant, fields })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(tcx), level = "debug")]
|
#[instrument(skip(tcx), level = "debug")]
|
||||||
|
@ -139,7 +139,7 @@ pub(crate) fn deref_mir_constant<'tcx>(
|
||||||
val: mir::ConstantKind<'tcx>,
|
val: mir::ConstantKind<'tcx>,
|
||||||
) -> mir::ConstantKind<'tcx> {
|
) -> mir::ConstantKind<'tcx> {
|
||||||
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
|
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
|
||||||
let op = ecx.mir_const_to_op(&val, None).unwrap();
|
let op = ecx.const_to_op(&val, None).unwrap();
|
||||||
let mplace = ecx.deref_operand(&op).unwrap();
|
let mplace = ecx.deref_operand(&op).unwrap();
|
||||||
if let Some(alloc_id) = mplace.ptr.provenance {
|
if let Some(alloc_id) = mplace.ptr.provenance {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -683,11 +683,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
self.stack_mut().push(frame);
|
self.stack_mut().push(frame);
|
||||||
|
|
||||||
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
||||||
for const_ in &body.required_consts {
|
for ct in &body.required_consts {
|
||||||
let span = const_.span;
|
let span = ct.span;
|
||||||
let const_ =
|
let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?;
|
||||||
self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal)?;
|
self.const_to_op(&ct, None).map_err(|err| {
|
||||||
self.mir_const_to_op(&const_, None).map_err(|err| {
|
|
||||||
// If there was an error, set the span of the current frame to this constant.
|
// If there was an error, set the span of the current frame to this constant.
|
||||||
// Avoiding doing this when evaluation succeeds.
|
// Avoiding doing this when evaluation succeeds.
|
||||||
self.frame_mut().loc = Err(span);
|
self.frame_mut().loc = Err(span);
|
||||||
|
|
|
@ -534,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
// * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
|
// * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
|
||||||
// checked yet.
|
// checked yet.
|
||||||
// * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
|
// * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
|
||||||
self.mir_const_to_op(&val, layout)?
|
self.const_to_op(&val, layout)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
trace!("{:?}: {:?}", mir_op, *op);
|
trace!("{:?}: {:?}", mir_op, *op);
|
||||||
|
@ -549,50 +549,42 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
ops.iter().map(|op| self.eval_operand(op, None)).collect()
|
ops.iter().map(|op| self.eval_operand(op, None)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used when the miri-engine runs into a constant and for extracting information from constants
|
|
||||||
// in patterns via the `const_eval` module
|
|
||||||
/// The `val` and `layout` are assumed to already be in our interpreter
|
|
||||||
/// "universe" (param_env).
|
|
||||||
pub fn const_to_op(
|
pub fn const_to_op(
|
||||||
&self,
|
|
||||||
c: ty::Const<'tcx>,
|
|
||||||
layout: Option<TyAndLayout<'tcx>>,
|
|
||||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
|
||||||
match c.kind() {
|
|
||||||
ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => throw_inval!(TooGeneric),
|
|
||||||
ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => {
|
|
||||||
throw_inval!(AlreadyReported(reported))
|
|
||||||
}
|
|
||||||
ty::ConstKind::Unevaluated(uv) => {
|
|
||||||
// NOTE: We evaluate to a `ValTree` here as a check to ensure
|
|
||||||
// we're working with valid constants, even though we never need it.
|
|
||||||
let instance = self.resolve(uv.def, uv.substs)?;
|
|
||||||
let cid = GlobalId { instance, promoted: None };
|
|
||||||
let _valtree = self
|
|
||||||
.tcx
|
|
||||||
.eval_to_valtree(self.param_env.and(cid))?
|
|
||||||
.unwrap_or_else(|| bug!("unable to create ValTree for {:?}", uv));
|
|
||||||
|
|
||||||
Ok(self.eval_to_allocation(cid)?.into())
|
|
||||||
}
|
|
||||||
ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => {
|
|
||||||
span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", c)
|
|
||||||
}
|
|
||||||
ty::ConstKind::Value(valtree) => {
|
|
||||||
let ty = c.ty();
|
|
||||||
let const_val = self.tcx.valtree_to_const_val((ty, valtree));
|
|
||||||
self.const_val_to_op(const_val, ty, layout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mir_const_to_op(
|
|
||||||
&self,
|
&self,
|
||||||
val: &mir::ConstantKind<'tcx>,
|
val: &mir::ConstantKind<'tcx>,
|
||||||
layout: Option<TyAndLayout<'tcx>>,
|
layout: Option<TyAndLayout<'tcx>>,
|
||||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||||
match val {
|
match val {
|
||||||
mir::ConstantKind::Ty(ct) => self.const_to_op(*ct, layout),
|
mir::ConstantKind::Ty(ct) => {
|
||||||
|
match ct.kind() {
|
||||||
|
ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
|
||||||
|
throw_inval!(TooGeneric)
|
||||||
|
}
|
||||||
|
ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => {
|
||||||
|
throw_inval!(AlreadyReported(reported))
|
||||||
|
}
|
||||||
|
ty::ConstKind::Unevaluated(uv) => {
|
||||||
|
// NOTE: We evaluate to a `ValTree` here as a check to ensure
|
||||||
|
// we're working with valid constants, even though we never need it.
|
||||||
|
let instance = self.resolve(uv.def, uv.substs)?;
|
||||||
|
let cid = GlobalId { instance, promoted: None };
|
||||||
|
let _valtree = self
|
||||||
|
.tcx
|
||||||
|
.eval_to_valtree(self.param_env.and(cid))?
|
||||||
|
.unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"));
|
||||||
|
|
||||||
|
Ok(self.eval_to_allocation(cid)?.into())
|
||||||
|
}
|
||||||
|
ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => {
|
||||||
|
span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {ct:?}")
|
||||||
|
}
|
||||||
|
ty::ConstKind::Value(valtree) => {
|
||||||
|
let ty = ct.ty();
|
||||||
|
let const_val = self.tcx.valtree_to_const_val((ty, valtree));
|
||||||
|
self.const_val_to_op(const_val, ty, layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout),
|
mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout),
|
||||||
mir::ConstantKind::Unevaluated(uv, _) => {
|
mir::ConstantKind::Unevaluated(uv, _) => {
|
||||||
let instance = self.resolve(uv.def, uv.substs)?;
|
let instance = self.resolve(uv.def, uv.substs)?;
|
||||||
|
|
|
@ -211,7 +211,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
self,
|
self,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
constant: mir::ConstantKind<'tcx>,
|
constant: mir::ConstantKind<'tcx>,
|
||||||
) -> mir::DestructuredMirConstant<'tcx> {
|
) -> mir::DestructuredConstant<'tcx> {
|
||||||
self.try_destructure_mir_constant(param_env.and(constant)).unwrap()
|
self.try_destructure_mir_constant(param_env.and(constant)).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,16 +392,9 @@ pub enum ClosureOutlivesSubject<'tcx> {
|
||||||
Region(ty::RegionVid),
|
Region(ty::RegionVid),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<VariantIdx>,
|
|
||||||
pub fields: &'tcx [ty::Const<'tcx>],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The constituent parts of a mir constant of kind ADT or array.
|
/// The constituent parts of a mir constant of kind ADT or array.
|
||||||
#[derive(Copy, Clone, Debug, HashStable)]
|
#[derive(Copy, Clone, Debug, HashStable)]
|
||||||
pub struct DestructuredMirConstant<'tcx> {
|
pub struct DestructuredConstant<'tcx> {
|
||||||
pub variant: Option<VariantIdx>,
|
pub variant: Option<VariantIdx>,
|
||||||
pub fields: &'tcx [ConstantKind<'tcx>],
|
pub fields: &'tcx [ConstantKind<'tcx>],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1007,7 +1007,9 @@ rustc_queries! {
|
||||||
|
|
||||||
/// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index
|
/// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index
|
||||||
/// and its field values.
|
/// and its field values.
|
||||||
query try_destructure_mir_constant(key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>) -> Option<mir::DestructuredMirConstant<'tcx>> {
|
query try_destructure_mir_constant(
|
||||||
|
key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
|
||||||
|
) -> Option<mir::DestructuredConstant<'tcx>> {
|
||||||
desc { "destructuring mir constant"}
|
desc { "destructuring mir constant"}
|
||||||
remap_env_constness
|
remap_env_constness
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use crate::mir::interpret::LitToConstInput;
|
use crate::mir::interpret::LitToConstInput;
|
||||||
use crate::mir::ConstantKind;
|
use crate::mir::ConstantKind;
|
||||||
use crate::ty::{
|
use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
|
||||||
self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty,
|
|
||||||
TyCtxt, TypeVisitable,
|
|
||||||
};
|
|
||||||
use rustc_data_structures::intern::Interned;
|
use rustc_data_structures::intern::Interned;
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -151,46 +148,6 @@ impl<'tcx> Const<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
|
|
||||||
debug!("Const::from_inline_const(def_id={:?})", def_id);
|
|
||||||
|
|
||||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
|
||||||
|
|
||||||
let body_id = match tcx.hir().get(hir_id) {
|
|
||||||
hir::Node::AnonConst(ac) => ac.body,
|
|
||||||
_ => span_bug!(
|
|
||||||
tcx.def_span(def_id.to_def_id()),
|
|
||||||
"from_inline_const can only process anonymous constants"
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let expr = &tcx.hir().body(body_id).value;
|
|
||||||
|
|
||||||
let ty = tcx.typeck(def_id).node_type(hir_id);
|
|
||||||
|
|
||||||
let ret = match Self::try_eval_lit_or_param(tcx, ty, expr) {
|
|
||||||
Some(v) => v,
|
|
||||||
None => {
|
|
||||||
let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
|
|
||||||
let parent_substs =
|
|
||||||
tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
|
|
||||||
let substs =
|
|
||||||
InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty })
|
|
||||||
.substs;
|
|
||||||
tcx.mk_const(ty::ConstS {
|
|
||||||
kind: ty::ConstKind::Unevaluated(ty::Unevaluated {
|
|
||||||
def: ty::WithOptConstParam::unknown(def_id).to_global(),
|
|
||||||
substs,
|
|
||||||
promoted: (),
|
|
||||||
}),
|
|
||||||
ty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
debug_assert!(!ret.has_free_regions());
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interns the given value as a constant.
|
/// Interns the given value as a constant.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Self {
|
pub fn from_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Self {
|
||||||
|
|
|
@ -680,7 +680,7 @@ macro_rules! ClonePatternFoldableImpls {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClonePatternFoldableImpls! { <'tcx>
|
ClonePatternFoldableImpls! { <'tcx>
|
||||||
Span, Field, Mutability, Symbol, LocalVarId, usize, ty::Const<'tcx>,
|
Span, Field, Mutability, Symbol, LocalVarId, usize,
|
||||||
Region<'tcx>, Ty<'tcx>, BindingMode, AdtDef<'tcx>,
|
Region<'tcx>, Ty<'tcx>, BindingMode, AdtDef<'tcx>,
|
||||||
SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>,
|
SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>,
|
||||||
UserTypeProjection, CanonicalUserTypeAnnotation<'tcx>
|
UserTypeProjection, CanonicalUserTypeAnnotation<'tcx>
|
||||||
|
|
|
@ -471,7 +471,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ecx.mir_const_to_op(&c.literal, None).ok()
|
self.ecx.const_to_op(&c.literal, None).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value, if any, of evaluating `place`.
|
/// Returns the value, if any, of evaluating `place`.
|
||||||
|
|
|
@ -292,7 +292,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.ecx.mir_const_to_op(&c.literal, None) {
|
match self.ecx.const_to_op(&c.literal, None) {
|
||||||
Ok(op) => Some(op),
|
Ok(op) => Some(op),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let tcx = self.ecx.tcx.at(c.span);
|
let tcx = self.ecx.tcx.at(c.span);
|
||||||
|
|
|
@ -487,18 +487,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
|
pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
|
||||||
match length {
|
match length {
|
||||||
&hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span),
|
&hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span),
|
||||||
hir::ArrayLen::Body(anon_const) => self.to_const(anon_const),
|
hir::ArrayLen::Body(anon_const) => {
|
||||||
|
let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
|
||||||
|
let span = self.tcx.hir().span(anon_const.hir_id);
|
||||||
|
let c = ty::Const::from_anon_const(self.tcx, const_def_id);
|
||||||
|
self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None));
|
||||||
|
self.normalize_associated_types_in(span, c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> {
|
|
||||||
let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
|
|
||||||
let span = self.tcx.hir().span(ast_c.hir_id);
|
|
||||||
let c = ty::Const::from_anon_const(self.tcx, const_def_id);
|
|
||||||
self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None));
|
|
||||||
self.normalize_associated_types_in(span, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn const_arg_to_const(
|
pub fn const_arg_to_const(
|
||||||
&self,
|
&self,
|
||||||
ast_c: &hir::AnonConst,
|
ast_c: &hir::AnonConst,
|
||||||
|
|
|
@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::struct_span_err;
|
||||||
use rustc_errors::{Diagnostic, ErrorGuaranteed};
|
use rustc_errors::{Diagnostic, ErrorGuaranteed};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
|
||||||
use rustc_middle::ty::subst::GenericArgKind;
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
use rustc_middle::ty::subst::InternalSubsts;
|
use rustc_middle::ty::subst::InternalSubsts;
|
||||||
use rustc_middle::ty::util::IgnoreRegions;
|
use rustc_middle::ty::util::IgnoreRegions;
|
||||||
|
@ -229,12 +228,8 @@ fn emit_orphan_check_error<'tcx>(
|
||||||
"only traits defined in the current crate {msg}"
|
"only traits defined in the current crate {msg}"
|
||||||
);
|
);
|
||||||
err.span_label(sp, "impl doesn't use only types from inside the current crate");
|
err.span_label(sp, "impl doesn't use only types from inside the current crate");
|
||||||
for (ty, is_target_ty) in &tys {
|
for &(mut ty, is_target_ty) in &tys {
|
||||||
let mut ty = *ty;
|
ty = tcx.erase_regions(ty);
|
||||||
tcx.infer_ctxt().enter(|infcx| {
|
|
||||||
// Remove the lifetimes unnecessary for this error.
|
|
||||||
ty = infcx.freshen(ty);
|
|
||||||
});
|
|
||||||
ty = match ty.kind() {
|
ty = match ty.kind() {
|
||||||
// Remove the type arguments from the output, as they are not relevant.
|
// Remove the type arguments from the output, as they are not relevant.
|
||||||
// You can think of this as the reverse of `resolve_vars_if_possible`.
|
// You can think of this as the reverse of `resolve_vars_if_possible`.
|
||||||
|
@ -264,7 +259,7 @@ fn emit_orphan_check_error<'tcx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let msg = format!("{} is not defined in the current crate{}", ty, postfix);
|
let msg = format!("{} is not defined in the current crate{}", ty, postfix);
|
||||||
if *is_target_ty {
|
if is_target_ty {
|
||||||
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
|
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
|
||||||
err.span_label(self_ty_span, &msg);
|
err.span_label(self_ty_span, &msg);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue