Skip to content

Commit

Permalink
Rollup merge of rust-lang#110791 - compiler-errors:negative-bounds, r…
Browse files Browse the repository at this point in the history
…=oli-obk

Implement negative bounds for internal testing purposes

Implements partial support the `!` negative polarity on trait bounds. This is incomplete, but should allow us to at least be able to play with the feature.

Not even gonna consider them as a public-facing feature, but I'm implementing them because would've been nice to have in UI tests, for example in rust-lang#110671.
  • Loading branch information
Dylan-DPC authored May 3, 2023
2 parents 32f3ddb + 6fca051 commit 80df4ab
Show file tree
Hide file tree
Showing 42 changed files with 478 additions and 185 deletions.
18 changes: 18 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
17 changes: 15 additions & 2 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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,
}
}

Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
17 changes: 16 additions & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ pub enum TildeConstReason {
pub struct OptionalConstExclusive {
#[primary_span]
pub span: Span,
pub modifier: &'static str,
}

#[derive(Diagnostic)]
Expand Down Expand Up @@ -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,
}
6 changes: 6 additions & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("?");
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ pub enum GenericArgsParentheses {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
pub enum TraitBoundModifier {
None,
Negative,
Maybe,
MaybeConst,
}
Expand Down
34 changes: 29 additions & 5 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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).
}
Expand Down Expand Up @@ -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,
Expand All @@ -764,6 +777,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
span,
binding_span,
constness,
polarity,
bounds,
speculative,
trait_ref_span,
Expand Down Expand Up @@ -799,6 +813,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
span,
binding_span,
constness,
ty::ImplPolarity::Positive,
bounds,
speculative,
trait_ref_span,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<T = X>`, we want to produce a
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_hir_analysis/src/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
}

Expand Down
10 changes: 2 additions & 8 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 80df4ab

Please sign in to comment.