1
Fork 0

add a #[rustc_coinductive] attribute

This commit is contained in:
lcnr 2023-02-14 10:17:19 +01:00
parent e9ab7872fd
commit 646e667200
7 changed files with 44 additions and 33 deletions

View file

@ -682,14 +682,17 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"language items are subject to change", "language items are subject to change",
), ),
rustc_attr!( rustc_attr!(
rustc_pass_by_value, Normal, rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
template!(Word), ErrorFollowing,
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
), ),
rustc_attr!( rustc_attr!(
rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true, rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true,
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
), ),
rustc_attr!(
rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true,
"#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver."
),
rustc_attr!( rustc_attr!(
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true, rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true,
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."

View file

@ -934,9 +934,10 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
} }
let is_marker = tcx.has_attr(def_id, sym::marker); let is_marker = tcx.has_attr(def_id, sym::marker);
let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
let skip_array_during_method_dispatch = let skip_array_during_method_dispatch =
tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch); tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
ty::trait_def::TraitSpecializationKind::Marker ty::trait_def::TraitSpecializationKind::Marker
} else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
ty::trait_def::TraitSpecializationKind::AlwaysApplicable ty::trait_def::TraitSpecializationKind::AlwaysApplicable
@ -1036,16 +1037,17 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
no_dups.then_some(list) no_dups.then_some(list)
}); });
ty::TraitDef::new( ty::TraitDef {
def_id, def_id,
unsafety, unsafety,
paren_sugar, paren_sugar,
is_auto, has_auto_impl: is_auto,
is_marker, is_marker,
is_coinductive: rustc_coinductive || is_auto,
skip_array_during_method_dispatch, skip_array_during_method_dispatch,
spec_kind, specialization_kind,
must_implement_one_of, must_implement_one_of,
) }
} }
fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool {

View file

@ -2388,15 +2388,17 @@ impl<'tcx> TyCtxt<'tcx> {
self.trait_def(trait_def_id).has_auto_impl self.trait_def(trait_def_id).has_auto_impl
} }
/// Returns `true` if this is coinductive, either because it is
/// an auto trait or because it has the `#[rustc_coinductive]` attribute.
pub fn trait_is_coinductive(self, trait_def_id: DefId) -> bool {
self.trait_def(trait_def_id).is_coinductive
}
/// Returns `true` if this is a trait alias. /// Returns `true` if this is a trait alias.
pub fn trait_is_alias(self, trait_def_id: DefId) -> bool { pub fn trait_is_alias(self, trait_def_id: DefId) -> bool {
self.def_kind(trait_def_id) == DefKind::TraitAlias self.def_kind(trait_def_id) == DefKind::TraitAlias
} }
pub fn trait_is_coinductive(self, trait_def_id: DefId) -> bool {
self.trait_is_auto(trait_def_id) || self.lang_items().sized_trait() == Some(trait_def_id)
}
/// Returns layout of a generator. Layout might be unavailable if the /// Returns layout of a generator. Layout might be unavailable if the
/// generator is tainted by errors. /// generator is tainted by errors.
pub fn generator_layout(self, def_id: DefId) -> Option<&'tcx GeneratorLayout<'tcx>> { pub fn generator_layout(self, def_id: DefId) -> Option<&'tcx GeneratorLayout<'tcx>> {

View file

@ -31,6 +31,15 @@ pub struct TraitDef {
/// and thus `impl`s of it are allowed to overlap. /// and thus `impl`s of it are allowed to overlap.
pub is_marker: bool, pub is_marker: bool,
/// If `true`, then this trait has to `#[rustc_coinductive]` attribute or
/// is an auto trait. This indicates that trait solver cycles involving an
/// `X: ThisTrait` goal are accepted.
///
/// In the future all traits should be coinductive, but we need a better
/// formal understanding of what exactly that means and should probably
/// also have already switched to the new trait solver.
pub is_coinductive: bool,
/// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]` /// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]`
/// attribute, indicating that editions before 2021 should not consider this trait /// attribute, indicating that editions before 2021 should not consider this trait
/// during method dispatch if the receiver is an array. /// during method dispatch if the receiver is an array.
@ -81,28 +90,6 @@ impl TraitImpls {
} }
impl<'tcx> TraitDef { impl<'tcx> TraitDef {
pub fn new(
def_id: DefId,
unsafety: hir::Unsafety,
paren_sugar: bool,
has_auto_impl: bool,
is_marker: bool,
skip_array_during_method_dispatch: bool,
specialization_kind: TraitSpecializationKind,
must_implement_one_of: Option<Box<[Ident]>>,
) -> TraitDef {
TraitDef {
def_id,
unsafety,
paren_sugar,
has_auto_impl,
is_marker,
skip_array_during_method_dispatch,
specialization_kind,
must_implement_one_of,
}
}
pub fn ancestors( pub fn ancestors(
&self, &self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,

View file

@ -156,6 +156,7 @@ impl CheckAttrVisitor<'_> {
| sym::rustc_dirty | sym::rustc_dirty
| sym::rustc_if_this_changed | sym::rustc_if_this_changed
| sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr), | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
sym::rustc_coinductive => self.check_rustc_coinductive(&attr, span, target),
sym::cmse_nonsecure_entry => { sym::cmse_nonsecure_entry => {
self.check_cmse_nonsecure_entry(hir_id, attr, span, target) self.check_cmse_nonsecure_entry(hir_id, attr, span, target)
} }
@ -1608,6 +1609,20 @@ impl CheckAttrVisitor<'_> {
} }
} }
/// Checks if the `#[rustc_coinductive]` attribute is applied to a trait.
fn check_rustc_coinductive(&self, attr: &Attribute, span: Span, target: Target) -> bool {
match target {
Target::Trait => true,
_ => {
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
attr_span: attr.span,
defn_span: span,
});
false
}
}
}
/// Checks if `#[link_section]` is applied to a function or static. /// Checks if `#[link_section]` is applied to a function or static.
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
match target { match target {

View file

@ -1224,6 +1224,7 @@ symbols! {
rustc_capture_analysis, rustc_capture_analysis,
rustc_clean, rustc_clean,
rustc_coherence_is_core, rustc_coherence_is_core,
rustc_coinductive,
rustc_const_stable, rustc_const_stable,
rustc_const_unstable, rustc_const_unstable,
rustc_conversion_suggestion, rustc_conversion_suggestion,

View file

@ -97,6 +97,7 @@ unsafe impl<T: Sync + ?Sized> Send for &T {}
#[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable
#[rustc_specialization_trait] #[rustc_specialization_trait]
#[rustc_deny_explicit_impl] #[rustc_deny_explicit_impl]
#[cfg_attr(not(bootstrap), rustc_coinductive)]
pub trait Sized { pub trait Sized {
// Empty. // Empty.
} }