Skip to content

More robustly deal with relaxed bounds and improve their diagnostics #142693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,6 @@ hir_analysis_parenthesized_fn_trait_expansion =
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
.label = not allowed in type signatures

hir_analysis_pointee_sized_trait_object = `PointeeSized` cannot be used with trait objects

hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias
.label = `Self` is not a generic argument, but an alias to the type of the {$what}

Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,6 @@ pub(crate) struct TraitObjectDeclaredWithNoTraits {
pub trait_alias_span: Option<Span>,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_pointee_sized_trait_object)]
pub(crate) struct PointeeSizedTraitObject {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_ambiguous_lifetime_bound, code = E0227)]
pub(crate) struct AmbiguousLifetimeBound {
Expand Down
36 changes: 7 additions & 29 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_hir::{AmbigArg, LangItem, PolyTraitRef};
use rustc_hir::{AmbigArg, PolyTraitRef};
use rustc_middle::bug;
use rustc_middle::ty::{
self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
Expand Down Expand Up @@ -124,13 +124,13 @@ fn collect_sizedness_bounds<'tcx>(
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
span: Span,
) -> CollectedSizednessBounds {
let sized_did = tcx.require_lang_item(LangItem::Sized, span);
let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
let sized = collect_bounds(hir_bounds, self_ty_where_predicates, sized_did);

let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span);
let meta_sized_did = tcx.require_lang_item(hir::LangItem::MetaSized, span);
let meta_sized = collect_bounds(hir_bounds, self_ty_where_predicates, meta_sized_did);

let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span);
let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);
let pointee_sized = collect_bounds(hir_bounds, self_ty_where_predicates, pointee_sized_did);

CollectedSizednessBounds { sized, meta_sized, pointee_sized }
Expand All @@ -151,24 +151,6 @@ fn add_trait_bound<'tcx>(
}

impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// Skip `PointeeSized` bounds.
///
/// `PointeeSized` is a "fake bound" insofar as anywhere a `PointeeSized` bound exists, there
/// is actually the absence of any bounds. This avoids limitations around non-global where
/// clauses being preferred over item bounds (where `PointeeSized` bounds would be
/// proven) - which can result in errors when a `PointeeSized` supertrait/bound/predicate is
/// added to some items.
pub(crate) fn should_skip_sizedness_bound<'hir>(
&self,
bound: &'hir hir::GenericBound<'tcx>,
) -> bool {
bound
.trait_ref()
.and_then(|tr| tr.trait_def_id())
.map(|did| self.tcx().is_lang_item(did, LangItem::PointeeSized))
.unwrap_or(false)
}

/// Adds sizedness bounds to a trait, trait alias, parameter, opaque type or associated type.
///
/// - On parameters, opaque type and associated types, add default `Sized` bound if no explicit
Expand All @@ -193,8 +175,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
return;
}

let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span);
let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span);
let meta_sized_did = tcx.require_lang_item(hir::LangItem::MetaSized, span);
let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);

// If adding sizedness bounds to a trait, then there are some relevant early exits
if let Some(trait_did) = trait_did {
Expand Down Expand Up @@ -244,7 +226,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} else {
// If there are no explicit sizedness bounds on a parameter then add a default
// `Sized` bound.
let sized_did = tcx.require_lang_item(LangItem::Sized, span);
let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
add_trait_bound(tcx, bounds, self_ty, sized_did, span);
}
}
Expand Down Expand Up @@ -476,10 +458,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
'tcx: 'hir,
{
for hir_bound in hir_bounds {
if self.should_skip_sizedness_bound(hir_bound) {
continue;
}

// In order to avoid cycles, when we're lowering `SelfTraitThatDefines`,
// we skip over any traits that don't define the given associated type.
if let PredicateFilter::SelfTraitThatDefines(assoc_ident) = predicate_filter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::LangItem;
use rustc_hir::def::{DefKind, Res};
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan;
Expand Down Expand Up @@ -70,13 +69,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let guar = self.report_trait_object_addition_traits(&regular_traits);
return Ty::new_error(tcx, guar);
}
// We don't support `PointeeSized` principals
Copy link
Member Author

@fmease fmease Jun 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My PR predates #143104.

This commit basically reverts this logic because it's unreachable due to us filtering out PointeeSized inside lower_poly_trait_ref. The diagnostic is definitely worse, we now say at least one trait is required for an object type instead of `PointeeSized` cannot be used with trait objects.

Welp, I guess I should address the diagnostic regression (😩) since it only makes sense if you know the lowering… This kinda ties in with finding a better solution for #142693 (comment)

let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span);
if regular_traits.iter().any(|(pred, _)| pred.def_id() == pointee_sized_did) {
let guar = self.report_pointee_sized_trait_object(span);
return Ty::new_error(tcx, guar);
}

