diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index dce7106d12804..b5dba0713bfa0 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 b5b28bf8e31e5..4100efb6eb330 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 ffca37ae9d5f6..2f413789e7704 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,9 @@ 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 + +ast_passes_constraint_on_negative_bound = + associated type constraints not allowed on negative bounds diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c79626ccd76f0..bf43bbdbbeeba 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1168,12 +1168,27 @@ 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: "!" }); } _ => {} } } + // Negative trait bounds are not allowed to have associated constraints + if let GenericBound::Trait(trait_ref, TraitBoundModifier::Negative) = bound + && let Some(segment) = trait_ref.trait_ref.path.segments.last() + && let Some(ast::GenericArgs::AngleBracketed(args)) = segment.args.as_deref() + { + for arg in &args.args { + if let ast::AngleBracketedArg::Constraint(constraint) = arg { + self.err_handler().emit_err(errors::ConstraintOnNegativeBound { span: constraint.span }); + } + } + } + visit::walk_param_bound(self, bound) } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 1732865f0bb64..82fe2a21d0876 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,17 @@ 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, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_constraint_on_negative_bound)] +pub struct ConstraintOnNegativeBound { + #[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 17bcd24ee39fd..77e83fdbbaaf8 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 849336c8669a1..ae346510ccc82 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 6201e5b619b87..294ed18a239f5 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 e220a0293393e..38cd5865cc326 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 6cb008bc5f8a0..1f01ad009dc39 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, constness: ty::BoundConstness, + polarity: ty::ImplPolarity, bounds: &mut Bounds<'tcx>, speculative: bool, trait_ref_span: Span, @@ -696,10 +697,20 @@ 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 { + // Don't register additional associated type bounds for negative bounds, + // since we should have emitten an error for them earlier, and they will + // not be well-formed! + if polarity == ty::ImplPolarity::Negative { + self.tcx() + .sess + .delay_span_bug(binding.span, "negative trait bounds should not have bindings"); + continue; + } + // 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 +722,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 +755,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 +777,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, binding_span, constness, + polarity, bounds, speculative, trait_ref_span, @@ -799,6 +813,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, binding_span, constness, + ty::ImplPolarity::Positive, bounds, speculative, trait_ref_span, @@ -961,16 +976,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 +1110,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`, we want to produce a // predicate like `::T = X`. This is somewhat @@ -1438,6 +1461,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 284b099e7bc71..7156fea8f896c 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 961457b75794a..3fe34f23aef42 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_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index ef01d5d513bbc..f54e5e5e56ffc 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -200,6 +200,10 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { let bound_predicate = elaboratable.predicate().kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + // Negative trait bounds do not imply any supertrait bounds + if data.polarity == ty::ImplPolarity::Negative { + return; + } // Get predicates implied by the trait, or only super predicates if we only care about self predicates. let predicates = if self.only_self { tcx.super_predicates_of(data.def_id()) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2aced27f7bbdc..6ac9f95045069 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 9a5232b1bcdd9..cd296dca133f5 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 b445ccc7ad0aa..010a13aefa420 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, - #[label] - pub last_span: Span, - #[subdiagnostic] - pub sub: Option, -} - -#[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 638a432cea5f1..e974df61dc966 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 61a7ae93bfa8b..e6d0f9fbc76d8 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 64ff7f1fb2c10..10a95cae3e11f 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 b9a2b141bce38..205d4d15e2e87 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 37c441fbecb96..04f7eea90ed56 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, + bound_polarity: BoundPolarity, /// `~const Trait`. maybe_const: Option, @@ -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, - ) -> 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, - ) -> 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, - bounds: &[GenericBound], - negative_bounds: Vec, - ) { - 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> { - 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 7969b848fd956..714d10f234107 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 595f6e0b9271b..d87a297942829 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/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 1db9b8ce92e4f..a8864f47ef036 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -57,6 +57,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if obligation.polarity() == ty::ImplPolarity::Negative { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; } else { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); @@ -187,6 +188,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Keep only those bounds which may apply, and propagate overflow if it occurs. for bound in matching_bounds { + if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity { + continue; + } + // FIXME(oli-obk): it is suspicious that we are dropping the constness and // polarity here. let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 40e19abc0d06a..0590e02d84ac0 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -328,6 +328,13 @@ impl<'tcx> WfPredicates<'tcx> { let tcx = self.tcx; let trait_ref = &trait_pred.trait_ref; + // Negative trait predicates don't require supertraits to hold, just + // that their substs are WF. + if trait_pred.polarity == ty::ImplPolarity::Negative { + self.compute_negative_trait_pred(trait_ref); + return; + } + // if the trait predicate is not const, the wf obligations should not be const as well. let obligations = if trait_pred.constness == ty::BoundConstness::NotConst { self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs) @@ -393,6 +400,14 @@ impl<'tcx> WfPredicates<'tcx> { ); } + // Compute the obligations that are required for `trait_ref` to be WF, + // given that it is a *negative* trait predicate. + fn compute_negative_trait_pred(&mut self, trait_ref: &ty::TraitRef<'tcx>) { + for arg in trait_ref.substs { + self.compute(arg); + } + } + /// Pushes the obligations required for `trait_ref::Item` to be WF /// into `self.out`. fn compute_projection(&mut self, data: ty::AliasTy<'tcx>) { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 297120da284b9..1c6810bdaf9be 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -439,6 +439,7 @@ impl clean::GenericBound { let modifier_str = match modifier { hir::TraitBoundModifier::None => "", hir::TraitBoundModifier::Maybe => "?", + hir::TraitBoundModifier::Negative => "!", // ~const is experimental; do not display those bounds in rustdoc hir::TraitBoundModifier::MaybeConst => "", }; diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 62aab46fa7e8b..b5bebb7059380 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -533,6 +533,10 @@ pub(crate) fn from_trait_bound_modifier( None => TraitBoundModifier::None, Maybe => TraitBoundModifier::Maybe, MaybeConst => TraitBoundModifier::MaybeConst, + // FIXME(negative-bounds): This bound should be rendered negative, but + // since that's experimental, maybe let's not add it to the rustdoc json + // API just now... + Negative => TraitBoundModifier::None, } } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 01e2fb6e61e15..f548388ed8ba2 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -552,6 +552,12 @@ impl Rewrite for ast::GenericBound { ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref .rewrite(context, shape.offset_left(8)?) .map(|s| format!("~const ?{}", s)), + ast::TraitBoundModifier::Negative => poly_trait_ref + .rewrite(context, shape.offset_left(1)?) + .map(|s| format!("!{}", s)), + ast::TraitBoundModifier::MaybeConstNegative => poly_trait_ref + .rewrite(context, shape.offset_left(8)?) + .map(|s| format!("~const !{}", s)), }; rewrite.map(|s| if has_paren { format!("({})", s) } else { s }) } diff --git a/src/tools/rustfmt/tests/target/negative-bounds.rs b/src/tools/rustfmt/tests/target/negative-bounds.rs new file mode 100644 index 0000000000000..4fb35cccf6684 --- /dev/null +++ b/src/tools/rustfmt/tests/target/negative-bounds.rs @@ -0,0 +1,11 @@ +fn negative() +where + i32: !Copy, +{ +} + +fn maybe_const_negative() +where + i32: ~const !Copy, +{ +} 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 0000000000000..533cb0ce5bcc5 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-negative_bounds.rs @@ -0,0 +1,4 @@ +fn test() {} +//~^ 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 0000000000000..ae010fdf3f848 --- /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() {} + | ^ + +error: aborting due to previous error + diff --git a/tests/ui/issues/issue-58857.stderr b/tests/ui/issues/issue-58857.stderr index e2acec47e5abf..6aef35f0bb96a 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 Conj{} - | ^^^^^^^^ 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 ed885ae143566..0000000000000 --- 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 9934284abfbbe..4ebd5871e53ca 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 9a8733e89292e..b111bcfd24093 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 95019b2786925..2c42f9731743e 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() {} +pub fn f1() {} //~^ 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 82f54f8faa98c..e510efaae5ba2 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() {} //~^ 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 a4a422948aca6..91fe02db3a606 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() {} + | ^ + +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() {} - | ^^^^^^^^^^ 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/associated-constraints.rs b/tests/ui/traits/negative-bounds/associated-constraints.rs new file mode 100644 index 0000000000000..bc1a0ef170837 --- /dev/null +++ b/tests/ui/traits/negative-bounds/associated-constraints.rs @@ -0,0 +1,20 @@ +#![feature(negative_bounds, associated_type_bounds)] +//~^ WARN the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + +trait Trait { + type Assoc; +} + +fn test>() {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn test2() where T: !Trait {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn test3>() {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn test4() where T: !Trait {} +//~^ ERROR associated type constraints not allowed on negative bounds + +fn main() {} diff --git a/tests/ui/traits/negative-bounds/associated-constraints.stderr b/tests/ui/traits/negative-bounds/associated-constraints.stderr new file mode 100644 index 0000000000000..335ac7e5ad903 --- /dev/null +++ b/tests/ui/traits/negative-bounds/associated-constraints.stderr @@ -0,0 +1,34 @@ +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:8:19 + | +LL | fn test>() {} + | ^^^^^^^^^^^ + +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:11:31 + | +LL | fn test2() where T: !Trait {} + | ^^^^^^^^^^^ + +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:14:20 + | +LL | fn test3>() {} + | ^^^^^^^^^^^ + +error: associated type constraints not allowed on negative bounds + --> $DIR/associated-constraints.rs:17:31 + | +LL | fn test4() where T: !Trait {} + | ^^^^^^^^^^^ + +warning: the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/associated-constraints.rs:1:12 + | +LL | #![feature(negative_bounds, associated_type_bounds)] + | ^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 4 previous errors; 1 warning emitted + diff --git a/tests/ui/traits/negative-bounds/simple.rs b/tests/ui/traits/negative-bounds/simple.rs new file mode 100644 index 0000000000000..f6d1d5169c4fc --- /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() {} + +fn neg_param_env() { + not_copy::(); +} + +fn pos_param_env() { + not_copy::(); + //~^ ERROR the trait bound `T: !Copy` is not satisfied +} + +fn unknown() { + not_copy::(); + //~^ ERROR the trait bound `T: !Copy` is not satisfied +} + +struct NotCopyable; +impl !Copy for NotCopyable {} + +fn neg_impl() { + not_copy::(); +} + +#[derive(Copy, Clone)] +struct Copyable; + +fn pos_impl() { + not_copy::(); + //~^ ERROR the trait bound `Copyable: !Copy` is not satisfied +} + +struct NotNecessarilyCopyable; + +fn unknown_impl() { + not_copy::(); + //~^ 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 0000000000000..a3cab41a2ce0f --- /dev/null +++ b/tests/ui/traits/negative-bounds/simple.stderr @@ -0,0 +1,70 @@ +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:11:16 + | +LL | not_copy::(); + | ^ 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() {} + | ^^^^^ 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::(); + | ^ 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() {} + | ^^^^^ 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::(); + | ^^^^^^^^ 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() {} + | ^^^^^ 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::(); + | ^^^^^^^^^^^^^^^^^^^^^^ 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() {} + | ^^^^^ required by this bound in `not_copy` +help: consider annotating `NotNecessarilyCopyable` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct NotNecessarilyCopyable; + | + +error: aborting due to 4 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`.