Skip to content

Implement negative bounds for internal testing purposes #110791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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