// Don't create a dyn trait if we have errors in the principal.
if let Err(guar) = regular_traits.error_reported() {
return Ty::new_error(tcx, guar);
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use tracing::debug;
use super::InherentAssocCandidate;
use crate::errors::{
self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams,
ParenthesizedFnTraitExpansion, PointeeSizedTraitObject, TraitObjectDeclaredWithNoTraits,
ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
};
use crate::fluent_generated as fluent;
use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
Expand Down Expand Up @@ -1405,10 +1405,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span })
}

pub(super) fn report_pointee_sized_trait_object(&self, span: Span) -> ErrorGuaranteed {
self.dcx().emit_err(PointeeSizedTraitObject { span })
}
}

/// Emit an error for the given associated item constraint.
Expand Down
47 changes: 24 additions & 23 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,13 +758,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
predicate_filter: PredicateFilter,
) -> GenericArgCountResult {
let tcx = self.tcx();

// We use the *resolved* bound vars later instead of the HIR ones since the former
// also include the bound vars of the overarching predicate if applicable.
let hir::PolyTraitRef { bound_generic_params: _, modifiers, ref trait_ref, span } =
*poly_trait_ref;
let hir::TraitBoundModifiers { constness, polarity } = modifiers;

let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());

let (polarity, bounds) = match polarity {
rustc_ast::BoundPolarity::Positive
if tcx.is_lang_item(trait_def_id, hir::LangItem::PointeeSized) =>
{
// Skip `PointeeSized` bounds.
//
// `PointeeSized` is a "fake bound" insofar as anywhere a `PointeeSized` bound exists, there
// is actually the absence of any bounds. This avoids limitations around non-global where
// clauses being preferred over item bounds (where `PointeeSized` bounds would be
// proven) - which can result in errors when a `PointeeSized` supertrait/bound/predicate is
// added to some items.
(ty::PredicatePolarity::Positive, &mut Vec::new())
}
rustc_ast::BoundPolarity::Positive => (ty::PredicatePolarity::Positive, bounds),
rustc_ast::BoundPolarity::Negative(_) => (ty::PredicatePolarity::Negative, bounds),
rustc_ast::BoundPolarity::Maybe(_) => {
(ty::PredicatePolarity::Positive, &mut Vec::new())
}
Comment on lines +786 to +788
Copy link
Member Author

@fmease fmease Jun 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(repost of #142693 (comment) which is on an outdated diff)

I'm not happy about the way this is structured and the fact that we map Maybe to Positive, it's a hack. The non-sentinel approach of using Option<ty::PredicatePolarity> just yields very verbose and still ugly code.

Bigger picture: As you can see a few lines further down we used to duplicate the lower_assoc_item_constraint loop to catch bad bounds like ?Sized<Undefined = ()>. This is obv annoying and not scalable (e.g., for PointeeSized we also need to validate the bound constness which is a lot more code I don't want to dupe / extract into a method (hmmm...)). So the new approach is to do as much normal lowering for these "fake bounds" as possible but send everything into the drain (&mut Vec::new()).

Idk :|

};

let trait_segment = trait_ref.path.segments.last().unwrap();

let _ = self.prohibit_generic_args(
Expand All @@ -781,7 +804,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Some(self_ty),
);

let tcx = self.tcx();
let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id);
debug!(?bound_vars);

Expand All @@ -792,27 +814,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

debug!(?poly_trait_ref);

let polarity = match polarity {
rustc_ast::BoundPolarity::Positive => ty::PredicatePolarity::Positive,
rustc_ast::BoundPolarity::Negative(_) => ty::PredicatePolarity::Negative,
rustc_ast::BoundPolarity::Maybe(_) => {
// Validate associated type at least. We may want to reject these
// outright in the future...
for constraint in trait_segment.args().constraints {
let _ = self.lower_assoc_item_constraint(
trait_ref.hir_ref_id,
poly_trait_ref,
constraint,
&mut Default::default(),
&mut Default::default(),
constraint.span,
predicate_filter,
);
}
return arg_count;
}
};

// We deal with const conditions later.
match predicate_filter {
PredicateFilter::All
Expand Down Expand Up @@ -915,7 +916,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Don't register any associated item constraints for negative bounds,
// since we should have emitted an error for them earlier, and they
// would not be well-formed!
if polarity != ty::PredicatePolarity::Positive {
if polarity == ty::PredicatePolarity::Negative {
self.dcx().span_delayed_bug(
constraint.span,
"negative trait bounds should not have assoc item constraints",
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/sized-hierarchy/pointee-validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Test that despite us dropping `PointeeSized` bounds during HIR ty lowering
// we still validate it first.
// issue: <https://github.com/rust-lang/rust/issues/142718>
#![feature(sized_hierarchy)]

use std::marker::PointeeSized;

struct T where (): PointeeSized<(), Undefined = ()>;
//~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied
//~| ERROR associated type `Undefined` not found for `PointeeSized`

const fn test<T, U>() where T: const PointeeSized, U: [const] PointeeSized {}
//~^ ERROR `const` can only be applied to `#[const_trait]` traits
//~| ERROR `const` can only be applied to `#[const_trait]` traits
//~| ERROR const trait impls are experimental
//~| ERROR `[const]` can only be applied to `#[const_trait]` traits
//~| ERROR `[const]` can only be applied to `#[const_trait]` traits
//~| ERROR const trait impls are experimental

fn main() {}
76 changes: 76 additions & 0 deletions tests/ui/sized-hierarchy/pointee-validation.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
error[E0658]: const trait impls are experimental
--> $DIR/pointee-validation.rs:12:32
|
LL | const fn test<T, U>() where T: const PointeeSized, U: [const] PointeeSized {}
| ^^^^^
|
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: const trait impls are experimental
--> $DIR/pointee-validation.rs:12:53
|
LL | const fn test<T, U>() where T: const PointeeSized, U: [const] PointeeSized {}
| ^^^^^^^^^
|
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied
--> $DIR/pointee-validation.rs:8:20
|
LL | struct T where (): PointeeSized<(), Undefined = ()>;
| ^^^^^^^^^^^^-------------------- help: remove the unnecessary generics
| |
| expected 0 generic arguments

error[E0220]: associated type `Undefined` not found for `PointeeSized`
--> $DIR/pointee-validation.rs:8:37
|
LL | struct T where (): PointeeSized<(), Undefined = ()>;
| ^^^^^^^^^ associated type `Undefined` not found

error: `const` can only be applied to `#[const_trait]` traits
--> $DIR/pointee-validation.rs:12:32
|
LL | const fn test<T, U>() where T: const PointeeSized, U: [const] PointeeSized {}
| ^^^^^ can't be applied to `PointeeSized`
|
note: `PointeeSized` can't be used with `const` because it isn't annotated with `#[const_trait]`
--> $SRC_DIR/core/src/marker.rs:LL:COL

error: `[const]` can only be applied to `#[const_trait]` traits
--> $DIR/pointee-validation.rs:12:53
|
LL | const fn test<T, U>() where T: const PointeeSized, U: [const] PointeeSized {}
| ^^^^^^^^^ can't be applied to `PointeeSized`
|
note: `PointeeSized` can't be used with `[const]` because it isn't annotated with `#[const_trait]`
--> $SRC_DIR/core/src/marker.rs:LL:COL

error: `const` can only be applied to `#[const_trait]` traits
--> $DIR/pointee-validation.rs:12:32
|
LL | const fn test<T, U>() where T: const PointeeSized, U: [const] PointeeSized {}
| ^^^^^ can't be applied to `PointeeSized`
|
note: `PointeeSized` can't be used with `const` because it isn't annotated with `#[const_trait]`
--> $SRC_DIR/core/src/marker.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: `[const]` can only be applied to `#[const_trait]` traits
--> $DIR/pointee-validation.rs:12:53
|
LL | const fn test<T, U>() where T: const PointeeSized, U: [const] PointeeSized {}
| ^^^^^^^^^ can't be applied to `PointeeSized`
|
note: `PointeeSized` can't be used with `[const]` because it isn't annotated with `#[const_trait]`
--> $SRC_DIR/core/src/marker.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 8 previous errors

Some errors have detailed explanations: E0107, E0220, E0658.
For more information about an error, try `rustc --explain E0107`.
4 changes: 2 additions & 2 deletions tests/ui/sized-hierarchy/reject-dyn-pointeesized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::marker::PointeeSized;

type Foo = dyn PointeeSized;
//~^ ERROR `PointeeSized` cannot be used with trait objects
//~^ ERROR at least one trait is required for an object type

fn foo(f: &Foo) {}

Expand All @@ -12,5 +12,5 @@ fn main() {

let x = main;
let y: Box<dyn PointeeSized> = x;
//~^ ERROR `PointeeSized` cannot be used with trait objects
//~^ ERROR at least one trait is required for an object type
}
5 changes: 3 additions & 2 deletions tests/ui/sized-hierarchy/reject-dyn-pointeesized.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
error: `PointeeSized` cannot be used with trait objects
error[E0224]: at least one trait is required for an object type
--> $DIR/reject-dyn-pointeesized.rs:5:12
|
LL | type Foo = dyn PointeeSized;
| ^^^^^^^^^^^^^^^^

error: `PointeeSized` cannot be used with trait objects
error[E0224]: at least one trait is required for an object type
--> $DIR/reject-dyn-pointeesized.rs:14:16
|
LL | let y: Box<dyn PointeeSized> = x;
| ^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0224`.
Loading