Skip to content

Commit

Permalink
Rollup merge of rust-lang#119354 - fmease:negative_bounds-fixes, r=co…
Browse files Browse the repository at this point in the history
…mpiler-errors

Make `negative_bounds` internal & fix some of its issues

r? compiler-errors
  • Loading branch information
matthiaskrgr authored Jan 5, 2024
2 parents 7e58b2f + 977546d commit e0c86de
Show file tree
Hide file tree
Showing 16 changed files with 251 additions and 90 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ ast_passes_module_nonascii = trying to load file for module `{$name}` with non-a
ast_passes_negative_bound_not_supported =
negative bounds are not supported
ast_passes_negative_bound_with_parenthetical_notation =
parenthetical notation may not be used for negative bounds
ast_passes_nested_impl_trait = nested `impl Trait` is not allowed
.outer = outer `impl Trait`
.inner = nested `impl Trait` here
Expand Down
21 changes: 16 additions & 5 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1312,13 +1312,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let GenericBound::Trait(trait_ref, modifiers) = bound
&& let BoundPolarity::Negative(_) = modifiers.polarity
&& 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.dcx()
.emit_err(errors::ConstraintOnNegativeBound { span: constraint.span });
match segment.args.as_deref() {
Some(ast::GenericArgs::AngleBracketed(args)) => {
for arg in &args.args {
if let ast::AngleBracketedArg::Constraint(constraint) = arg {
self.dcx().emit_err(errors::ConstraintOnNegativeBound {
span: constraint.span,
});
}
}
}
// The lowered form of parenthesized generic args contains a type binding.
Some(ast::GenericArgs::Parenthesized(args)) => {
self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation {
span: args.span,
});
}
None => {}
}
}

Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,13 @@ pub struct ConstraintOnNegativeBound {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_negative_bound_with_parenthetical_notation)]
pub struct NegativeBoundWithParentheticalNotation {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_invalid_unnamed_field_ty)]
pub struct InvalidUnnamedFieldTy {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ declare_features! (
/// Allows the `multiple_supertrait_upcastable` lint.
(unstable, multiple_supertrait_upcastable, "1.69.0", None),
/// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
(incomplete, negative_bounds, "1.71.0", None),
(internal, negative_bounds, "1.71.0", None),
/// Allows using `#[omit_gdb_pretty_printer_section]`.
(internal, omit_gdb_pretty_printer_section, "1.5.0", None),
/// Allows using `#[prelude_import]` on glob `use` items.
Expand Down
52 changes: 30 additions & 22 deletions compiler/rustc_hir_analysis/src/astconv/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,36 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
span: Span,
) {
let tcx = self.tcx();
let sized_def_id = tcx.lang_items().sized_trait();
let mut seen_negative_sized_bound = false;

// Try to find an unbound in bounds.
let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| {
for ab in ast_bounds {
if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
unbounds.push(ptr)
let hir::GenericBound::Trait(ptr, modifier) = ab else {
continue;
};
match modifier {
hir::TraitBoundModifier::Maybe => unbounds.push(ptr),
hir::TraitBoundModifier::Negative => {
if let Some(sized_def_id) = sized_def_id
&& ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
{
seen_negative_sized_bound = true;
}
}
_ => {}
}
}
};
search_bounds(ast_bounds);
if let Some((self_ty, where_clause)) = self_ty_where_predicates {
for clause in where_clause {
if let hir::WherePredicate::BoundPredicate(pred) = clause {
if pred.is_param_bound(self_ty.to_def_id()) {
search_bounds(pred.bounds);
}
if let hir::WherePredicate::BoundPredicate(pred) = clause
&& pred.is_param_bound(self_ty.to_def_id())
{
search_bounds(pred.bounds);
}
}
}
Expand All @@ -53,15 +66,13 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
});
}

let sized_def_id = tcx.lang_items().sized_trait();

let mut seen_sized_unbound = false;
for unbound in unbounds {
if let Some(sized_def_id) = sized_def_id {
if unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) {
seen_sized_unbound = true;
continue;
}
if let Some(sized_def_id) = sized_def_id
&& unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
{
seen_sized_unbound = true;
continue;
}
// There was a `?Trait` bound, but it was not `?Sized`; warn.
tcx.dcx().span_warn(
Expand All @@ -71,15 +82,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
);
}

