diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index a81df02f023fc..44cbd557ede96 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -36,7 +36,7 @@ use super::compare_impl_item::check_type_bounds; use super::*; use crate::check::wfcheck::{ check_associated_item, check_trait_item, check_variances_for_type_defn, check_where_clauses, - enter_wf_checking_ctxt, + enter_wf_checking_ctxt, enter_wf_checking_ctxt_without_checking_global_bounds, }; fn add_abi_diag_help(abi: ExternAbi, diag: &mut Diag<'_, T>) { @@ -937,10 +937,11 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); check_type_alias_type_params_are_used(tcx, def_id); + + let ty = tcx.type_of(def_id).instantiate_identity(); + let span = tcx.def_span(def_id); if tcx.type_alias_is_lazy(def_id) { res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| { - let ty = tcx.type_of(def_id).instantiate_identity(); - let span = tcx.def_span(def_id); let item_ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(def_id)), ty); wfcx.register_wf_obligation( span, @@ -951,6 +952,24 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Ok(()) })); check_variances_for_type_defn(tcx, def_id); + } else { + res = res.and(enter_wf_checking_ctxt_without_checking_global_bounds( + tcx, + def_id, + |wfcx| { + // HACK: We sometimes incidentally check that const arguments + // have the correct type as a side effect of the anon const + // desugaring. To make this "consistent" for users we explicitly + // check `ConstArgHasType` clauses so that const args that don't + // go through an anon const still have their types checked. + // + // We use the unnormalized type as this mirrors the behaviour + // that we previously would have had when all const arguments + // were anon consts. + wfcx.register_const_arg_has_type_obligations_from_wf(span, ty); + Ok(()) + }, + )); } // Only `Node::Item` and `Node::ForeignItem` still have HIR based diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 16f5222f621cf..dc08f0d3922ee 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -123,6 +123,29 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ty::ClauseKind::WellFormed(term), )); } + + pub(super) fn register_const_arg_has_type_obligations_from_wf(&self, span: Span, ty: Ty<'tcx>) { + let Some(wf_obligations) = traits::wf::unnormalized_obligations( + self.ocx.infcx, + self.param_env, + ty.into(), + span, + self.body_def_id, + ) else { + return; + }; + + let const_arg_has_type_clauses = + wf_obligations.into_iter().filter(|o| match o.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) + if matches!(ct.kind(), ty::ConstKind::Param(..)) => + { + true + } + _ => false, + }); + self.ocx.register_obligations(const_arg_has_type_clauses); + } } pub(super) fn enter_wf_checking_ctxt<'tcx, F>( @@ -130,6 +153,29 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( body_def_id: LocalDefId, f: F, ) -> Result<(), ErrorGuaranteed> +where + F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> Result<(), ErrorGuaranteed>, +{ + enter_wf_checking_ctxt_inner(tcx, body_def_id, !tcx.features().trivial_bounds(), f) +} + +pub(super) fn enter_wf_checking_ctxt_without_checking_global_bounds<'tcx, F>( + tcx: TyCtxt<'tcx>, + body_def_id: LocalDefId, + f: F, +) -> Result<(), ErrorGuaranteed> +where + F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> Result<(), ErrorGuaranteed>, +{ + enter_wf_checking_ctxt_inner(tcx, body_def_id, false, f) +} + +pub(super) fn enter_wf_checking_ctxt_inner<'tcx, F>( + tcx: TyCtxt<'tcx>, + body_def_id: LocalDefId, + check_false_global_bounds: bool, + f: F, +) -> Result<(), ErrorGuaranteed> where F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> Result<(), ErrorGuaranteed>, { @@ -139,7 +185,7 @@ where let mut wfcx = WfCheckingCtxt { ocx, body_def_id, param_env }; - if !tcx.features().trivial_bounds() { + if check_false_global_bounds { wfcx.check_false_global_bounds() } f(&mut wfcx)?; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 394095508393c..fe685be98a788 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -3,12 +3,13 @@ //! well formed is performed elsewhere (e.g. during type checking or item well formedness //! checking). +use core::ops::ControlFlow; use std::iter; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; -use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; +use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, PredicateObligations}; use rustc_middle::bug; use rustc_middle::ty::{ self, GenericArgsRef, Term, TermKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, @@ -562,7 +563,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { debug!(?self.out); } - #[instrument(level = "debug", skip(self))] fn nominal_obligations( &mut self, def_id: DefId, @@ -604,69 +604,29 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .collect() } - fn add_wf_preds_for_dyn_ty( + fn nominal_obligations_not_referencing_self( &mut self, - ty: Ty<'tcx>, - data: &'tcx ty::List>, - region: ty::Region<'tcx>, - ) { - // Imagine a type like this: - // - // trait Foo { } - // trait Bar<'c> : 'c { } - // - // &'b (Foo+'c+Bar<'d>) - // ^ - // - // In this case, the following relationships must hold: - // - // 'b <= 'c - // 'd <= 'c - // - // The first conditions is due to the normal region pointer - // rules, which say that a reference cannot outlive its - // referent. - // - // The final condition may be a bit surprising. In particular, - // you may expect that it would have been `'c <= 'd`, since - // usually lifetimes of outer things are conservative - // approximations for inner things. However, it works somewhat - // differently with trait objects: here the idea is that if the - // user specifies a region bound (`'c`, in this case) it is the - // "master bound" that *implies* that bounds from other traits are - // all met. (Remember that *all bounds* in a type like - // `Foo+Bar+Zed` must be met, not just one, hence if we write - // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and - // 'y.) - // - // Note: in fact we only permit builtin traits, not `Bar<'d>`, I - // am looking forward to the future here. - if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { - let implicit_bounds = object_region_bounds(self.tcx(), data); - - let explicit_bound = region; - - self.out.reserve(implicit_bounds.len()); - for implicit_bound in implicit_bounds { - let cause = self.cause(ObligationCauseCode::ObjectTypeBound(ty, explicit_bound)); - let outlives = - ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - self.param_env, - outlives, - )); + trait_id_and_args: ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>, + ) -> impl Iterator> + use<'tcx> { + let tcx = self.tcx(); + let def_id = trait_id_and_args.skip_binder().def_id; + let bogus_self_ty: Ty<'tcx> = + Ty::new_param(tcx, u32::MAX, rustc_span::sym::RustaceansAreAwesome); + let args = trait_id_and_args.skip_binder().with_self_ty(tcx, bogus_self_ty).args; + + struct HasBogusSelfTy<'tcx>(Ty<'tcx>); + impl<'tcx> TypeVisitor> for HasBogusSelfTy<'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { + // FIXME: should this check be post normalization rather than quite so syntactic + if ty == self.0 { ControlFlow::Break(()) } else { ty.super_visit_with(self) } } - - // We don't add any wf predicates corresponding to the trait ref's generic arguments - // which allows code like this to compile: - // ```rust - // trait Trait {} - // fn foo(_: &dyn Trait<[u32]>) {} - // ``` } + + self.nominal_obligations(def_id, args).into_iter().filter(move |o| { + o.predicate.visit_with(&mut HasBogusSelfTy(bogus_self_ty)).is_continue() + }) } fn add_wf_preds_for_pat_ty(&mut self, base_ty: Ty<'tcx>, pat: ty::Pattern<'tcx>) { @@ -932,24 +892,103 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // We recurse into the binder below. } - ty::Dynamic(data, r) => { - // WfObject + ty::Dynamic(data, region) => { + // Imagine a type like this: + // + // trait Foo { } + // trait Bar<'c> : 'c { } + // + // &'b (Foo+'c+Bar<'d>) + // ^ + // + // In this case, the following relationships must hold: + // + // 'b <= 'c + // 'd <= 'c + // + // The first conditions is due to the normal region pointer + // rules, which say that a reference cannot outlive its + // referent. // - // Here, we defer WF checking due to higher-ranked - // regions. This is perhaps not ideal. - self.add_wf_preds_for_dyn_ty(t, data, r); + // The final condition may be a bit surprising. In particular, + // you may expect that it would have been `'c <= 'd`, since + // usually lifetimes of outer things are conservative + // approximations for inner things. However, it works somewhat + // differently with trait objects: here the idea is that if the + // user specifies a region bound (`'c`, in this case) it is the + // "master bound" that *implies* that bounds from other traits are + // all met. (Remember that *all bounds* in a type like + // `Foo+Bar+Zed` must be met, not just one, hence if we write + // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and + // 'y.) + // + // Note: in fact we only permit builtin traits, not `Bar<'d>`, I + // am looking forward to the future here. + if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { + let implicit_bounds = object_region_bounds(self.tcx(), data); + + let explicit_bound = region; + + self.out.reserve(implicit_bounds.len()); + for implicit_bound in implicit_bounds { + let cause = + self.cause(ObligationCauseCode::ObjectTypeBound(t, explicit_bound)); + let outlives = ty::Binder::dummy(ty::OutlivesPredicate( + explicit_bound, + implicit_bound, + )); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + outlives, + )); + } + } // FIXME(#27579) RFC also considers adding trait // obligations that don't refer to Self and // checking those - if let Some(principal) = data.principal_def_id() { + if let Some(principal) = data.principal() { + let principal_def_id = principal.skip_binder().def_id; self.out.push(traits::Obligation::with_depth( tcx, self.cause(ObligationCauseCode::WellFormed(None)), self.recursion_depth, self.param_env, - ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal)), + ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal_def_id)), )); + + // For the most part we don't add wf predicates corresponding to + // the trait ref's generic arguments which allows code like this + // to compile: + // ```rust + // trait Trait {} + // fn foo(_: &dyn Trait<[u32]>) {} + // ``` + // + // However, we sometimes incidentally check that const arguments + // have the correct type as a side effect of the anon const + // desugaring. To make this "consistent" for users we explicitly + // check `ConstArgHasType` clauses so that const args that don't + // go through an anon const still have their types checked. + let wf_obligations = self.nominal_obligations_not_referencing_self(principal); + let const_arg_has_type_clauses = + wf_obligations.filter(|o| match o.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) + if matches!(ct.kind(), ty::ConstKind::Param(..)) => + { + debug!("skipped o: {:?}", o); + true + } + _ => false, + }); + + let collected = const_arg_has_type_clauses.collect::>(); + debug!("{:?}", collected); + + self.out.extend(collected); } } diff --git a/tests/ui/const-generics/check_const_arg_type_in_free_alias.eager.stderr b/tests/ui/const-generics/check_const_arg_type_in_free_alias.eager.stderr new file mode 100644 index 0000000000000..37f9b0d2adc2c --- /dev/null +++ b/tests/ui/const-generics/check_const_arg_type_in_free_alias.eager.stderr @@ -0,0 +1,105 @@ +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:15:1 + | +LL | type ArrLen = [(); B]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[(); B]` must be type `usize` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:19:1 + | +LL | type ConstArg = Foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required by a const generic parameter in `Foo` + --> $DIR/check_const_arg_type_in_free_alias.rs:13:12 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:32:1 + | +LL | type Alias = <() as IdentityWithUnused>::This; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required by a const generic parameter in `IdentityWithUnused` + --> $DIR/check_const_arg_type_in_free_alias.rs:24:26 + | +LL | trait IdentityWithUnused { + | ^^^^^^^^^^^^^^ required by this const generic parameter in `IdentityWithUnused` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:38:1 + | +LL | type UseFree = Free; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[(); B]` must be type `usize` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:58:1 + | +LL | type IndirectArr = Wrap>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[(); B]` must be type `usize` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:62:1 + | +LL | type IndirectConstArg = Wrap>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required by a const generic parameter in `Foo` + --> $DIR/check_const_arg_type_in_free_alias.rs:13:12 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:17:24 + | +LL | type AnonArrLen = [(); true]; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:21:25 + | +LL | type AnonConstArg = Foo; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:34:44 + | +LL | type AnonAlias = <() as IdentityWithUnused>::This; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:40:25 + | +LL | type AnonUseFree = Free; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:53:45 + | +LL | type AnonUseFreeIndirectlyCorrect = UseFree<1_usize>; + | ^^^^^^^ expected `bool`, found `usize` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:60:39 + | +LL | type AnonIndirectArr = Wrap>; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:64:43 + | +LL | type AnonIndirectConstArg = Wrap>>; + | ^^^^ expected `usize`, found `bool` + +error: aborting due to 13 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/check_const_arg_type_in_free_alias.lazy.stderr b/tests/ui/const-generics/check_const_arg_type_in_free_alias.lazy.stderr new file mode 100644 index 0000000000000..72f7a33b6c3be --- /dev/null +++ b/tests/ui/const-generics/check_const_arg_type_in_free_alias.lazy.stderr @@ -0,0 +1,123 @@ +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:15:1 + | +LL | type ArrLen = [(); B]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[(); B]` must be type `usize` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:19:1 + | +LL | type ConstArg = Foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required by a const generic parameter in `Foo` + --> $DIR/check_const_arg_type_in_free_alias.rs:13:12 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:32:1 + | +LL | type Alias = <() as IdentityWithUnused>::This; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required for `()` to implement `IdentityWithUnused` + --> $DIR/check_const_arg_type_in_free_alias.rs:28:25 + | +LL | impl IdentityWithUnused for T { + | -------------- ^^^^^^^^^^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:38:1 + | +LL | type UseFree = Free; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required by a bound on the type alias `Free` + --> $DIR/check_const_arg_type_in_free_alias.rs:37:11 + | +LL | type Free = [(); N]; + | ^^^^^^^^^^^^^^ required by this bound + +error: the constant `N` is not of type `bool` + --> $DIR/check_const_arg_type_in_free_alias.rs:51:1 + | +LL | type UseFreeIndirectlyCorrect = UseFree; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `usize` + | +note: required by a bound on the type alias `UseFree` + --> $DIR/check_const_arg_type_in_free_alias.rs:38:14 + | +LL | type UseFree = Free; + | ^^^^^^^^^^^^^ required by this bound + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:58:1 + | +LL | type IndirectArr = Wrap>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[(); B]` must be type `usize` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_free_alias.rs:62:1 + | +LL | type IndirectConstArg = Wrap>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required by a const generic parameter in `Foo` + --> $DIR/check_const_arg_type_in_free_alias.rs:13:12 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:17:24 + | +LL | type AnonArrLen = [(); true]; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:21:25 + | +LL | type AnonConstArg = Foo; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:34:44 + | +LL | type AnonAlias = <() as IdentityWithUnused>::This; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:40:25 + | +LL | type AnonUseFree = Free; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:53:45 + | +LL | type AnonUseFreeIndirectlyCorrect = UseFree<1_usize>; + | ^^^^^^^ expected `bool`, found `usize` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:60:39 + | +LL | type AnonIndirectArr = Wrap>; + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_free_alias.rs:64:43 + | +LL | type AnonIndirectConstArg = Wrap>>; + | ^^^^ expected `usize`, found `bool` + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/check_const_arg_type_in_free_alias.rs b/tests/ui/const-generics/check_const_arg_type_in_free_alias.rs new file mode 100644 index 0000000000000..12e277f0c545a --- /dev/null +++ b/tests/ui/const-generics/check_const_arg_type_in_free_alias.rs @@ -0,0 +1,67 @@ +//@ revisions: eager lazy +#![cfg_attr(lazy, feature(lazy_type_alias))] +#![cfg_attr(lazy, expect(incomplete_features))] + +// We currently do not check free type aliases for well formedness. +// However, previously we desugared all const arguments to anon +// consts which resulted in us happening to check that they were +// of the correct type. Nowadays we don't necessarily lower to a +// const argument, but to continue erroring on such code we special +// case `ConstArgHasType` clauses to be checked for free type aliases +// even though we ignore the rest of the wf requirements. + +struct Foo; + +type ArrLen = [(); B]; +//~^ ERROR: the constant `B` is not of type +type AnonArrLen = [(); true]; +//~^ ERROR: mismatched types +type ConstArg = Foo; +//~^ ERROR: the constant `B` is not of type +type AnonConstArg = Foo; +//~^ ERROR: mismatched types + +trait IdentityWithUnused { + type This; +} + +impl IdentityWithUnused for T { + type This = T; +} + +type Alias = <() as IdentityWithUnused>::This; +//~^ ERROR: the constant `B` is not of type +type AnonAlias = <() as IdentityWithUnused>::This; +//~^ ERROR: mismatched types + +type Free = [(); N]; +type UseFree = Free; +//~^ ERROR: the constant `B` is not of type +type AnonUseFree = Free; +//~^ ERROR: mismatched types + +// This previously emitted an error before we stopped using +// anon consts. Now, as free aliases don't exist after ty +// lowering, we don't emit an error because we only see `N` +// being used as an argument to an array length. +// +// Free type aliases are not allowed to have unused generic +// parameters so this shouldn't be able to cause code to +// pass that should error. +type UseFreeIndirectlyCorrect = UseFree; +//[lazy]~^ ERROR: the constant `N` is not of type +type AnonUseFreeIndirectlyCorrect = UseFree<1_usize>; +//~^ ERROR: mismatched types + +struct Wrap(T); + +type IndirectArr = Wrap>; +//~^ ERROR: the constant `B` is not of type +type AnonIndirectArr = Wrap>; +//~^ ERROR: mismatched types +type IndirectConstArg = Wrap>>; +//~^ ERROR: the constant `B` is not of type +type AnonIndirectConstArg = Wrap>>; +//~^ ERROR: mismatched types + +fn main() {} diff --git a/tests/ui/const-generics/check_const_arg_type_in_trait_object.rs b/tests/ui/const-generics/check_const_arg_type_in_trait_object.rs new file mode 100644 index 0000000000000..a451d155ccd8e --- /dev/null +++ b/tests/ui/const-generics/check_const_arg_type_in_trait_object.rs @@ -0,0 +1,34 @@ +// We currently do not check trait objects for well formedness. +// However, previously we desugared all const arguments to anon +// consts which resulted in us happening to check that they were +// of the correct type. Nowadays we don't necessarily lower to a +// const argument, but to continue erroring on such code we special +// case `ConstArgHasType` clauses to be checked for trait objects +// even though we ignore the rest of the wf requirements. + +trait Object {} +trait Object2 {} + +struct Wrap(T); + +fn arg< + const B: bool, + const N: usize, +>( + param: &dyn Object, + //~^ ERROR: the constant `B` is not of type + anon: &dyn Object, + //~^ ERROR: mismatched types +) {} + +fn indirect< + const B: bool, + const N: usize, +>( + param: &dyn Object2>, + //~^ ERROR: the constant `B` is not of type + anon: &dyn Object2>, + //~^ ERROR: mismatched types +) {} + +fn main() {} diff --git a/tests/ui/const-generics/check_const_arg_type_in_trait_object.stderr b/tests/ui/const-generics/check_const_arg_type_in_trait_object.stderr new file mode 100644 index 0000000000000..d3335a7472987 --- /dev/null +++ b/tests/ui/const-generics/check_const_arg_type_in_trait_object.stderr @@ -0,0 +1,35 @@ +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_trait_object.rs:18:12 + | +LL | param: &dyn Object, + | ^^^^^^^^^^^^^^ expected `usize`, found `bool` + | +note: required by a const generic parameter in `Object` + --> $DIR/check_const_arg_type_in_trait_object.rs:9:14 + | +LL | trait Object {} + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Object` + +error: the constant `B` is not of type `usize` + --> $DIR/check_const_arg_type_in_trait_object.rs:28:12 + | +LL | param: &dyn Object2>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[(); B]` must be type `usize` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_trait_object.rs:20:23 + | +LL | anon: &dyn Object, + | ^^^^ expected `usize`, found `bool` + +error[E0308]: mismatched types + --> $DIR/check_const_arg_type_in_trait_object.rs:30:34 + | +LL | anon: &dyn Object2>, + | ^^^^ expected `usize`, found `bool` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`.