Skip to content

Commit 80df4ab

Browse files
authored
Rollup merge of rust-lang#110791 - compiler-errors:negative-bounds, r=oli-obk
Implement negative bounds for internal testing purposes Implements partial support the `!` negative polarity on trait bounds. This is incomplete, but should allow us to at least be able to play with the feature. Not even gonna consider them as a public-facing feature, but I'm implementing them because would've been nice to have in UI tests, for example in rust-lang#110671.
2 parents 32f3ddb + 6fca051 commit 80df4ab

File tree

42 files changed

+478
-185
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+478
-185
lines changed

compiler/rustc_ast/src/ast.rs

+18
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,20 @@ pub enum TraitBoundModifier {
287287
/// No modifiers
288288
None,
289289

290+
/// `!Trait`
291+
Negative,
292+
290293
/// `?Trait`
291294
Maybe,
292295

293296
/// `~const Trait`
294297
MaybeConst,
295298

299+
/// `~const !Trait`
300+
//
301+
// This parses but will be rejected during AST validation.
302+
MaybeConstNegative,
303+
296304
/// `~const ?Trait`
297305
//
298306
// This parses but will be rejected during AST validation.
@@ -2446,6 +2454,16 @@ impl fmt::Debug for ImplPolarity {
24462454
}
24472455
}
24482456

2457+
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
2458+
pub enum BoundPolarity {
2459+
/// `Type: Trait`
2460+
Positive,
2461+
/// `Type: !Trait`
2462+
Negative(Span),
2463+
/// `Type: ?Trait`
2464+
Maybe(Span),
2465+
}
2466+
24492467
#[derive(Clone, Encodable, Decodable, Debug)]
24502468
pub enum FnRetTy {
24512469
/// Returns type is not specified.

compiler/rustc_ast_lowering/src/lib.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -1368,13 +1368,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13681368
this.arena.alloc_from_iter(bounds.iter().filter_map(|bound| match bound {
13691369
GenericBound::Trait(
13701370
ty,
1371-
TraitBoundModifier::None | TraitBoundModifier::MaybeConst,
1371+
TraitBoundModifier::None
1372+
| TraitBoundModifier::MaybeConst
1373+
| TraitBoundModifier::Negative,
13721374
) => Some(this.lower_poly_trait_ref(ty, itctx)),
13731375
// `~const ?Bound` will cause an error during AST validation
13741376
// anyways, so treat it like `?Bound` as compilation proceeds.
13751377
GenericBound::Trait(
13761378
_,
1377-
TraitBoundModifier::Maybe | TraitBoundModifier::MaybeConstMaybe,
1379+
TraitBoundModifier::Maybe
1380+
| TraitBoundModifier::MaybeConstMaybe
1381+
| TraitBoundModifier::MaybeConstNegative,
13781382
) => None,
13791383
GenericBound::Outlives(lifetime) => {
13801384
if lifetime_bound.is_none() {
@@ -2421,11 +2425,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
24212425
TraitBoundModifier::None => hir::TraitBoundModifier::None,
24222426
TraitBoundModifier::MaybeConst => hir::TraitBoundModifier::MaybeConst,
24232427

2428+
TraitBoundModifier::Negative => {
2429+
if self.tcx.features().negative_bounds {
2430+
hir::TraitBoundModifier::Negative
2431+
} else {
2432+
hir::TraitBoundModifier::None
2433+
}
2434+
}
2435+
24242436
// `MaybeConstMaybe` will cause an error during AST validation, but we need to pick a
24252437
// placeholder for compilation to proceed.
24262438
TraitBoundModifier::MaybeConstMaybe | TraitBoundModifier::Maybe => {
24272439
hir::TraitBoundModifier::Maybe
24282440
}
2441+
TraitBoundModifier::MaybeConstNegative => hir::TraitBoundModifier::MaybeConst,
24292442
}
24302443
}
24312444

compiler/rustc_ast_passes/messages.ftl

+7-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ ast_passes_tilde_const_disallowed = `~const` is not allowed here
206206
.closure = closures cannot have `~const` trait bounds
207207
.function = this function is not `const`, so it cannot have `~const` trait bounds
208208
209-
ast_passes_optional_const_exclusive = `~const` and `?` are mutually exclusive
209+
ast_passes_optional_const_exclusive = `~const` and `{$modifier}` are mutually exclusive
210210
211211
ast_passes_const_and_async = functions cannot be both `const` and `async`
212212
.const = `const` because of this
@@ -235,3 +235,9 @@ ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using t
235235
.help = remove one of these features
236236
237237
ast_passes_show_span = {$msg}
238+
239+
ast_passes_negative_bound_not_supported =
240+
negative bounds are not supported
241+
242+
ast_passes_constraint_on_negative_bound =
243+
associated type constraints not allowed on negative bounds

compiler/rustc_ast_passes/src/ast_validation.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1168,12 +1168,27 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11681168
});
11691169
}
11701170
(_, TraitBoundModifier::MaybeConstMaybe) => {
1171-
self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span()});
1171+
self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "?" });
1172+
}
1173+
(_, TraitBoundModifier::MaybeConstNegative) => {
1174+
self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "!" });
11721175
}
11731176
_ => {}
11741177
}
11751178
}
11761179