// If the above loop finished there was no `?Sized` bound; add implicitly sized if `Sized` is available.
if sized_def_id.is_none() {
// No lang item for `Sized`, so we can't add it as a bound.
return;
}
if seen_sized_unbound {
// There was in fact a `?Sized` bound, return without doing anything
} else {
// There was no `?Sized` bound; add implicitly sized if `Sized` is available.
if seen_sized_unbound || seen_negative_sized_bound {
// There was in fact a `?Sized` or `!Sized` bound;
// we don't need to do anything.
} else if sized_def_id.is_some() {
// There was no `?Sized` or `!Sized` bound;
// add `Sized` if it's available.
bounds.push_sized(tcx, self_ty, span);
}
}
Expand Down
72 changes: 51 additions & 21 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

let mut traits = FxIndexMap::default();
let mut fn_traits = FxIndexMap::default();
let mut is_sized = false;
let mut has_sized_bound = false;
let mut has_negative_sized_bound = false;
let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new();

for (predicate, _) in bounds.iter_instantiated_copied(tcx, args) {
Expand All @@ -922,13 +923,24 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
ty::ClauseKind::Trait(pred) => {
let trait_ref = bound_predicate.rebind(pred.trait_ref);

// Don't print + Sized, but rather + ?Sized if absent.
// Don't print `+ Sized`, but rather `+ ?Sized` if absent.
if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() {
is_sized = true;
continue;
match pred.polarity {
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {
has_sized_bound = true;
continue;
}
ty::ImplPolarity::Negative => has_negative_sized_bound = true,
}
}

self.insert_trait_and_projection(trait_ref, None, &mut traits, &mut fn_traits);
self.insert_trait_and_projection(
trait_ref,
pred.polarity,
None,
&mut traits,
&mut fn_traits,
);
}
ty::ClauseKind::Projection(pred) => {
let proj_ref = bound_predicate.rebind(pred);
Expand All @@ -939,6 +951,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

self.insert_trait_and_projection(
trait_ref,
ty::ImplPolarity::Positive,
Some(proj_ty),
&mut traits,
&mut fn_traits,
Expand All @@ -955,7 +968,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

let mut first = true;
// Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait
let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !is_sized;
let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !has_sized_bound;

for (fn_once_trait_ref, entry) in fn_traits {
write!(self, "{}", if first { "" } else { " + " })?;
Expand Down Expand Up @@ -1002,18 +1015,21 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
// trait_refs we collected in the OpaqueFnEntry as normal trait refs.
_ => {
if entry.has_fn_once {
traits.entry(fn_once_trait_ref).or_default().extend(
// Group the return ty with its def id, if we had one.
entry
.return_ty
.map(|ty| (tcx.require_lang_item(LangItem::FnOnce, None), ty)),
);
traits
.entry((fn_once_trait_ref, ty::ImplPolarity::Positive))
.or_default()
.extend(
// Group the return ty with its def id, if we had one.
entry.return_ty.map(|ty| {
(tcx.require_lang_item(LangItem::FnOnce, None), ty)
}),
);
}
if let Some(trait_ref) = entry.fn_mut_trait_ref {
traits.entry(trait_ref).or_default();
traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default();
}
if let Some(trait_ref) = entry.fn_trait_ref {
traits.entry(trait_ref).or_default();
traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default();
}
}
}
Expand All @@ -1023,11 +1039,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}

// Print the rest of the trait types (that aren't Fn* family of traits)
for (trait_ref, assoc_items) in traits {
for ((trait_ref, polarity), assoc_items) in traits {
write!(self, "{}", if first { "" } else { " + " })?;

self.wrap_binder(&trait_ref, |trait_ref, cx| {
define_scoped_cx!(cx);

if polarity == ty::ImplPolarity::Negative {
p!("!");
}
p!(print(trait_ref.print_only_trait_name()));

let generics = tcx.generics_of(trait_ref.def_id);
Expand Down Expand Up @@ -1094,9 +1114,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
})?;
}

if !is_sized {
write!(self, "{}?Sized", if first { "" } else { " + " })?;
} else if first {
let add_sized = has_sized_bound && (first || has_negative_sized_bound);
let add_maybe_sized = !has_sized_bound && !has_negative_sized_bound;
if add_sized || add_maybe_sized {
if !first {
write!(self, " + ")?;
}
if add_maybe_sized {
write!(self, "?")?;
}
write!(self, "Sized")?;
}

Expand Down Expand Up @@ -1128,9 +1154,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
fn insert_trait_and_projection(
&mut self,
trait_ref: ty::PolyTraitRef<'tcx>,
polarity: ty::ImplPolarity,
proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>,
traits: &mut FxIndexMap<
ty::PolyTraitRef<'tcx>,
(ty::PolyTraitRef<'tcx>, ty::ImplPolarity),
FxIndexMap<DefId, ty::Binder<'tcx, Term<'tcx>>>,
>,
fn_traits: &mut FxIndexMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>,
Expand All @@ -1139,7 +1166,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

// If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce
// super-trait ref and record it there.
if let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() {
// We skip negative Fn* bounds since they can't use parenthetical notation anyway.
if polarity == ty::ImplPolarity::Positive
&& let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait()
{
// If we have a FnOnce, then insert it into
if trait_def_id == fn_once_trait {
let entry = fn_traits.entry(trait_ref).or_default();
Expand Down Expand Up @@ -1167,7 +1197,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}

// Otherwise, just group our traits and projection types.
traits.entry(trait_ref).or_default().extend(proj_ty);
traits.entry((trait_ref, polarity)).or_default().extend(proj_ty);
}

fn pretty_print_inherent_projection(
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/traits/negative-bounds/associated-constraints.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![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;
Expand All @@ -17,4 +16,7 @@ fn test3<T: !Trait<Assoc: Send>>() {}
fn test4<T>() where T: !Trait<Assoc: Send> {}
//~^ ERROR associated type constraints not allowed on negative bounds

fn test5<T>() where T: !Fn() -> i32 {}
//~^ ERROR parenthetical notation may not be used for negative bounds

fn main() {}
20 changes: 9 additions & 11 deletions tests/ui/traits/negative-bounds/associated-constraints.stderr
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
error: associated type constraints not allowed on negative bounds
--> $DIR/associated-constraints.rs:8:19
--> $DIR/associated-constraints.rs:7:19
|
LL | fn test<T: !Trait<Assoc = i32>>() {}
| ^^^^^^^^^^^

error: associated type constraints not allowed on negative bounds
--> $DIR/associated-constraints.rs:11:31
--> $DIR/associated-constraints.rs:10:31
|
LL | fn test2<T>() where T: !Trait<Assoc = i32> {}
| ^^^^^^^^^^^

error: associated type constraints not allowed on negative bounds
--> $DIR/associated-constraints.rs:14:20
--> $DIR/associated-constraints.rs:13:20
|
LL | fn test3<T: !Trait<Assoc: Send>>() {}
| ^^^^^^^^^^^

error: associated type constraints not allowed on negative bounds
--> $DIR/associated-constraints.rs:17:31
--> $DIR/associated-constraints.rs:16:31
|
LL | fn test4<T>() where T: !Trait<Assoc: Send> {}
| ^^^^^^^^^^^

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
error: parenthetical notation may not be used for negative bounds
--> $DIR/associated-constraints.rs:19:25
|
LL | #![feature(negative_bounds, associated_type_bounds)]
| ^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
LL | fn test5<T>() where T: !Fn() -> i32 {}
| ^^^^^^^^^^^

error: aborting due to 4 previous errors; 1 warning emitted
error: aborting due to 5 previous errors

23 changes: 23 additions & 0 deletions tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// compile-flags: -Znext-solver

#![feature(negative_bounds, negative_impls)]

trait Trait {}
impl !Trait for () {}

fn produce() -> impl !Trait {}
fn consume(_: impl Trait) {}

fn main() {
consume(produce()); //~ ERROR the trait bound `impl !Trait: Trait` is not satisfied
}

fn weird0() -> impl Sized + !Sized {}
//~^ ERROR mismatched types
//~| ERROR type mismatch resolving `() == impl !Sized + Sized`
fn weird1() -> impl !Sized + Sized {}
//~^ ERROR mismatched types
//~| ERROR type mismatch resolving `() == impl !Sized + Sized`
fn weird2() -> impl !Sized {}
//~^ ERROR mismatched types
//~| ERROR type mismatch resolving `() == impl !Sized`
Loading

0 comments on commit e0c86de

Please sign in to comment.