Skip to content

Commit 6e01e91

Browse files
Implement negative bounds
1 parent 98c33e4 commit 6e01e91

File tree

33 files changed

+360
-185
lines changed

33 files changed

+360
-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

+4-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,6 @@ 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

compiler/rustc_ast_passes/src/ast_validation.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,10 @@ 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
}

compiler/rustc_ast_passes/src/errors.rs

+8
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,10 @@ 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+
}

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

+21-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,12 @@ 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+
// TODO: negative polarity can't have associated type bindings!
705+
703706
// Specify type to assert that error was already reported in `Err` case.
704707
let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding(
705708
hir_id,
@@ -711,6 +714,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
711714
binding_span.unwrap_or(binding.span),
712715
constness,
713716
only_self_bounds,
717+
polarity,
714718
);
715719
// Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
716720
}
@@ -743,6 +747,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
743747
trait_ref: &hir::TraitRef<'_>,
744748
span: Span,
745749
constness: ty::BoundConstness,
750+
polarity: ty::ImplPolarity,
746751
self_ty: Ty<'tcx>,
747752
bounds: &mut Bounds<'tcx>,
748753
speculative: bool,
@@ -764,6 +769,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
764769
span,
765770
binding_span,
766771
constness,
772+
polarity,
767773
bounds,
768774
speculative,
769775
trait_ref_span,
@@ -799,6 +805,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
799805
span,
800806
binding_span,
801807
constness,
808+
ty::ImplPolarity::Positive,
802809
bounds,
803810
speculative,
804811
trait_ref_span,
@@ -961,16 +968,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
961968
for ast_bound in ast_bounds {
962969
match ast_bound {
963970
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,
971+
let (constness, polarity) = match modifier {
972+
hir::TraitBoundModifier::MaybeConst => {
973+
(ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive)
974+
}
975+
hir::TraitBoundModifier::None => {
976+
(ty::BoundConstness::NotConst, ty::ImplPolarity::Positive)
977+
}
978+
hir::TraitBoundModifier::Negative => {
979+
(ty::BoundConstness::NotConst, ty::ImplPolarity::Negative)
980+
}
967981
hir::TraitBoundModifier::Maybe => continue,
968982
};
969-
970983
let _ = self.instantiate_poly_trait_ref(
971984
&poly_trait_ref.trait_ref,
972985
poly_trait_ref.span,
973986
constness,
987+
polarity,
974988
param_ty,
975989
bounds,
976990
false,
@@ -1088,6 +1102,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10881102
path_span: Span,
10891103
constness: ty::BoundConstness,
10901104
only_self_bounds: OnlySelfBounds,
1105+
polarity: ty::ImplPolarity,
10911106
) -> Result<(), ErrorGuaranteed> {
10921107
// Given something like `U: SomeTrait<T = X>`, we want to produce a
10931108
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
@@ -1438,6 +1453,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14381453
&trait_bound.trait_ref,
14391454
trait_bound.span,
14401455
ty::BoundConstness::NotConst,
1456+
ty::ImplPolarity::Positive,
14411457
dummy_self,
14421458
&mut bounds,
14431459
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_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

compiler/rustc_parse/src/errors.rs

+4-27
Original file line numberDiff line numberDiff line change
@@ -2280,31 +2280,6 @@ pub(crate) struct InvalidDynKeyword {
22802280
pub span: Span,
22812281
}
22822282

2283-
#[derive(Diagnostic)]
2284-
#[diag(parse_negative_bounds_not_supported)]
2285-
pub(crate) struct NegativeBoundsNotSupported {
2286-
#[primary_span]
2287-
pub negative_bounds: Vec<Span>,
2288-
#[label]
2289-
pub last_span: Span,
2290-
#[subdiagnostic]
2291-
pub sub: Option<NegativeBoundsNotSupportedSugg>,
2292-
}
2293-
2294-
#[derive(Subdiagnostic)]
2295-
#[suggestion(
2296-
parse_suggestion,
2297-
style = "tool-only",
2298-
code = "{fixed}",
2299-
applicability = "machine-applicable"
2300-
)]
2301-
pub(crate) struct NegativeBoundsNotSupportedSugg {
2302-
#[primary_span]
2303-
pub bound_list: Span,
2304-
pub num_bounds: usize,
2305-
pub fixed: String,
2306-
}
2307-
23082283
#[derive(Subdiagnostic)]
23092284
pub enum HelpUseLatestEdition {
23102285
#[help(parse_help_set_edition_cargo)]
@@ -2412,10 +2387,12 @@ pub(crate) struct TildeConstLifetime {
24122387
}
24132388

24142389
#[derive(Diagnostic)]
2415-
#[diag(parse_maybe_lifetime)]
2416-
pub(crate) struct MaybeLifetime {
2390+
#[diag(parse_modifier_lifetime)]
2391+
pub(crate) struct ModifierLifetime {
24172392
#[primary_span]
2393+
#[suggestion(style = "tool-only", applicability = "maybe-incorrect", code = "")]
24182394
pub span: Span,
2395+
pub sigil: &'static str,
24192396
}
24202397

24212398
#[derive(Diagnostic)]

compiler/rustc_parse/src/parser/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,7 @@ impl<'a> Parser<'a> {
12841284
}
12851285

12861286
self.bump(); // `+`
1287-
let bounds = self.parse_generic_bounds(None)?;
1287+
let bounds = self.parse_generic_bounds()?;
12881288
let sum_span = ty.span.to(self.prev_token.span);
12891289

12901290
let sub = match &ty.kind {

0 commit comments

Comments
 (0)