From 7767e7fb165d527f1991175809a361f2d2313b80 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 21 Aug 2019 02:23:46 +0200 Subject: [PATCH] Stabilize `str::len`, `[T]::len`, `is_empty` and `str::as_bytes` as const fn --- src/libcore/lib.rs | 6 +- src/libcore/slice/mod.rs | 6 +- src/libcore/str/mod.rs | 8 +- src/librustc_mir/transform/check_unsafety.rs | 4 +- .../transform/qualify_min_const_fn.rs | 81 +++++++++++++------ src/libsyntax/attr/mod.rs | 24 ++++++ src/libsyntax/ext/base.rs | 30 +------ src/test/ui/consts/const-eval/strlen.rs | 12 ++- .../min_const_fn/min_const_fn_unsafe_bad.rs | 2 +- .../min_const_fn_unsafe_bad.stderr | 8 +- src/test/ui/issues/issue-52060.rs | 1 - src/test/ui/issues/issue-52060.stderr | 10 +-- 12 files changed, 114 insertions(+), 78 deletions(-) diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 5c681b3a5d807..8221df56a5142 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 931768564ca3c..0c2a4e086727b 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -62,7 +62,9 @@ impl [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] { /// ``` #[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 5e5b5593fd8a7..a6ec757faf1d6 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 39aa5c717acc1..61e32ca8de522 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 80e020a9eb7e6..be83c823d4242 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(..., , ...)` 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 1f954064944dc..9d06b926f972e 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 + '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 { attrs.iter().filter(move |attr| attr.check_name(name)) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index a6be5b101788e..e189d8f86362e 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::>() - .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::>().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 6a5cad82c3aa3..7b14a52354348 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 4281874a0314d..0b1ab1c34ff2f 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 9de0e732f33d2..2a4c627438d61 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 54eff228934e3..13b914c0331d1 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::::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 7c3f7695549f0..2f90f7f9e035b 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::::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`.