move unique param check into rustc_middle
This commit is contained in:
parent
52cc779524
commit
f3f68324cc
2 changed files with 93 additions and 84 deletions
|
@ -5,10 +5,7 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFolder};
|
||||||
use crate::ty::layout::IntegerExt;
|
use crate::ty::layout::IntegerExt;
|
||||||
use crate::ty::query::TyCtxtAt;
|
use crate::ty::query::TyCtxtAt;
|
||||||
use crate::ty::subst::{GenericArgKind, Subst, SubstsRef};
|
use crate::ty::subst::{GenericArgKind, Subst, SubstsRef};
|
||||||
use crate::ty::{
|
use crate::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable};
|
||||||
self, DebruijnIndex, DefIdTree, EarlyBinder, List, ReEarlyBound, Ty, TyCtxt, TyKind::*,
|
|
||||||
TypeFoldable,
|
|
||||||
};
|
|
||||||
use rustc_apfloat::Float as _;
|
use rustc_apfloat::Float as _;
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_attr::{self as attr, SignedInt, UnsignedInt};
|
use rustc_attr::{self as attr, SignedInt, UnsignedInt};
|
||||||
|
@ -18,6 +15,7 @@ use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
use rustc_index::bit_set::GrowableBitSet;
|
||||||
use rustc_macros::HashStable;
|
use rustc_macros::HashStable;
|
||||||
use rustc_span::{sym, DUMMY_SP};
|
use rustc_span::{sym, DUMMY_SP};
|
||||||
use rustc_target::abi::{Integer, Size, TargetDataLayout};
|
use rustc_target::abi::{Integer, Size, TargetDataLayout};
|
||||||
|
@ -32,6 +30,12 @@ pub struct Discr<'tcx> {
|
||||||
pub ty: Ty<'tcx>,
|
pub ty: Ty<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum NotUniqueParam<'tcx> {
|
||||||
|
DuplicateParam(ty::GenericArg<'tcx>),
|
||||||
|
NotParam(ty::GenericArg<'tcx>),
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> fmt::Display for Discr<'tcx> {
|
impl<'tcx> fmt::Display for Discr<'tcx> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self.ty.kind() {
|
match *self.ty.kind() {
|
||||||
|
@ -49,8 +53,8 @@ impl<'tcx> fmt::Display for Discr<'tcx> {
|
||||||
|
|
||||||
fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) {
|
fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) {
|
||||||
let (int, signed) = match *ty.kind() {
|
let (int, signed) = match *ty.kind() {
|
||||||
Int(ity) => (Integer::from_int_ty(&tcx, ity), true),
|
ty::Int(ity) => (Integer::from_int_ty(&tcx, ity), true),
|
||||||
Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false),
|
ty::Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false),
|
||||||
_ => bug!("non integer discriminant"),
|
_ => bug!("non integer discriminant"),
|
||||||
};
|
};
|
||||||
(int.size(), signed)
|
(int.size(), signed)
|
||||||
|
@ -176,7 +180,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
if let ty::Adt(def, substs) = *ty.kind() {
|
if let ty::Adt(def, substs) = *ty.kind() {
|
||||||
for field in def.all_fields() {
|
for field in def.all_fields() {
|
||||||
let field_ty = field.ty(self, substs);
|
let field_ty = field.ty(self, substs);
|
||||||
if let Error(_) = field_ty.kind() {
|
if let ty::Error(_) = field_ty.kind() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,7 +315,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
let (mut a, mut b) = (source, target);
|
let (mut a, mut b) = (source, target);
|
||||||
loop {
|
loop {
|
||||||
match (&a.kind(), &b.kind()) {
|
match (&a.kind(), &b.kind()) {
|
||||||
(&Adt(a_def, a_substs), &Adt(b_def, b_substs))
|
(&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
|
||||||
if a_def == b_def && a_def.is_struct() =>
|
if a_def == b_def && a_def.is_struct() =>
|
||||||
{
|
{
|
||||||
if let Some(f) = a_def.non_enum_variant().fields.last() {
|
if let Some(f) = a_def.non_enum_variant().fields.last() {
|
||||||
|
@ -321,7 +325,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Tuple(a_tys), &Tuple(b_tys)) if a_tys.len() == b_tys.len() => {
|
(&ty::Tuple(a_tys), &ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() => {
|
||||||
if let Some(&a_last) = a_tys.last() {
|
if let Some(&a_last) = a_tys.last() {
|
||||||
a = a_last;
|
a = a_last;
|
||||||
b = *b_tys.last().unwrap();
|
b = *b_tys.last().unwrap();
|
||||||
|
@ -427,7 +431,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
.filter(|&(_, k)| {
|
.filter(|&(_, k)| {
|
||||||
match k.unpack() {
|
match k.unpack() {
|
||||||
GenericArgKind::Lifetime(region) => match region.kind() {
|
GenericArgKind::Lifetime(region) => match region.kind() {
|
||||||
ReEarlyBound(ref ebr) => {
|
ty::ReEarlyBound(ref ebr) => {
|
||||||
!impl_generics.region_param(ebr, self).pure_wrt_drop
|
!impl_generics.region_param(ebr, self).pure_wrt_drop
|
||||||
}
|
}
|
||||||
// Error: not a region param
|
// Error: not a region param
|
||||||
|
@ -453,6 +457,49 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether each generic argument is simply a unique generic parameter.
|
||||||
|
pub fn uses_unique_generic_params(
|
||||||
|
self,
|
||||||
|
substs: SubstsRef<'tcx>,
|
||||||
|
ignore_regions: bool,
|
||||||
|
) -> Result<(), NotUniqueParam<'tcx>> {
|
||||||
|
let mut seen = GrowableBitSet::default();
|
||||||
|
for arg in substs {
|
||||||
|
match arg.unpack() {
|
||||||
|
GenericArgKind::Lifetime(lt) => {
|
||||||
|
if !ignore_regions {
|
||||||
|
match lt.kind() {
|
||||||
|
ty::ReEarlyBound(p) => {
|
||||||
|
if !seen.insert(p.index) {
|
||||||
|
return Err(NotUniqueParam::DuplicateParam(lt.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(NotUniqueParam::NotParam(lt.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GenericArgKind::Type(t) => match t.kind() {
|
||||||
|
ty::Param(p) => {
|
||||||
|
if !seen.insert(p.index) {
|
||||||
|
return Err(NotUniqueParam::DuplicateParam(t.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(NotUniqueParam::NotParam(t.into())),
|
||||||
|
},
|
||||||
|
GenericArgKind::Const(c) => match c.val() {
|
||||||
|
ty::ConstKind::Param(p) => {
|
||||||
|
if !seen.insert(p.index) {
|
||||||
|
return Err(NotUniqueParam::DuplicateParam(c.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(NotUniqueParam::NotParam(c.into())),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note
|
/// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note
|
||||||
/// that closures have a `DefId`, but the closure *expression* also
|
/// that closures have a `DefId`, but the closure *expression* also
|
||||||
/// has a `HirId` that is located within the context where the
|
/// has a `HirId` that is located within the context where the
|
||||||
|
@ -594,30 +641,33 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
|
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bound_type_of(self, def_id: DefId) -> EarlyBinder<Ty<'tcx>> {
|
pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder<Ty<'tcx>> {
|
||||||
EarlyBinder(self.type_of(def_id))
|
ty::EarlyBinder(self.type_of(def_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bound_fn_sig(self, def_id: DefId) -> EarlyBinder<ty::PolyFnSig<'tcx>> {
|
pub fn bound_fn_sig(self, def_id: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'tcx>> {
|
||||||
EarlyBinder(self.fn_sig(def_id))
|
ty::EarlyBinder(self.fn_sig(def_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bound_impl_trait_ref(self, def_id: DefId) -> Option<EarlyBinder<ty::TraitRef<'tcx>>> {
|
pub fn bound_impl_trait_ref(
|
||||||
self.impl_trait_ref(def_id).map(|i| EarlyBinder(i))
|
self,
|
||||||
|
def_id: DefId,
|
||||||
|
) -> Option<ty::EarlyBinder<ty::TraitRef<'tcx>>> {
|
||||||
|
self.impl_trait_ref(def_id).map(|i| ty::EarlyBinder(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bound_explicit_item_bounds(
|
pub fn bound_explicit_item_bounds(
|
||||||
self,
|
self,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
) -> EarlyBinder<&'tcx [(ty::Predicate<'tcx>, rustc_span::Span)]> {
|
) -> ty::EarlyBinder<&'tcx [(ty::Predicate<'tcx>, rustc_span::Span)]> {
|
||||||
EarlyBinder(self.explicit_item_bounds(def_id))
|
ty::EarlyBinder(self.explicit_item_bounds(def_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bound_item_bounds(
|
pub fn bound_item_bounds(
|
||||||
self,
|
self,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
) -> EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
|
) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
|
||||||
EarlyBinder(self.item_bounds(def_id))
|
ty::EarlyBinder(self.item_bounds(def_id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -930,35 +980,40 @@ impl<'tcx> Ty<'tcx> {
|
||||||
pub fn is_structural_eq_shallow(self, tcx: TyCtxt<'tcx>) -> bool {
|
pub fn is_structural_eq_shallow(self, tcx: TyCtxt<'tcx>) -> bool {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
// Look for an impl of both `PartialStructuralEq` and `StructuralEq`.
|
// Look for an impl of both `PartialStructuralEq` and `StructuralEq`.
|
||||||
Adt(..) => tcx.has_structural_eq_impls(self),
|
ty::Adt(..) => tcx.has_structural_eq_impls(self),
|
||||||
|
|
||||||
// Primitive types that satisfy `Eq`.
|
// Primitive types that satisfy `Eq`.
|
||||||
Bool | Char | Int(_) | Uint(_) | Str | Never => true,
|
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => true,
|
||||||
|
|
||||||
// Composite types that satisfy `Eq` when all of their fields do.
|
// Composite types that satisfy `Eq` when all of their fields do.
|
||||||
//
|
//
|
||||||
// Because this function is "shallow", we return `true` for these composites regardless
|
// Because this function is "shallow", we return `true` for these composites regardless
|
||||||
// of the type(s) contained within.
|
// of the type(s) contained within.
|
||||||
Ref(..) | Array(..) | Slice(_) | Tuple(..) => true,
|
ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true,
|
||||||
|
|
||||||
// Raw pointers use bitwise comparison.
|
// Raw pointers use bitwise comparison.
|
||||||
RawPtr(_) | FnPtr(_) => true,
|
ty::RawPtr(_) | ty::FnPtr(_) => true,
|
||||||
|
|
||||||
// Floating point numbers are not `Eq`.
|
// Floating point numbers are not `Eq`.
|
||||||
Float(_) => false,
|
ty::Float(_) => false,
|
||||||
|
|
||||||
// Conservatively return `false` for all others...
|
// Conservatively return `false` for all others...
|
||||||
|
|
||||||
// Anonymous function types
|
// Anonymous function types
|
||||||
FnDef(..) | Closure(..) | Dynamic(..) | Generator(..) => false,
|
ty::FnDef(..) | ty::Closure(..) | ty::Dynamic(..) | ty::Generator(..) => false,
|
||||||
|
|
||||||
// Generic or inferred types
|
// Generic or inferred types
|
||||||
//
|
//
|
||||||
// FIXME(ecstaticmorse): Maybe we should `bug` here? This should probably only be
|
// FIXME(ecstaticmorse): Maybe we should `bug` here? This should probably only be
|
||||||
// called for known, fully-monomorphized types.
|
// called for known, fully-monomorphized types.
|
||||||
Projection(_) | Opaque(..) | Param(_) | Bound(..) | Placeholder(_) | Infer(_) => false,
|
ty::Projection(_)
|
||||||
|
| ty::Opaque(..)
|
||||||
|
| ty::Param(_)
|
||||||
|
| ty::Bound(..)
|
||||||
|
| ty::Placeholder(_)
|
||||||
|
| ty::Infer(_) => false,
|
||||||
|
|
||||||
Foreign(_) | GeneratorWitness(..) | Error(_) => false,
|
ty::Foreign(_) | ty::GeneratorWitness(..) | ty::Error(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,13 +1029,13 @@ impl<'tcx> Ty<'tcx> {
|
||||||
/// - `&'a *const &'b u8 -> *const &'b u8`
|
/// - `&'a *const &'b u8 -> *const &'b u8`
|
||||||
pub fn peel_refs(self) -> Ty<'tcx> {
|
pub fn peel_refs(self) -> Ty<'tcx> {
|
||||||
let mut ty = self;
|
let mut ty = self;
|
||||||
while let Ref(_, inner_ty, _) = ty.kind() {
|
while let ty::Ref(_, inner_ty, _) = ty.kind() {
|
||||||
ty = *inner_ty;
|
ty = *inner_ty;
|
||||||
}
|
}
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outer_exclusive_binder(self) -> DebruijnIndex {
|
pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
|
||||||
self.0.outer_exclusive_binder
|
self.0.outer_exclusive_binder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1177,8 +1232,8 @@ pub struct AlwaysRequiresDrop;
|
||||||
/// with their underlying types.
|
/// with their underlying types.
|
||||||
pub fn normalize_opaque_types<'tcx>(
|
pub fn normalize_opaque_types<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
val: &'tcx List<ty::Predicate<'tcx>>,
|
val: &'tcx ty::List<ty::Predicate<'tcx>>,
|
||||||
) -> &'tcx List<ty::Predicate<'tcx>> {
|
) -> &'tcx ty::List<ty::Predicate<'tcx>> {
|
||||||
let mut visitor = OpaqueTypeExpander {
|
let mut visitor = OpaqueTypeExpander {
|
||||||
seen_opaque_tys: FxHashSet::default(),
|
seen_opaque_tys: FxHashSet::default(),
|
||||||
expanded_cache: FxHashMap::default(),
|
expanded_cache: FxHashMap::default(),
|
||||||
|
|
|
@ -5,10 +5,9 @@ use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::struct_span_err;
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_index::bit_set::GrowableBitSet;
|
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_middle::ty::subst::GenericArgKind;
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
|
use rustc_middle::ty::subst::InternalSubsts;
|
||||||
use rustc_middle::ty::{self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeVisitor};
|
use rustc_middle::ty::{self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeVisitor};
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
use rustc_span::def_id::{DefId, LocalDefId};
|
use rustc_span::def_id::{DefId, LocalDefId};
|
||||||
|
@ -325,51 +324,6 @@ fn emit_orphan_check_error<'tcx>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct AreUniqueParamsVisitor {
|
|
||||||
seen: GrowableBitSet<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
enum NotUniqueParam<'tcx> {
|
|
||||||
DuplicateParam(GenericArg<'tcx>),
|
|
||||||
NotParam(GenericArg<'tcx>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> TypeVisitor<'tcx> for AreUniqueParamsVisitor {
|
|
||||||
type BreakTy = NotUniqueParam<'tcx>;
|
|
||||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
|
||||||
match t.kind() {
|
|
||||||
ty::Param(p) => {
|
|
||||||
if self.seen.insert(p.index) {
|
|
||||||
ControlFlow::CONTINUE
|
|
||||||
} else {
|
|
||||||
ControlFlow::Break(NotUniqueParam::DuplicateParam(t.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => ControlFlow::Break(NotUniqueParam::NotParam(t.into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
|
||||||
// We don't drop candidates during candidate assembly because of region
|
|
||||||
// constraints, so the behavior for impls only constrained by regions
|
|
||||||
// will not change.
|
|
||||||
ControlFlow::CONTINUE
|
|
||||||
}
|
|
||||||
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
|
||||||
match c.val() {
|
|
||||||
ty::ConstKind::Param(p) => {
|
|
||||||
if self.seen.insert(p.index) {
|
|
||||||
ControlFlow::CONTINUE
|
|
||||||
} else {
|
|
||||||
ControlFlow::Break(NotUniqueParam::DuplicateParam(c.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => ControlFlow::Break(NotUniqueParam::NotParam(c.into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lint impls of auto traits if they are likely to have
|
/// Lint impls of auto traits if they are likely to have
|
||||||
/// unsound or surprising effects on auto impls.
|
/// unsound or surprising effects on auto impls.
|
||||||
fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) {
|
fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) {
|
||||||
|
@ -400,9 +354,9 @@ fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDef
|
||||||
// Impls which completely cover a given root type are fine as they
|
// Impls which completely cover a given root type are fine as they
|
||||||
// disable auto impls entirely. So only lint if the substs
|
// disable auto impls entirely. So only lint if the substs
|
||||||
// are not a permutation of the identity substs.
|
// are not a permutation of the identity substs.
|
||||||
match substs.visit_with(&mut AreUniqueParamsVisitor::default()) {
|
match tcx.uses_unique_generic_params(substs, true) {
|
||||||
ControlFlow::Continue(()) => {} // ok
|
Ok(()) => {} // ok
|
||||||
ControlFlow::Break(arg) => {
|
Err(arg) => {
|
||||||
// Ideally:
|
// Ideally:
|
||||||
//
|
//
|
||||||
// - compute the requirements for the auto impl candidate
|
// - compute the requirements for the auto impl candidate
|
||||||
|
@ -444,10 +398,10 @@ fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDef
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
match arg {
|
match arg {
|
||||||
NotUniqueParam::DuplicateParam(arg) => {
|
ty::util::NotUniqueParam::DuplicateParam(arg) => {
|
||||||
err.note(&format!("`{}` is mentioned multiple times", arg));
|
err.note(&format!("`{}` is mentioned multiple times", arg));
|
||||||
}
|
}
|
||||||
NotUniqueParam::NotParam(arg) => {
|
ty::util::NotUniqueParam::NotParam(arg) => {
|
||||||
err.note(&format!("`{}` is not a generic parameter", arg));
|
err.note(&format!("`{}` is not a generic parameter", arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue