Skip to content
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

Support ?Trait bounds in supertraits and dyn Trait under a feature gate #121676

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 8 additions & 2 deletions compiler/rustc_ast_lowering/src/item.rs
Expand Up @@ -1469,8 +1469,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
continue;
}
let is_param = *is_param.get_or_insert_with(compute_is_param);
if !is_param {
self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() });
if !is_param && !self.tcx.features().more_maybe_bounds {
self.tcx
.sess
.create_feature_err(
MisplacedRelaxTraitBound { span: bound.span() },
sym::more_maybe_bounds,
)
.emit();
}
}
}
Expand Down
28 changes: 13 additions & 15 deletions compiler/rustc_ast_lowering/src/lib.rs
Expand Up @@ -1252,6 +1252,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
itctx,
TraitBoundModifiers::NONE,
);
let bound = (bound, hir::TraitBoundModifier::None);
let bounds = this.arena.alloc_from_iter([bound]);
let lifetime_bound = this.elided_dyn_bound(t.span);
(bounds, lifetime_bound)
Expand Down Expand Up @@ -1383,21 +1384,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// We can safely ignore constness here since AST validation
// takes care of rejecting invalid modifier combinations and
// const trait bounds in trait object types.
GenericBound::Trait(ty, modifiers) => match modifiers.polarity {
BoundPolarity::Positive | BoundPolarity::Negative(_) => {
Some(this.lower_poly_trait_ref(
ty,
itctx,
// Still, don't pass along the constness here; we don't want to
// synthesize any host effect args, it'd only cause problems.
TraitBoundModifiers {
constness: BoundConstness::Never,
..*modifiers
},
))
}
BoundPolarity::Maybe(_) => None,
},
GenericBound::Trait(ty, modifiers) => {
// Still, don't pass along the constness here; we don't want to
// synthesize any host effect args, it'd only cause problems.
let modifiers = TraitBoundModifiers {
constness: BoundConstness::Never,
..*modifiers
};
let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers);
let polarity = this.lower_trait_bound_modifiers(modifiers);
Some((trait_ref, polarity))
}
GenericBound::Outlives(lifetime) => {
if lifetime_bound.is_none() {
lifetime_bound = Some(this.lower_lifetime(lifetime));
Expand Down Expand Up @@ -2495,6 +2492,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
span: self.lower_span(span),
};
let principal = (principal, hir::TraitBoundModifier::None);

// The original ID is taken by the `PolyTraitRef`,
// so the `Ty` itself needs a different one.
Expand Down
28 changes: 21 additions & 7 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Expand Up @@ -1266,14 +1266,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
if let GenericBound::Trait(poly, modifiers) = bound {
match (ctxt, modifiers.constness, modifiers.polarity) {
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
self.dcx().emit_err(errors::OptionalTraitSupertrait {
span: poly.span,
path_str: pprust::path_to_string(&poly.trait_ref.path),
});
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
if !self.features.more_maybe_bounds =>
{
self.session
.create_feature_err(
errors::OptionalTraitSupertrait {
span: poly.span,
path_str: pprust::path_to_string(&poly.trait_ref.path),
},
sym::more_maybe_bounds,
)
.emit();
}
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
self.dcx().emit_err(errors::OptionalTraitObject { span: poly.span });
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
if !self.features.more_maybe_bounds =>
{
self.session
.create_feature_err(
errors::OptionalTraitObject { span: poly.span },
sym::more_maybe_bounds,
)
.emit();
}
(BoundKind::TraitObject, BoundConstness::Always(_), BoundPolarity::Positive) => {
self.dcx().emit_err(errors::ConstBoundTraitObject { span: poly.span });
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Expand Up @@ -207,6 +207,8 @@ declare_features! (
(unstable, lifetime_capture_rules_2024, "1.76.0", None),
/// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406
(unstable, link_cfg, "1.14.0", None),
/// Allows using `?Trait` trait bounds in more contexts.
(internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None),
/// Allows the `multiple_supertrait_upcastable` lint.
(unstable, multiple_supertrait_upcastable, "1.69.0", None),
/// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir/src/hir.rs
Expand Up @@ -2610,7 +2610,11 @@ pub enum TyKind<'hir> {
OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool),
/// A trait object type `Bound1 + Bound2 + Bound3`
/// where `Bound` is a trait or a lifetime.
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
TraitObject(
&'hir [(PolyTraitRef<'hir>, TraitBoundModifier)],
&'hir Lifetime,
TraitObjectSyntax,
),
/// Unused for now.
Typeof(AnonConst),
/// `TyKind::Infer` means the type should be inferred instead of it having been
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_hir/src/intravisit.rs
Expand Up @@ -872,7 +872,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
try_visit!(visitor.visit_array_length(length));
}
TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
walk_list!(visitor, visit_poly_trait_ref, bounds);
for (bound, _modifier) in bounds {
try_visit!(visitor.visit_poly_trait_ref(bound));
}
try_visit!(visitor.visit_lifetime(lifetime));
}
TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),
Expand Down
16 changes: 11 additions & 5 deletions compiler/rustc_hir_analysis/src/astconv/bounds.rs
Expand Up @@ -5,7 +5,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::{self as ty, Ty};
use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_span::{sym, ErrorGuaranteed, Span};
use rustc_trait_selection::traits;
use smallvec::SmallVec;

