From 0d75ab2293a106eb674ac01860910cfc1580837e Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 26 May 2019 09:58:06 +0100 Subject: [PATCH] Make constructors actually be const functions --- src/librustc/ty/constness.rs | 32 +++-- src/librustc_metadata/decoder.rs | 1 + src/libsyntax/feature_gate.rs | 4 + src/libsyntax_pos/symbol.rs | 1 + .../const_constructor/const-construct-call.rs | 116 ++++++++++++++++++ ...ure-gate-const_constructor.const_fn.stderr | 34 +++++ ...gate-const_constructor.min_const_fn.stderr | 34 +++++ .../feature-gate-const_constructor.rs | 28 +++++ 8 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/consts/const_constructor/const-construct-call.rs create mode 100644 src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr create mode 100644 src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr create mode 100644 src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index 7298b548f31..56f75e800f2 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -2,7 +2,7 @@ use crate::hir::def_id::DefId; use crate::hir; use crate::ty::TyCtxt; -use syntax_pos::symbol::Symbol; +use syntax_pos::symbol::{sym, Symbol}; use crate::hir::map::blocks::FnLikeNode; use syntax::attr; @@ -10,27 +10,30 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { /// Whether the `def_id` counts as const fn in your current crate, considering all active /// feature gates pub fn is_const_fn(self, def_id: DefId) -> bool { - self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) { - Some(stab) => match stab.const_stability { + self.is_const_fn_raw(def_id) && match self.is_unstable_const_fn(def_id) { + Some(feature_name) => { // has a `rustc_const_unstable` attribute, check whether the user enabled the - // corresponding feature gate - Some(feature_name) => self.features() + // corresponding feature gate, const_constructor is not a lib feature, so has + // to be checked separately. + self.features() .declared_lib_features .iter() - .any(|&(sym, _)| sym == feature_name), - // the function has no stability attribute, it is stable as const fn or the user - // needs to use feature gates to use the function at all - None => true, + .any(|&(sym, _)| sym == feature_name) + || (feature_name == sym::const_constructor + && self.features().const_constructor) }, - // functions without stability are either stable user written const fn or the user is - // using feature gates and we thus don't care what they do + // functions without const stability are either stable user written + // const fn or the user is using feature gates and we thus don't + // care what they do None => true, } } /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it pub fn is_unstable_const_fn(self, def_id: DefId) -> Option { - if self.is_const_fn_raw(def_id) { + if self.is_constructor(def_id) { + Some(sym::const_constructor) + } else if self.is_const_fn_raw(def_id) { self.lookup_stability(def_id)?.const_stability } else { None @@ -70,8 +73,11 @@ fn is_const_fn_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool let hir_id = tcx.hir().as_local_hir_id(def_id) .expect("Non-local call to local provider is_const_fn"); - if let Some(fn_like) = FnLikeNode::from_node(tcx.hir().get_by_hir_id(hir_id)) { + let node = tcx.hir().get_by_hir_id(hir_id); + if let Some(fn_like) = FnLikeNode::from_node(node) { fn_like.constness() == hir::Constness::Const + } else if let hir::Node::Ctor(_) = node { + true } else { false } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 10ff606b013..e3e327d0a5b 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -1167,6 +1167,7 @@ pub fn get_macro(&self, id: DefIndex) -> MacroDef { let constness = match self.entry(id).kind { EntryKind::Method(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, + EntryKind::Variant(..) | EntryKind::Struct(..) => hir::Constness::Const, _ => hir::Constness::NotConst, }; constness == hir::Constness::Const diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 4a95b6f69a1..0043b8a1c47 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -560,6 +560,10 @@ pub fn walk_feature_fields(&self, mut f: F) // Allows the user of associated type bounds. (active, associated_type_bounds, "1.34.0", Some(52662), None), + // Allows calling constructor functions in `const fn` + // FIXME Create issue + (active, const_constructor, "1.37.0", Some(61456), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 4e080d115d2..6fac3435814 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -185,6 +185,7 @@ conservative_impl_trait, console, const_compare_raw_pointers, + const_constructor, const_fn, const_fn_union, const_generics, diff --git a/src/test/ui/consts/const_constructor/const-construct-call.rs b/src/test/ui/consts/const_constructor/const-construct-call.rs new file mode 100644 index 00000000000..f2d2bda53c0 --- /dev/null +++ b/src/test/ui/consts/const_constructor/const-construct-call.rs @@ -0,0 +1,116 @@ +// Test that constructors are considered to be const fns with the required feature. + +// run-pass + +// revisions: min_const_fn const_fn + +#![cfg_attr(const_fn, feature(const_fn))] + +#![feature(const_constructor)] + +// Ctor(..) is transformed to Ctor { 0: ... } in HAIR lowering, so directly +// calling constructors doesn't require them to be const. + +type ExternalType = std::panic::AssertUnwindSafe<(Option, Result)>; + +const fn call_external_constructors_in_local_vars() -> ExternalType { + let f = Some; + let g = Err; + let h = std::panic::AssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +} + +const CALL_EXTERNAL_CONSTRUCTORS_IN_LOCAL_VARS: ExternalType = { + let f = Some; + let g = Err; + let h = std::panic::AssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +}; + +const fn call_external_constructors_in_temps() -> ExternalType { + let x = { Some }(5); + let y = (*&Err)(false); + let z = [std::panic::AssertUnwindSafe][0]((x, y)); + z +} + +const CALL_EXTERNAL_CONSTRUCTORS_IN_TEMPS: ExternalType = { + let x = { Some }(5); + let y = (*&Err)(false); + let z = [std::panic::AssertUnwindSafe][0]((x, y)); + z +}; + +#[derive(Debug, PartialEq)] +enum LocalOption { + Some(T), + _None, +} + +#[derive(Debug, PartialEq)] +enum LocalResult { + _Ok(T), + Err(E), +} + +#[derive(Debug, PartialEq)] +struct LocalAssertUnwindSafe(T); + +type LocalType = LocalAssertUnwindSafe<(LocalOption, LocalResult)>; + +const fn call_local_constructors_in_local_vars() -> LocalType { + let f = LocalOption::Some; + let g = LocalResult::Err; + let h = LocalAssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +} + +const CALL_LOCAL_CONSTRUCTORS_IN_LOCAL_VARS: LocalType = { + let f = LocalOption::Some; + let g = LocalResult::Err; + let h = LocalAssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +}; + +const fn call_local_constructors_in_temps() -> LocalType { + let x = { LocalOption::Some }(5); + let y = (*&LocalResult::Err)(false); + let z = [LocalAssertUnwindSafe][0]((x, y)); + z +} + +const CALL_LOCAL_CONSTRUCTORS_IN_TEMPS: LocalType = { + let x = { LocalOption::Some }(5); + let y = (*&LocalResult::Err)(false); + let z = [LocalAssertUnwindSafe][0]((x, y)); + z +}; + +fn main() { + assert_eq!( + ( + call_external_constructors_in_local_vars().0, + call_external_constructors_in_temps().0, + call_local_constructors_in_local_vars(), + call_local_constructors_in_temps(), + ), + ( + CALL_EXTERNAL_CONSTRUCTORS_IN_LOCAL_VARS.0, + CALL_EXTERNAL_CONSTRUCTORS_IN_TEMPS.0, + CALL_LOCAL_CONSTRUCTORS_IN_LOCAL_VARS, + CALL_LOCAL_CONSTRUCTORS_IN_TEMPS, + ) + ); +} diff --git a/src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr new file mode 100644 index 00000000000..fa4f83ed01e --- /dev/null +++ b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr @@ -0,0 +1,34 @@ +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:9:37 + | +LL | const EXTERNAL_CONST: Option = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:12:24 + | +LL | const LOCAL_CONST: E = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:17:13 + | +LL | let _ = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:23:13 + | +LL | let _ = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr new file mode 100644 index 00000000000..fa4f83ed01e --- /dev/null +++ b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr @@ -0,0 +1,34 @@ +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:9:37 + | +LL | const EXTERNAL_CONST: Option = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:12:24 + | +LL | const LOCAL_CONST: E = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:17:13 + | +LL | let _ = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:23:13 + | +LL | let _ = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs new file mode 100644 index 00000000000..b37fd2fd243 --- /dev/null +++ b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs @@ -0,0 +1,28 @@ +// revisions: min_const_fn const_fn + +#![cfg_attr(const_fn, feature(const_fn))] + +enum E { + V(i32), +} + +const EXTERNAL_CONST: Option = {Some}(1); +//[min_const_fn]~^ ERROR is not yet stable as a const fn +//[const_fn]~^^ ERROR is not yet stable as a const fn +const LOCAL_CONST: E = {E::V}(1); +//[min_const_fn]~^ ERROR is not yet stable as a const fn +//[const_fn]~^^ ERROR is not yet stable as a const fn + +const fn external_fn() { + let _ = {Some}(1); + //[min_const_fn]~^ ERROR is not yet stable as a const fn + //[const_fn]~^^ ERROR is not yet stable as a const fn +} + +const fn local_fn() { + let _ = {E::V}(1); + //[min_const_fn]~^ ERROR is not yet stable as a const fn + //[const_fn]~^^ ERROR is not yet stable as a const fn +} + +fn main() {} -- 2.44.0