1180+
// Negative trait bounds are not allowed to have associated constraints
1181+
if let GenericBound::Trait(trait_ref, TraitBoundModifier::Negative) = bound
1182+
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
1183+
&& let Some(ast::GenericArgs::AngleBracketed(args)) = segment.args.as_deref()
1184+
{
1185+
for arg in &args.args {
1186+
if let ast::AngleBracketedArg::Constraint(constraint) = arg {
1187+
self.err_handler().emit_err(errors::ConstraintOnNegativeBound { span: constraint.span });
1188+
}
1189+
}
1190+
}
1191+
11771192
visit::walk_param_bound(self, bound)
11781193
}
11791194

compiler/rustc_ast_passes/src/errors.rs

+15
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ pub enum TildeConstReason {
567567
pub struct OptionalConstExclusive {
568568
#[primary_span]
569569
pub span: Span,
570+
pub modifier: &'static str,
570571
}
571572

572573
#[derive(Diagnostic)]
@@ -693,3 +694,17 @@ pub struct ShowSpan {
693694
pub span: Span,
694695
pub msg: &'static str,
695696
}
697+
698+
#[derive(Diagnostic)]
699+
#[diag(ast_passes_negative_bound_not_supported)]
700+
pub struct NegativeBoundUnsupported {
701+
#[primary_span]
702+
pub span: Span,
703+
}
704+
705+
#[derive(Diagnostic)]
706+
#[diag(ast_passes_constraint_on_negative_bound)]
707+
pub struct ConstraintOnNegativeBound {
708+
#[primary_span]
709+
pub span: Span,
710+
}

compiler/rustc_ast_passes/src/feature_gate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
603603
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
604604
gate_all!(const_closures, "const closures are experimental");
605605

606+
if !visitor.features.negative_bounds {
607+
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
608+
sess.emit_err(errors::NegativeBoundUnsupported { span });
609+
}
610+
}
611+
606612
// All uses of `gate_all!` below this point were added in #65742,
607613
// and subsequently disabled (with the non-early gating readded).
608614
// We emit an early future-incompatible warning for these.

