Skip to content
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

Implement proper stability check for const impl Trait, fall back to unstable const when undeclared #97177

Merged
merged 7 commits into from
May 22, 2022
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
20 changes: 20 additions & 0 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ pub struct Stability {
pub feature: Symbol,
}

impl Stability {
pub fn is_unstable(&self) -> bool {
self.level.is_unstable()
}

pub fn is_stable(&self) -> bool {
self.level.is_stable()
}
}

/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic)]
Expand All @@ -111,6 +121,16 @@ pub struct ConstStability {
pub promotable: bool,
}

impl ConstStability {
pub fn is_const_unstable(&self) -> bool {
self.level.is_unstable()
}

pub fn is_const_stable(&self) -> bool {
self.level.is_stable()
}
}

/// The available stability levels.
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/const_eval/fn_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_span::symbol::Symbol;
pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
if tcx.is_const_fn_raw(def_id) {
let const_stab = tcx.lookup_const_stability(def_id)?;
if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None }
if const_stab.is_const_unstable() { Some(const_stab.feature) } else { None }
} else {
None
}
Expand Down
14 changes: 1 addition & 13 deletions compiler/rustc_const_eval/src/transform/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {

// The local type and predicate checks are not free and only relevant for `const fn`s.
if self.const_kind() == hir::ConstContext::ConstFn {
// Prevent const trait methods from being annotated as `stable`.
// FIXME: Do this as part of stability checking.
if self.is_const_stable_const_fn() {
if crate::const_eval::is_parent_const_impl_raw(tcx, def_id) {
self.ccx
.tcx
.sess
.struct_span_err(self.span, "trait methods cannot be stable const fn")
.emit();
}
}

for (idx, local) in body.local_decls.iter_enumerated() {
// Handle the return place below.
if idx == RETURN_PLACE || local.internal {
Expand Down Expand Up @@ -944,7 +932,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// have no `rustc_const_stable` attributes to be const-unstable as well. This
// should be fixed later.
let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none()
&& tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable());
&& tcx.lookup_stability(callee).map_or(false, |s| s.is_unstable());
if callee_is_unstable_unmarked {
trace!("callee_is_unstable_unmarked");
// We do not use `const` modifiers for intrinsic "functions", as intrinsics are
Expand Down
43 changes: 29 additions & 14 deletions compiler/rustc_const_eval/src/transform/check_consts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ pub fn rustc_allow_const_fn_unstable(
// functions are subject to more stringent restrictions than "const-unstable" functions: They
// cannot use unstable features and can only call other "const-stable" functions.
pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
use attr::{ConstStability, Stability, StabilityLevel};

// A default body marked const is not const-stable because const
// trait fns currently cannot be const-stable. We shouldn't
// restrict default bodies to only call const-stable functions.
Expand All @@ -96,22 +94,39 @@ pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
// Const-stability is only relevant for `const fn`.
assert!(tcx.is_const_fn_raw(def_id));

// Functions with `#[rustc_const_unstable]` are const-unstable.
// A function is only const-stable if it has `#[rustc_const_stable]` or it the trait it belongs
// to is const-stable.
match tcx.lookup_const_stability(def_id) {
Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. }) => return false,
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => return true,
None => {}
Some(stab) => stab.is_const_stable(),
None if is_parent_const_stable_trait(tcx, def_id) => {
// Remove this when `#![feature(const_trait_impl)]` is stabilized,
// returning `true` unconditionally.
tcx.sess.delay_span_bug(
tcx.def_span(def_id),
"trait implementations cannot be const stable yet",
);
true
}
None => false, // By default, items are not const stable.
}
}

fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let local_def_id = def_id.expect_local();
let hir_id = tcx.local_def_id_to_hir_id(local_def_id);

let Some(parent) = tcx.hir().find_parent_node(hir_id) else { return false };
let parent_def = tcx.hir().get(parent);

// Functions with `#[unstable]` are const-unstable.
//
// FIXME(ecstaticmorse): We should keep const-stability attributes wholly separate from normal stability
// attributes. `#[unstable]` should be irrelevant.
if let Some(Stability { level: StabilityLevel::Unstable { .. }, .. }) =
tcx.lookup_stability(def_id)
{
if !matches!(
parent_def,
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }),
..
})
) {
return false;
}

true
tcx.lookup_const_stability(parent.owner).map_or(false, |stab| stab.is_const_stable())
}
17 changes: 16 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2791,7 +2791,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn is_const_fn(self, def_id: DefId) -> bool {
if self.is_const_fn_raw(def_id) {
match self.lookup_const_stability(def_id) {
Some(stability) if stability.level.is_unstable() => {
Some(stability) if stability.is_const_unstable() => {
// has a `rustc_const_unstable` attribute, check whether the user enabled the
// corresponding feature gate.
self.features()
Expand All @@ -2808,6 +2808,21 @@ impl<'tcx> TyCtxt<'tcx> {
false
}
}

/// Whether the trait impl is marked const. This does not consider stability or feature gates.
pub fn is_const_trait_impl_raw(self, def_id: DefId) -> bool {
let Some(local_def_id) = def_id.as_local() else { return false };
let hir_id = self.local_def_id_to_hir_id(local_def_id);
let node = self.hir().get(hir_id);

matches!(
node,
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }),
..
})
)
}
}

