Auto merge of #123240 - compiler-errors:assert-args-compat, r=fmease

Assert that args are actually compatible with their generics, rather than just their count

Right now we just check that the number of args is right, rather than actually checking the kinds. Uplift a helper fn that I wrote from trait selection to do just that. Found a couple bugs along the way.

r? `@lcnr` or `@fmease` (or anyone really lol)
This commit is contained in:
bors 2024-04-04 00:09:02 +00:00
commit b4acbe4233
10 changed files with 151 additions and 114 deletions

View File

@ -6,9 +6,10 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{DynKind, ToPredicate};
use rustc_span::Span;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations};
@ -228,12 +229,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if arg == dummy_self.into() {
let param = &generics.params[index];
missing_type_params.push(param.name);
return Ty::new_misc_error(tcx).into();
Ty::new_misc_error(tcx).into()
} else if arg.walk().any(|arg| arg == dummy_self.into()) {
references_self = true;
return Ty::new_misc_error(tcx).into();
let guar = tcx.dcx().span_delayed_bug(
span,
"trait object trait bounds reference `Self`",
);
replace_dummy_self_with_error(tcx, arg, guar)
} else {
arg
}
arg
})
.collect();
let args = tcx.mk_args(&args);
@ -288,18 +294,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let guar = tcx
.dcx()
.span_delayed_bug(span, "trait object projection bounds reference `Self`");
let args: Vec<_> = b
.projection_ty
.args
.iter()
.map(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return Ty::new_error(tcx, guar).into();
}
arg
})
.collect();
b.projection_ty.args = tcx.mk_args(&args);
b.projection_ty = replace_dummy_self_with_error(tcx, b.projection_ty, guar);
}
ty::ExistentialProjection::erase_self_ty(tcx, b)
@ -357,3 +352,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ty::new_dynamic(tcx, existential_predicates, region_bound, representation)
}
}
fn replace_dummy_self_with_error<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
tcx: TyCtxt<'tcx>,
t: T,
guar: ErrorGuaranteed,
) -> T {
t.fold_with(&mut BottomUpFolder {
tcx,
ty_op: |ty| {
if ty == tcx.types.trait_object_dummy_self { Ty::new_error(tcx, guar) } else { ty }
},
lt_op: |lt| lt,
ct_op: |ct| ct,
})
}

View File

@ -1961,33 +1961,104 @@ impl<'tcx> TyCtxt<'tcx> {
if pred.kind() != binder { self.mk_predicate(binder) } else { pred }
}
pub fn check_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) -> bool {
self.check_args_compatible_inner(def_id, args, false)
}
fn check_args_compatible_inner(
self,
def_id: DefId,
args: &'tcx [ty::GenericArg<'tcx>],
nested: bool,
) -> bool {
let generics = self.generics_of(def_id);
// IATs themselves have a weird arg setup (self + own args), but nested items *in* IATs
// (namely: opaques, i.e. ATPITs) do not.
let own_args = if !nested
&& let DefKind::AssocTy = self.def_kind(def_id)
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id))
{
if generics.params.len() + 1 != args.len() {
return false;
}
if !matches!(args[0].unpack(), ty::GenericArgKind::Type(_)) {
return false;
}
&args[1..]
} else {
if generics.count() != args.len() {
return false;
}
let (parent_args, own_args) = args.split_at(generics.parent_count);
if let Some(parent) = generics.parent
&& !self.check_args_compatible_inner(parent, parent_args, true)
{
return false;
}
own_args
};
for (param, arg) in std::iter::zip(&generics.params, own_args) {
match (&param.kind, arg.unpack()) {
(ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
| (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
| (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
_ => return false,
}
}
true
}
/// With `cfg(debug_assertions)`, assert that args are compatible with their generics,
/// and print out the args if not.
pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) {
if cfg!(debug_assertions) {
if !self.check_args_compatible(def_id, args) {
if let DefKind::AssocTy = self.def_kind(def_id)
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id))
{
bug!(
"args not compatible with generics for {}: args={:#?}, generics={:#?}",
self.def_path_str(def_id),
args,
// Make `[Self, GAT_ARGS...]` (this could be simplified)
self.mk_args_from_iter(
[self.types.self_param.into()].into_iter().chain(
self.generics_of(def_id)
.own_args(ty::GenericArgs::identity_for_item(self, def_id))
.iter()
.copied()
)
)
);
} else {
bug!(
"args not compatible with generics for {}: args={:#?}, generics={:#?}",
self.def_path_str(def_id),
args,
ty::GenericArgs::identity_for_item(self, def_id)
);
}
}
}
}
#[inline(always)]
pub(crate) fn check_and_mk_args(
self,
_def_id: DefId,
def_id: DefId,
args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
) -> GenericArgsRef<'tcx> {
let args = args.into_iter().map(Into::into);
#[cfg(debug_assertions)]
{
let generics = self.generics_of(_def_id);
let n = if let DefKind::AssocTy = self.def_kind(_def_id)
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id))
{
// If this is an inherent projection.
generics.params.len() + 1
} else {
generics.count()
};
assert_eq!(
(n, Some(n)),
args.size_hint(),
"wrong number of generic parameters for {_def_id:?}: {:?}",
args.collect::<Vec<_>>(),
);
}
self.mk_args_from_iter(args)
let args = self.mk_args_from_iter(args.into_iter().map(Into::into));
self.debug_assert_args_compatible(def_id, args);
args
}
#[inline]