compiler/rustc_ast_pretty/src/pprust/state.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1570,12 +1570,19 @@ impl<'a> State<'a> {
15701570
GenericBound::Trait(tref, modifier) => {
15711571
match modifier {
15721572
TraitBoundModifier::None => {}
1573+
TraitBoundModifier::Negative => {
1574+
self.word("!");
1575+
}
15731576
TraitBoundModifier::Maybe => {
15741577
self.word("?");
15751578
}
15761579
TraitBoundModifier::MaybeConst => {
15771580
self.word_space("~const");
15781581
}
1582+
TraitBoundModifier::MaybeConstNegative => {
1583+
self.word_space("~const");
1584+
self.word("!");
1585+
}
15791586
TraitBoundModifier::MaybeConstMaybe => {
15801587
self.word_space("~const");
15811588
self.word("?");

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ declare_features! (
164164
(active, link_cfg, "1.14.0", None, None),
165165
/// Allows the `multiple_supertrait_upcastable` lint.
166166
(active, multiple_supertrait_upcastable, "1.69.0", None, None),
167+
/// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
168+
(incomplete, negative_bounds, "CURRENT_RUSTC_VERSION", None, None),
167169
/// Allows using `#[omit_gdb_pretty_printer_section]`.
168170
(active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
169171
/// Allows using `#[prelude_import]` on glob `use` items.

compiler/rustc_hir/src/hir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ pub enum GenericArgsParentheses {
435435
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
436436
pub enum TraitBoundModifier {
437437
None,
438+
Negative,
438439
Maybe,
439440
MaybeConst,
440441
}

compiler/rustc_hir_analysis/src/astconv/mod.rs

+29-5
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
665665
span: Span,
666666
binding_span: Option<Span>,
667667
constness: ty::BoundConstness,
668+
polarity: ty::ImplPolarity,
668669
bounds: &mut Bounds<'tcx>,
669670
speculative: bool,
670671
trait_ref_span: Span,
@@ -696,10 +697,20 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
696697
ty::Binder::bind_with_vars(tcx.mk_trait_ref(trait_def_id, substs), bound_vars);
697698

698699
debug!(?poly_trait_ref, ?assoc_bindings);
699-
bounds.push_trait_bound(tcx, poly_trait_ref, span, constness);
700+
bounds.push_trait_bound(tcx, poly_trait_ref, span, constness, polarity);
700701

701702
let mut dup_bindings = FxHashMap::default();
702703
for binding in &assoc_bindings {
704+
// Don't register additional associated type bounds for negative bounds,
705+
// since we should have emitten an error for them earlier, and they will
706+
// not be well-formed!
707+
if polarity == ty::ImplPolarity::Negative {
708+
self.tcx()
709+
.sess
710+
.delay_span_bug(binding.span, "negative trait bounds should not have bindings");
711+
continue;
712+
}
713+
703714
// Specify type to assert that error was already reported in `Err` case.
704715
let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding(
705716
hir_id,
@@ -711,6 +722,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
711722
binding_span.unwrap_or(binding.span),
712723
constness,
713724
only_self_bounds,
725+
polarity,
714726
);
715727
// Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
716728
}
@@ -743,6 +755,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
743755
trait_ref: &hir::TraitRef<'_>,
744756
span: Span,
745757
constness: ty::BoundConstness,
758+
polarity: ty::ImplPolarity,
746759
self_ty: Ty<'tcx>,
747760
bounds: &mut Bounds<'tcx>,
748761
speculative: bool,
@@ -764,6 +777,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
764777
span,
765778
binding_span,
766779
constness,
780+
polarity,
767781
bounds,
768782
speculative,
769783
trait_ref_span,
@@ -799,6 +813,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
799813
span,
800814
binding_span,
801815
constness,
816+
ty::ImplPolarity::Positive,
802817
bounds,
803818
speculative,
804819
trait_ref_span,
@@ -961,16 +976,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
961976
for ast_bound in ast_bounds {
962977
match ast_bound {
963978
hir::GenericBound::Trait(poly_trait_ref, modifier) => {
964-
let constness = match modifier {
965-
hir::TraitBoundModifier::MaybeConst => ty::BoundConstness::ConstIfConst,
966-
hir::TraitBoundModifier::None => ty::BoundConstness::NotConst,
979+
let (constness, polarity) = match modifier {
980+
hir::TraitBoundModifier::MaybeConst => {
981+
(ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive)
982+
}
983+
hir::TraitBoundModifier::None => {
984+
(ty::BoundConstness::NotConst, ty::ImplPolarity::Positive)
985+
}
986+
hir::TraitBoundModifier::Negative => {
987+
(ty::BoundConstness::NotConst, ty::ImplPolarity::Negative)
988+
}
967989
hir::TraitBoundModifier::Maybe => continue,
968990
};
969-
970991
let _ = self.instantiate_poly_trait_ref(
971992
&poly_trait_ref.trait_ref,
972993
poly_trait_ref.span,
973994
constness,
995+
polarity,
974996
param_ty,
975997
bounds,
976998
false,
@@ -1088,6 +1110,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10881110
path_span: Span,
10891111
constness: ty::BoundConstness,
10901112
only_self_bounds: OnlySelfBounds,
1113+
polarity: ty::ImplPolarity,
10911114
) -> Result<(), ErrorGuaranteed> {
10921115
// Given something like `U: SomeTrait<T = X>`, we want to produce a
10931116
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
@@ -1438,6 +1461,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14381461
&trait_bound.trait_ref,
14391462
trait_bound.span,
14401463
ty::BoundConstness::NotConst,
1464+
ty::ImplPolarity::Positive,
14411465
dummy_self,
14421466
&mut bounds,
14431467
false,

compiler/rustc_hir_analysis/src/bounds.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,14 @@ impl<'tcx> Bounds<'tcx> {
4242
trait_ref: ty::PolyTraitRef<'tcx>,
4343
span: Span,
4444
constness: ty::BoundConstness,
45+
polarity: ty::ImplPolarity,
4546
) {
46-
self.predicates.push((trait_ref.with_constness(constness).to_predicate(tcx), span));
47+
self.predicates.push((
48+
trait_ref
49+
.map_bound(|trait_ref| ty::TraitPredicate { trait_ref, constness, polarity })
50+
.to_predicate(tcx),
51+
span,
52+
));
4753
}
4854

4955
pub fn push_projection_bound(

compiler/rustc_hir_analysis/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ pub fn hir_trait_to_predicates<'tcx>(
528528
hir_trait,
529529
DUMMY_SP,
530530
ty::BoundConstness::NotConst,
531+
ty::ImplPolarity::Positive,
531532
self_ty,
532533
&mut bounds,
533534
true,

compiler/rustc_infer/src/traits/util.rs

+4
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
200200
let bound_predicate = elaboratable.predicate().kind();
201201
match bound_predicate.skip_binder() {
202202
ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
203+
// Negative trait bounds do not imply any supertrait bounds
204+
if data.polarity == ty::ImplPolarity::Negative {
205+
return;
206+
}
203207
// Get predicates implied by the trait, or only super predicates if we only care about self predicates.
204208
let predicates = if self.only_self {
205209
tcx.super_predicates_of(data.def_id())

compiler/rustc_middle/src/ty/print/pretty.rs

+3
Original file line numberDiff line numberDiff line change
@@ -2816,6 +2816,9 @@ define_print_and_forward_display! {
28162816
if let ty::BoundConstness::ConstIfConst = self.constness && cx.tcx().features().const_trait_impl {
28172817
p!("~const ");
28182818
}
2819+
if let ty::ImplPolarity::Negative = self.polarity {
2820+
p!("!");
2821+
}
28192822
p!(print(self.trait_ref.print_only_trait_path()))
28202823
}
28212824

compiler/rustc_parse/messages.ftl

+2-8
Original file line numberDiff line numberDiff line change
@@ -615,13 +615,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword
615615
.help = `dyn` is only needed at the start of a trait `+`-separated list
616616
.suggestion = remove this keyword
617617
618-
parse_negative_bounds_not_supported = negative bounds are not supported
619-
.label = negative bounds are not supported
620-
.suggestion = {$num_bounds ->
621-
[one] remove the bound
622-
*[other] remove the bounds
623-
}
624-
625618
parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
626619
parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
627620
parse_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide
@@ -772,7 +765,8 @@ parse_assoc_lifetime = associated lifetimes are not supported
772765
773766
parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds
774767
775-
parse_maybe_lifetime = `?` may only modify trait bounds, not lifetime bounds
768+
parse_modifier_lifetime = `{$sigil}` may only modify trait bounds, not lifetime bounds
769+
.suggestion = remove the `{$sigil}`
776770
777771
parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
778772
.suggestion = remove the parentheses

0 commit comments

Comments
 (0)