Skip to content

Commit 7c2012d

Browse files
committed
Auto merge of rust-lang#121676 - Bryanskiy:polarity, r=petrochenkov
Support ?Trait bounds in supertraits and dyn Trait under a feature gate This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example: ```rust #![feature(allow_maybe_polarity)] ... trait Trait1 : ?Trait { ... } // ok fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok ``` Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](rust-lang#120706 (comment)). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727)
2 parents 47243b3 + fd9d0bf commit 7c2012d

File tree

36 files changed

+364
-83
lines changed

36 files changed

+364
-83
lines changed

compiler/rustc_ast_lowering/src/item.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1525,8 +1525,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
15251525
continue;
15261526
}
15271527
let is_param = *is_param.get_or_insert_with(compute_is_param);
1528-
if !is_param {
1529-
self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() });
1528+
if !is_param && !self.tcx.features().more_maybe_bounds {
1529+
self.tcx
1530+
.sess
1531+
.create_feature_err(
1532+
MisplacedRelaxTraitBound { span: bound.span() },
1533+
sym::more_maybe_bounds,
1534+
)
1535+
.emit();
15301536
}
15311537
}
15321538
}

compiler/rustc_ast_lowering/src/lib.rs

+13-15
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12161216
itctx,
12171217
TraitBoundModifiers::NONE,
12181218
);
1219+
let bound = (bound, hir::TraitBoundModifier::None);
12191220
let bounds = this.arena.alloc_from_iter([bound]);
12201221
let lifetime_bound = this.elided_dyn_bound(t.span);
12211222
(bounds, lifetime_bound)
@@ -1348,21 +1349,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13481349
// We can safely ignore constness here since AST validation
13491350
// takes care of rejecting invalid modifier combinations and
13501351
// const trait bounds in trait object types.
1351-
GenericBound::Trait(ty, modifiers) => match modifiers.polarity {
1352-
BoundPolarity::Positive | BoundPolarity::Negative(_) => {
1353-
Some(this.lower_poly_trait_ref(
1354-
ty,
1355-
itctx,
1356-
// Still, don't pass along the constness here; we don't want to
1357-
// synthesize any host effect args, it'd only cause problems.
1358-
TraitBoundModifiers {
1359-
constness: BoundConstness::Never,
1360-
..*modifiers
1361-
},
1362-
))
1363-
}
1364-
BoundPolarity::Maybe(_) => None,
1365-
},
1352+
GenericBound::Trait(ty, modifiers) => {
1353+
// Still, don't pass along the constness here; we don't want to
1354+
// synthesize any host effect args, it'd only cause problems.
1355+
let modifiers = TraitBoundModifiers {
1356+
constness: BoundConstness::Never,
1357+
..*modifiers
1358+
};
1359+
let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers);
1360+
let polarity = this.lower_trait_bound_modifiers(modifiers);
1361+
Some((trait_ref, polarity))
1362+
}
13661363
GenericBound::Outlives(lifetime) => {
13671364
if lifetime_bound.is_none() {
13681365
lifetime_bound = Some(this.lower_lifetime(lifetime));
@@ -2688,6 +2685,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
26882685
trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
26892686
span: self.lower_span(span),
26902687
};
2688+
let principal = (principal, hir::TraitBoundModifier::None);
26912689

26922690
// The original ID is taken by the `PolyTraitRef`,
26932691
// so the `Ty` itself needs a different one.

compiler/rustc_ast_passes/src/ast_validation.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -1345,14 +1345,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13451345
match bound {
13461346
GenericBound::Trait(trait_ref, modifiers) => {
13471347
match (ctxt, modifiers.constness, modifiers.polarity) {
1348-
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
1349-
self.dcx().emit_err(errors::OptionalTraitSupertrait {
1350-
span: trait_ref.span,
1351-
path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
1352-
});
1348+
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
1349+
if !self.features.more_maybe_bounds =>
1350+
{
1351+
self.session
1352+
.create_feature_err(
1353+
errors::OptionalTraitSupertrait {
1354+
span: trait_ref.span,
1355+
path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
1356+
},
1357+
sym::more_maybe_bounds,
1358+
)
1359+
.emit();
13531360
}
1354-
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
1355-
self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span });
1361+
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
1362+
if !self.features.more_maybe_bounds =>
1363+
{
1364+
self.session
1365+
.create_feature_err(
1366+
errors::OptionalTraitObject { span: trait_ref.span },
1367+
sym::more_maybe_bounds,
1368+
)
1369+
.emit();
13561370
}
13571371
(
13581372
BoundKind::TraitObject,

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ declare_features! (
205205
(unstable, lifetime_capture_rules_2024, "1.76.0", None),
206206
/// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406
207207
(unstable, link_cfg, "1.14.0", None),
208+
/// Allows using `?Trait` trait bounds in more contexts.
209+
(internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None),
208210
/// Allows the `multiple_supertrait_upcastable` lint.
209211
(unstable, multiple_supertrait_upcastable, "1.69.0", None),
210212
/// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!

compiler/rustc_hir/src/hir.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -2832,7 +2832,11 @@ pub enum TyKind<'hir> {
28322832
OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool),
28332833
/// A trait object type `Bound1 + Bound2 + Bound3`
28342834
/// where `Bound` is a trait or a lifetime.
2835-
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
2835+
TraitObject(
2836+
&'hir [(PolyTraitRef<'hir>, TraitBoundModifier)],
2837+
&'hir Lifetime,
2838+
TraitObjectSyntax,
2839+
),
28362840
/// Unused for now.
28372841
Typeof(&'hir AnonConst),
28382842
/// `TyKind::Infer` means the type should be inferred instead of it having been

compiler/rustc_hir/src/intravisit.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
902902
try_visit!(visitor.visit_array_length(length));
903903
}
904904
TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
905-
walk_list!(visitor, visit_poly_trait_ref, bounds);
905+
for (bound, _modifier) in bounds {
906+
try_visit!(visitor.visit_poly_trait_ref(bound));
907+
}
906908
try_visit!(visitor.visit_lifetime(lifetime));
907909
}
908910
TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
831831

