Skip to content

Commit

Permalink
Auto merge of #97177 - oli-obk:const-stability, r=davidtwco
Browse files Browse the repository at this point in the history
Implement proper stability check for const impl Trait, fall back to unstable const when undeclared

Continuation of #93960

`@jhpratt` it looks to me like the test was simply not testing for the failure you were looking for? Your checks actually do the right thing for const traits?
  • Loading branch information
bors committed May 22, 2022
2 parents bb5e6c9 + f9c4f2b commit acfd327
Show file tree
Hide file tree
Showing 22 changed files with 259 additions and 168 deletions.
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 @@ pub(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 @@ pub(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

0 comments on commit acfd327

Please sign in to comment.