about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-09-24 10:58:41 +0000
committerbors <bors@rust-lang.org>2019-09-24 10:58:41 +0000
commit6ef275e6c3cb1384ec78128eceeb4963ff788dca (patch)
treee2e776a76307c183bb2a762a2b1f630b71339b68 /src
parent7fdea7a72abb9f5a58fdc19c0a298042291c53b2 (diff)
parent7767e7fb165d527f1991175809a361f2d2313b80 (diff)
downloadrust-6ef275e6c3cb1384ec78128eceeb4963ff788dca.tar.gz
rust-6ef275e6c3cb1384ec78128eceeb4963ff788dca.zip
Auto merge of #63770 - oli-obk:allow_internal_unstable, r=Centril
Stabilize `str::len`, `[T]::len` and `str::as_bytes` as const fn

r? @Centril

cc @RalfJung

This also introduces a scheme for making certain feature gates legal in stabilized const fns
Diffstat (limited to 'src')
-rw-r--r--src/libcore/lib.rs6
-rw-r--r--src/libcore/slice/mod.rs6
-rw-r--r--src/libcore/str/mod.rs8
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs4
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs81
-rw-r--r--src/libsyntax/attr/mod.rs24
-rw-r--r--src/libsyntax/ext/base.rs30
-rw-r--r--src/test/ui/consts/const-eval/strlen.rs12
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs2
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr8
-rw-r--r--src/test/ui/issues/issue-52060.rs1
-rw-r--r--src/test/ui/issues/issue-52060.stderr10
12 files changed, 114 insertions, 78 deletions
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 5c681b3a5d8..8221df56a51 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -120,9 +120,9 @@
 #![feature(rtm_target_feature)]
 #![feature(f16c_target_feature)]
 #![feature(hexagon_target_feature)]
-#![feature(const_slice_len)]
-#![feature(const_str_as_bytes)]
-#![feature(const_str_len)]
+#![cfg_attr(bootstrap, feature(const_slice_len))]
+#![cfg_attr(bootstrap, feature(const_str_as_bytes))]
+#![cfg_attr(bootstrap, feature(const_str_len))]
 #![feature(const_int_conversion)]
 #![feature(const_transmute)]
 #![feature(non_exhaustive)]
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index 931768564ca..0c2a4e08672 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -62,7 +62,9 @@ impl<T> [T] {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
-    #[rustc_const_unstable(feature = "const_slice_len")]
+    #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_slice_len"))]
+    // SAFETY: const sound because we transmute out the length field as a usize (which it must be)
+    #[cfg_attr(not(bootstrap), allow_internal_unstable(const_fn_union))]
     pub const fn len(&self) -> usize {
         unsafe {
             crate::ptr::Repr { rust: self }.raw.len
@@ -79,7 +81,7 @@ impl<T> [T] {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
-    #[rustc_const_unstable(feature = "const_slice_len")]
+    #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_slice_len"))]
     pub const fn is_empty(&self) -> bool {
         self.len() == 0
     }
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index 5e5b5593fd8..a6ec757faf1 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -2090,7 +2090,7 @@ impl str {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
-    #[rustc_const_unstable(feature = "const_str_len")]
+    #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_str_len"))]
     pub const fn len(&self) -> usize {
         self.as_bytes().len()
     }
@@ -2110,7 +2110,7 @@ impl str {
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_str_len")]
+    #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_str_len"))]
     pub const fn is_empty(&self) -> bool {
         self.len() == 0
     }
@@ -2168,7 +2168,9 @@ impl str {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline(always)]
-    #[rustc_const_unstable(feature="const_str_as_bytes")]
+    #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_str_as_bytes"))]
+    // SAFETY: const sound because we transmute two types with the same layout
+    #[cfg_attr(not(bootstrap), allow_internal_unstable(const_fn_union))]
     pub const fn as_bytes(&self) -> &[u8] {
         #[repr(C)]
         union Slices<'a> {
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index 39aa5c717ac..61e32ca8de5 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -305,7 +305,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                                     "assignment to non-`Copy` union field",
                                     "the previous content of the field will be dropped, which \
                                      causes undefined behavior if the field was not properly \
-                                     initialized", UnsafetyViolationKind::General)
+                                     initialized", UnsafetyViolationKind::GeneralAndConstFn)
                             } else {
                                 // write to non-move union, safe
                             }
@@ -313,7 +313,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                             self.require_unsafe("access to union field",
                                 "the field may not be properly initialized: using \
                                  uninitialized data will cause undefined behavior",
-                                 UnsafetyViolationKind::General)
+                                 UnsafetyViolationKind::GeneralAndConstFn)
                         }
                     }
                 }
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index 80e020a9eb7..be83c823d42 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -5,6 +5,8 @@ use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
 use rustc_target::spec::abi;
 use std::borrow::Cow;
 use syntax_pos::Span;
+use syntax::symbol::{sym, Symbol};
+use syntax::attr;
 
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
@@ -67,9 +69,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -
     )?;
 
     for bb in body.basic_blocks() {
-        check_terminator(tcx, body, bb.terminator())?;
+        check_terminator(tcx, body, def_id, bb.terminator())?;
         for stmt in &bb.statements {
-            check_statement(tcx, body, stmt)?;
+            check_statement(tcx, body, def_id, stmt)?;
         }
     }
     Ok(())
