diff options
| author | Michael Goulet <michael@errs.io> | 2023-04-25 05:15:50 +0000 |
|---|---|---|
| committer | Michael Goulet <michael@errs.io> | 2023-05-02 22:36:24 +0000 |
| commit | 6e01e910cb8ab0109235be7cc7ab7ef465724255 (patch) | |
| tree | d63e7272022d540f0a93d43f36e359ff31c3f56d | |
| parent | 98c33e47a495fbd7b22bce9ce32f2815991bc414 (diff) | |
| download | rust-6e01e910cb8ab0109235be7cc7ab7ef465724255.tar.gz rust-6e01e910cb8ab0109235be7cc7ab7ef465724255.zip | |
Implement negative bounds
33 files changed, 360 insertions, 185 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index dce7106d128..b5dba0713bf 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -287,12 +287,20 @@ pub enum TraitBoundModifier { /// No modifiers None, + /// `!Trait` + Negative, + /// `?Trait` Maybe, /// `~const Trait` MaybeConst, + /// `~const !Trait` + // + // This parses but will be rejected during AST validation. + MaybeConstNegative, + /// `~const ?Trait` // // This parses but will be rejected during AST validation. @@ -2446,6 +2454,16 @@ impl fmt::Debug for ImplPolarity { } } +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub enum BoundPolarity { + /// `Type: Trait` + Positive, + /// `Type: !Trait` + Negative(Span), + /// `Type: ?Trait` + Maybe(Span), +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum FnRetTy { /// Returns type is not specified. diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b5b28bf8e31..4100efb6eb3 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1368,13 +1368,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { this.arena.alloc_from_iter(bounds.iter().filter_map(|bound| match bound { GenericBound::Trait( ty, - TraitBoundModifier::None | TraitBoundModifier::MaybeConst, + TraitBoundModifier::None + | TraitBoundModifier::MaybeConst + | TraitBoundModifier::Negative, ) => Some(this.lower_poly_trait_ref(ty, itctx)), // `~const ?Bound` will cause an error during AST validation // anyways, so treat it like `?Bound` as compilation proceeds. GenericBound::Trait( _, - TraitBoundModifier::Maybe | TraitBoundModifier::MaybeConstMaybe, + TraitBoundModifier::Maybe + | TraitBoundModifier::MaybeConstMaybe + | TraitBoundModifier::MaybeConstNegative, ) => None, GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { @@ -2421,11 +2425,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TraitBoundModifier::None => hir::TraitBoundModifier::None, TraitBoundModifier::MaybeConst => hir::TraitBoundModifier::MaybeConst, + TraitBoundModifier::Negative => { + if self.tcx.features().negative_bounds { + hir::TraitBoundModifier::Negative + } else { + hir::TraitBoundModifier::None + } + } + // `MaybeConstMaybe` will cause an error during AST validation, but we need to pick a // placeholder for compilation to proceed. TraitBoundModifier::MaybeConstMaybe | TraitBoundModifier::Maybe => { hir::TraitBoundModifier::Maybe } + TraitBoundModifier::MaybeConstNegative => hir::TraitBoundModifier::MaybeConst, } } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index ffca37ae9d5..90535af781d 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -206,7 +206,7 @@ ast_passes_tilde_const_disallowed = `~const` is not allowed here .closure = closures cannot have `~const` trait bounds .function = this function is not `const`, so it cannot have `~const` trait bounds -ast_passes_optional_const_exclusive = `~const` and `?` are mutually exclusive +ast_passes_optional_const_exclusive = `~const` and `{$modifier}` are mutually exclusive ast_passes_const_and_async = functions cannot be both `const` and `async` .const = `const` because of this @@ -235,3 +235,6 @@ ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using t .help = remove one of these features ast_passes_show_span = {$msg} + +ast_passes_negative_bound_not_supported = + negative bounds are not supported diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c79626ccd76..6e9d33516ce 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1168,7 +1168,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } (_, TraitBoundModifier::MaybeConstMaybe) => { - self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span()}); + self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "?" }); + } + (_, TraitBoundModifier::MaybeConstNegative) => { + self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "!" }); } _ => {} } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 1732865f0bb..66a47db9074 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -567,6 +567,7 @@ pub enum TildeConstReason { pub struct OptionalConstExclusive { #[primary_span] pub span: Span, + pub modifier: &'static str, } #[derive(Diagnostic)] @@ -693,3 +694,10 @@ pub struct ShowSpan { pub span: Span, pub msg: &'static str, } + +#[derive(Diagnostic)] +#[diag(ast_passes_negative_bound_not_supported)] +pub struct NegativeBoundUnsupported { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 17bcd24ee39..77e83fdbbaa 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -603,6 +603,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(dyn_star, "`dyn*` trait objects are experimental"); gate_all!(const_closures, "const closures are experimental"); + if !visitor.features.negative_bounds { + for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { + sess.emit_err(errors::NegativeBoundUnsupported { span }); + } + } + // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). // We emit an early future-incompatible warning for these. diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 849336c8669..ae346510ccc 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1570,12 +1570,19 @@ impl<'a> State<'a> { GenericBound::Trait(tref, modifier) => { match modifier { TraitBoundModifier::None => {} + TraitBoundModifier::Negative => { + self.word("!"); + } TraitBoundModifier::Maybe => { self.word("?"); } TraitBoundModifier::MaybeConst => { self.word_space("~const"); } + TraitBoundModifier::MaybeConstNegative => { + self.word_space("~const"); + self.word("!"); + } TraitBoundModifier::MaybeConstMaybe => { self.word_space("~const"); self.word("?"); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 6201e5b619b..294ed18a239 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -164,6 +164,8 @@ declare_features! ( (active, link_cfg, "1.14.0", None, None), /// Allows the `multiple_supertrait_upcastable` lint. (active, multiple_supertrait_upcastable, "1.69.0", None, None), + /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! + (incomplete, negative_bounds, "CURRENT_RUSTC_VERSION", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), /// Allows using `#[prelude_import]` on glob `use` items. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e220a029339..38cd5865cc3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -435,6 +435,7 @@ pub enum GenericArgsParentheses { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub enum TraitBoundModifier { None, + Negative, Maybe, MaybeConst, } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 6cb008bc5f8..0faf7a4ba45 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -665,6 +665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, binding_span: Option<Span>, constness: ty::BoundConstness, + polarity: ty::ImplPolarity, bounds: &mut Bounds<'tcx>, speculative: bool, trait_ref_span: Span, @@ -696,10 +697,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::Binder::bind_with_vars(tcx.mk_trait_ref(trait_def_id, substs), bound_vars); debug!(?poly_trait_ref, ?assoc_bindings); - bounds.push_trait_bound(tcx, poly_trait_ref, span, constness); + bounds.push_trait_bound(tcx, poly_trait_ref, span, constness, polarity); let mut dup_bindings = FxHashMap::default(); for binding in &assoc_bindings { + // TODO: negative polarity can't have associated type bindings! + // Specify type to assert that error was already reported in `Err` case. let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding( hir_id, @@ -711,6 +714,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { binding_span.unwrap_or(binding.span), constness, only_self_bounds, + polarity, ); // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). } @@ -743,6 +747,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_ref: &hir::TraitRef<'_>, span: Span, constness: ty::BoundConstness, + polarity: ty::ImplPolarity, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, speculative: bool, @@ -764,6 +769,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, binding_span, constness, + polarity, bounds, speculative, trait_ref_span, @@ -799,6 +805,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, binding_span, constness, + ty::ImplPolarity::Positive, bounds, speculative, trait_ref_span, @@ -961,16 +968,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for ast_bound in ast_bounds { match ast_bound { hir::GenericBound::Trait(poly_trait_ref, modifier) => { - let constness = match modifier { - hir::TraitBoundModifier::MaybeConst => ty::BoundConstness::ConstIfConst, - hir::TraitBoundModifier::None => ty::BoundConstness::NotConst, + let (constness, polarity) = match modifier { + hir::TraitBoundModifier::MaybeConst => { + (ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive) + } + hir::TraitBoundModifier::None => { + (ty::BoundConstness::NotConst, ty::ImplPolarity::Positive) + } + hir::TraitBoundModifier::Negative => { + (ty::BoundConstness::NotConst, ty::ImplPolarity::Negative) + } hir::TraitBoundModifier::Maybe => continue, }; - let _ = self.instantiate_poly_trait_ref( &poly_trait_ref.trait_ref, poly_trait_ref.span, constness, + polarity, param_ty, bounds, false, @@ -1088,6 +1102,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { path_span: Span, constness: ty::BoundConstness, only_self_bounds: OnlySelfBounds, + polarity: ty::ImplPolarity, ) -> Result<(), ErrorGuaranteed> { // Given something like `U: SomeTrait<T = X>`, we want to produce a // predicate like `<U as SomeTrait>::T = X`. This is somewhat @@ -1438,6 +1453,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &trait_bound.trait_ref, trait_bound.span, ty::BoundConstness::NotConst, + ty::ImplPolarity::Positive, dummy_self, &mut bounds, false, diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 284b099e7bc..7156fea8f89 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -42,8 +42,14 @@ impl<'tcx> Bounds<'tcx> { trait_ref: ty::PolyTraitRef<'tcx>, span: Span, constness: ty::BoundConstness, + polarity: ty::ImplPolarity, ) { - self.predicates.push((trait_ref.with_constness(constness).to_predicate(tcx), span)); + self.predicates.push(( + trait_ref + .map_bound(|trait_ref| ty::TraitPredicate { trait_ref, constness, polarity }) + .to_predicate(tcx), + span, + )); } pub fn push_projection_bound( diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 961457b7579..3fe34f23aef 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -528,6 +528,7 @@ pub fn hir_trait_to_predicates<'tcx>( hir_trait, DUMMY_SP, ty::BoundConstness::NotConst, + ty::ImplPolarity::Positive, self_ty, &mut bounds, true, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2aced27f7bb..6ac9f950450 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2816,6 +2816,9 @@ define_print_and_forward_display! { if let ty::BoundConstness::ConstIfConst = self.constness && cx.tcx().features().const_trait_impl { p!("~const "); } + if let ty::ImplPolarity::Negative = self.polarity { + p!("!"); + } p!(print(self.trait_ref.print_only_trait_path())) } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9a5232b1bcd..cd296dca133 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -615,13 +615,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword .help = `dyn` is only needed at the start of a trait `+`-separated list .suggestion = remove this keyword -parse_negative_bounds_not_supported = negative bounds are not supported - .label = negative bounds are not supported - .suggestion = {$num_bounds -> - [one] remove the bound - *[other] remove the bounds - } - parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` 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 parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds -parse_maybe_lifetime = `?` may only modify trait bounds, not lifetime bounds +parse_modifier_lifetime = `{$sigil}` may only modify trait bounds, not lifetime bounds + .suggestion = remove the `{$sigil}` parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported .suggestion = remove the parentheses diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index b445ccc7ad0..010a13aefa4 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2280,31 +2280,6 @@ pub(crate) struct InvalidDynKeyword { pub span: Span, } -#[derive(Diagnostic)] -#[diag(parse_negative_bounds_not_supported)] -pub(crate) struct NegativeBoundsNotSupported { - #[primary_span] - pub negative_bounds: Vec<Span>, - #[label] - pub last_span: Span, - #[subdiagnostic] - pub sub: Option<NegativeBoundsNotSupportedSugg>, -} - -#[derive(Subdiagnostic)] -#[suggestion( - parse_suggestion, - style = "tool-only", - code = "{fixed}", - applicability = "machine-applicable" -)] -pub(crate) struct NegativeBoundsNotSupportedSugg { - #[primary_span] - pub bound_list: Span, - pub num_bounds: usize, - pub fixed: String, -} - #[derive(Subdiagnostic)] pub enum HelpUseLatestEdition { #[help(parse_help_set_edition_cargo)] @@ -2412,10 +2387,12 @@ pub(crate) struct TildeConstLifetime { } #[derive(Diagnostic)] -#[diag(parse_maybe_lifetime)] -pub(crate) struct MaybeLifetime { +#[diag(parse_modifier_lifetime)] +pub(crate) struct ModifierLifetime { #[primary_span] + #[suggestion(style = "tool-only", applicability = "maybe-incorrect", code = "")] pub span: Span, + pub sigil: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 638a432cea5..e974df61dc9 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1284,7 +1284,7 @@ impl<'a> Parser<'a> { } self.bump(); // `+` - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; let sum_span = ty.span.to(self.prev_token.span); let sub = match &ty.kind { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 61a7ae93bfa..e6d0f9fbc76 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -78,7 +78,7 @@ impl<'a> Parser<'a> { } self.restore_snapshot(snapshot); } - self.parse_generic_bounds(colon_span)? + self.parse_generic_bounds()? } else { Vec::new() }; @@ -419,7 +419,7 @@ impl<'a> Parser<'a> { // or with mandatory equality sign and the second type. let ty = self.parse_ty_for_where_clause()?; if self.eat(&token::Colon) { - let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; + let bounds = self.parse_generic_bounds()?; Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { span: lo.to(self.prev_token.span), bound_generic_params: lifetime_defs, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 64ff7f1fb2c..10a95cae3e1 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -788,11 +788,7 @@ impl<'a> Parser<'a> { // Parse optional colon and supertrait bounds. let had_colon = self.eat(&token::Colon); let span_at_colon = self.prev_token.span; - let bounds = if had_colon { - self.parse_generic_bounds(Some(self.prev_token.span))? - } else { - Vec::new() - }; + let bounds = if had_colon { self.parse_generic_bounds()? } else { Vec::new() }; let span_before_eq = self.prev_token.span; if self.eat(&token::Eq) { @@ -802,7 +798,7 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::BoundsNotAllowedOnTraitAliases { span }); } - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; generics.where_clause = self.parse_where_clause()?; self.expect_semi()?; @@ -883,7 +879,7 @@ impl<'a> Parser<'a> { // Parse optional colon and param bounds. let bounds = - if self.eat(&token::Colon) { self.parse_generic_bounds(None)? } else { Vec::new() }; + if self.eat(&token::Colon) { self.parse_generic_bounds()? } else { Vec::new() }; let before_where_clause = self.parse_where_clause()?; let ty = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index b9a2b141bce..205d4d15e2e 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -606,7 +606,7 @@ impl<'a> Parser<'a> { let kind = if self.eat(&token::Colon) { // Parse associated type constraint bound. - let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; + let bounds = self.parse_generic_bounds()?; AssocConstraintKind::Bound { bounds } } else if self.eat(&token::Eq) { self.parse_assoc_equality_term(ident, self.prev_token.span)? diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 37c441fbecb..04f7eea90ed 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -3,8 +3,7 @@ use super::{Parser, PathStyle, TokenType}; use crate::errors::{ self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg, - InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, - NegativeBoundsNotSupported, NegativeBoundsNotSupportedSugg, NestedCVariadicType, + InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -14,8 +13,9 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::{ - self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, - MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, + self as ast, BareFnTy, BoundPolarity, FnRetTy, GenericBound, GenericBounds, GenericParam, + Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, + TraitObjectSyntax, Ty, TyKind, }; use rustc_errors::{Applicability, PResult}; use rustc_span::source_map::Span; @@ -23,10 +23,10 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use thin_vec::{thin_vec, ThinVec}; -/// Any `?` or `~const` modifiers that appear at the start of a bound. +/// Any `?`, `!`, or `~const` modifiers that appear at the start of a bound. struct BoundModifiers { /// `?Trait`. - maybe: Option<Span>, + bound_polarity: BoundPolarity, /// `~const Trait`. maybe_const: Option<Span>, @@ -34,11 +34,13 @@ struct BoundModifiers { impl BoundModifiers { fn to_trait_bound_modifier(&self) -> TraitBoundModifier { - match (self.maybe, self.maybe_const) { - (None, None) => TraitBoundModifier::None, - (Some(_), None) => TraitBoundModifier::Maybe, - (None, Some(_)) => TraitBoundModifier::MaybeConst, - (Some(_), Some(_)) => TraitBoundModifier::MaybeConstMaybe, + match (self.bound_polarity, self.maybe_const) { + (BoundPolarity::Positive, None) => TraitBoundModifier::None, + (BoundPolarity::Negative(_), None) => TraitBoundModifier::Negative, + (BoundPolarity::Maybe(_), None) => TraitBoundModifier::Maybe, + (BoundPolarity::Positive, Some(_)) => TraitBoundModifier::MaybeConst, + (BoundPolarity::Negative(_), Some(_)) => TraitBoundModifier::MaybeConstNegative, + (BoundPolarity::Maybe(_), Some(_)) => TraitBoundModifier::MaybeConstMaybe, } } } @@ -368,7 +370,7 @@ impl<'a> Parser<'a> { fn parse_bare_trait_object(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { let lt_no_plus = self.check_lifetime() && !self.look_ahead(1, |t| t.is_like_plus()); - let bounds = self.parse_generic_bounds_common(allow_plus, None)?; + let bounds = self.parse_generic_bounds_common(allow_plus)?; if lt_no_plus { self.sess.emit_err(NeedPlusAfterTraitObjectLifetime { span: lo }); } @@ -395,7 +397,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, TyKind> { if plus { self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded - bounds.append(&mut self.parse_generic_bounds(Some(self.prev_token.span))?); + bounds.append(&mut self.parse_generic_bounds()?); } Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } @@ -598,7 +600,7 @@ impl<'a> Parser<'a> { } }) } - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus); Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)) } @@ -629,7 +631,7 @@ impl<'a> Parser<'a> { }; // Always parse bounds greedily for better error recovery. - let bounds = self.parse_generic_bounds(None)?; + let bounds = self.parse_generic_bounds()?; *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus); Ok(TyKind::TraitObject(bounds, syntax)) } @@ -660,23 +662,15 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_generic_bounds( - &mut self, - colon_span: Option<Span>, - ) -> PResult<'a, GenericBounds> { - self.parse_generic_bounds_common(AllowPlus::Yes, colon_span) + pub(super) fn parse_generic_bounds(&mut self) -> PResult<'a, GenericBounds> { + self.parse_generic_bounds_common(AllowPlus::Yes) } /// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`. /// /// See `parse_generic_bound` for the `BOUND` grammar. - fn parse_generic_bounds_common( - &mut self, - allow_plus: AllowPlus, - colon_span: Option<Span>, - ) -> PResult<'a, GenericBounds> { + fn parse_generic_bounds_common(&mut self, allow_plus: AllowPlus) -> PResult<'a, GenericBounds> { let mut bounds = Vec::new(); - let mut negative_bounds = Vec::new(); // In addition to looping while we find generic bounds: // We continue even if we find a keyword. This is necessary for error recovery on, @@ -693,19 +687,12 @@ impl<'a> Parser<'a> { self.sess.emit_err(InvalidDynKeyword { span: self.token.span }); self.bump(); } - match self.parse_generic_bound()? { - Ok(bound) => bounds.push(bound), - Err(neg_sp) => negative_bounds.push(neg_sp), - } + bounds.push(self.parse_generic_bound()?); if allow_plus == AllowPlus::No || !self.eat_plus() { break; } } - if !negative_bounds.is_empty() { - self.error_negative_bounds(colon_span, &bounds, negative_bounds); - } - Ok(bounds) } @@ -713,55 +700,22 @@ impl<'a> Parser<'a> { fn can_begin_bound(&mut self) -> bool { // This needs to be synchronized with `TokenKind::can_begin_bound`. self.check_path() - || self.check_lifetime() - || self.check(&token::Not) // Used for error reporting only. - || self.check(&token::Question) - || self.check(&token::Tilde) - || self.check_keyword(kw::For) - || self.check(&token::OpenDelim(Delimiter::Parenthesis)) - } - - fn error_negative_bounds( - &self, - colon_span: Option<Span>, - bounds: &[GenericBound], - negative_bounds: Vec<Span>, - ) { - let sub = if let Some(bound_list) = colon_span { - let bound_list = bound_list.to(self.prev_token.span); - let mut new_bound_list = String::new(); - if !bounds.is_empty() { - let mut snippets = bounds.iter().map(|bound| self.span_to_snippet(bound.span())); - while let Some(Ok(snippet)) = snippets.next() { - new_bound_list.push_str(" + "); - new_bound_list.push_str(&snippet); - } - new_bound_list = new_bound_list.replacen(" +", ":", 1); - } - - Some(NegativeBoundsNotSupportedSugg { - bound_list, - num_bounds: negative_bounds.len(), - fixed: new_bound_list, - }) - } else { - None - }; - - let last_span = *negative_bounds.last().expect("no negative bounds, but still error?"); - self.sess.emit_err(NegativeBoundsNotSupported { negative_bounds, last_span, sub }); + || self.check_lifetime() + || self.check(&token::Not) + || self.check(&token::Question) + || self.check(&token::Tilde) + || self.check_keyword(kw::For) + || self.check(&token::OpenDelim(Delimiter::Parenthesis)) } /// Parses a bound according to the grammar: /// ```ebnf /// BOUND = TY_BOUND | LT_BOUND /// ``` - fn parse_generic_bound(&mut self) -> PResult<'a, Result<GenericBound, Span>> { - let anchor_lo = self.prev_token.span; + fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> { let lo = self.token.span; let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis)); let inner_lo = self.token.span; - let is_negative = self.eat(&token::Not); let modifiers = self.parse_ty_bound_modifiers()?; let bound = if self.token.is_lifetime() { @@ -771,7 +725,7 @@ impl<'a> Parser<'a> { self.parse_generic_ty_bound(lo, has_parens, modifiers)? }; - Ok(if is_negative { Err(anchor_lo.to(self.prev_token.span)) } else { Ok(bound) }) + Ok(bound) } /// Parses a lifetime ("outlives") bound, e.g. `'a`, according to: @@ -799,8 +753,14 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::TildeConstLifetime { span }); } - if let Some(span) = modifiers.maybe { - self.sess.emit_err(errors::MaybeLifetime { span }); + match modifiers.bound_polarity { + BoundPolarity::Positive => {} + BoundPolarity::Negative(span) => { + self.sess.emit_err(errors::ModifierLifetime { span, sigil: "!" }); + } + BoundPolarity::Maybe(span) => { + self.sess.emit_err(errors::ModifierLifetime { span, sigil: "?" }); + } } } @@ -843,9 +803,16 @@ impl<'a> Parser<'a> { None }; - let maybe = self.eat(&token::Question).then_some(self.prev_token.span); + let bound_polarity = if self.eat(&token::Question) { + BoundPolarity::Maybe(self.prev_token.span) + } else if self.eat(&token::Not) { + self.sess.gated_spans.gate(sym::negative_bounds, self.prev_token.span); + BoundPolarity::Negative(self.prev_token.span) + } else { + BoundPolarity::Positive + }; - Ok(BoundModifiers { maybe, maybe_const }) + Ok(BoundModifiers { bound_polarity, maybe_const }) } /// Parses a type bound according to: diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7969b848fd9..714d10f2341 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -984,6 +984,7 @@ symbols! { needs_panic_runtime, neg, negate_unsigned, + negative_bounds, negative_impls, neon, never, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 595f6e0b927..d87a2979428 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -530,6 +530,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, ) { + if trait_pred.skip_binder().polarity == ty::ImplPolarity::Negative { + return; + } + let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); let self_ty = trait_pred.skip_binder().self_ty(); diff --git a/tests/ui/feature-gates/feature-gate-negative_bounds.rs b/tests/ui/feature-gates/feature-gate-negative_bounds.rs new file mode 100644 index 00000000000..533cb0ce5bc --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-negative_bounds.rs @@ -0,0 +1,4 @@ +fn test<T: !Copy>() {} +//~^ ERROR negative bounds are not supported + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-negative_bounds.stderr b/tests/ui/feature-gates/feature-gate-negative_bounds.stderr new file mode 100644 index 00000000000..ae010fdf3f8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-negative_bounds.stderr @@ -0,0 +1,8 @@ +error: negative bounds are not supported + --> $DIR/feature-gate-negative_bounds.rs:1:12 + | +LL | fn test<T: !Copy>() {} + | ^ + +error: aborting due to previous error + diff --git a/tests/ui/issues/issue-58857.stderr b/tests/ui/issues/issue-58857.stderr index e2acec47e5a..6aef35f0bb9 100644 --- a/tests/ui/issues/issue-58857.stderr +++ b/tests/ui/issues/issue-58857.stderr @@ -1,8 +1,8 @@ error: negative bounds are not supported - --> $DIR/issue-58857.rs:4:7 + --> $DIR/issue-58857.rs:4:9 | LL | impl<A: !Valid> Conj<A>{} - | ^^^^^^^^ negative bounds are not supported + | ^ error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-33418.fixed b/tests/ui/parser/issues/issue-33418.fixed deleted file mode 100644 index ed885ae1435..00000000000 --- a/tests/ui/parser/issues/issue-33418.fixed +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -trait Tr {} -//~^ ERROR negative bounds are not supported -trait Tr2: SuperA {} -//~^ ERROR negative bounds are not supported -trait Tr3: SuperB {} -//~^ ERROR negative bounds are not supported -trait Tr4: SuperB + SuperD {} -//~^ ERROR negative bounds are not supported -trait Tr5 {} -//~^ ERROR negative bounds are not supported - -trait SuperA {} -trait SuperB {} -trait SuperC {} -trait SuperD {} - -fn main() {} diff --git a/tests/ui/parser/issues/issue-33418.rs b/tests/ui/parser/issues/issue-33418.rs index 9934284abfb..4ebd5871e53 100644 --- a/tests/ui/parser/issues/issue-33418.rs +++ b/tests/ui/parser/issues/issue-33418.rs @@ -1,5 +1,3 @@ -// run-rustfix - trait Tr: !SuperA {} //~^ ERROR negative bounds are not supported trait Tr2: SuperA + !SuperB {} @@ -7,10 +5,12 @@ trait Tr2: SuperA + !SuperB {} trait Tr3: !SuperA + SuperB {} //~^ ERROR negative bounds are not supported trait Tr4: !SuperA + SuperB - + !SuperC + SuperD {} +//~^ ERROR negative bounds are not supported ++ !SuperC + SuperD {} //~^ ERROR negative bounds are not supported trait Tr5: !SuperA - + !SuperB {} +//~^ ERROR negative bounds are not supported ++ !SuperB {} //~^ ERROR negative bounds are not supported trait SuperA {} diff --git a/tests/ui/parser/issues/issue-33418.stderr b/tests/ui/parser/issues/issue-33418.stderr index 9a8733e8929..b111bcfd240 100644 --- a/tests/ui/parser/issues/issue-33418.stderr +++ b/tests/ui/parser/issues/issue-33418.stderr @@ -1,36 +1,44 @@ error: negative bounds are not supported - --> $DIR/issue-33418.rs:3:9 + --> $DIR/issue-33418.rs:1:11 | LL | trait Tr: !SuperA {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:5:19 + --> $DIR/issue-33418.rs:3:21 | LL | trait Tr2: SuperA + !SuperB {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:7:10 + --> $DIR/issue-33418.rs:5:12 | LL | trait Tr3: !SuperA + SuperB {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:9:10 + --> $DIR/issue-33418.rs:7:12 | LL | trait Tr4: !SuperA + SuperB - | ^^^^^^^^^ -LL | + !SuperC + SuperD {} - | ^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-33418.rs:12:10 + --> $DIR/issue-33418.rs:9:3 + | +LL | + !SuperC + SuperD {} + | ^ + +error: negative bounds are not supported + --> $DIR/issue-33418.rs:11:12 | LL | trait Tr5: !SuperA - | ^^^^^^^^^ -LL | + !SuperB {} - | ^^^^^^^^^ negative bounds are not supported + | ^ + +error: negative bounds are not supported + --> $DIR/issue-33418.rs:13:3 + | +LL | + !SuperB {} + | ^ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed index 95019b27869..2c42f973174 100644 --- a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed +++ b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.fixed @@ -6,9 +6,12 @@ fn main() {} -pub fn f1<T>() {} +pub fn f1<T: 'static>() {} //~^ ERROR negative bounds are not supported -pub fn f2<'a, T: Ord>() {} +//~| ERROR `!` may only modify trait bounds, not lifetime bound +pub fn f2<'a, T: Ord + 'a>() {} //~^ ERROR negative bounds are not supported -pub fn f3<'a, T: Ord>() {} +//~| ERROR `!` may only modify trait bounds, not lifetime bound +pub fn f3<'a, T: 'a + Ord>() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound diff --git a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs index 82f54f8faa9..e510efaae5b 100644 --- a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs +++ b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.rs @@ -8,7 +8,10 @@ fn main() {} pub fn f1<T: !'static>() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound pub fn f2<'a, T: Ord + !'a>() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound pub fn f3<'a, T: !'a + Ord>() {} //~^ ERROR negative bounds are not supported +//~| ERROR `!` may only modify trait bounds, not lifetime bound diff --git a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr index a4a422948ac..91fe02db3a6 100644 --- a/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr +++ b/tests/ui/parser/issues/issue-67146-negative-outlives-bound-syntactic-fail.stderr @@ -1,20 +1,38 @@ +error: `!` may only modify trait bounds, not lifetime bounds + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:14 + | +LL | pub fn f1<T: !'static>() {} + | ^ + +error: `!` may only modify trait bounds, not lifetime bounds + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:12:24 + | +LL | pub fn f2<'a, T: Ord + !'a>() {} + | ^ + +error: `!` may only modify trait bounds, not lifetime bounds + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:15:18 + | +LL | pub fn f3<'a, T: !'a + Ord>() {} + | ^ + error: negative bounds are not supported - --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:12 + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:14 | LL | pub fn f1<T: !'static>() {} - | ^^^^^^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:11:22 + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:12:24 | LL | pub fn f2<'a, T: Ord + !'a>() {} - | ^^^^^ negative bounds are not supported + | ^ error: negative bounds are not supported - --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:13:16 + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:15:18 | LL | pub fn f3<'a, T: !'a + Ord>() {} - | ^^^^^ negative bounds are not supported + | ^ -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/traits/negative-bounds/simple.rs b/tests/ui/traits/negative-bounds/simple.rs new file mode 100644 index 00000000000..f6d1d5169c4 --- /dev/null +++ b/tests/ui/traits/negative-bounds/simple.rs @@ -0,0 +1,42 @@ +#![feature(negative_bounds, negative_impls)] +//~^ WARN the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + +fn not_copy<T: !Copy>() {} + +fn neg_param_env<T: !Copy>() { + not_copy::<T>(); +} + +fn pos_param_env<T: Copy>() { + not_copy::<T>(); + //~^ ERROR the trait bound `T: !Copy` is not satisfied +} + +fn unknown<T>() { + not_copy::<T>(); + //~^ ERROR the trait bound `T: !Copy` is not satisfied +} + +struct NotCopyable; +impl !Copy for NotCopyable {} + +fn neg_impl() { + not_copy::<NotCopyable>(); +} + +#[derive(Copy, Clone)] +struct Copyable; + +fn pos_impl() { + not_copy::<Copyable>(); + //~^ ERROR the trait bound `Copyable: !Copy` is not satisfied +} + +struct NotNecessarilyCopyable; + +fn unknown_impl() { + not_copy::<NotNecessarilyCopyable>(); + //~^ ERROR the trait bound `NotNecessarilyCopyable: !Copy` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/negative-bounds/simple.stderr b/tests/ui/traits/negative-bounds/simple.stderr new file mode 100644 index 00000000000..efd82c09d62 --- /dev/null +++ b/tests/ui/traits/negative-bounds/simple.stderr @@ -0,0 +1,82 @@ +warning: the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/simple.rs:1:12 + | +LL | #![feature(negative_bounds, negative_impls)] + | ^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `T: !Copy` is not satisfied + --> $DIR/simple.rs:6:16 + | +LL | not_copy::<T>(); + | ^ the trait `!Copy` is not implemented for `T` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:3:16 + | +LL | fn not_copy<T: !Copy>() {} + | ^^^^^ required by this bound in `not_copy` + +error[E0277]: the trait bound `T: !Copy` is not satisfied + --> $DIR/simple.rs:10:16 + | +LL | not_copy::<T>(); + | ^ the trait `!Copy` is not implemented for `T` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy<T: !Copy>() {} + | ^^^^^ required by this bound in `not_copy` + +error[E0277]: the trait bound `T: !Copy` is not satisfied + --> $DIR/simple.rs:16:16 + | +LL | not_copy::<T>(); + | ^ the trait `!Copy` is not implemented for `T` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy<T: !Copy>() {} + | ^^^^^ required by this bound in `not_copy` + +error[E0277]: the trait bound `Copyable: !Copy` is not satisfied + --> $DIR/simple.rs:31:16 + | +LL | not_copy::<Copyable>(); + | ^^^^^^^^ the trait `!Copy` is not implemented for `Copyable` + | + = help: the trait `Copy` is implemented for `Copyable` +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy<T: !Copy>() {} + | ^^^^^ required by this bound in `not_copy` +help: consider annotating `Copyable` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct Copyable; + | + +error[E0277]: the trait bound `NotNecessarilyCopyable: !Copy` is not satisfied + --> $DIR/simple.rs:38:16 + | +LL | not_copy::<NotNecessarilyCopyable>(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `!Copy` is not implemented for `NotNecessarilyCopyable` + | +note: required by a bound in `not_copy` + --> $DIR/simple.rs:4:16 + | +LL | fn not_copy<T: !Copy>() {} + | ^^^^^ required by this bound in `not_copy` +help: consider annotating `NotNecessarilyCopyable` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct NotNecessarilyCopyable; + | + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. |
