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
This commit is contained in:
commit
6ef275e6c3
12 changed files with 114 additions and 78 deletions
|
@ -120,9 +120,9 @@
|
||||||
#![feature(rtm_target_feature)]
|
#![feature(rtm_target_feature)]
|
||||||
#![feature(f16c_target_feature)]
|
#![feature(f16c_target_feature)]
|
||||||
#![feature(hexagon_target_feature)]
|
#![feature(hexagon_target_feature)]
|
||||||
#![feature(const_slice_len)]
|
#![cfg_attr(bootstrap, feature(const_slice_len))]
|
||||||
#![feature(const_str_as_bytes)]
|
#![cfg_attr(bootstrap, feature(const_str_as_bytes))]
|
||||||
#![feature(const_str_len)]
|
#![cfg_attr(bootstrap, feature(const_str_len))]
|
||||||
#![feature(const_int_conversion)]
|
#![feature(const_int_conversion)]
|
||||||
#![feature(const_transmute)]
|
#![feature(const_transmute)]
|
||||||
#![feature(non_exhaustive)]
|
#![feature(non_exhaustive)]
|
||||||
|
|
|
@ -62,7 +62,9 @@ impl<T> [T] {
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline]
|
#[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 {
|
pub const fn len(&self) -> usize {
|
||||||
unsafe {
|
unsafe {
|
||||||
crate::ptr::Repr { rust: self }.raw.len
|
crate::ptr::Repr { rust: self }.raw.len
|
||||||
|
@ -79,7 +81,7 @@ impl<T> [T] {
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline]
|
#[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 {
|
pub const fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -2090,7 +2090,7 @@ impl str {
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[rustc_const_unstable(feature = "const_str_len")]
|
#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_str_len"))]
|
||||||
pub const fn len(&self) -> usize {
|
pub const fn len(&self) -> usize {
|
||||||
self.as_bytes().len()
|
self.as_bytes().len()
|
||||||
}
|
}
|
||||||
|
@ -2110,7 +2110,7 @@ impl str {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[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 {
|
pub const fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
@ -2168,7 +2168,9 @@ impl str {
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline(always)]
|
#[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] {
|
pub const fn as_bytes(&self) -> &[u8] {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
union Slices<'a> {
|
union Slices<'a> {
|
||||||
|
|
|
@ -305,7 +305,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||||
"assignment to non-`Copy` union field",
|
"assignment to non-`Copy` union field",
|
||||||
"the previous content of the field will be dropped, which \
|
"the previous content of the field will be dropped, which \
|
||||||
causes undefined behavior if the field was not properly \
|
causes undefined behavior if the field was not properly \
|
||||||
initialized", UnsafetyViolationKind::General)
|
initialized", UnsafetyViolationKind::GeneralAndConstFn)
|
||||||
} else {
|
} else {
|
||||||
// write to non-move union, safe
|
// 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",
|
self.require_unsafe("access to union field",
|
||||||
"the field may not be properly initialized: using \
|
"the field may not be properly initialized: using \
|
||||||
uninitialized data will cause undefined behavior",
|
uninitialized data will cause undefined behavior",
|
||||||
UnsafetyViolationKind::General)
|
UnsafetyViolationKind::GeneralAndConstFn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
|
||||||
use rustc_target::spec::abi;
|
use rustc_target::spec::abi;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
use syntax::symbol::{sym, Symbol};
|
||||||
|
use syntax::attr;
|
||||||
|
|
||||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
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() {
|
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 {
|
for stmt in &bb.statements {
|
||||||
check_statement(tcx, body, stmt)?;
|
check_statement(tcx, body, def_id, stmt)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -121,16 +123,17 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> Mc
|
||||||
|
|
||||||
fn check_rvalue(
|
fn check_rvalue(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &'a Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
|
def_id: DefId,
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
match rvalue {
|
match rvalue {
|
||||||
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
|
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) => {
|
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) => {
|
Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
|
||||||
use rustc::ty::cast::CastTy;
|
use rustc::ty::cast::CastTy;
|
||||||
|
@ -144,11 +147,11 @@ fn check_rvalue(
|
||||||
(CastTy::RPtr(_), CastTy::Float) => bug!(),
|
(CastTy::RPtr(_), CastTy::Float) => bug!(),
|
||||||
(CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
|
(CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
|
||||||
(CastTy::Ptr(_), CastTy::RPtr(_)) => 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, _) => {
|
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::UnsafeFnPointer), _, _) |
|
||||||
Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
|
Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
|
||||||
|
@ -162,8 +165,8 @@ fn check_rvalue(
|
||||||
)),
|
)),
|
||||||
// binops are fine on integers
|
// binops are fine on integers
|
||||||
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
|
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
|
||||||
check_operand(lhs, span)?;
|
check_operand(tcx, lhs, span, def_id, body)?;
|
||||||
check_operand(rhs, span)?;
|
check_operand(tcx, rhs, span, def_id, body)?;
|
||||||
let ty = lhs.ty(body, tcx);
|
let ty = lhs.ty(body, tcx);
|
||||||
if ty.is_integral() || ty.is_bool() || ty.is_char() {
|
if ty.is_integral() || ty.is_bool() || ty.is_char() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -182,7 +185,7 @@ fn check_rvalue(
|
||||||
Rvalue::UnaryOp(_, operand) => {
|
Rvalue::UnaryOp(_, operand) => {
|
||||||
let ty = operand.ty(body, tcx);
|
let ty = operand.ty(body, tcx);
|
||||||
if ty.is_integral() || ty.is_bool() {
|
if ty.is_integral() || ty.is_bool() {
|
||||||
check_operand(operand, span)
|
check_operand(tcx, operand, span, def_id, body)
|
||||||
} else {
|
} else {
|
||||||
Err((
|
Err((
|
||||||
span,
|
span,
|
||||||
|
@ -192,7 +195,7 @@ fn check_rvalue(
|
||||||
}
|
}
|
||||||
Rvalue::Aggregate(_, operands) => {
|
Rvalue::Aggregate(_, operands) => {
|
||||||
for operand in operands {
|
for operand in operands {
|
||||||
check_operand(operand, span)?;
|
check_operand(tcx, operand, span, def_id, body)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -201,21 +204,22 @@ fn check_rvalue(
|
||||||
|
|
||||||
fn check_statement(
|
fn check_statement(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &'a Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
|
def_id: DefId,
|
||||||
statement: &Statement<'tcx>,
|
statement: &Statement<'tcx>,
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
let span = statement.source_info.span;
|
let span = statement.source_info.span;
|
||||||
match &statement.kind {
|
match &statement.kind {
|
||||||
StatementKind::Assign(box(place, rval)) => {
|
StatementKind::Assign(box(place, rval)) => {
|
||||||
check_place(place, span)?;
|
check_place(tcx, place, span, def_id, body)?;
|
||||||
check_rvalue(tcx, body, rval, span)
|
check_rvalue(tcx, body, def_id, rval, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
|
StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
|
||||||
Err((span, "loops and conditional expressions are not stable in const fn".into()))
|
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
|
// just an assignment
|
||||||
StatementKind::SetDiscriminant { .. } => Ok(()),
|
StatementKind::SetDiscriminant { .. } => Ok(()),
|
||||||
|
@ -234,30 +238,48 @@ fn check_statement(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_operand(
|
fn check_operand(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
operand: &Operand<'tcx>,
|
operand: &Operand<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
def_id: DefId,
|
||||||
|
body: &Body<'tcx>
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
match operand {
|
match operand {
|
||||||
Operand::Move(place) | Operand::Copy(place) => {
|
Operand::Move(place) | Operand::Copy(place) => {
|
||||||
check_place(place, span)
|
check_place(tcx, place, span, def_id, body)
|
||||||
}
|
}
|
||||||
Operand::Constant(_) => Ok(()),
|
Operand::Constant(_) => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_place(
|
fn check_place(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
place: &Place<'tcx>,
|
place: &Place<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
def_id: DefId,
|
||||||
|
body: &Body<'tcx>
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
for elem in place.projection.iter() {
|
let mut cursor = &*place.projection;
|
||||||
|
while let [proj_base @ .., elem] = cursor {
|
||||||
|
cursor = proj_base;
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::Downcast(..) => {
|
ProjectionElem::Downcast(..) => {
|
||||||
return Err((span, "`match` or `if let` in `const fn` is unstable".into()));
|
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::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Deref
|
| ProjectionElem::Deref
|
||||||
| ProjectionElem::Field(..)
|
|
||||||
| ProjectionElem::Index(_) => {}
|
| 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(
|
fn check_terminator(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &'a Body<'tcx>,
|
body: &'a Body<'tcx>,
|
||||||
|
def_id: DefId,
|
||||||
terminator: &Terminator<'tcx>,
|
terminator: &Terminator<'tcx>,
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
let span = terminator.source_info.span;
|
let span = terminator.source_info.span;
|
||||||
|
@ -283,11 +316,11 @@ fn check_terminator(
|
||||||
| TerminatorKind::Resume => Ok(()),
|
| TerminatorKind::Resume => Ok(()),
|
||||||
|
|
||||||
TerminatorKind::Drop { location, .. } => {
|
TerminatorKind::Drop { location, .. } => {
|
||||||
check_place(location, span)
|
check_place(tcx, location, span, def_id, body)
|
||||||
}
|
}
|
||||||
TerminatorKind::DropAndReplace { location, value, .. } => {
|
TerminatorKind::DropAndReplace { location, value, .. } => {
|
||||||
check_place(location, span)?;
|
check_place(tcx, location, span, def_id, body)?;
|
||||||
check_operand(value, span)
|
check_operand(tcx, value, span, def_id, body)
|
||||||
},
|
},
|
||||||
|
|
||||||
TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
|
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 {
|
for arg in args {
|
||||||
check_operand(arg, span)?;
|
check_operand(tcx, arg, span, def_id, body)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -356,7 +389,7 @@ fn check_terminator(
|
||||||
msg: _,
|
msg: _,
|
||||||
target: _,
|
target: _,
|
||||||
cleanup: _,
|
cleanup: _,
|
||||||
} => check_operand(cond, span),
|
} => check_operand(tcx, cond, span, def_id, body),
|
||||||
|
|
||||||
TerminatorKind::FalseUnwind { .. } => {
|
TerminatorKind::FalseUnwind { .. } => {
|
||||||
Err((span, "loops are not allowed in const fn".into()))
|
Err((span, "loops are not allowed in const fn".into()))
|
||||||
|
|
|
@ -433,6 +433,30 @@ pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
|
||||||
attrs.iter().find(|attr| attr.check_name(name))
|
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)
|
pub fn filter_by_name(attrs: &[Attribute], name: Symbol)
|
||||||
-> impl Iterator<Item=&Attribute> {
|
-> impl Iterator<Item=&Attribute> {
|
||||||
attrs.iter().filter(move |attr| attr.check_name(name))
|
attrs.iter().filter(move |attr| attr.check_name(name))
|
||||||
|
|
|
@ -762,33 +762,9 @@ impl SyntaxExtension {
|
||||||
name: Name,
|
name: Name,
|
||||||
attrs: &[ast::Attribute],
|
attrs: &[ast::Attribute],
|
||||||
) -> SyntaxExtension {
|
) -> SyntaxExtension {
|
||||||
let allow_internal_unstable =
|
let allow_internal_unstable = attr::allow_internal_unstable(
|
||||||
attr::find_by_name(attrs, sym::allow_internal_unstable).map(|attr| {
|
&attrs, &sess.span_diagnostic,
|
||||||
attr.meta_item_list()
|
).map(|features| features.collect::<Vec<Symbol>>().into());
|
||||||
.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 mut local_inner_macros = false;
|
let mut local_inner_macros = false;
|
||||||
if let Some(macro_export) = attr::find_by_name(attrs, sym::macro_export) {
|
if let Some(macro_export) = attr::find_by_name(attrs, sym::macro_export) {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![feature(const_str_len, const_str_as_bytes)]
|
|
||||||
|
|
||||||
const S: &str = "foo";
|
const S: &str = "foo";
|
||||||
pub const B: &[u8] = S.as_bytes();
|
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()] {
|
pub fn foo() -> [u8; S.len()] {
|
||||||
let mut buf = [0; S.len()];
|
let mut buf = [0; S.len()];
|
||||||
|
@ -20,4 +22,10 @@ fn main() {
|
||||||
assert_eq!(LEN, S.len());
|
assert_eq!(LEN, S.len());
|
||||||
assert_eq!(B, foo());
|
assert_eq!(B, foo());
|
||||||
assert_eq!(B, 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,5 +12,5 @@ fn main() {}
|
||||||
const unsafe fn no_union() {
|
const unsafe fn no_union() {
|
||||||
union Foo { x: (), y: () }
|
union Foo { x: (), y: () }
|
||||||
Foo { x: () }.y
|
Foo { x: () }.y
|
||||||
//~^ unions in const fn
|
//~^ accessing union fields is unstable
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
= 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
|
= 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
|
--> $DIR/min_const_fn_unsafe_bad.rs:14:5
|
||||||
|
|
|
|
||||||
LL | Foo { x: () }.y
|
LL | Foo { x: () }.y
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rust/issues/51909
|
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||||
= help: add `#![feature(const_fn_union)]` to the crate attributes to enable
|
= 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
|
error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
|
||||||
--> $DIR/min_const_fn_unsafe_bad.rs:1:77
|
--> $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
|
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`.
|
For more information about an error, try `rustc --explain E0133`.
|
||||||
|
|
|
@ -3,6 +3,5 @@
|
||||||
static A: &'static [u32] = &[1];
|
static A: &'static [u32] = &[1];
|
||||||
static B: [u32; 1] = [0; A.len()];
|
static B: [u32; 1] = [0; A.len()];
|
||||||
//~^ ERROR [E0013]
|
//~^ ERROR [E0013]
|
||||||
//~| ERROR `core::slice::<impl [T]>::len` is not yet stable as a const fn
|
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -4,14 +4,6 @@ error[E0013]: constants cannot refer to statics, use a constant instead
|
||||||
LL | static B: [u32; 1] = [0; A.len()];
|
LL | static B: [u32; 1] = [0; A.len()];
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: `core::slice::<impl [T]>::len` is not yet stable as a const fn
|
error: aborting due to previous error
|
||||||
--> $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
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0013`.
|
For more information about this error, try `rustc --explain E0013`.
|
||||||
|
|
Loading…
Add table
Reference in a new issue