Expand Down Expand Up @@ -66,10 +66,16 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
}
}

if unbounds.len() > 1 {
tcx.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
spans: unbounds.iter().map(|ptr| ptr.span).collect(),
});
if unbounds.len() > 1 && !tcx.features().more_maybe_bounds {
self.tcx()
.sess
.create_feature_err(
errors::MultipleRelaxedDefaultBounds {
spans: unbounds.iter().map(|ptr| ptr.span).collect(),
},
sym::more_maybe_bounds,
)
.emit();
}

let mut seen_sized_unbound = false;
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_analysis/src/astconv/errors.rs
Expand Up @@ -592,7 +592,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&self,
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
potential_assoc_types: Vec<Span>,
trait_bounds: &[hir::PolyTraitRef<'_>],
trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)],
) {
if associated_types.values().all(|v| v.is_empty()) {
return;
Expand Down Expand Up @@ -634,7 +634,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
if object_safety_violations {
return;
}
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
if let ([], [(bound, _)]) = (&potential_assoc_types[..], &trait_bounds) {
match bound.trait_ref.path.segments {
// FIXME: `trait_ref.path.span` can point to a full path with multiple
// segments, even though `trait_ref.path.segments` is of length `1`. Work
Expand Down Expand Up @@ -676,7 +676,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// and we can then use their span to indicate this to the user.
let bound_names = trait_bounds
.iter()
.filter_map(|poly_trait_ref| {
.filter_map(|(poly_trait_ref, _)| {
let path = poly_trait_ref.trait_ref.path.segments.last()?;
let args = path.args?;

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/astconv/lint.rs
Expand Up @@ -97,7 +97,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut is_downgradable = true;
let is_object_safe = match self_ty.kind {
hir::TyKind::TraitObject(objects, ..) => {
objects.iter().all(|o| match o.trait_ref.path.res {
objects.iter().all(|(o, _)| match o.trait_ref.path.res {
Res::Def(DefKind::Trait, id) => {
if Some(id) == owner {
// For recursive traits, don't downgrade the error. (#119652)
Expand Down Expand Up @@ -188,7 +188,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
let tcx = self.tcx();
if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
if let hir::TyKind::TraitObject([(poly_trait_ref, _), ..], _, TraitObjectSyntax::None) =
self_ty.kind
{
let needs_bracket = in_path
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_hir_analysis/src/astconv/object_safety.rs
Expand Up @@ -22,7 +22,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&self,
span: Span,
hir_id: hir::HirId,
hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)],
lifetime: &hir::Lifetime,
borrowed: bool,
representation: DynKind,
Expand All @@ -32,7 +32,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut bounds = Bounds::default();
let mut potential_assoc_types = Vec::new();
let dummy_self = self.tcx().types.trait_object_dummy_self;
for trait_bound in hir_trait_bounds.iter().rev() {
for (trait_bound, modifier) in hir_trait_bounds.iter().rev() {
if *modifier == hir::TraitBoundModifier::Maybe {
continue;
}
if let GenericArgCountResult {
correct:
Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
Expand Down Expand Up @@ -277,7 +280,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let args = tcx.mk_args(&args);

let span = i.bottom().1;
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| {
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
&& hir_bound.span.contains(span)
});
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/wfcheck.rs
Expand Up @@ -844,7 +844,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {

fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
match ty.kind {
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments {
[s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
_ => false,
},
Expand Down
Expand Up @@ -606,7 +606,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
debug!(?bounds, ?lifetime, "TraitObject");
let scope = Scope::TraitRefBoundary { s: self.scope };
self.with(scope, |this| {
for bound in bounds {
for (bound, _) in bounds {
this.visit_poly_trait_ref_inner(
bound,
NonLifetimeBinderAllowed::Deny("trait object types"),
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_hir_pretty/src/lib.rs
Expand Up @@ -293,13 +293,16 @@ impl<'a> State<'a> {
self.word_space("dyn");
}
let mut first = true;
for bound in bounds {
for (bound, modifier) in bounds {
if first {
first = false;
} else {
self.nbsp();
self.word_space("+");
}
if *modifier == TraitBoundModifier::Maybe {
self.word("?");
}
self.print_poly_trait_ref(bound);
}
if !lifetime.is_elided() {
Expand Down
Expand Up @@ -82,7 +82,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
}

hir::TyKind::TraitObject(bounds, ..) => {
for bound in bounds {
for (bound, _) in bounds {
self.current_index.shift_in(1);
self.visit_poly_trait_ref(bound);
self.current_index.shift_out(1);
Expand Down
Expand Up @@ -567,7 +567,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
_,
) = t.kind
{
for ptr in poly_trait_refs {
for (ptr, _) in poly_trait_refs {
if Some(self.1) == ptr.trait_ref.trait_def_id() {
self.0.push(ptr.span);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/traits/error_reporting/mod.rs
Expand Up @@ -62,7 +62,7 @@ pub fn report_object_safety_error<'tcx>(

if let Some(hir_id) = hir_id
&& let hir::Node::Ty(ty) = tcx.hir_node(hir_id)
&& let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
&& let hir::TyKind::TraitObject([(trait_ref, _), ..], ..) = ty.kind
{
let mut hir_id = hir_id;
while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/non_local_def.rs
Expand Up @@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
TyKind::Path(QPath::Resolved(_, ty_path)) => {
path_has_local_parent(ty_path, cx, parent, parent_parent)
}
TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
TyKind::TraitObject([(principle_poly_trait_ref, _), ..], _, _) => {
path_has_local_parent(
principle_poly_trait_ref.trait_ref.path,
cx,
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_lint/src/traits.rs
Expand Up @@ -112,9 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {

fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
for bound in &bounds[..] {
for (bound, modifier) in &bounds[..] {
let def_id = bound.trait_ref.trait_def_id();
if cx.tcx.lang_items().drop_trait() == def_id {
if cx.tcx.lang_items().drop_trait() == def_id
&& *modifier != hir::TraitBoundModifier::Maybe
{
let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Expand Up @@ -1170,6 +1170,7 @@ symbols! {
modifiers,
module,
module_path,
more_maybe_bounds,
more_qualified_paths,
more_struct_aliases,
movbe_target_feature,
Expand Down
Expand Up @@ -2983,11 +2983,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
match ty.kind {
hir::TyKind::TraitObject(traits, _, _) => {
let (span, kw) = match traits {
[first, ..] if first.span.lo() == ty.span.lo() => {
[(first, _), ..] if first.span.lo() == ty.span.lo() => {
// Missing `dyn` in front of trait object.
(ty.span.shrink_to_lo(), "dyn ")
}
[first, ..] => (ty.span.until(first.span), ""),
[(first, _), ..] => (ty.span.until(first.span), ""),
[] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
};
let needs_parens = traits.len() != 1;
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/clean/mod.rs
Expand Up @@ -1859,7 +1859,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
}
TyKind::Path(_) => clean_qpath(ty, cx),
TyKind::TraitObject(bounds, ref lifetime, _) => {
let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect();
let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect();
let lifetime =
if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None };
DynTrait(bounds, lifetime)
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/lifetimes.rs
Expand Up @@ -549,7 +549,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
if !lt.is_elided() {
self.unelided_trait_object_lifetime = true;
}
for bound in bounds {
for (bound, _) in bounds {
self.visit_poly_trait_ref(bound);
}
},
Expand Down
6 changes: 3 additions & 3 deletions src/tools/clippy/clippy_lints/src/trait_bounds.rs
Expand Up @@ -181,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {

// Iterate the bounds and add them to our seen hash
// If we haven't yet seen it, add it to the fixed traits
for bound in bounds {
for (bound, _) in bounds {
let Some(def_id) = bound.trait_ref.trait_def_id() else {
continue;
};
Expand All @@ -196,9 +196,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
// If the number of unique traits isn't the same as the number of traits in the bounds,
// there must be 1 or more duplicates
if bounds.len() != unique_traits.len() {
let mut bounds_span = bounds[0].span;
let mut bounds_span = bounds[0].0.span;

for bound in bounds.iter().skip(1) {
for (bound, _) in bounds.iter().skip(1) {
bounds_span = bounds_span.to(bound.span);
}

Expand Down