impl<'tcx> TyCtxtAt<'tcx> {
Expand Down
43 changes: 31 additions & 12 deletions compiler/rustc_passes/src/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
// Propagate unstability. This can happen even for non-staged-api crates in case
// -Zforce-unstable-if-unmarked is set.
if let Some(stab) = self.parent_stab {
if inherit_deprecation.yes() && stab.level.is_unstable() {
if inherit_deprecation.yes() && stab.is_unstable() {
self.index.stab_map.insert(def_id, stab);
}
}
Expand Down Expand Up @@ -190,7 +190,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
if const_stab.is_none() {
debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
if let Some(parent) = self.parent_const_stab {
if parent.level.is_unstable() {
if parent.is_const_unstable() {
self.index.const_stab_map.insert(def_id, parent);
}
}
Expand Down Expand Up @@ -272,9 +272,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
if stab.is_none() {
debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
if let Some(stab) = self.parent_stab {
if inherit_deprecation.yes() && stab.level.is_unstable()
|| inherit_from_parent.yes()
{
if inherit_deprecation.yes() && stab.is_unstable() || inherit_from_parent.yes() {
self.index.stab_map.insert(def_id, stab);
}
}
Expand Down Expand Up @@ -532,7 +530,8 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
return;
}

let is_const = self.tcx.is_const_fn(def_id.to_def_id());
let is_const = self.tcx.is_const_fn(def_id.to_def_id())
|| self.tcx.is_const_trait_impl_raw(def_id.to_def_id());
let is_stable = self
.tcx
.lookup_stability(def_id)
Expand Down Expand Up @@ -710,16 +709,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
// For implementations of traits, check the stability of each item
// individually as it's possible to have a stable trait with unstable
// items.
hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => {
if self.tcx.features().staged_api {
hir::ItemKind::Impl(hir::Impl {
of_trait: Some(ref t),
self_ty,
items,
constness,
..
}) => {
let features = self.tcx.features();
if features.staged_api {
let attrs = self.tcx.hir().attrs(item.hir_id());
let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item.span);

// If this impl block has an #[unstable] attribute, give an
// error if all involved types and traits are stable, because
// it will have no effect.
// See: https://github.com/rust-lang/rust/issues/55436
let attrs = self.tcx.hir().attrs(item.hir_id());
if let (Some((Stability { level: attr::Unstable { .. }, .. }, span)), _) =
attr::find_stability(&self.tcx.sess, attrs, item.span)
{
if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab {
let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
c.visit_ty(self_ty);
c.visit_trait_ref(t);
Expand All @@ -735,6 +741,19 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
);
}
}

// `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
// needs to have an error emitted.
if features.const_trait_impl
&& *constness == hir::Constness::Const
&& const_stab.map_or(false, |(stab, _)| stab.is_const_stable())
{
self.tcx
.sess
.struct_span_err(item.span, "trait implementations cannot be const stable yet")
.note("see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information")
.emit();
}
}

for impl_item_ref in *items {
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ crate fn build_impl(
}

if let Some(stab) = tcx.lookup_stability(did) {
if stab.level.is_unstable() && stab.feature == sym::rustc_private {
if stab.is_unstable() && stab.feature == sym::rustc_private {
return;
}
}
Expand Down Expand Up @@ -373,7 +373,7 @@ crate fn build_impl(
}

if let Some(stab) = tcx.lookup_stability(did) {
if stab.level.is_unstable() && stab.feature == sym::rustc_private {
if stab.is_unstable() && stab.feature == sym::rustc_private {
return;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ impl Item {
self.stability(tcx).as_ref().and_then(|s| {
let mut classes = Vec::with_capacity(2);

if s.level.is_unstable() {
if s.is_unstable() {
classes.push("unstable");
}

Expand Down
5 changes: 1 addition & 4 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,7 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->

// The "rustc_private" crates are permanently unstable so it makes no sense
// to render "unstable" everywhere.
if item
.stability(tcx)
.as_ref()
.map(|s| s.level.is_unstable() && s.feature != sym::rustc_private)
if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private)
== Some(true)
{
tags += &tag_html("unstable", "", "Experimental");
Expand Down
7 changes: 3 additions & 4 deletions src/test/ui/consts/rustc-impl-const-stability.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
// build-pass
// check-pass

#![crate_type = "lib"]
#![feature(staged_api)]
#![feature(const_trait_impl)]
#![stable(feature = "foo", since = "1.0.0")]


#[stable(feature = "potato", since = "1.27.0")]
pub struct Data {
_data: u128
_data: u128,
}

#[stable(feature = "potato", since = "1.27.0")]
#[rustc_const_unstable(feature = "data_foo", issue = "none")]
impl const Default for Data {
#[rustc_const_unstable(feature = "data_foo", issue = "none")]
fn default() -> Data {
Data { _data: 42 }
}
Expand Down
7 changes: 2 additions & 5 deletions src/test/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![feature(const_trait_impl)]

#![feature(staged_api)]
#![stable(feature = "rust1", since = "1.0.0")]

Expand All @@ -13,9 +12,7 @@ pub trait MyTrait {
pub struct Unstable;

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "staged", issue = "none")]
#[rustc_const_unstable(feature = "unstable", issue = "none")]
impl const MyTrait for Unstable {
fn func() {

}
fn func() {}
}
45 changes: 0 additions & 45 deletions src/test/ui/rfc-2632-const-trait-impl/stability.rs

This file was deleted.

19 changes: 0 additions & 19 deletions src/test/ui/rfc-2632-const-trait-impl/stability.stderr

This file was deleted.

Loading