@@ -121,16 +123,17 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> Mc
 
 fn check_rvalue(
     tcx: TyCtxt<'tcx>,
-    body: &'a Body<'tcx>,
+    body: &Body<'tcx>,
+    def_id: DefId,
     rvalue: &Rvalue<'tcx>,
     span: Span,
 ) -> McfResult {
     match rvalue {
         Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
-            check_operand(operand, span)
+            check_operand(tcx, operand, span, def_id, body)
         }
         Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
-            check_place(place, span)
+            check_place(tcx, place, span, def_id, body)
         }
         Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
             use rustc::ty::cast::CastTy;
@@ -144,11 +147,11 @@ fn check_rvalue(
                 (CastTy::RPtr(_), CastTy::Float) => bug!(),
                 (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                 (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
-                _ => check_operand(operand, span),
+                _ => check_operand(tcx, operand, span, def_id, body),
             }
         }
         Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) => {
-            check_operand(operand, span)
+            check_operand(tcx, operand, span, def_id, body)
         }
         Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) |
         Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
@@ -162,8 +165,8 @@ fn check_rvalue(
         )),
         // binops are fine on integers
         Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
-            check_operand(lhs, span)?;
-            check_operand(rhs, span)?;
+            check_operand(tcx, lhs, span, def_id, body)?;
+            check_operand(tcx, rhs, span, def_id, body)?;
             let ty = lhs.ty(body, tcx);
             if ty.is_integral() || ty.is_bool() || ty.is_char() {
                 Ok(())
@@ -182,7 +185,7 @@ fn check_rvalue(
         Rvalue::UnaryOp(_, operand) => {
             let ty = operand.ty(body, tcx);
             if ty.is_integral() || ty.is_bool() {
-                check_operand(operand, span)
+                check_operand(tcx, operand, span, def_id, body)
             } else {
                 Err((
                     span,
@@ -192,7 +195,7 @@ fn check_rvalue(
         }
         Rvalue::Aggregate(_, operands) => {
             for operand in operands {
-                check_operand(operand, span)?;
+                check_operand(tcx, operand, span, def_id, body)?;
             }
             Ok(())
         }
@@ -201,21 +204,22 @@ fn check_rvalue(
 
 fn check_statement(
     tcx: TyCtxt<'tcx>,
-    body: &'a Body<'tcx>,
+    body: &Body<'tcx>,
+    def_id: DefId,
     statement: &Statement<'tcx>,
 ) -> McfResult {
     let span = statement.source_info.span;
     match &statement.kind {
         StatementKind::Assign(box(place, rval)) => {
-            check_place(place, span)?;
-            check_rvalue(tcx, body, rval, span)
+            check_place(tcx, place, span, def_id, body)?;
+            check_rvalue(tcx, body, def_id, rval, span)
         }
 
         StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
             Err((span, "loops and conditional expressions are not stable in const fn".into()))
         }
 
-        StatementKind::FakeRead(_, place) => check_place(place, span),
+        StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body),
 
         // just an assignment
         StatementKind::SetDiscriminant { .. } => Ok(()),
@@ -234,30 +238,48 @@ fn check_statement(
 }
 
 fn check_operand(
+    tcx: TyCtxt<'tcx>,
     operand: &Operand<'tcx>,
     span: Span,
+    def_id: DefId,
+    body: &Body<'tcx>
 ) -> McfResult {
     match operand {
         Operand::Move(place) | Operand::Copy(place) => {
-            check_place(place, span)
+            check_place(tcx, place, span, def_id, body)
         }
         Operand::Constant(_) => Ok(()),
     }
 }
 
 fn check_place(
+    tcx: TyCtxt<'tcx>,
     place: &Place<'tcx>,
     span: Span,
+    def_id: DefId,
+    body: &Body<'tcx>
 ) -> McfResult {
-    for elem in place.projection.iter() {
+    let mut cursor = &*place.projection;
+    while let [proj_base @ .., elem] = cursor {
+        cursor = proj_base;
         match elem {
             ProjectionElem::Downcast(..) => {
                 return Err((span, "`match` or `if let` in `const fn` is unstable".into()));
             }
+            ProjectionElem::Field(..) => {
+                let base_ty = Place::ty_from(&place.base, &proj_base, body, tcx).ty;
+                if let Some(def) = base_ty.ty_adt_def() {
+                    // No union field accesses in `const fn`
+                    if def.is_union() {
+                        if !feature_allowed(tcx, def_id, sym::const_fn_union) {
+                            return Err((span, "accessing union fields is unstable".into()));
+                        }
+                    }
+                }
+            }
             ProjectionElem::ConstantIndex { .. }
             | ProjectionElem::Subslice { .. }
             | ProjectionElem::Deref
-            | ProjectionElem::Field(..)
             | ProjectionElem::Index(_) => {}
         }
     }
@@ -271,9 +293,20 @@ fn check_place(
     }
 }
 
+/// Returns whether `allow_internal_unstable(..., <feature_gate>, ...)` is present.
+fn feature_allowed(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    feature_gate: Symbol,
+) -> bool {
+    attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
+        .map_or(false, |mut features| features.any(|name| name == feature_gate))
+}
+
 fn check_terminator(
     tcx: TyCtxt<'tcx>,
     body: &'a Body<'tcx>,
+    def_id: DefId,
     terminator: &Terminator<'tcx>,
 ) -> McfResult {
     let span = terminator.source_info.span;
@@ -283,11 +316,11 @@ fn check_terminator(
         | TerminatorKind::Resume => Ok(()),
 
         TerminatorKind::Drop { location, .. } => {
-            check_place(location, span)
+            check_place(tcx, location, span, def_id, body)
         }
         TerminatorKind::DropAndReplace { location, value, .. } => {
-            check_place(location, span)?;
-            check_operand(value, span)
+            check_place(tcx, location, span, def_id, body)?;
+            check_operand(tcx, value, span, def_id, body)
         },
 
         TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
@@ -339,10 +372,10 @@ fn check_terminator(
                     )),
                 }
 
-                check_operand(func, span)?;
+                check_operand(tcx, func, span, def_id, body)?;
 
                 for arg in args {
-                    check_operand(arg, span)?;
+                    check_operand(tcx, arg, span, def_id, body)?;
                 }
                 Ok(())
             } else {
@@ -356,7 +389,7 @@ fn check_terminator(
             msg: _,
             target: _,
             cleanup: _,
-        } => check_operand(cond, span),
+        } => check_operand(tcx, cond, span, def_id, body),
 
         TerminatorKind::FalseUnwind { .. } => {
             Err((span, "loops are not allowed in const fn".into()))
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 1f954064944..9d06b926f97 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -433,6 +433,30 @@ pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
     attrs.iter().find(|attr| attr.check_name(name))
 }
 
+pub fn allow_internal_unstable<'a>(
+    attrs: &[Attribute],
+    span_diagnostic: &'a errors::Handler,
+) -> Option<impl Iterator<Item = Symbol> + 'a> {
+    find_by_name(attrs, sym::allow_internal_unstable).and_then(|attr| {
+        attr.meta_item_list().or_else(|| {
+            span_diagnostic.span_err(
+                attr.span,
+                "allow_internal_unstable expects list of feature names"
+            );
+            None
+        }).map(|features| features.into_iter().filter_map(move |it| {
+            let name = it.ident().map(|ident| ident.name);
+            if name.is_none() {
+                span_diagnostic.span_err(
+                    it.span(),
+                    "`allow_internal_unstable` expects feature names",
+                )
+            }
+            name
+        }))
+    })
+}
+
 pub fn filter_by_name(attrs: &[Attribute], name: Symbol)
                       -> impl Iterator<Item=&Attribute> {
     attrs.iter().filter(move |attr| attr.check_name(name))
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index a6be5b10178..e189d8f8636 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -762,33 +762,9 @@ impl SyntaxExtension {
         name: Name,
         attrs: &[ast::Attribute],
     ) -> SyntaxExtension {
-        let allow_internal_unstable =
-            attr::find_by_name(attrs, sym::allow_internal_unstable).map(|attr| {
-                attr.meta_item_list()
-                    .map(|list| {
-                        list.iter()
-                            .filter_map(|it| {
-                                let name = it.ident().map(|ident| ident.name);
-                                if name.is_none() {
-                                    sess.span_diagnostic.span_err(
-                                        it.span(), "allow internal unstable expects feature names"
-                                    )
-                                }
-                                name
-                            })
-                            .collect::<Vec<Symbol>>()
-                            .into()
-                    })
-                    .unwrap_or_else(|| {
-                        sess.span_diagnostic.span_warn(
-                            attr.span,
-                            "allow_internal_unstable expects list of feature names. In the future \
-                             this will become a hard error. Please use `allow_internal_unstable(\
-                             foo, bar)` to only allow the `foo` and `bar` features",
-                        );
-                        vec![sym::allow_internal_unstable_backcompat_hack].into()
-                    })
-            });
+        let allow_internal_unstable = attr::allow_internal_unstable(
+            &attrs, &sess.span_diagnostic,
+        ).map(|features| features.collect::<Vec<Symbol>>().into());
 
         let mut local_inner_macros = false;
         if let Some(macro_export) = attr::find_by_name(attrs, sym::macro_export) {
diff --git a/src/test/ui/consts/const-eval/strlen.rs b/src/test/ui/consts/const-eval/strlen.rs
index 6a5cad82c3a..7b14a523543 100644
--- a/src/test/ui/consts/const-eval/strlen.rs
+++ b/src/test/ui/consts/const-eval/strlen.rs
@@ -1,9 +1,11 @@
 // run-pass
 
-#![feature(const_str_len, const_str_as_bytes)]
-
 const S: &str = "foo";
 pub const B: &[u8] = S.as_bytes();
+pub const C: usize = B.len();
+pub const D: bool = B.is_empty();
+pub const E: bool = S.is_empty();
+pub const F: usize = S.len();
 
 pub fn foo() -> [u8; S.len()] {
     let mut buf = [0; S.len()];
@@ -20,4 +22,10 @@ fn main() {
     assert_eq!(LEN, S.len());
     assert_eq!(B, foo());
     assert_eq!(B, b"foo");
+    assert_eq!(C, 3);
+    assert_eq!(F, 3);
+    assert!(!D);
+    assert!(!E);
+    const EMPTY: bool = "".is_empty();
+    assert!(EMPTY);
 }
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs
index 4281874a031..0b1ab1c34ff 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs
@@ -12,5 +12,5 @@ fn main() {}
 const unsafe fn no_union() {
     union Foo { x: (), y: () }
     Foo { x: () }.y
-    //~^ unions in const fn
+    //~^ accessing union fields is unstable
 }
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr
index 9de0e732f33..2a4c627438d 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr
@@ -25,14 +25,14 @@ LL | const unsafe fn bad_const_unsafe_deref_raw_ref(x: *mut usize) -> &'static u
    = note: for more information, see https://github.com/rust-lang/rust/issues/51911
    = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
 
-error[E0658]: unions in const fn are unstable
+error[E0723]: accessing union fields is unstable
   --> $DIR/min_const_fn_unsafe_bad.rs:14:5
    |
 LL |     Foo { x: () }.y
    |     ^^^^^^^^^^^^^^^
    |
-   = note: for more information, see https://github.com/rust-lang/rust/issues/51909
-   = help: add `#![feature(const_fn_union)]` to the crate attributes to enable
+   = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+   = help: add `#![feature(const_fn)]` to the crate attributes to enable
 
 error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
   --> $DIR/min_const_fn_unsafe_bad.rs:1:77
@@ -44,5 +44,5 @@ LL | const fn bad_const_fn_deref_raw(x: *mut usize) -> &'static usize { unsafe {
 
 error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0133, E0658.
+Some errors have detailed explanations: E0133, E0658, E0723.
 For more information about an error, try `rustc --explain E0133`.
diff --git a/src/test/ui/issues/issue-52060.rs b/src/test/ui/issues/issue-52060.rs
index 54eff228934..13b914c0331 100644
--- a/src/test/ui/issues/issue-52060.rs
+++ b/src/test/ui/issues/issue-52060.rs
@@ -3,6 +3,5 @@
 static A: &'static [u32] = &[1];
 static B: [u32; 1] = [0; A.len()];
 //~^ ERROR [E0013]
-//~| ERROR `core::slice::<impl [T]>::len` is not yet stable as a const fn
 
 fn main() {}
diff --git a/src/test/ui/issues/issue-52060.stderr b/src/test/ui/issues/issue-52060.stderr
index 7c3f7695549..2f90f7f9e03 100644
--- a/src/test/ui/issues/issue-52060.stderr
+++ b/src/test/ui/issues/issue-52060.stderr
@@ -4,14 +4,6 @@ error[E0013]: constants cannot refer to statics, use a constant instead
 LL | static B: [u32; 1] = [0; A.len()];
    |                          ^
 
-error: `core::slice::<impl [T]>::len` is not yet stable as a const fn
-  --> $DIR/issue-52060.rs:4:26
-   |
-LL | static B: [u32; 1] = [0; A.len()];
-   |                          ^^^^^^^
-   |
-   = help: add `#![feature(const_slice_len)]` to the crate attributes to enable
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0013`.