832832
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
833833
match ty.kind {
834-
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
834+
hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments {
835835
[s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
836836
_ => false,
837837
},

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
652652
debug!(?bounds, ?lifetime, "TraitObject");
653653
let scope = Scope::TraitRefBoundary { s: self.scope };
654654
self.with(scope, |this| {
655-
for bound in bounds {
655+
for (bound, _) in bounds {
656656
this.visit_poly_trait_ref_inner(
657657
bound,
658658
NonLifetimeBinderAllowed::Deny("trait object types"),

compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
88
use rustc_middle::bug;
99
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
1010
use rustc_span::symbol::Ident;
11-
use rustc_span::{ErrorGuaranteed, Span, Symbol};
11+
use rustc_span::{sym, ErrorGuaranteed, Span, Symbol};
1212
use rustc_trait_selection::traits;
1313
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
1414
use smallvec::SmallVec;
@@ -73,10 +73,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
7373
}
7474
}
7575

76+
let mut unique_bounds = FxIndexSet::default();
77+
let mut seen_repeat = false;
78+
for unbound in &unbounds {
79+
if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res {
80+
seen_repeat |= !unique_bounds.insert(unbound_def_id);
81+
}
82+
}
7683
if unbounds.len() > 1 {
77-
self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
84+
let err = errors::MultipleRelaxedDefaultBounds {
7885
spans: unbounds.iter().map(|ptr| ptr.span).collect(),
79-
});
86+
};
87+
if seen_repeat {
88+
self.dcx().emit_err(err);
89+
} else if !tcx.features().more_maybe_bounds {
90+
self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit();
91+
};
8092
}
8193

8294
let mut seen_sized_unbound = false;

compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
719719
&self,
720720
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
721721
potential_assoc_types: Vec<usize>,
722-
trait_bounds: &[hir::PolyTraitRef<'_>],
722+
trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)],
723723
) {
724724
if associated_types.values().all(|v| v.is_empty()) {
725725
return;
@@ -765,12 +765,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
765765
// related to issue #91997, turbofishes added only when in an expr or pat
766766
let mut in_expr_or_pat = false;
767767
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
768-
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
768+
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id));
769769
in_expr_or_pat = match grandparent {
770770
hir::Node::Expr(_) | hir::Node::Pat(_) => true,
771771
_ => false,
772772
};
773-
match bound.trait_ref.path.segments {
773+
match bound.0.trait_ref.path.segments {
774774
// FIXME: `trait_ref.path.span` can point to a full path with multiple
775775
// segments, even though `trait_ref.path.segments` is of length `1`. Work
776776
// around that bug here, even though it should be fixed elsewhere.
@@ -811,7 +811,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
811811
// and we can then use their span to indicate this to the user.
812812
let bound_names = trait_bounds
813813
.iter()
814-
.filter_map(|poly_trait_ref| {
814+
.filter_map(|(poly_trait_ref, _)| {
815815
let path = poly_trait_ref.trait_ref.path.segments.last()?;
816816
let args = path.args?;
817817

compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
3434
.ok()
3535
.is_some_and(|s| s.trim_end().ends_with('<'));
3636

37-
let is_global = poly_trait_ref.trait_ref.path.is_global();
37+
let is_global = poly_trait_ref.0.trait_ref.path.is_global();
3838

3939
let mut sugg = vec![(
4040
self_ty.span.shrink_to_lo(),
@@ -176,7 +176,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
176176
let mut is_downgradable = true;
177177
let is_object_safe = match self_ty.kind {
178178
hir::TyKind::TraitObject(objects, ..) => {
179-
objects.iter().all(|o| match o.trait_ref.path.res {
179+
objects.iter().all(|(o, _)| match o.trait_ref.path.res {
180180
Res::Def(DefKind::Trait, id) => {
181181
if Some(id) == owner {
182182
// For recursive traits, don't downgrade the error. (#119652)

compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
2727
&self,
2828
span: Span,
2929
hir_id: hir::HirId,
30-
hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
30+
hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)],
3131
lifetime: &hir::Lifetime,
3232
borrowed: bool,
3333
representation: DynKind,
@@ -37,7 +37,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
3737
let mut bounds = Bounds::default();
3838
let mut potential_assoc_types = Vec::new();
3939
let dummy_self = self.tcx().types.trait_object_dummy_self;
40-
for trait_bound in hir_trait_bounds.iter().rev() {
40+
for (trait_bound, modifier) in hir_trait_bounds.iter().rev() {
41+
if *modifier == hir::TraitBoundModifier::Maybe {
42+
continue;
43+
}
4144
if let GenericArgCountResult {
4245
correct:
4346
Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
@@ -249,7 +252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
249252
let args = tcx.mk_args(&args);
250253

251254
let span = i.bottom().1;
252-
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
255+
let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| {
253256
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
254257
&& hir_bound.span.contains(span)
255258
});

compiler/rustc_hir_pretty/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -300,13 +300,16 @@ impl<'a> State<'a> {
300300
self.word_space("dyn");
301301
}
302302
let mut first = true;
303-
for bound in bounds {
303+
for (bound, modifier) in bounds {
304304
if first {
305305
first = false;
306306
} else {
307307
self.nbsp();
308308
self.word_space("+");
309309
}
310+
if *modifier == TraitBoundModifier::Maybe {
311+
self.word("?");
312+
}
310313
self.print_poly_trait_ref(bound);
311314
}
312315
if !lifetime.is_elided() {

compiler/rustc_lint/src/non_local_def.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ fn ty_has_local_parent(
429429
path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent)
430430
}
431431
TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent(
432-
principle_poly_trait_ref.trait_ref.path,
432+
principle_poly_trait_ref.0.trait_ref.path,
433433
cx,
434434
impl_parent,
435435
impl_parent_parent,
@@ -527,7 +527,7 @@ fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span
527527
.to_string(),
528528
),
529529
TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
530-
let path = &principle_poly_trait_ref.trait_ref.path;
530+
let path = &principle_poly_trait_ref.0.trait_ref.path;
531531
(
532532
path_span_without_args(path),
533533
path.res

compiler/rustc_lint/src/traits.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
113113

114114
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
115115
let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
116-
for bound in &bounds[..] {
116+
for (bound, modifier) in &bounds[..] {
117117
let def_id = bound.trait_ref.trait_def_id();
118-
if cx.tcx.lang_items().drop_trait() == def_id {
118+
if cx.tcx.lang_items().drop_trait() == def_id
119+
&& *modifier != hir::TraitBoundModifier::Maybe
120+
{
119121
let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
120122
cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
121123
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,7 @@ symbols! {
12301230
modifiers,
12311231
module,
12321232
module_path,
1233+
more_maybe_bounds,
12331234
more_qualified_paths,
12341235
more_struct_aliases,
12351236
movbe_target_feature,

compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
8484
}
8585

8686
hir::TyKind::TraitObject(bounds, ..) => {
87-
for bound in bounds {
87+
for (bound, _) in bounds {
8888
self.current_index.shift_in(1);
8989
self.visit_poly_trait_ref(bound);
9090
self.current_index.shift_out(1);

compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
607607
_,
608608
) = t.kind
609609
{
610-
for ptr in poly_trait_refs {
610+
for (ptr, _) in poly_trait_refs {
611611
if Some(self.1) == ptr.trait_ref.trait_def_id() {
612612
self.0.push(ptr.span);
613613
}

compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ pub fn report_object_safety_error<'tcx>(
437437
if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
438438
// Do not suggest `impl Trait` when dealing with things like super-traits.
439439
err.span_suggestion_verbose(
440-
ty.span.until(trait_ref.span),
440+
ty.span.until(trait_ref.0.span),
441441
"consider using an opaque type instead",
442442
"impl ",
443443
Applicability::MaybeIncorrect,

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3040,11 +3040,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
30403040
match ty.kind {
30413041
hir::TyKind::TraitObject(traits, _, _) => {
30423042
let (span, kw) = match traits {
3043-
[first, ..] if first.span.lo() == ty.span.lo() => {
3043+
[(first, _), ..] if first.span.lo() == ty.span.lo() => {
30443044
// Missing `dyn` in front of trait object.
30453045
(ty.span.shrink_to_lo(), "dyn ")
30463046
}
3047-
[first, ..] => (ty.span.until(first.span), ""),
3047+
[(first, _), ..] => (ty.span.until(first.span), ""),
30483048
[] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
30493049
};
30503050
let needs_parens = traits.len() != 1;

src/librustdoc/clean/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1858,7 +1858,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
18581858
}
18591859
TyKind::Path(_) => clean_qpath(ty, cx),
18601860
TyKind::TraitObject(bounds, ref lifetime, _) => {
1861-
let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect();
1861+
let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect();
18621862
let lifetime =
18631863
if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None };
18641864
DynTrait(bounds, lifetime)

src/tools/clippy/clippy_lints/src/lifetimes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
543543
if !lt.is_elided() {
544544
self.unelided_trait_object_lifetime = true;
545545
}
546-
for bound in bounds {
546+
for (bound, _) in bounds {
547547
self.visit_poly_trait_ref(bound);
548548
}
549549
},

0 commit comments

Comments
 (0)