mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 16:54:01 +00:00
Move ReverseMapper logic onto OpaqueHiddenType
This commit is contained in:
parent
d643ae1bbf
commit
9eb69e82e0
@ -5,8 +5,7 @@ use rustc_hir::OpaqueTyOrigin;
|
||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||
use rustc_infer::infer::{DefiningAnchor, InferCtxt};
|
||||
use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine};
|
||||
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts};
|
||||
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
|
||||
use rustc_middle::ty::visit::TypeVisitable;
|
||||
use rustc_middle::ty::{
|
||||
self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt, TypeFoldable,
|
||||
@ -15,8 +14,6 @@ use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
||||
use rustc_trait_selection::traits::TraitEngineExt as _;
|
||||
|
||||
use crate::session_diagnostics::ConstNotUsedTraitAlias;
|
||||
|
||||
use super::RegionInferenceContext;
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
@ -228,29 +225,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||
return self.tcx.ty_error();
|
||||
}
|
||||
|
||||
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
|
||||
|
||||
// Use substs to build up a reverse map from regions to their
|
||||
// identity mappings. This is necessary because of `impl
|
||||
// Trait` lifetimes are computed by replacing existing
|
||||
// lifetimes with 'static and remapping only those used in the
|
||||
// `impl Trait` return type, resulting in the parameters
|
||||
// shifting.
|
||||
let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id());
|
||||
debug!(?id_substs);
|
||||
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
|
||||
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
|
||||
debug!("map = {:#?}", map);
|
||||
|
||||
// Convert the type from the function into a type valid outside
|
||||
// the function, by replacing invalid regions with 'static,
|
||||
// after producing an error for each of them.
|
||||
let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new(
|
||||
self.tcx,
|
||||
map,
|
||||
instantiated_ty.span,
|
||||
));
|
||||
debug!(?definition_ty);
|
||||
let definition_ty = instantiated_ty
|
||||
.remap_generic_params_to_declaration_params(opaque_type_key, self.tcx)
|
||||
.ty;
|
||||
|
||||
if !check_opaque_type_parameter_valid(
|
||||
self.tcx,
|
||||
@ -266,6 +243,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||
let OpaqueTyOrigin::TyAlias = origin else {
|
||||
return definition_ty;
|
||||
};
|
||||
let def_id = opaque_type_key.def_id;
|
||||
// This logic duplicates most of `check_opaque_meets_bounds`.
|
||||
// FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
|
||||
let param_env = self.tcx.param_env(def_id);
|
||||
@ -281,6 +259,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||
.to_predicate(infcx.tcx);
|
||||
let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
||||
|
||||
let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id());
|
||||
|
||||
// Require that the hidden type actually fulfills all the bounds of the opaque type, even without
|
||||
// the bounds that the function supplies.
|
||||
match infcx.register_hidden_type(
|
||||
@ -421,200 +401,3 @@ fn check_opaque_type_parameter_valid(
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
struct ReverseMapper<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
||||
do_not_error: bool,
|
||||
|
||||
/// Span of function being checked.
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> ReverseMapper<'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
Self { tcx, map, do_not_error: false, span }
|
||||
}
|
||||
|
||||
fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
||||
assert!(!self.do_not_error);
|
||||
self.do_not_error = true;
|
||||
let kind = kind.fold_with(self);
|
||||
self.do_not_error = false;
|
||||
kind
|
||||
}
|
||||
|
||||
fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
||||
assert!(!self.do_not_error);
|
||||
kind.fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
match *r {
|
||||
// Ignore bound regions and `'static` regions that appear in the
|
||||
// type, we only need to remap regions that reference lifetimes
|
||||
// from the function declaration.
|
||||
// This would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
|
||||
ty::ReLateBound(..) | ty::ReStatic => return r,
|
||||
|
||||
// If regions have been erased (by writeback), don't try to unerase
|
||||
// them.
|
||||
ty::ReErased => return r,
|
||||
|
||||
// The regions that we expect from borrow checking.
|
||||
ty::ReEarlyBound(_) | ty::ReFree(_) => {}
|
||||
|
||||
ty::RePlaceholder(_) | ty::ReVar(_) => {
|
||||
// All of the regions in the type should either have been
|
||||
// erased by writeback, or mapped back to named regions by
|
||||
// borrow checking.
|
||||
bug!("unexpected region kind in opaque type: {:?}", r);
|
||||
}
|
||||
}
|
||||
|
||||
match self.map.get(&r.into()).map(|k| k.unpack()) {
|
||||
Some(GenericArgKind::Lifetime(r1)) => r1,
|
||||
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
|
||||
None if self.do_not_error => self.tcx.lifetimes.re_static,
|
||||
None => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(self.span, "non-defining opaque type use in defining scope")
|
||||
.span_label(
|
||||
self.span,
|
||||
format!(
|
||||
"lifetime `{}` is part of concrete type but not used in \
|
||||
parameter list of the `impl Trait` type alias",
|
||||
r
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
|
||||
self.tcx().lifetimes.re_static
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match *ty.kind() {
|
||||
ty::Closure(def_id, substs) => {
|
||||
// I am a horrible monster and I pray for death. When
|
||||
// we encounter a closure here, it is always a closure
|
||||
// from within the function that we are currently
|
||||
// type-checking -- one that is now being encapsulated
|
||||
// in an opaque type. Ideally, we would
|
||||
// go through the types/lifetimes that it references
|
||||
// and treat them just like we would any other type,
|
||||
// which means we would error out if we find any
|
||||
// reference to a type/region that is not in the
|
||||
// "reverse map".
|
||||
//
|
||||
// **However,** in the case of closures, there is a
|
||||
// somewhat subtle (read: hacky) consideration. The
|
||||
// problem is that our closure types currently include
|
||||
// all the lifetime parameters declared on the
|
||||
// enclosing function, even if they are unused by the
|
||||
// closure itself. We can't readily filter them out,
|
||||
// so here we replace those values with `'empty`. This
|
||||
// can't really make a difference to the rest of the
|
||||
// compiler; those regions are ignored for the
|
||||
// outlives relation, and hence don't affect trait
|
||||
// selection or auto traits, and they are erased
|
||||
// during codegen.
|
||||
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
||||
if index < generics.parent_count {
|
||||
// Accommodate missing regions in the parent kinds...
|
||||
self.fold_kind_no_missing_regions_error(kind)
|
||||
} else {
|
||||
// ...but not elsewhere.
|
||||
self.fold_kind_normally(kind)
|
||||
}
|
||||
}));
|
||||
|
||||
self.tcx.mk_closure(def_id, substs)
|
||||
}
|
||||
|
||||
ty::Generator(def_id, substs, movability) => {
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
||||
if index < generics.parent_count {
|
||||
// Accommodate missing regions in the parent kinds...
|
||||
self.fold_kind_no_missing_regions_error(kind)
|
||||
} else {
|
||||
// ...but not elsewhere.
|
||||
self.fold_kind_normally(kind)
|
||||
}
|
||||
}));
|
||||
|
||||
self.tcx.mk_generator(def_id, substs, movability)
|
||||
}
|
||||
|
||||
ty::Param(param) => {
|
||||
// Look it up in the substitution list.
|
||||
match self.map.get(&ty.into()).map(|k| k.unpack()) {
|
||||
// Found it in the substitution list; replace with the parameter from the
|
||||
// opaque type.
|
||||
Some(GenericArgKind::Type(t1)) => t1,
|
||||
Some(u) => panic!("type mapped to unexpected kind: {:?}", u),
|
||||
None => {
|
||||
debug!(?param, ?self.map);
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
self.span,
|
||||
&format!(
|
||||
"type parameter `{}` is part of concrete type but not \
|
||||
used in parameter list for the `impl Trait` type alias",
|
||||
ty
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
|
||||
self.tcx().ty_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => ty.super_fold_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
trace!("checking const {:?}", ct);
|
||||
// Find a const parameter
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Param(..) => {
|
||||
// Look it up in the substitution list.
|
||||
match self.map.get(&ct.into()).map(|k| k.unpack()) {
|
||||
// Found it in the substitution list, replace with the parameter from the
|
||||
// opaque type.
|
||||
Some(GenericArgKind::Const(c1)) => c1,
|
||||
Some(u) => panic!("const mapped to unexpected kind: {:?}", u),
|
||||
None => {
|
||||
self.tcx.sess.emit_err(ConstNotUsedTraitAlias {
|
||||
ct: ct.to_string(),
|
||||
span: self.span,
|
||||
});
|
||||
|
||||
self.tcx().const_error(ct.ty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => ct,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,15 +52,6 @@ pub(crate) struct VarNeedNotMut {
|
||||
#[suggestion_short(applicability = "machine-applicable", code = "")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck::const_not_used_in_type_alias)]
|
||||
pub(crate) struct ConstNotUsedTraitAlias {
|
||||
pub ct: String,
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck::var_cannot_escape_closure)]
|
||||
#[note]
|
||||
|
@ -511,3 +511,11 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck::const_not_used_in_type_alias)]
|
||||
pub(super) struct ConstNotUsedTraitAlias {
|
||||
pub ct: String,
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
@ -131,6 +131,7 @@ mod generics;
|
||||
mod impls_ty;
|
||||
mod instance;
|
||||
mod list;
|
||||
mod opaque_types;
|
||||
mod parameterized;
|
||||
mod rvalue_scopes;
|
||||
mod structural_impls;
|
||||
@ -1300,6 +1301,32 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
|
||||
sub: sub_diag,
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
pub fn remap_generic_params_to_declaration_params(
|
||||
self,
|
||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Self {
|
||||
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
|
||||
|
||||
// Use substs to build up a reverse map from regions to their
|
||||
// identity mappings. This is necessary because of `impl
|
||||
// Trait` lifetimes are computed by replacing existing
|
||||
// lifetimes with 'static and remapping only those used in the
|
||||
// `impl Trait` return type, resulting in the parameters
|
||||
// shifting.
|
||||
let id_substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
|
||||
debug!(?id_substs);
|
||||
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
|
||||
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
|
||||
debug!("map = {:#?}", map);
|
||||
|
||||
// Convert the type from the function into a type valid outside
|
||||
// the function, by replacing invalid regions with 'static,
|
||||
// after producing an error for each of them.
|
||||
self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span))
|
||||
}
|
||||
}
|
||||
|
||||
/// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are
|
||||
|
205
compiler/rustc_middle/src/ty/opaque_types.rs
Normal file
205
compiler/rustc_middle/src/ty/opaque_types.rs
Normal file
@ -0,0 +1,205 @@
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_span::Span;
|
||||
|
||||
/// Converts generic params of a TypeFoldable from one
|
||||
/// item's generics to another. Usually from a function's generics
|
||||
/// list to the opaque type's own generics.
|
||||
pub(super) struct ReverseMapper<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
||||
do_not_error: bool,
|
||||
|
||||
/// Span of function being checked.
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> ReverseMapper<'tcx> {
|
||||
pub(super) fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
Self { tcx, map, do_not_error: false, span }
|
||||
}
|
||||
|
||||
fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
||||
assert!(!self.do_not_error);
|
||||
self.do_not_error = true;
|
||||
let kind = kind.fold_with(self);
|
||||
self.do_not_error = false;
|
||||
kind
|
||||
}
|
||||
|
||||
fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
||||
assert!(!self.do_not_error);
|
||||
kind.fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
match *r {
|
||||
// Ignore bound regions and `'static` regions that appear in the
|
||||
// type, we only need to remap regions that reference lifetimes
|
||||
// from the function declaration.
|
||||
// This would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
|
||||
ty::ReLateBound(..) | ty::ReStatic => return r,
|
||||
|
||||
// If regions have been erased (by writeback), don't try to unerase
|
||||
// them.
|
||||
ty::ReErased => return r,
|
||||
|
||||
// The regions that we expect from borrow checking.
|
||||
ty::ReEarlyBound(_) | ty::ReFree(_) => {}
|
||||
|
||||
ty::RePlaceholder(_) | ty::ReVar(_) => {
|
||||
// All of the regions in the type should either have been
|
||||
// erased by writeback, or mapped back to named regions by
|
||||
// borrow checking.
|
||||
bug!("unexpected region kind in opaque type: {:?}", r);
|
||||
}
|
||||
}
|
||||
|
||||
match self.map.get(&r.into()).map(|k| k.unpack()) {
|
||||
Some(GenericArgKind::Lifetime(r1)) => r1,
|
||||
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
|
||||
None if self.do_not_error => self.tcx.lifetimes.re_static,
|
||||
None => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(self.span, "non-defining opaque type use in defining scope")
|
||||
.span_label(
|
||||
self.span,
|
||||
format!(
|
||||
"lifetime `{}` is part of concrete type but not used in \
|
||||
parameter list of the `impl Trait` type alias",
|
||||
r
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
|
||||
self.tcx().lifetimes.re_static
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match *ty.kind() {
|
||||
ty::Closure(def_id, substs) => {
|
||||
// I am a horrible monster and I pray for death. When
|
||||
// we encounter a closure here, it is always a closure
|
||||
// from within the function that we are currently
|
||||
// type-checking -- one that is now being encapsulated
|
||||
// in an opaque type. Ideally, we would
|
||||
// go through the types/lifetimes that it references
|
||||
// and treat them just like we would any other type,
|
||||
// which means we would error out if we find any
|
||||
// reference to a type/region that is not in the
|
||||
// "reverse map".
|
||||
//
|
||||
// **However,** in the case of closures, there is a
|
||||
// somewhat subtle (read: hacky) consideration. The
|
||||
// problem is that our closure types currently include
|
||||
// all the lifetime parameters declared on the
|
||||
// enclosing function, even if they are unused by the
|
||||
// closure itself. We can't readily filter them out,
|
||||
// so here we replace those values with `'empty`. This
|
||||
// can't really make a difference to the rest of the
|
||||
// compiler; those regions are ignored for the
|
||||
// outlives relation, and hence don't affect trait
|
||||
// selection or auto traits, and they are erased
|
||||
// during codegen.
|
||||
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
||||
if index < generics.parent_count {
|
||||
// Accommodate missing regions in the parent kinds...
|
||||
self.fold_kind_no_missing_regions_error(kind)
|
||||
} else {
|
||||
// ...but not elsewhere.
|
||||
self.fold_kind_normally(kind)
|
||||
}
|
||||
}));
|
||||
|
||||
self.tcx.mk_closure(def_id, substs)
|
||||
}
|
||||
|
||||
ty::Generator(def_id, substs, movability) => {
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
||||
if index < generics.parent_count {
|
||||
// Accommodate missing regions in the parent kinds...
|
||||
self.fold_kind_no_missing_regions_error(kind)
|
||||
} else {
|
||||
// ...but not elsewhere.
|
||||
self.fold_kind_normally(kind)
|
||||
}
|
||||
}));
|
||||
|
||||
self.tcx.mk_generator(def_id, substs, movability)
|
||||
}
|
||||
|
||||
ty::Param(param) => {
|
||||
// Look it up in the substitution list.
|
||||
match self.map.get(&ty.into()).map(|k| k.unpack()) {
|
||||
// Found it in the substitution list; replace with the parameter from the
|
||||
// opaque type.
|
||||
Some(GenericArgKind::Type(t1)) => t1,
|
||||
Some(u) => panic!("type mapped to unexpected kind: {:?}", u),
|
||||
None => {
|
||||
debug!(?param, ?self.map);
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
self.span,
|
||||
&format!(
|
||||
"type parameter `{}` is part of concrete type but not \
|
||||
used in parameter list for the `impl Trait` type alias",
|
||||
ty
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
|
||||
self.tcx().ty_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => ty.super_fold_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
trace!("checking const {:?}", ct);
|
||||
// Find a const parameter
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Param(..) => {
|
||||
// Look it up in the substitution list.
|
||||
match self.map.get(&ct.into()).map(|k| k.unpack()) {
|
||||
// Found it in the substitution list, replace with the parameter from the
|
||||
// opaque type.
|
||||
Some(GenericArgKind::Const(c1)) => c1,
|
||||
Some(u) => panic!("const mapped to unexpected kind: {:?}", u),
|
||||
None => {
|
||||
self.tcx.sess.emit_err(ty::ConstNotUsedTraitAlias {
|
||||
ct: ct.to_string(),
|
||||
span: self.span,
|
||||
});
|
||||
|
||||
self.tcx().const_error(ct.ty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => ct,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user