Auto merge of #120706 - Bryanskiy:leak, r=lcnr
Initial support for auto traits with default bounds This PR is part of ["MCP: Low level components for async drop"](https://github.com/rust-lang/compiler-team/issues/727) Tracking issue: #138781 Summary: https://github.com/rust-lang/rust/pull/120706#issuecomment-1934006762 ### Intro Sometimes we want to use type system to express specific behavior and provide safety guarantees. This behavior can be specified by various "marker" traits. For example, we use `Send` and `Sync` to keep track of which types are thread safe. As the language develops, there are more problems that could be solved by adding new marker traits: - to forbid types with an async destructor to be dropped in a synchronous context a trait like `SyncDrop` could be used [Async destructors, async genericity and completion futures](https://sabrinajewson.org/blog/async-drop). - to support [scoped tasks](https://without.boats/blog/the-scoped-task-trilemma/) or in a more general sense to provide a [destruction guarantee](https://zetanumbers.github.io/book/myosotis.html) there is a desire among some users to see a `Leak` (or `Forget`) trait. - Withoutboats in his [post](https://without.boats/blog/changing-the-rules-of-rust/) reflected on the use of `Move` trait instead of a `Pin`. All the traits proposed above are supposed to be auto traits implemented for most types, and usually implemented automatically by compiler. For backward compatibility these traits have to be added implicitly to all bound lists in old code (see below). Adding new default bounds involves many difficulties: many standard library interfaces may need to opt out of those default bounds, and therefore be infected with confusing `?Trait` syntax, migration to a new edition may contain backward compatibility holes, supporting new traits in the compiler can be quite difficult and so forth. Anyway, it's hard to evaluate the complexity until we try the system on a practice. In this PR we introduce new optional lang items for traits that are added to all bound lists by default, similarly to existing `Sized`. The examples of such traits could be `Leak`, `Move`, `SyncDrop` or something else, it doesn't matter much right now (further I will call them `DefaultAutoTrait`'s). We want to land this change into rustc under an option, so it becomes available in bootstrap compiler. Then we'll be able to do standard library experiments with the aforementioned traits without adding hundreds of `#[cfg(not(bootstrap))]`s. Based on the experiments, we can come up with some scheme for the next edition, in which such bounds are added in a more targeted way, and not just everywhere. Most of the implementation is basically a refactoring that replaces hardcoded uses of `Sized` with iterating over a list of traits including both `Sized` and the new traits when `-Zexperimental-default-bounds` is enabled (or just `Sized` as before, if the option is not enabled). ### Default bounds for old editions All existing types, including generic parameters, are considered `Leak`/`Move`/`SyncDrop` and can be forgotten, moved or destroyed in generic contexts without specifying any bounds. New types that cannot be, for example, forgotten and do not implement `Leak` can be added at some point, and they should not be usable in such generic contexts in existing code. To both maintain this property and keep backward compatibility with existing code, the new traits should be added as default bounds _everywhere_ in previous editions. Besides the implicit `Sized` bound contexts that includes supertrait lists and trait lists in trait objects (`dyn Trait1 + ... + TraitN`). Compiler should also generate implicit `DefaultAutoTrait` implementations for foreign types (`extern { type Foo; }`) because they are also currently usable in generic contexts without any bounds. #### Supertraits Adding the new traits as supertraits to all existing traits is potentially necessary, because, for example, using a `Self` param in a trait's associated item may be a breaking change otherwise: ```rust trait Foo: Sized { fn new() -> Option<Self>; // ERROR: `Option` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait` } // desugared `Option` enum Option<T: DefaultAutoTrait + Sized> { Some(T), None, } ``` However, default supertraits can significantly affect compiler performance. For example, if we know that `T: Trait`, the compiler would deduce that `T: DefaultAutoTrait`. It also implies proving `F: DefaultAutoTrait` for each field `F` of type `T` until an explicit impl is be provided. If the standard library is not modified, then even traits like `Copy` or `Send` would get these supertraits. In this PR for optimization purposes instead of adding default supertraits, bounds are added to the associated items: ```rust // Default bounds are generated in the following way: trait Trait { fn foo(&self) where Self: DefaultAutoTrait {} } // instead of this: trait Trait: DefaultAutoTrait { fn foo(&self) {} } ``` It is not always possible to do this optimization because of backward compatibility: ```rust pub trait Trait<Rhs = Self> {} pub trait Trait1 : Trait {} // ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait` ``` or ```rust trait Trait { type Type where Self: Sized; } trait Trait2<T> : Trait<Type = T> {} // ERROR: `???` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait` ``` Therefore, `DefaultAutoTrait`'s are still being added to supertraits if the `Self` params or type bindings were found in the trait header. #### Trait objects Trait objects requires explicit `+ Trait` bound to implement corresponding trait which is not backward compatible: ```rust fn use_trait_object(x: Box<dyn Trait>) { foo(x) // ERROR: `foo` requires `DefaultAutoTrait`, but `dyn Trait` is not `DefaultAutoTrait` } // implicit T: DefaultAutoTrait here fn foo<T>(_: T) {} ``` So, for a trait object `dyn Trait` we should add an implicit bound `dyn Trait + DefaultAutoTrait` to make it usable, and allow relaxing it with a question mark syntax `dyn Trait + ?DefaultAutoTrait` when it's not necessary. #### Foreign types If compiler doesn't generate auto trait implementations for a foreign type, then it's a breaking change if the default bounds are added everywhere else: ```rust // implicit T: DefaultAutoTrait here fn foo<T: ?Sized>(_: &T) {} extern "C" { type ExternTy; } fn forward_extern_ty(x: &ExternTy) { foo(x); // ERROR: `foo` requires `DefaultAutoTrait`, but `ExternTy` is not `DefaultAutoTrait` } ``` We'll have to enable implicit `DefaultAutoTrait` implementations for foreign types at least for previous editions: ```rust // implicit T: DefaultAutoTrait here fn foo<T: ?Sized>(_: &T) {} extern "C" { type ExternTy; } impl DefaultAutoTrait for ExternTy {} // implicit impl fn forward_extern_ty(x: &ExternTy) { foo(x); // OK } ``` ### Unresolved questions New default bounds affect all existing Rust code complicating an already complex type system. - Proving an auto trait predicate requires recursively traversing the type and proving the predicate for it's fields. This leads to a significant performance regression. Measurements for the stage 2 compiler build show up to 3x regression. - We hope that fast path optimizations for well known traits could mitigate such regressions at least partially. - New default bounds trigger some compiler bugs in both old and new trait solver. - With new default bounds we encounter some trait solver cycle errors that break existing code. - We hope that these cases are bugs that can be addressed in the new trait solver. Also migration to a new edition could be quite ugly and enormous, but that's actually what we want to solve. For other issues there's a chance that they could be solved by a new solver.
This commit is contained in:
commit
9e14530c7c
24 changed files with 830 additions and 86 deletions
|
@ -433,6 +433,12 @@ language_item_table! {
|
|||
// Experimental lang items for implementing contract pre- and post-condition checking.
|
||||
ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
|
||||
ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
// Experimental lang items for `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727)
|
||||
DefaultTrait4, sym::default_trait4, default_trait4_trait, Target::Trait, GenericRequirement::None;
|
||||
DefaultTrait3, sym::default_trait3, default_trait3_trait, Target::Trait, GenericRequirement::None;
|
||||
DefaultTrait2, sym::default_trait2, default_trait2_trait, Target::Trait, GenericRequirement::None;
|
||||
DefaultTrait1, sym::default_trait1, default_trait1_trait, Target::Trait, GenericRequirement::None;
|
||||
}
|
||||
|
||||
/// The requirement imposed on the generics of a lang item
|
||||
|
|
|
@ -341,9 +341,8 @@ fn bounds_from_generic_predicates<'tcx>(
|
|||
ty::ClauseKind::Trait(trait_predicate) => {
|
||||
let entry = types.entry(trait_predicate.self_ty()).or_default();
|
||||
let def_id = trait_predicate.def_id();
|
||||
if Some(def_id) != tcx.lang_items().sized_trait() {
|
||||
// Type params are `Sized` by default, do not add that restriction to the list
|
||||
// if it is a positive requirement.
|
||||
if !tcx.is_default_trait(def_id) {
|
||||
// Do not add that restriction to the list if it is a positive requirement.
|
||||
entry.push(trait_predicate.def_id());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,13 +38,13 @@ fn associated_type_bounds<'tcx>(
|
|||
let icx = ItemCtxt::new(tcx, assoc_item_def_id);
|
||||
let mut bounds = Vec::new();
|
||||
icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
|
||||
// Associated types are implicitly sized unless a `?Sized` bound is found
|
||||
// Implicit bounds are added to associated types unless a `?Trait` bound is found
|
||||
match filter {
|
||||
PredicateFilter::All
|
||||
| PredicateFilter::SelfOnly
|
||||
| PredicateFilter::SelfTraitThatDefines(_)
|
||||
| PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
|
||||
icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
|
||||
}
|
||||
// `ConstIfConst` is only interested in `~const` bounds.
|
||||
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
|
||||
|
@ -327,14 +327,13 @@ fn opaque_type_bounds<'tcx>(
|
|||
let icx = ItemCtxt::new(tcx, opaque_def_id);
|
||||
let mut bounds = Vec::new();
|
||||
icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
|
||||
// Opaque types are implicitly sized unless a `?Sized` bound is found
|
||||
// Implicit bounds are added to opaque types unless a `?Trait` bound is found
|
||||
match filter {
|
||||
PredicateFilter::All
|
||||
| PredicateFilter::SelfOnly
|
||||
| PredicateFilter::SelfTraitThatDefines(_)
|
||||
| PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
// Associated types are implicitly sized unless a `?Sized` bound is found
|
||||
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
|
||||
icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
|
||||
}
|
||||
//`ConstIfConst` is only interested in `~const` bounds.
|
||||
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
|
||||
|
|
|
@ -165,12 +165,42 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
|
||||
ItemKind::Trait(_, _, _, _, self_bounds, ..)
|
||||
| ItemKind::TraitAlias(_, _, self_bounds) => {
|
||||
is_trait = Some(self_bounds);
|
||||
is_trait = Some((self_bounds, item.span));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
if let Node::TraitItem(item) = node {
|
||||
let parent = tcx.local_parent(item.hir_id().owner.def_id);
|
||||
let Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
let (trait_generics, trait_bounds) = match parent_trait.kind {
|
||||
hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits),
|
||||
hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if
|
||||
// they are not added as super trait bounds to the trait itself. See comment on
|
||||
// `requires_default_supertraits` for more details.
|
||||
if !icx.lowerer().requires_default_supertraits(trait_bounds, trait_generics) {
|
||||
let mut bounds = Vec::new();
|
||||
let self_ty_where_predicates = (parent, item.generics.predicates);
|
||||
icx.lowerer().add_default_traits_with_filter(
|
||||
&mut bounds,
|
||||
tcx.types.self_param,
|
||||
&[],
|
||||
Some(self_ty_where_predicates),
|
||||
item.span,
|
||||
|tr| tr != hir::LangItem::Sized,
|
||||
);
|
||||
predicates.extend(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
let generics = tcx.generics_of(def_id);
|
||||
|
||||
// Below we'll consider the bounds on the type parameters (including `Self`)
|
||||
|
@ -181,11 +211,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
let mut bounds = Vec::new();
|
||||
icx.lowerer().lower_bounds(
|
||||
tcx.types.self_param,
|
||||
self_bounds,
|
||||
self_bounds.0,
|
||||
&mut bounds,
|
||||
ty::List::empty(),
|
||||
PredicateFilter::All,
|
||||
);
|
||||
icx.lowerer().add_default_super_traits(
|
||||
def_id,
|
||||
&mut bounds,
|
||||
self_bounds.0,
|
||||
hir_generics,
|
||||
self_bounds.1,
|
||||
);
|
||||
predicates.extend(bounds);
|
||||
}
|
||||
|
||||
|
@ -210,8 +247,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
GenericParamKind::Type { .. } => {
|
||||
let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
|
||||
let mut bounds = Vec::new();
|
||||
// Params are implicitly sized unless a `?Sized` bound is found
|
||||
icx.lowerer().add_sized_bound(
|
||||
// Implicit bounds are added to type params unless a `?Trait` bound is found
|
||||
icx.lowerer().add_default_traits(
|
||||
&mut bounds,
|
||||
param_ty,
|
||||
&[],
|
||||
|
@ -625,6 +662,22 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
|
|||
let self_param_ty = tcx.types.self_param;
|
||||
let mut bounds = Vec::new();
|
||||
icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter);
|
||||
match filter {
|
||||
PredicateFilter::All
|
||||
| PredicateFilter::SelfOnly
|
||||
| PredicateFilter::SelfTraitThatDefines(_)
|
||||
| PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
icx.lowerer().add_default_super_traits(
|
||||
trait_def_id,
|
||||
&mut bounds,
|
||||
superbounds,
|
||||
generics,
|
||||
item.span,
|
||||
);
|
||||
}
|
||||
//`ConstIfConst` is only interested in `~const` bounds.
|
||||
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
|
||||
}
|
||||
|
||||
let where_bounds_that_match =
|
||||
icx.probe_ty_param_bounds_in_generics(generics, item.owner_id.def_id, filter);
|
||||
|
|
|
@ -4,9 +4,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
|||
use rustc_errors::codes::*;
|
||||
use rustc_errors::struct_span_code_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{AmbigArg, HirId};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{
|
||||
self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
|
||||
|
@ -24,25 +24,190 @@ use crate::hir_ty_lowering::{
|
|||
};
|
||||
|
||||
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
/// Add a `Sized` bound to the `bounds` if appropriate.
|
||||
///
|
||||
/// Doesn't add the bound if the HIR bounds contain any of `Sized`, `?Sized` or `!Sized`.
|
||||
pub(crate) fn add_sized_bound(
|
||||
pub(crate) fn add_default_traits(
|
||||
&self,
|
||||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
self_ty: Ty<'tcx>,
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
hir_bounds: &[hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
span: Span,
|
||||
) {
|
||||
self.add_default_traits_with_filter(
|
||||
bounds,
|
||||
self_ty,
|
||||
hir_bounds,
|
||||
self_ty_where_predicates,
|
||||
span,
|
||||
|_| true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds
|
||||
/// or associative items.
|
||||
///
|
||||
/// To keep backward compatibility with existing code, `experimental_default_bounds` bounds
|
||||
/// should be added everywhere, including super bounds. However this causes a huge performance
|
||||
/// costs. For optimization purposes instead of adding default supertraits, bounds
|
||||
/// are added to the associative items:
|
||||
///
|
||||
/// ```ignore(illustrative)
|
||||
/// // Default bounds are generated in the following way:
|
||||
/// trait Trait {
|
||||
/// fn foo(&self) where Self: Leak {}
|
||||
/// }
|
||||
///
|
||||
/// // instead of this:
|
||||
/// trait Trait: Leak {
|
||||
/// fn foo(&self) {}
|
||||
/// }
|
||||
/// ```
|
||||
/// It is not always possible to do this because of backward compatibility:
|
||||
///
|
||||
/// ```ignore(illustrative)
|
||||
/// pub trait Trait<Rhs = Self> {}
|
||||
/// pub trait Trait1 : Trait {}
|
||||
/// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait`
|
||||
/// ```
|
||||
///
|
||||
/// or:
|
||||
///
|
||||
/// ```ignore(illustrative)
|
||||
/// trait Trait {
|
||||
/// type Type where Self: Sized;
|
||||
/// }
|
||||
/// trait Trait2<T> : Trait<Type = T> {}
|
||||
/// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit `Self: DefaultAutoTrait` in `Trait::Type`
|
||||
/// ```
|
||||
///
|
||||
/// Therefore, `experimental_default_bounds` are still being added to supertraits if
|
||||
/// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header.
|
||||
pub(crate) fn requires_default_supertraits(
|
||||
&self,
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
hir_generics: &'tcx hir::Generics<'tcx>,
|
||||
) -> bool {
|
||||
struct TraitInfoCollector;
|
||||
|
||||
impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector {
|
||||
type Result = ControlFlow<()>;
|
||||
|
||||
fn visit_assoc_item_constraint(
|
||||
&mut self,
|
||||
_constraint: &'tcx hir::AssocItemConstraint<'tcx>,
|
||||
) -> Self::Result {
|
||||
ControlFlow::Break(())
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
|
||||
if matches!(
|
||||
&t.kind,
|
||||
hir::TyKind::Path(hir::QPath::Resolved(
|
||||
_,
|
||||
hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. },
|
||||
))
|
||||
) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
hir::intravisit::walk_ty(self, t)
|
||||
}
|
||||
}
|
||||
|
||||
let mut found = false;
|
||||
for bound in hir_bounds {
|
||||
found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break();
|
||||
}
|
||||
found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break();
|
||||
found
|
||||
}
|
||||
|
||||
/// Lazily sets `experimental_default_bounds` to true on trait super bounds.
|
||||
/// See `requires_default_supertraits` for more information.
|
||||
pub(crate) fn add_default_super_traits(
|
||||
&self,
|
||||
trait_def_id: LocalDefId,
|
||||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
hir_generics: &'tcx hir::Generics<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias));
|
||||
if self.requires_default_supertraits(hir_bounds, hir_generics) {
|
||||
let self_ty_where_predicates = (trait_def_id, hir_generics.predicates);
|
||||
self.add_default_traits_with_filter(
|
||||
bounds,
|
||||
self.tcx().types.self_param,
|
||||
hir_bounds,
|
||||
Some(self_ty_where_predicates),
|
||||
span,
|
||||
|default_trait| default_trait != hir::LangItem::Sized,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_default_traits_with_filter(
|
||||
&self,
|
||||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
self_ty: Ty<'tcx>,
|
||||
hir_bounds: &[hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
span: Span,
|
||||
f: impl Fn(hir::LangItem) -> bool,
|
||||
) {
|
||||
self.tcx().default_traits().iter().filter(|&&default_trait| f(default_trait)).for_each(
|
||||
|default_trait| {
|
||||
self.add_default_trait(
|
||||
*default_trait,
|
||||
bounds,
|
||||
self_ty,
|
||||
hir_bounds,
|
||||
self_ty_where_predicates,
|
||||
span,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a `Sized` or `experimental_default_bounds` bounds to the `bounds` if appropriate.
|
||||
///
|
||||
/// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`.
|
||||
pub(crate) fn add_default_trait(
|
||||
&self,
|
||||
trait_: hir::LangItem,
|
||||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
self_ty: Ty<'tcx>,
|
||||
hir_bounds: &[hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
span: Span,
|
||||
) {
|
||||
let trait_id = self.tcx().lang_items().get(trait_);
|
||||
if let Some(trait_id) = trait_id
|
||||
&& self.do_not_provide_default_trait_bound(
|
||||
trait_id,
|
||||
hir_bounds,
|
||||
self_ty_where_predicates,
|
||||
)
|
||||
{
|
||||
// There was no `?Trait` or `!Trait` bound;
|
||||
// add `Trait` if it's available.
|
||||
let trait_ref = ty::TraitRef::new(self.tcx(), trait_id, [self_ty]);
|
||||
// Preferable to put this obligation first, since we report better errors for sized ambiguity.
|
||||
bounds.insert(0, (trait_ref.upcast(self.tcx()), span));
|
||||
}
|
||||
}
|
||||
|
||||
fn do_not_provide_default_trait_bound<'a>(
|
||||
&self,
|
||||
trait_def_id: DefId,
|
||||
hir_bounds: &'a [hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx();
|
||||
let sized_def_id = tcx.lang_items().sized_trait();
|
||||
let mut seen_negative_sized_bound = false;
|
||||
let mut seen_positive_sized_bound = false;
|
||||
let mut seen_negative_bound = false;
|
||||
let mut seen_positive_bound = false;
|
||||
|
||||
// Try to find an unbound in bounds.
|
||||
let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
|
||||
let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
|
||||
let mut search_bounds = |hir_bounds: &'a [hir::GenericBound<'tcx>]| {
|
||||
for hir_bound in hir_bounds {
|
||||
let hir::GenericBound::Trait(ptr) = hir_bound else {
|
||||
continue;
|
||||
|
@ -50,17 +215,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
match ptr.modifiers.polarity {
|
||||
hir::BoundPolarity::Maybe(_) => unbounds.push(ptr),
|
||||
hir::BoundPolarity::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;
|
||||
if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
|
||||
seen_negative_bound = true;
|
||||
}
|
||||
}
|
||||
hir::BoundPolarity::Positive => {
|
||||
if let Some(sized_def_id) = sized_def_id
|
||||
&& ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
|
||||
{
|
||||
seen_positive_sized_bound = true;
|
||||
if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
|
||||
seen_positive_bound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,32 +256,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
};
|
||||
}
|
||||
|
||||
let mut seen_sized_unbound = false;
|
||||
let mut seen_unbound = false;
|
||||
for unbound in unbounds {
|
||||
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;
|
||||
let unbound_def_id = unbound.trait_ref.trait_def_id();
|
||||
if unbound_def_id == Some(trait_def_id) {
|
||||
seen_unbound = true;
|
||||
}
|
||||
// There was a `?Trait` bound, but it was not `?Sized`
|
||||
self.dcx().span_err(
|
||||
let emit_relax_err = || {
|
||||
let unbound_traits =
|
||||
match self.tcx().sess.opts.unstable_opts.experimental_default_bounds {
|
||||
true => "`?Sized` and `experimental_default_bounds`",
|
||||
false => "`?Sized`",
|
||||
};
|
||||
// There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`.
|
||||
tcx.dcx().span_err(
|
||||
unbound.span,
|
||||
"relaxing a default bound only does something for `?Sized`; \
|
||||
format!(
|
||||
"relaxing a default bound only does something for {}; \
|
||||
all other traits are not bound by default",
|
||||
unbound_traits
|
||||
),
|
||||
);
|
||||
};
|
||||
match unbound_def_id {
|
||||
Some(def_id) if !tcx.is_default_trait(def_id) => emit_relax_err(),
|
||||
None => emit_relax_err(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if seen_sized_unbound || seen_negative_sized_bound || seen_positive_sized_bound {
|
||||
// There was in fact a `?Sized`, `!Sized` or explicit `Sized` bound;
|
||||
// we don't need to do anything.
|
||||
} else if let Some(sized_def_id) = sized_def_id {
|
||||
// There was no `?Sized`, `!Sized` or explicit `Sized` bound;
|
||||
// add `Sized` if it's available.
|
||||
let trait_ref = ty::TraitRef::new(tcx, sized_def_id, [self_ty]);
|
||||
// Preferable to put this obligation first, since we report better errors for sized ambiguity.
|
||||
bounds.insert(0, (trait_ref.upcast(tcx), span));
|
||||
}
|
||||
!(seen_unbound || seen_negative_bound || seen_positive_bound)
|
||||
}
|
||||
|
||||
/// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any.
|
||||
|
|
|
@ -57,6 +57,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
}
|
||||
}
|
||||
|
||||
let ast_bounds: Vec<_> =
|
||||
hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect();
|
||||
|
||||
self.add_default_traits_with_filter(
|
||||
&mut user_written_bounds,
|
||||
dummy_self,
|
||||
&ast_bounds,
|
||||
None,
|
||||
span,
|
||||
|tr| tr != hir::LangItem::Sized,
|
||||
);
|
||||
|
||||
let (elaborated_trait_bounds, elaborated_projection_bounds) =
|
||||
traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
|
||||
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
|
||||
|
|
|
@ -446,6 +446,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item))
|
||||
}
|
||||
|
||||
fn is_default_trait(self, def_id: DefId) -> bool {
|
||||
self.is_default_trait(def_id)
|
||||
}
|
||||
|
||||
fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> {
|
||||
lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?)
|
||||
}
|
||||
|
@ -1539,6 +1543,25 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.reserve_and_set_memory_dedup(alloc, salt)
|
||||
}
|
||||
|
||||
pub fn default_traits(self) -> &'static [rustc_hir::LangItem] {
|
||||
match self.sess.opts.unstable_opts.experimental_default_bounds {
|
||||
true => &[
|
||||
LangItem::Sized,
|
||||
LangItem::DefaultTrait1,
|
||||
LangItem::DefaultTrait2,
|
||||
LangItem::DefaultTrait3,
|
||||
LangItem::DefaultTrait4,
|
||||
],
|
||||
false => &[LangItem::Sized],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_default_trait(self, def_id: DefId) -> bool {
|
||||
self.default_traits()
|
||||
.iter()
|
||||
.any(|&default_trait| self.lang_items().get(default_trait) == Some(def_id))
|
||||
}
|
||||
|
||||
/// Returns a range of the start/end indices specified with the
|
||||
/// `rustc_layout_scalar_valid_range` attribute.
|
||||
// FIXME(eddyb) this is an awkward spot for this method, maybe move it?
|
||||
|
|
|
@ -37,12 +37,16 @@ where
|
|||
| ty::Never
|
||||
| ty::Char => Ok(ty::Binder::dummy(vec![])),
|
||||
|
||||
// This branch is only for `experimental_default_bounds`.
|
||||
// Other foreign types were rejected earlier in
|
||||
// `disqualify_auto_trait_candidate_due_to_possible_impl`.
|
||||
ty::Foreign(..) => Ok(ty::Binder::dummy(vec![])),
|
||||
|
||||
// Treat `str` like it's defined as `struct str([u8]);`
|
||||
ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])),
|
||||
|
||||
ty::Dynamic(..)
|
||||
| ty::Param(..)
|
||||
| ty::Foreign(..)
|
||||
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Bound(..)
|
||||
|
|
|
@ -1086,6 +1086,25 @@ where
|
|||
goal: Goal<I, TraitPredicate<I>>,
|
||||
) -> Option<Result<Candidate<I>, NoSolution>> {
|
||||
let self_ty = goal.predicate.self_ty();
|
||||
let check_impls = || {
|
||||
let mut disqualifying_impl = None;
|
||||
self.cx().for_each_relevant_impl(
|
||||
goal.predicate.def_id(),
|
||||
goal.predicate.self_ty(),
|
||||
|impl_def_id| {
|
||||
disqualifying_impl = Some(impl_def_id);
|
||||
},
|
||||
);
|
||||
if let Some(def_id) = disqualifying_impl {
|
||||
trace!(?def_id, ?goal, "disqualified auto-trait implementation");
|
||||
// No need to actually consider the candidate here,
|
||||
// since we do that in `consider_impl_candidate`.
|
||||
return Some(Err(NoSolution));
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
match self_ty.kind() {
|
||||
// Stall int and float vars until they are resolved to a concrete
|
||||
// numerical type. That's because the check for impls below treats
|
||||
|
@ -1096,6 +1115,10 @@ where
|
|||
Some(self.forced_ambiguity(MaybeCause::Ambiguity))
|
||||
}
|
||||
|
||||
// Backward compatibility for default auto traits.
|
||||
// Test: ui/traits/default_auto_traits/extern-types.rs
|
||||
ty::Foreign(..) if self.cx().is_default_trait(goal.predicate.def_id()) => check_impls(),
|
||||
|
||||
// These types cannot be structurally decomposed into constituent
|
||||
// types, and therefore have no built-in auto impl.
|
||||
ty::Dynamic(..)
|
||||
|
@ -1156,24 +1179,7 @@ where
|
|||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Adt(_, _)
|
||||
| ty::UnsafeBinder(_) => {
|
||||
let mut disqualifying_impl = None;
|
||||
self.cx().for_each_relevant_impl(
|
||||
goal.predicate.def_id(),
|
||||
goal.predicate.self_ty(),
|
||||
|impl_def_id| {
|
||||
disqualifying_impl = Some(impl_def_id);
|
||||
},
|
||||
);
|
||||
if let Some(def_id) = disqualifying_impl {
|
||||
trace!(?def_id, ?goal, "disqualified auto-trait implementation");
|
||||
// No need to actually consider the candidate here,
|
||||
// since we do that in `consider_impl_candidate`.
|
||||
return Some(Err(NoSolution));
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
| ty::UnsafeBinder(_) => check_impls(),
|
||||
ty::Error(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2187,6 +2187,8 @@ options! {
|
|||
"Use WebAssembly error handling for wasm32-unknown-emscripten"),
|
||||
enforce_type_length_limit: bool = (false, parse_bool, [TRACKED],
|
||||
"enforce the type length limit when monomorphizing instances in codegen"),
|
||||
experimental_default_bounds: bool = (false, parse_bool, [TRACKED],
|
||||
"enable default bounds for experimental group of auto traits"),
|
||||
export_executable_symbols: bool = (false, parse_bool, [TRACKED],
|
||||
"export symbols from executables, as if they were dynamic libraries"),
|
||||
external_clangrt: bool = (false, parse_bool, [UNTRACKED],
|
||||
|
|
|
@ -800,6 +800,15 @@ symbols! {
|
|||
default_fn,
|
||||
default_lib_allocator,
|
||||
default_method_body_is_const,
|
||||
// --------------------------
|
||||
// Lang items which are used only for experiments with auto traits with default bounds.
|
||||
// These lang items are not actually defined in core/std. Experiment is a part of
|
||||
// `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727)
|
||||
default_trait1,
|
||||
default_trait2,
|
||||
default_trait3,
|
||||
default_trait4,
|
||||
// --------------------------
|
||||
default_type_parameter_fallback,
|
||||
default_type_params,
|
||||
define_opaque,
|
||||
|
|
|
@ -692,6 +692,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
|
||||
let def_id = obligation.predicate.def_id();
|
||||
|
||||
let mut check_impls = || {
|
||||
// Only consider auto impls if there are no manual impls for the root of `self_ty`.
|
||||
//
|
||||
// For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
|
||||
// for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
|
||||
// for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
|
||||
//
|
||||
// Generally, we have to guarantee that for all `SimplifiedType`s the only crate
|
||||
// which may define impls for that type is either the crate defining the type
|
||||
// or the trait. This should be guaranteed by the orphan check.
|
||||
let mut has_impl = false;
|
||||
self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
|
||||
if !has_impl {
|
||||
candidates.vec.push(AutoImplCandidate)
|
||||
}
|
||||
};
|
||||
|
||||
if self.tcx().trait_is_auto(def_id) {
|
||||
match *self_ty.kind() {
|
||||
ty::Dynamic(..) => {
|
||||
|
@ -705,6 +722,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// we don't add any `..` impl. Default traits could
|
||||
// still be provided by a manual implementation for
|
||||
// this trait and type.
|
||||
|
||||
// Backward compatibility for default auto traits.
|
||||
// Test: ui/traits/default_auto_traits/extern-types.rs
|
||||
if self.tcx().is_default_trait(def_id) {
|
||||
check_impls()
|
||||
}
|
||||
}
|
||||
ty::Param(..)
|
||||
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
|
||||
|
@ -805,20 +828,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Only consider auto impls if there are no manual impls for the root of `self_ty`.
|
||||
//
|
||||
// For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
|
||||
// for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
|
||||
// for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
|
||||
//
|
||||
// Generally, we have to guarantee that for all `SimplifiedType`s the only crate
|
||||
// which may define impls for that type is either the crate defining the type
|
||||
// or the trait. This should be guaranteed by the orphan check.
|
||||
let mut has_impl = false;
|
||||
self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
|
||||
if !has_impl {
|
||||
candidates.vec.push(AutoImplCandidate)
|
||||
}
|
||||
check_impls();
|
||||
}
|
||||
ty::Error(_) => {
|
||||
candidates.vec.push(AutoImplCandidate);
|
||||
|
|
|
@ -2299,6 +2299,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
| ty::Never
|
||||
| ty::Char => ty::Binder::dummy(Vec::new()),
|
||||
|
||||
// This branch is only for `experimental_default_bounds`.
|
||||
// Other foreign types were rejected earlier in
|
||||
// `assemble_candidates_from_auto_impls`.
|
||||
ty::Foreign(..) => ty::Binder::dummy(Vec::new()),
|
||||
|
||||
// FIXME(unsafe_binders): Squash the double binder for now, I guess.
|
||||
ty::UnsafeBinder(_) => return Err(SelectionError::Unimplemented),
|
||||
|
||||
|
@ -2308,7 +2313,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
ty::Placeholder(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Param(..)
|
||||
| ty::Foreign(..)
|
||||
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
|
||||
| ty::Bound(..)
|
||||
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
|
|
|
@ -259,6 +259,8 @@ pub trait Interner:
|
|||
|
||||
fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool;
|
||||
|
||||
fn is_default_trait(self, def_id: Self::DefId) -> bool;
|
||||
|
||||
fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>;
|
||||
|
||||
fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
//@ check-pass
|
||||
//@ compile-flags: -Zexperimental-default-bounds
|
||||
|
||||
#![feature(auto_traits, lang_items, no_core, rustc_attrs, trait_alias)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized {}
|
||||
|
||||
#[lang = "default_trait1"]
|
||||
auto trait DefaultTrait1 {}
|
||||
|
||||
#[lang = "default_trait2"]
|
||||
auto trait DefaultTrait2 {}
|
||||
|
||||
trait Trait<Rhs: ?Sized = Self> {}
|
||||
trait Trait1 : Trait {}
|
||||
|
||||
trait Trait2 {
|
||||
type Type;
|
||||
}
|
||||
trait Trait3<T> = Trait2<Type = T>;
|
||||
|
||||
fn main() {}
|
41
tests/ui/traits/default_auto_traits/default-bounds.rs
Normal file
41
tests/ui/traits/default_auto_traits/default-bounds.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
//@ compile-flags: -Zexperimental-default-bounds
|
||||
|
||||
#![feature(
|
||||
auto_traits,
|
||||
lang_items,
|
||||
negative_impls,
|
||||
no_core,
|
||||
rustc_attrs
|
||||
)]
|
||||
#![allow(incomplete_features)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized {}
|
||||
|
||||
#[lang = "copy"]
|
||||
pub trait Copy {}
|
||||
|
||||
#[lang = "default_trait1"]
|
||||
auto trait Leak {}
|
||||
|
||||
#[lang = "default_trait2"]
|
||||
auto trait SyncDrop {}
|
||||
|
||||
struct Forbidden;
|
||||
|
||||
impl !Leak for Forbidden {}
|
||||
impl !SyncDrop for Forbidden {}
|
||||
|
||||
struct Accepted;
|
||||
|
||||
fn bar<T: Leak>(_: T) {}
|
||||
|
||||
fn main() {
|
||||
// checking that bounds can be added explicitly
|
||||
bar(Forbidden);
|
||||
//~^ ERROR the trait bound `Forbidden: Leak` is not satisfied
|
||||
//~| ERROR the trait bound `Forbidden: SyncDrop` is not satisfied
|
||||
bar(Accepted);
|
||||
}
|
31
tests/ui/traits/default_auto_traits/default-bounds.stderr
Normal file
31
tests/ui/traits/default_auto_traits/default-bounds.stderr
Normal file
|
@ -0,0 +1,31 @@
|
|||
error[E0277]: the trait bound `Forbidden: SyncDrop` is not satisfied
|
||||
--> $DIR/default-bounds.rs:37:9
|
||||
|
|
||||
LL | bar(Forbidden);
|
||||
| --- ^^^^^^^^^ the trait `SyncDrop` is not implemented for `Forbidden`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required by a bound in `bar`
|
||||
--> $DIR/default-bounds.rs:33:8
|
||||
|
|
||||
LL | fn bar<T: Leak>(_: T) {}
|
||||
| ^ required by this bound in `bar`
|
||||
|
||||
error[E0277]: the trait bound `Forbidden: Leak` is not satisfied
|
||||
--> $DIR/default-bounds.rs:37:9
|
||||
|
|
||||
LL | bar(Forbidden);
|
||||
| --- ^^^^^^^^^ the trait `Leak` is not implemented for `Forbidden`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required by a bound in `bar`
|
||||
--> $DIR/default-bounds.rs:33:11
|
||||
|
|
||||
LL | fn bar<T: Leak>(_: T) {}
|
||||
| ^^^^ required by this bound in `bar`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -0,0 +1,17 @@
|
|||
error[E0277]: the trait bound `extern_non_leak::Opaque: Leak` is not satisfied
|
||||
--> $DIR/extern-types.rs:44:13
|
||||
|
|
||||
LL | foo(x);
|
||||
| --- ^ the trait `Leak` is not implemented for `extern_non_leak::Opaque`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/extern-types.rs:20:8
|
||||
|
|
||||
LL | fn foo<T: ?Sized>(_: &T) {}
|
||||
| ^ required by this bound in `foo`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
17
tests/ui/traits/default_auto_traits/extern-types.next.stderr
Normal file
17
tests/ui/traits/default_auto_traits/extern-types.next.stderr
Normal file
|
@ -0,0 +1,17 @@
|
|||
error[E0277]: the trait bound `extern_non_leak::Opaque: Leak` is not satisfied
|
||||
--> $DIR/extern-types.rs:44:13
|
||||
|
|
||||
LL | foo(x);
|
||||
| --- ^ the trait `Leak` is not implemented for `extern_non_leak::Opaque`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/extern-types.rs:20:8
|
||||
|
|
||||
LL | fn foo<T: ?Sized>(_: &T) {}
|
||||
| ^ required by this bound in `foo`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
49
tests/ui/traits/default_auto_traits/extern-types.rs
Normal file
49
tests/ui/traits/default_auto_traits/extern-types.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
//@ compile-flags: -Zexperimental-default-bounds
|
||||
//@ revisions: current next
|
||||
//@ [next] compile-flags: -Znext-solver
|
||||
|
||||
#![feature(auto_traits, extern_types, lang_items, negative_impls, no_core, rustc_attrs)]
|
||||
#![allow(incomplete_features)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized {}
|
||||
|
||||
#[lang = "copy"]
|
||||
pub trait Copy {}
|
||||
|
||||
#[lang = "default_trait1"]
|
||||
auto trait Leak {}
|
||||
|
||||
// implicit T: Leak here
|
||||
fn foo<T: ?Sized>(_: &T) {}
|
||||
|
||||
mod extern_leak {
|
||||
use crate::*;
|
||||
|
||||
extern "C" {
|
||||
type Opaque;
|
||||
}
|
||||
|
||||
fn forward_extern_ty(x: &Opaque) {
|
||||
// ok, extern type leak by default
|
||||
crate::foo(x);
|
||||
}
|
||||
}
|
||||
|
||||
mod extern_non_leak {
|
||||
use crate::*;
|
||||
|
||||
extern "C" {
|
||||
type Opaque;
|
||||
}
|
||||
|
||||
impl !Leak for Opaque {}
|
||||
fn forward_extern_ty(x: &Opaque) {
|
||||
foo(x);
|
||||
//~^ ERROR: the trait bound `extern_non_leak::Opaque: Leak` is not satisfied
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,61 @@
|
|||
//@ compile-flags: -Zexperimental-default-bounds
|
||||
|
||||
#![feature(
|
||||
auto_traits,
|
||||
lang_items,
|
||||
more_maybe_bounds,
|
||||
negative_impls,
|
||||
no_core,
|
||||
rustc_attrs
|
||||
)]
|
||||
#![allow(internal_features)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized {}
|
||||
|
||||
#[lang = "copy"]
|
||||
pub trait Copy {}
|
||||
impl<'a, T: ?Sized> Copy for &'a T {}
|
||||
|
||||
#[lang = "legacy_receiver"]
|
||||
trait Receiver {}
|
||||
impl<T: ?Sized + ?Leak> Receiver for &T {}
|
||||
|
||||
#[lang = "unsize"]
|
||||
trait Unsize<T: ?Sized + ?Leak> {}
|
||||
|
||||
#[lang = "coerce_unsized"]
|
||||
trait CoerceUnsized<T: ?Leak + ?Sized> {}
|
||||
impl<'a, 'b: 'a, T: ?Sized + ?Leak + Unsize<U>, U: ?Sized + ?Leak> CoerceUnsized<&'a U> for &'b T {}
|
||||
|
||||
#[lang = "dispatch_from_dyn"]
|
||||
trait DispatchFromDyn<T: ?Leak> {}
|
||||
impl<'a, T: ?Sized + ?Leak + Unsize<U>, U: ?Sized + ?Leak> DispatchFromDyn<&'a U> for &'a T {}
|
||||
|
||||
#[lang = "default_trait1"]
|
||||
auto trait Leak {}
|
||||
|
||||
struct NonLeakS;
|
||||
impl !Leak for NonLeakS {}
|
||||
struct LeakS;
|
||||
|
||||
trait Trait {
|
||||
fn leak_foo(&self) {}
|
||||
fn maybe_leak_foo(&self) where Self: ?Leak {}
|
||||
}
|
||||
|
||||
impl Trait for NonLeakS {}
|
||||
impl Trait for LeakS {}
|
||||
|
||||
fn main() {
|
||||
let _: &dyn Trait = &NonLeakS;
|
||||
//~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied
|
||||
let _: &dyn Trait = &LeakS;
|
||||
let _: &(dyn Trait + ?Leak) = &LeakS;
|
||||
let x: &(dyn Trait + ?Leak) = &NonLeakS;
|
||||
x.leak_foo();
|
||||
//~^ ERROR the trait bound `dyn Trait: Leak` is not satisfied
|
||||
x.maybe_leak_foo();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied
|
||||
--> $DIR/maybe-bounds-in-dyn-traits.rs:53:25
|
||||
|
|
||||
LL | let _: &dyn Trait = &NonLeakS;
|
||||
| ^^^^^^^^^ the trait `Leak` is not implemented for `NonLeakS`
|
||||
|
|
||||
= note: required for the cast from `&NonLeakS` to `&dyn Trait + Leak`
|
||||
|
||||
error[E0277]: the trait bound `dyn Trait: Leak` is not satisfied
|
||||
--> $DIR/maybe-bounds-in-dyn-traits.rs:58:7
|
||||
|
|
||||
LL | x.leak_foo();
|
||||
| ^^^^^^^^ the trait `Leak` is not implemented for `dyn Trait`
|
||||
|
|
||||
note: required by a bound in `Trait::leak_foo`
|
||||
--> $DIR/maybe-bounds-in-dyn-traits.rs:45:5
|
||||
|
|
||||
LL | fn leak_foo(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
115
tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs
Normal file
115
tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
//@ compile-flags: -Zexperimental-default-bounds
|
||||
|
||||
#![feature(
|
||||
auto_traits,
|
||||
associated_type_defaults,
|
||||
generic_const_items,
|
||||
lang_items,
|
||||
more_maybe_bounds,
|
||||
negative_impls,
|
||||
no_core,
|
||||
rustc_attrs
|
||||
)]
|
||||
#![allow(incomplete_features)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized {}
|
||||
|
||||
#[lang = "legacy_receiver"]
|
||||
trait LegacyReceiver {}
|
||||
impl<T: ?Sized + ?Leak> LegacyReceiver for &T {}
|
||||
impl<T: ?Sized> LegacyReceiver for &mut T {}
|
||||
|
||||
#[lang = "default_trait1"]
|
||||
auto trait Leak {}
|
||||
|
||||
struct NonLeakS;
|
||||
impl !Leak for NonLeakS {}
|
||||
struct LeakS;
|
||||
|
||||
mod supertraits {
|
||||
use crate::*;
|
||||
|
||||
trait MaybeLeakT1: ?Leak {}
|
||||
trait MaybeLeakT2 where Self: ?Leak {}
|
||||
|
||||
impl MaybeLeakT1 for NonLeakS {}
|
||||
impl MaybeLeakT2 for NonLeakS {}
|
||||
}
|
||||
|
||||
mod maybe_self_assoc_type {
|
||||
use crate::*;
|
||||
|
||||
trait TestBase1<T: ?Sized> {}
|
||||
trait TestBase2<T: ?Leak + ?Sized> {}
|
||||
|
||||
trait Test1<T> {
|
||||
type MaybeLeakSelf: TestBase1<Self> where Self: ?Leak;
|
||||
//~^ ERROR the trait bound `Self: Leak` is not satisfied
|
||||
type LeakSelf: TestBase1<Self>;
|
||||
}
|
||||
|
||||
trait Test2<T> {
|
||||
type MaybeLeakSelf: TestBase2<Self> where Self: ?Leak;
|
||||
type LeakSelf: TestBase2<Self>;
|
||||
}
|
||||
|
||||
trait Test3 {
|
||||
type Leak1 = LeakS;
|
||||
type Leak2 = NonLeakS;
|
||||
//~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied
|
||||
}
|
||||
|
||||
trait Test4 {
|
||||
type MaybeLeak1: ?Leak = LeakS;
|
||||
type MaybeLeak2: ?Leak = NonLeakS;
|
||||
}
|
||||
|
||||
trait Test5: ?Leak {
|
||||
// ok, because assoc types have implicit where Self: Leak
|
||||
type MaybeLeakSelf1: TestBase1<Self>;
|
||||
type MaybeLeakSelf2: TestBase2<Self>;
|
||||
}
|
||||
}
|
||||
|
||||
mod maybe_self_assoc_const {
|
||||
use crate::*;
|
||||
|
||||
const fn size_of<T: ?Sized>() -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
trait Trait {
|
||||
const CLeak: usize = size_of::<Self>();
|
||||
const CNonLeak: usize = size_of::<Self>() where Self: ?Leak;
|
||||
//~^ ERROR the trait bound `Self: Leak` is not satisfied
|
||||
}
|
||||
}
|
||||
|
||||
mod methods {
|
||||
use crate::*;
|
||||
|
||||
trait Trait {
|
||||
fn leak_foo(&self) {}
|
||||
fn maybe_leak_foo(&self) where Self: ?Leak {}
|
||||
fn mut_leak_foo(&mut self) {}
|
||||
// there is no relax bound on corresponding Receiver impl
|
||||
fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {}
|
||||
//~^ `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types`
|
||||
}
|
||||
|
||||
impl Trait for NonLeakS {}
|
||||
impl Trait for LeakS {}
|
||||
|
||||
fn foo() {
|
||||
LeakS.leak_foo();
|
||||
LeakS.maybe_leak_foo();
|
||||
NonLeakS.leak_foo();
|
||||
//~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied
|
||||
NonLeakS.maybe_leak_foo();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,71 @@
|
|||
error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied
|
||||
--> $DIR/maybe-bounds-in-traits.rs:61:22
|
||||
|
|
||||
LL | type Leak2 = NonLeakS;
|
||||
| ^^^^^^^^ the trait `Leak` is not implemented for `NonLeakS`
|
||||
|
|
||||
note: required by a bound in `Test3::Leak2`
|
||||
--> $DIR/maybe-bounds-in-traits.rs:61:9
|
||||
|
|
||||
LL | type Leak2 = NonLeakS;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Test3::Leak2`
|
||||
|
||||
error[E0277]: the trait bound `Self: Leak` is not satisfied
|
||||
--> $DIR/maybe-bounds-in-traits.rs:49:29
|
||||
|
|
||||
LL | type MaybeLeakSelf: TestBase1<Self> where Self: ?Leak;
|
||||
| ^^^^^^^^^^^^^^^ the trait `Leak` is not implemented for `Self`
|
||||
|
|
||||
note: required by a bound in `TestBase1`
|
||||
--> $DIR/maybe-bounds-in-traits.rs:45:21
|
||||
|
|
||||
LL | trait TestBase1<T: ?Sized> {}
|
||||
| ^ required by this bound in `TestBase1`
|
||||
help: consider further restricting `Self`
|
||||
|
|
||||
LL | trait Test1<T>: Leak {
|
||||
| ++++++
|
||||
|
||||
error[E0658]: `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` feature
|
||||
--> $DIR/maybe-bounds-in-traits.rs:99:31
|
||||
|
|
||||
LL | fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information
|
||||
= help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
= help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
|
||||
|
||||
error[E0277]: the trait bound `Self: Leak` is not satisfied
|
||||
--> $DIR/maybe-bounds-in-traits.rs:86:43
|
||||
|
|
||||
LL | const CNonLeak: usize = size_of::<Self>() where Self: ?Leak;
|
||||
| ^^^^ the trait `Leak` is not implemented for `Self`
|
||||
|
|
||||
note: required by a bound in `size_of`
|
||||
--> $DIR/maybe-bounds-in-traits.rs:80:22
|
||||
|
|
||||
LL | const fn size_of<T: ?Sized>() -> usize {
|
||||
| ^ required by this bound in `size_of`
|
||||
help: consider further restricting `Self`
|
||||
|
|
||||
LL | trait Trait: Leak {
|
||||
| ++++++
|
||||
|
||||
error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied
|
||||
--> $DIR/maybe-bounds-in-traits.rs:109:18
|
||||
|
|
||||
LL | NonLeakS.leak_foo();
|
||||
| ^^^^^^^^ the trait `Leak` is not implemented for `NonLeakS`
|
||||
|
|
||||
note: required by a bound in `methods::Trait::leak_foo`
|
||||
--> $DIR/maybe-bounds-in-traits.rs:95:9
|
||||
|
|
||||
LL | fn leak_foo(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0658.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
Loading…
Add table
Add a link
Reference in a new issue