Implement negative bounds

This commit is contained in:
Michael Goulet 2023-04-25 05:15:50 +00:00
parent 98c33e47a4
commit 6e01e910cb
33 changed files with 363 additions and 188 deletions

View File

@ -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.

View File

@ -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,
}
}

View File

@ -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

View File

@ -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: "!" });
}
_ => {}
}

View File

@ -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,
}

View File

@ -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.

View File

@ -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("?");

View File

@ -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.

View File

@ -435,6 +435,7 @@ pub enum GenericArgsParentheses {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
pub enum TraitBoundModifier {
None,
Negative,
Maybe,
MaybeConst,
}

View File

@ -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,

View File

@ -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(

View File

@ -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,

View File

@ -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()))
}

View File

@ -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

View File

@ -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)]

View File

@ -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 {

View File

@ -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,

View File

@ -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 };

View File

@ -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)?

View File

@ -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:

View File

@ -984,6 +984,7 @@ symbols! {
needs_panic_runtime,
neg,
negate_unsigned,
negative_bounds,
negative_impls,
neon,
never,

View File

@ -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();

View File

@ -0,0 +1,4 @@
fn test<T: !Copy>() {}
//~^ ERROR negative bounds are not supported
fn main() {}

View File

@ -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

View File

@ -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

View File

@ -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() {}

View File

@ -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 {}

View File

@ -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: aborting due to 5 previous errors
error: negative bounds are not supported
--> $DIR/issue-33418.rs:13:3
|
LL | + !SuperB {}
| ^
error: aborting due to 7 previous errors

View File

@ -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

View File

@ -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

View File

@ -1,20 +1,38 @@
error: negative bounds are not supported
--> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:12
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>() {}
| ^^^^^^^^^^ negative bounds are not supported
| ^
error: negative bounds are not supported
--> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:11:22
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>() {}
| ^^^^^ negative bounds are not supported
| ^
error: negative bounds are not supported
--> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:13:16
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>() {}
| ^^^^^ negative bounds are not supported
| ^
error: aborting due to 3 previous errors
error: negative bounds are not supported
--> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:14
|
LL | pub fn f1<T: !'static>() {}
| ^
error: negative bounds are not supported
--> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:12:24
|
LL | pub fn f2<'a, T: Ord + !'a>() {}
| ^
error: negative bounds are not supported
--> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:15:18
|
LL | pub fn f3<'a, T: !'a + Ord>() {}
| ^
error: aborting due to 6 previous errors

View File

@ -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() {}

View File

@ -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`.