View File

@ -1624,13 +1624,7 @@ impl<'tcx> Ty<'tcx> {
#[inline]
pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
debug_assert_eq!(
tcx.generics_of(def.did()).count(),
args.len(),
"wrong number of args for ADT: {:#?} vs {:#?}",
tcx.generics_of(def.did()).params,
args
);
tcx.debug_assert_args_compatible(def.did(), args);
Ty::new(tcx, Adt(def, args))
}
@ -1715,11 +1709,7 @@ impl<'tcx> Ty<'tcx> {
def_id: DefId,
closure_args: GenericArgsRef<'tcx>,
) -> Ty<'tcx> {
debug_assert_eq!(
closure_args.len(),
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 3,
"closure constructed with incorrect generic parameters"
);
tcx.debug_assert_args_compatible(def_id, closure_args);
Ty::new(tcx, Closure(def_id, closure_args))
}
@ -1729,11 +1719,7 @@ impl<'tcx> Ty<'tcx> {
def_id: DefId,
closure_args: GenericArgsRef<'tcx>,
) -> Ty<'tcx> {
debug_assert_eq!(
closure_args.len(),
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5,
"closure constructed with incorrect generic parameters"
);
tcx.debug_assert_args_compatible(def_id, closure_args);
Ty::new(tcx, CoroutineClosure(def_id, closure_args))
}
@ -1743,11 +1729,7 @@ impl<'tcx> Ty<'tcx> {
def_id: DefId,
coroutine_args: GenericArgsRef<'tcx>,
) -> Ty<'tcx> {
debug_assert_eq!(
coroutine_args.len(),
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 6,
"coroutine constructed with incorrect number of generic parameters"
);
tcx.debug_assert_args_compatible(def_id, coroutine_args);
Ty::new(tcx, Coroutine(def_id, coroutine_args))
}

View File

@ -1,4 +1,4 @@
use crate::traits::{check_args_compatible, specialization_graph};
use crate::traits::specialization_graph;
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
use super::assembly::{self, structural_traits, Candidate};
@ -247,7 +247,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
assoc_def.defining_node,
);
if !check_args_compatible(tcx, assoc_def.item, args) {
if !tcx.check_args_compatible(assoc_def.item.def_id, args) {
return error_response(
ecx,
"associated item has mismatched generic item arguments",

View File

@ -61,12 +61,12 @@ pub use self::specialize::{
pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_normalize::StructurallyNormalizeExt;
pub use self::util::elaborate;
pub use self::util::{
check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds,
transitive_bounds_that_define_assoc_item, SupertraitDefIds,
};
pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo};
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
pub use self::util::{
supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item,
SupertraitDefIds,
};
pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
pub use rustc_infer::traits::*;

View File

@ -2,7 +2,6 @@
use std::ops::ControlFlow;
use super::check_args_compatible;
use super::specialization_graph;
use super::translate_args;
use super::util;
@ -2030,7 +2029,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
} else {
ty.map_bound(|ty| ty.into())
};
if !check_args_compatible(tcx, assoc_ty.item, args) {
if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
let err = Ty::new_error_with_message(
tcx,
obligation.cause.span,

View File

@ -344,48 +344,6 @@ pub enum TupleArgumentsFlag {
No,
}
// Verify that the trait item and its implementation have compatible args lists
pub fn check_args_compatible<'tcx>(
tcx: TyCtxt<'tcx>,
assoc_item: ty::AssocItem,
args: ty::GenericArgsRef<'tcx>,
) -> bool {
fn check_args_compatible_inner<'tcx>(
tcx: TyCtxt<'tcx>,
generics: &'tcx ty::Generics,
args: &'tcx [ty::GenericArg<'tcx>],
) -> bool {
if generics.count() != args.len() {
return false;
}
let (parent_args, own_args) = args.split_at(generics.parent_count);
if let Some(parent) = generics.parent
&& let parent_generics = tcx.generics_of(parent)
&& !check_args_compatible_inner(tcx, parent_generics, parent_args)
{
return false;
}
for (param, arg) in std::iter::zip(&generics.params, own_args) {
match (&param.kind, arg.unpack()) {
(ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
| (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
| (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
_ => return false,
}
}
true
}
let generics = tcx.generics_of(assoc_item.def_id);
// Chop off any additional args (RPITIT) args
let args = &args[0..generics.count().min(args.len())];
check_args_compatible_inner(tcx, generics, args)
}
/// Executes `f` on `value` after replacing all escaping bound variables with placeholders
/// and then replaces these placeholders with the original bound variables in the result.
///

View File

@ -7,7 +7,6 @@ use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_span::Span;
use rustc_trait_selection::traits::check_args_compatible;
use crate::errors::{DuplicateArg, NotParam};
@ -250,7 +249,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
ty::GenericArgs::identity_for_item(self.tcx, parent),
);
if check_args_compatible(self.tcx, assoc, impl_args) {
if self.tcx.check_args_compatible(assoc.def_id, impl_args) {
self.tcx
.type_of(assoc.def_id)
.instantiate(self.tcx, impl_args)

View File

@ -14,6 +14,7 @@ impl<'a, I: 'a + Iterable> Iterable for &'a I {
//~^ ERROR binding for associated type `Item` references lifetime `'missing`
//~| ERROR binding for associated type `Item` references lifetime `'missing`
//~| ERROR `()` is not an iterator
//~| WARNING impl trait in impl method signature does not match trait method signature
}
fn main() {}

View File

@ -32,7 +32,24 @@ LL | fn iter(&self) -> impl for<'missing> Iterator<Item = Self::Item<'missin
|
= help: the trait `Iterator` is not implemented for `()`
error: aborting due to 4 previous errors
warning: impl trait in impl method signature does not match trait method signature
--> $DIR/span-bug-issue-121457.rs:13:51
|
LL | fn iter(&self) -> impl Iterator;
| ------------- return type from trait method defined here
...
LL | fn iter(&self) -> impl for<'missing> Iterator<Item = Self::Item<'missing>> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this bound is stronger than that defined on the trait
|
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
= note: `#[warn(refining_impl_trait_reachable)]` on by default
help: replace the return type so that it matches the trait
|
LL | fn iter(&self) -> impl Iterator {}
| ~~~~~~~~~~~~~
error: aborting due to 4 previous errors; 1 warning emitted
Some errors have detailed explanations: E0195, E0277, E0582.
For more information about an error, try `rustc --explain E0195`.