mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
Implement the min_const_fn
feature gate
This commit is contained in:
parent
1114ab684f
commit
472ca71598
@ -285,6 +285,15 @@ pub fn forget<T>(t: T) {
|
||||
/// [alignment]: ./fn.align_of.html
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg(not(stage0))]
|
||||
pub const fn size_of<T>() -> usize {
|
||||
intrinsics::size_of::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg(stage0)]
|
||||
/// Ceci n'est pas la documentation
|
||||
pub const fn size_of<T>() -> usize {
|
||||
unsafe { intrinsics::size_of::<T>() }
|
||||
}
|
||||
@ -334,6 +343,16 @@ pub fn size_of_val<T: ?Sized>(val: &T) -> usize {
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_deprecated(reason = "use `align_of` instead", since = "1.2.0")]
|
||||
#[cfg(not(stage0))]
|
||||
pub fn min_align_of<T>() -> usize {
|
||||
intrinsics::min_align_of::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_deprecated(reason = "use `align_of` instead", since = "1.2.0")]
|
||||
#[cfg(stage0)]
|
||||
/// Ceci n'est pas la documentation
|
||||
pub fn min_align_of<T>() -> usize {
|
||||
unsafe { intrinsics::min_align_of::<T>() }
|
||||
}
|
||||
@ -376,6 +395,15 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg(not(stage0))]
|
||||
pub const fn align_of<T>() -> usize {
|
||||
intrinsics::min_align_of::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg(stage0)]
|
||||
/// Ceci n'est pas la documentation
|
||||
pub const fn align_of<T>() -> usize {
|
||||
unsafe { intrinsics::min_align_of::<T>() }
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ for mir::UnsafetyViolationKind {
|
||||
|
||||
match *self {
|
||||
mir::UnsafetyViolationKind::General => {}
|
||||
mir::UnsafetyViolationKind::MinConstFn => {}
|
||||
mir::UnsafetyViolationKind::ExternStatic(lint_node_id) |
|
||||
mir::UnsafetyViolationKind::BorrowPacked(lint_node_id) => {
|
||||
lint_node_id.hash_stable(hcx, hasher);
|
||||
|
@ -130,7 +130,7 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability {
|
||||
level,
|
||||
feature,
|
||||
rustc_depr,
|
||||
rustc_const_unstable
|
||||
const_stability
|
||||
});
|
||||
|
||||
impl<'a> HashStable<StableHashingContext<'a>>
|
||||
@ -161,7 +161,6 @@ for ::syntax::attr::StabilityLevel {
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(struct ::syntax::attr::RustcDeprecation { since, reason });
|
||||
impl_stable_hash_for!(struct ::syntax::attr::RustcConstUnstable { feature });
|
||||
|
||||
|
||||
impl_stable_hash_for!(enum ::syntax::attr::IntType {
|
||||
|
@ -440,7 +440,7 @@ impl<'a, 'tcx> Index<'tcx> {
|
||||
},
|
||||
feature: Symbol::intern("rustc_private"),
|
||||
rustc_depr: None,
|
||||
rustc_const_unstable: None,
|
||||
const_stability: None,
|
||||
});
|
||||
annotator.parent_stab = Some(stability);
|
||||
}
|
||||
|
@ -2394,6 +2394,8 @@ impl Location {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
||||
pub enum UnsafetyViolationKind {
|
||||
General,
|
||||
/// unsafety is not allowed at all in min const fn
|
||||
MinConstFn,
|
||||
ExternStatic(ast::NodeId),
|
||||
BorrowPacked(ast::NodeId),
|
||||
}
|
||||
|
@ -1099,6 +1099,37 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
local as usize == global as usize
|
||||
}
|
||||
|
||||
/// Returns true if this function must conform to `min_const_fn`
|
||||
pub fn is_min_const_fn(self, def_id: DefId) -> bool {
|
||||
if self.features().staged_api {
|
||||
// some intrinsics are waved through if called inside the
|
||||
// standard library. Users never need to call them directly
|
||||
if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
|
||||
assert!(!self.is_const_fn(def_id));
|
||||
match &self.item_name(def_id).as_str()[..] {
|
||||
| "size_of"
|
||||
| "min_align_of"
|
||||
=> return true,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
// in order for a libstd function to be considered min_const_fn
|
||||
// it needs to be stable and have no `rustc_const_unstable` attribute
|
||||
match self.lookup_stability(def_id) {
|
||||
// stable functions with unstable const fn aren't `min_const_fn`
|
||||
Some(&attr::Stability { const_stability: Some(_), .. }) => false,
|
||||
// unstable functions don't need to conform
|
||||
Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
|
||||
// everything else needs to conform, because it would be callable from
|
||||
// other `min_const_fn` functions
|
||||
_ => true,
|
||||
}
|
||||
} else {
|
||||
// users enabling the `const_fn` can do what they want
|
||||
!self.sess.features_untracked().const_fn
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a type context and call the closure with a `TyCtxt` reference
|
||||
/// to the context. The closure enforces that the type context and any interned
|
||||
/// value (types, substs, etc.) can only be used while `ty::tls` has a valid
|
||||
|
@ -18,7 +18,6 @@ use hair::*;
|
||||
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::hir::map::blocks::FnLikeNode;
|
||||
use rustc::hir::Node;
|
||||
use rustc::middle::region;
|
||||
use rustc::infer::InferCtxt;
|
||||
@ -67,10 +66,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
||||
let constness = match body_owner_kind {
|
||||
hir::BodyOwnerKind::Const |
|
||||
hir::BodyOwnerKind::Static(_) => hir::Constness::Const,
|
||||
hir::BodyOwnerKind::Fn => {
|
||||
let fn_like = FnLikeNode::from_node(infcx.tcx.hir.get(src_id));
|
||||
fn_like.map_or(hir::Constness::NotConst, |f| f.constness())
|
||||
}
|
||||
hir::BodyOwnerKind::Fn => hir::Constness::NotConst,
|
||||
};
|
||||
|
||||
let attrs = tcx.hir.attrs(src_id);
|
||||
@ -83,7 +79,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
||||
// Respect -C overflow-checks.
|
||||
check_overflow |= tcx.sess.overflow_checks();
|
||||
|
||||
// Constants and const fn's always need overflow checks.
|
||||
// Constants always need overflow checks.
|
||||
check_overflow |= constness == hir::Constness::Const;
|
||||
|
||||
let lint_level = lint_level_for_hir_id(tcx, src_id);
|
||||
|
@ -28,6 +28,7 @@ use util;
|
||||
|
||||
pub struct UnsafetyChecker<'a, 'tcx: 'a> {
|
||||
mir: &'a Mir<'tcx>,
|
||||
min_const_fn: bool,
|
||||
source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
|
||||
violations: Vec<UnsafetyViolation>,
|
||||
source_info: SourceInfo,
|
||||
@ -38,12 +39,16 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||
fn new(mir: &'a Mir<'tcx>,
|
||||
source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>) -> Self {
|
||||
fn new(
|
||||
min_const_fn: bool,
|
||||
mir: &'a Mir<'tcx>,
|
||||
source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Self {
|
||||
Self {
|
||||
mir,
|
||||
min_const_fn,
|
||||
source_scope_local_data,
|
||||
violations: vec![],
|
||||
source_info: SourceInfo {
|
||||
@ -269,6 +274,15 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||
fn register_violations(&mut self,
|
||||
violations: &[UnsafetyViolation],
|
||||
unsafe_blocks: &[(ast::NodeId, bool)]) {
|
||||
if self.min_const_fn {
|
||||
for violation in violations {
|
||||
let mut violation = violation.clone();
|
||||
violation.kind = UnsafetyViolationKind::MinConstFn;
|
||||
if !self.violations.contains(&violation) {
|
||||
self.violations.push(violation)
|
||||
}
|
||||
}
|
||||
}
|
||||
let within_unsafe = match self.source_scope_local_data[self.source_info.scope].safety {
|
||||
Safety::Safe => {
|
||||
for violation in violations {
|
||||
@ -276,7 +290,6 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||
self.violations.push(violation.clone())
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
|
||||
@ -369,6 +382,7 @@ fn unsafety_check_result<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
|
||||
|
||||
let param_env = tcx.param_env(def_id);
|
||||
let mut checker = UnsafetyChecker::new(
|
||||
tcx.is_const_fn(def_id) && tcx.is_min_const_fn(def_id),
|
||||
mir, source_scope_local_data, tcx, param_env);
|
||||
checker.visit_mir(mir);
|
||||
|
||||
@ -478,6 +492,15 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
|
||||
.note(&details.as_str()[..])
|
||||
.emit();
|
||||
}
|
||||
UnsafetyViolationKind::MinConstFn => {
|
||||
tcx.sess.struct_span_err(
|
||||
source_info.span,
|
||||
&format!("{} is unsafe and unsafe operations \
|
||||
are not allowed in const fn", description))
|
||||
.span_label(source_info.span, &description.as_str()[..])
|
||||
.note(&details.as_str()[..])
|
||||
.emit();
|
||||
}
|
||||
UnsafetyViolationKind::ExternStatic(lint_node_id) => {
|
||||
tcx.lint_node_note(SAFE_EXTERN_STATICS,
|
||||
lint_node_id,
|
||||
|
@ -36,6 +36,7 @@ pub mod elaborate_drops;
|
||||
pub mod add_call_guards;
|
||||
pub mod promote_consts;
|
||||
pub mod qualify_consts;
|
||||
mod qualify_min_const_fn;
|
||||
pub mod remove_noop_landing_pads;
|
||||
pub mod dump_mir;
|
||||
pub mod deaggregator;
|
||||
|
@ -916,9 +916,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||
);
|
||||
}
|
||||
} else if let Some(&attr::Stability {
|
||||
rustc_const_unstable: Some(attr::RustcConstUnstable {
|
||||
feature: ref feature_name
|
||||
}),
|
||||
const_stability: Some(ref feature_name),
|
||||
.. }) = self.tcx.lookup_stability(def_id) {
|
||||
if
|
||||
// feature-gate is not enabled,
|
||||
@ -1175,8 +1173,20 @@ impl MirPass for QualifyAndPromoteConstants {
|
||||
let (temps, candidates) = {
|
||||
let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
|
||||
if mode == Mode::ConstFn {
|
||||
// Enforce a constant-like CFG for `const fn`.
|
||||
qualifier.qualify_const();
|
||||
if tcx.is_min_const_fn(def_id) {
|
||||
// enforce `min_const_fn` for stable const fns
|
||||
use super::qualify_min_const_fn::is_min_const_fn;
|
||||
if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
|
||||
tcx.sess.span_err(span, &err);
|
||||
} else {
|
||||
// this should not produce any errors, but better safe than sorry
|
||||
// FIXME(#53819)
|
||||
qualifier.qualify_const();
|
||||
}
|
||||
} else {
|
||||
// Enforce a constant-like CFG for `const fn`.
|
||||
qualifier.qualify_const();
|
||||
}
|
||||
} else {
|
||||
while let Some((bb, data)) = qualifier.rpo.next() {
|
||||
qualifier.visit_basic_block_data(bb, data);
|
||||
|
378
src/librustc_mir/transform/qualify_min_const_fn.rs
Normal file
378
src/librustc_mir/transform/qualify_min_const_fn.rs
Normal file
@ -0,0 +1,378 @@
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir;
|
||||
use rustc::mir::*;
|
||||
use rustc::ty::{self, Predicate, TyCtxt};
|
||||
use std::borrow::Cow;
|
||||
use syntax_pos::Span;
|
||||
|
||||
mod helper {
|
||||
pub struct IsMinConstFn(());
|
||||
/// This should only ever be used *once* and then passed around as a token.
|
||||
pub fn ensure_that_you_really_intended_to_create_an_instance_of_this() -> IsMinConstFn {
|
||||
IsMinConstFn(())
|
||||
}
|
||||
}
|
||||
|
||||
use self::helper::*;
|
||||
|
||||
type McfResult = Result<IsMinConstFn, (Span, Cow<'static, str>)>;
|
||||
|
||||
pub fn is_min_const_fn(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
mir: &'a Mir<'tcx>,
|
||||
) -> McfResult {
|
||||
let mut current = def_id;
|
||||
loop {
|
||||
let predicates = tcx.predicates_of(current);
|
||||
for predicate in &predicates.predicates {
|
||||
match predicate {
|
||||
| Predicate::RegionOutlives(_)
|
||||
| Predicate::TypeOutlives(_)
|
||||
| Predicate::WellFormed(_)
|
||||
| Predicate::ConstEvaluatable(..) => continue,
|
||||
| Predicate::ObjectSafe(_) => {
|
||||
bug!("object safe predicate on function: {:#?}", predicate)
|
||||
}
|
||||
Predicate::ClosureKind(..) => {
|
||||
bug!("closure kind predicate on function: {:#?}", predicate)
|
||||
}
|
||||
Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
|
||||
Predicate::Projection(_) => {
|
||||
let span = tcx.def_span(current);
|
||||
// we'll hit a `Predicate::Trait` later which will report an error
|
||||
tcx.sess
|
||||
.delay_span_bug(span, "projection without trait bound");
|
||||
continue;
|
||||
}
|
||||
Predicate::Trait(pred) => {
|
||||
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
|
||||
continue;
|
||||
}
|
||||
match pred.skip_binder().self_ty().sty {
|
||||
ty::Param(ref p) => {
|
||||
let generics = tcx.generics_of(current);
|
||||
let def = generics.type_param(p, tcx);
|
||||
let span = tcx.def_span(def.def_id);
|
||||
return Err((
|
||||
span,
|
||||
"trait bounds other than `Sized` \
|
||||
on const fn parameters are unstable"
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
// other kinds of bounds are either tautologies
|
||||
// or cause errors in other passes
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match predicates.parent {
|
||||
Some(parent) => current = parent,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
let mut token = ensure_that_you_really_intended_to_create_an_instance_of_this();
|
||||
|
||||
for local in mir.vars_iter() {
|
||||
return Err((
|
||||
mir.local_decls[local].source_info.span,
|
||||
"local variables in const fn are unstable".into(),
|
||||
));
|
||||
}
|
||||
for local in &mir.local_decls {
|
||||
token = check_ty(tcx, local.ty, local.source_info.span, token)?;
|
||||
}
|
||||
// impl trait is gone in MIR, so check the return type manually
|
||||
token = check_ty(
|
||||
tcx,
|
||||
tcx.fn_sig(def_id).output().skip_binder(),
|
||||
mir.local_decls.iter().next().unwrap().source_info.span,
|
||||
token,
|
||||
)?;
|
||||
|
||||
for bb in mir.basic_blocks() {
|
||||
token = check_terminator(tcx, mir, bb.terminator(), token)?;
|
||||
for stmt in &bb.statements {
|
||||
token = check_statement(tcx, mir, stmt, token)?;
|
||||
}
|
||||
}
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
fn check_ty(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
ty: ty::Ty<'tcx>,
|
||||
span: Span,
|
||||
token: IsMinConstFn,
|
||||
) -> McfResult {
|
||||
for ty in ty.walk() {
|
||||
match ty.sty {
|
||||
ty::Ref(_, _, hir::Mutability::MutMutable) => return Err((
|
||||
span,
|
||||
"mutable references in const fn are unstable".into(),
|
||||
)),
|
||||
ty::Anon(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
|
||||
ty::FnPtr(..) => {
|
||||
return Err((span, "function pointers in const fn are unstable".into()))
|
||||
}
|
||||
ty::Dynamic(preds, _) => {
|
||||
for pred in preds.iter() {
|
||||
match pred.skip_binder() {
|
||||
| ty::ExistentialPredicate::AutoTrait(_)
|
||||
| ty::ExistentialPredicate::Projection(_) => {
|
||||
return Err((
|
||||
span,
|
||||
"trait bounds other than `Sized` \
|
||||
on const fn parameters are unstable"
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
ty::ExistentialPredicate::Trait(trait_ref) => {
|
||||
if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
|
||||
return Err((
|
||||
span,
|
||||
"trait bounds other than `Sized` \
|
||||
on const fn parameters are unstable"
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
fn check_rvalue(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
span: Span,
|
||||
token: IsMinConstFn,
|
||||
) -> McfResult {
|
||||
match rvalue {
|
||||
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
|
||||
check_operand(tcx, mir, operand, span, token)
|
||||
}
|
||||
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
|
||||
check_place(tcx, mir, place, span, token, PlaceMode::Read)
|
||||
}
|
||||
Rvalue::Cast(_, operand, cast_ty) => {
|
||||
use rustc::ty::cast::CastTy;
|
||||
let cast_in = CastTy::from_ty(operand.ty(mir, tcx)).expect("bad input type for cast");
|
||||
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
|
||||
match (cast_in, cast_out) {
|
||||
(CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
|
||||
span,
|
||||
"casting pointers to ints is unstable in const fn".into(),
|
||||
)),
|
||||
(CastTy::RPtr(_), CastTy::Float) => bug!(),
|
||||
(CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
|
||||
(CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
|
||||
_ => check_operand(tcx, mir, operand, span, token),
|
||||
}
|
||||
}
|
||||
// binops are fine on integers
|
||||
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
|
||||
let token = check_operand(tcx, mir, lhs, span, token)?;
|
||||
let token = check_operand(tcx, mir, rhs, span, token)?;
|
||||
let ty = lhs.ty(mir, tcx);
|
||||
if ty.is_integral() || ty.is_bool() || ty.is_char() {
|
||||
Ok(token)
|
||||
} else {
|
||||
Err((
|
||||
span,
|
||||
"only int, `bool` and `char` operations are stable in const fn".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
// checked by regular const fn checks
|
||||
Rvalue::NullaryOp(..) => Ok(token),
|
||||
Rvalue::UnaryOp(_, operand) => {
|
||||
let ty = operand.ty(mir, tcx);
|
||||
if ty.is_integral() || ty.is_bool() {
|
||||
check_operand(tcx, mir, operand, span, token)
|
||||
} else {
|
||||
Err((
|
||||
span,
|
||||
"only int and `bool` operations are stable in const fn".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Rvalue::Aggregate(_, operands) => {
|
||||
let mut token = token;
|
||||
for operand in operands {
|
||||
token = check_operand(tcx, mir, operand, span, token)?;
|
||||
}
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PlaceMode {
|
||||
Assign,
|
||||
Read,
|
||||
}
|
||||
|
||||
fn check_statement(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
statement: &Statement<'tcx>,
|
||||
token: IsMinConstFn,
|
||||
) -> McfResult {
|
||||
let span = statement.source_info.span;
|
||||
match &statement.kind {
|
||||
StatementKind::Assign(place, rval) => {
|
||||
let token = check_place(tcx, mir, place, span, token, PlaceMode::Assign)?;
|
||||
check_rvalue(tcx, mir, rval, span, token)
|
||||
}
|
||||
|
||||
StatementKind::ReadForMatch(_) => Err((span, "match in const fn is unstable".into())),
|
||||
|
||||
// just an assignment
|
||||
StatementKind::SetDiscriminant { .. } => Ok(token),
|
||||
|
||||
| StatementKind::InlineAsm { .. } => {
|
||||
Err((span, "cannot use inline assembly in const fn".into()))
|
||||
}
|
||||
|
||||
// These are all NOPs
|
||||
| StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Validate(..)
|
||||
| StatementKind::EndRegion(_)
|
||||
| StatementKind::UserAssertTy(..)
|
||||
| StatementKind::Nop => Ok(token),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_operand(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
operand: &Operand<'tcx>,
|
||||
span: Span,
|
||||
token: IsMinConstFn,
|
||||
) -> McfResult {
|
||||
match operand {
|
||||
Operand::Move(place) | Operand::Copy(place) => {
|
||||
check_place(tcx, mir, place, span, token, PlaceMode::Read)
|
||||
}
|
||||
Operand::Constant(_) => Ok(token),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_place(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
place: &Place<'tcx>,
|
||||
span: Span,
|
||||
token: IsMinConstFn,
|
||||
mode: PlaceMode,
|
||||
) -> McfResult {
|
||||
match place {
|
||||
Place::Local(l) => match mode {
|
||||
PlaceMode::Assign => match mir.local_kind(*l) {
|
||||
LocalKind::Temp | LocalKind::ReturnPointer => Ok(token),
|
||||
LocalKind::Arg | LocalKind::Var => {
|
||||
Err((span, "assignments in const fn are unstable".into()))
|
||||
}
|
||||
},
|
||||
PlaceMode::Read => Ok(token),
|
||||
},
|
||||
// promoteds are always fine, they are essentially constants
|
||||
Place::Promoted(_) => Ok(token),
|
||||
Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
|
||||
Place::Projection(proj) => {
|
||||
match proj.elem {
|
||||
| ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
|
||||
check_place(tcx, mir, &proj.base, span, token, mode)
|
||||
}
|
||||
// slice patterns are unstable
|
||||
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
|
||||
return Err((span, "slice patterns in const fn are unstable".into()))
|
||||
}
|
||||
| ProjectionElem::Downcast(..) => {
|
||||
Err((span, "`match` or `if let` in `const fn` is unstable".into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_terminator(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
terminator: &Terminator<'tcx>,
|
||||
token: IsMinConstFn,
|
||||
) -> McfResult {
|
||||
let span = terminator.source_info.span;
|
||||
match &terminator.kind {
|
||||
| TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Resume => Ok(token),
|
||||
|
||||
TerminatorKind::Drop { location, .. } => {
|
||||
check_place(tcx, mir, location, span, token, PlaceMode::Read)
|
||||
}
|
||||
TerminatorKind::DropAndReplace { location, value, .. } => {
|
||||
let token = check_place(tcx, mir, location, span, token, PlaceMode::Read)?;
|
||||
check_operand(tcx, mir, value, span, token)
|
||||
},
|
||||
TerminatorKind::SwitchInt { .. } => Err((
|
||||
span,
|
||||
"`if`, `match`, `&&` and `||` are not stable in const fn".into(),
|
||||
)),
|
||||
| TerminatorKind::Abort | TerminatorKind::Unreachable => {
|
||||
Err((span, "const fn with unreachable code is not stable".into()))
|
||||
}
|
||||
| TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
|
||||
Err((span, "const fn generators are unstable".into()))
|
||||
}
|
||||
|
||||
TerminatorKind::Call {
|
||||
func,
|
||||
args,
|
||||
destination: _,
|
||||
cleanup: _,
|
||||
} => {
|
||||
let fn_ty = func.ty(mir, tcx);
|
||||
if let ty::FnDef(def_id, _) = fn_ty.sty {
|
||||
if tcx.is_min_const_fn(def_id) {
|
||||
let mut token = check_operand(tcx, mir, func, span, token)?;
|
||||
|
||||
for arg in args {
|
||||
token = check_operand(tcx, mir, arg, span, token)?;
|
||||
}
|
||||
Ok(token)
|
||||
} else {
|
||||
Err((
|
||||
span,
|
||||
"can only call other `min_const_fn` within a `min_const_fn`".into(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err((span, "can only call other const fns within const fn".into()))
|
||||
}
|
||||
}
|
||||
|
||||
TerminatorKind::Assert {
|
||||
cond,
|
||||
expected: _,
|
||||
msg: _,
|
||||
target: _,
|
||||
cleanup: _,
|
||||
} => check_operand(tcx, mir, cond, span, token),
|
||||
|
||||
| TerminatorKind::FalseEdges { .. } | TerminatorKind::FalseUnwind { .. } => span_bug!(
|
||||
terminator.source_info.span,
|
||||
"min_const_fn encountered `{:#?}`",
|
||||
terminator
|
||||
),
|
||||
}
|
||||
}
|
@ -178,9 +178,7 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
|
||||
}
|
||||
|
||||
if let Some(&attr::Stability {
|
||||
rustc_const_unstable: Some(attr::RustcConstUnstable {
|
||||
feature: ref feature_name
|
||||
}),
|
||||
const_stability: Some(ref feature_name),
|
||||
.. }) = self.tcx.lookup_stability(def_id) {
|
||||
let stable_check =
|
||||
// feature-gate is enabled,
|
||||
|
@ -26,12 +26,15 @@ use rustc::hir;
|
||||
|
||||
use std::iter;
|
||||
|
||||
fn equate_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
it: &hir::ForeignItem,
|
||||
n_tps: usize,
|
||||
abi: Abi,
|
||||
inputs: Vec<Ty<'tcx>>,
|
||||
output: Ty<'tcx>) {
|
||||
fn equate_intrinsic_type<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
it: &hir::ForeignItem,
|
||||
n_tps: usize,
|
||||
abi: Abi,
|
||||
safety: hir::Unsafety,
|
||||
inputs: Vec<Ty<'tcx>>,
|
||||
output: Ty<'tcx>,
|
||||
) {
|
||||
let def_id = tcx.hir.local_def_id(it.id);
|
||||
|
||||
match it.node {
|
||||
@ -65,7 +68,7 @@ fn equate_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
inputs.into_iter(),
|
||||
output,
|
||||
false,
|
||||
hir::Unsafety::Unsafe,
|
||||
safety,
|
||||
abi
|
||||
)));
|
||||
let cause = ObligationCause::new(it.span, it.id, ObligationCauseCode::IntrinsicType);
|
||||
@ -78,7 +81,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
it: &hir::ForeignItem) {
|
||||
let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)).as_interned_str());
|
||||
let name = it.name.as_str();
|
||||
let (n_tps, inputs, output) = if name.starts_with("atomic_") {
|
||||
let (n_tps, inputs, output, unsafety) = if name.starts_with("atomic_") {
|
||||
let split : Vec<&str> = name.split('_').collect();
|
||||
assert!(split.len() >= 2, "Atomic intrinsic not correct format");
|
||||
|
||||
@ -109,10 +112,14 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
return;
|
||||
}
|
||||
};
|
||||
(n_tps, inputs, output)
|
||||
(n_tps, inputs, output, hir::Unsafety::Unsafe)
|
||||
} else if &name[..] == "abort" || &name[..] == "unreachable" {
|
||||
(0, Vec::new(), tcx.types.never)
|
||||
(0, Vec::new(), tcx.types.never, hir::Unsafety::Unsafe)
|
||||
} else {
|
||||
let unsafety = match &name[..] {
|
||||
"size_of" | "min_align_of" => hir::Unsafety::Normal,
|
||||
_ => hir::Unsafety::Unsafe,
|
||||
};
|
||||
let (n_tps, inputs, output) = match &name[..] {
|
||||
"breakpoint" => (0, Vec::new(), tcx.mk_nil()),
|
||||
"size_of" |
|
||||
@ -327,9 +334,9 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
return;
|
||||
}
|
||||
};
|
||||
(n_tps, inputs, output)
|
||||
(n_tps, inputs, output, unsafety)
|
||||
};
|
||||
equate_intrinsic_type(tcx, it, n_tps, Abi::RustIntrinsic, inputs, output)
|
||||
equate_intrinsic_type(tcx, it, n_tps, Abi::RustIntrinsic, unsafety, inputs, output)
|
||||
}
|
||||
|
||||
/// Type-check `extern "platform-intrinsic" { ... }` functions.
|
||||
@ -439,7 +446,7 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
};
|
||||
|
||||
equate_intrinsic_type(tcx, it, n_tps, Abi::PlatformIntrinsic,
|
||||
equate_intrinsic_type(tcx, it, n_tps, Abi::PlatformIntrinsic, hir::Unsafety::Unsafe,
|
||||
inputs, output)
|
||||
}
|
||||
|
||||
|
@ -1981,12 +1981,15 @@ fn compute_sig_of_foreign_fn_decl<'a, 'tcx>(
|
||||
decl: &hir::FnDecl,
|
||||
abi: abi::Abi,
|
||||
) -> ty::PolyFnSig<'tcx> {
|
||||
let fty = AstConv::ty_of_fn(
|
||||
&ItemCtxt::new(tcx, def_id),
|
||||
hir::Unsafety::Unsafe,
|
||||
abi,
|
||||
decl,
|
||||
);
|
||||
let unsafety = if abi == abi::Abi::RustIntrinsic {
|
||||
match &*tcx.item_name(def_id).as_str() {
|
||||
"size_of" | "min_align_of" => hir::Unsafety::Normal,
|
||||
_ => hir::Unsafety::Unsafe,
|
||||
}
|
||||
} else {
|
||||
hir::Unsafety::Unsafe
|
||||
};
|
||||
let fty = AstConv::ty_of_fn(&ItemCtxt::new(tcx, def_id), unsafety, abi, decl);
|
||||
|
||||
// feature gate SIMD types in FFI, since I (huonw) am not sure the
|
||||
// ABIs are handled at all correctly.
|
||||
|
@ -18,7 +18,6 @@ pub struct Lazy<T> {
|
||||
// We never call `lock.init()`, so it is UB to attempt to acquire this mutex reentrantly!
|
||||
lock: Mutex,
|
||||
ptr: Cell<*mut Arc<T>>,
|
||||
init: fn() -> Arc<T>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -26,33 +25,32 @@ const fn done<T>() -> *mut Arc<T> { 1_usize as *mut _ }
|
||||
|
||||
unsafe impl<T> Sync for Lazy<T> {}
|
||||
|
||||
impl<T: Send + Sync + 'static> Lazy<T> {
|
||||
/// Safety: `init` must not call `get` on the variable that is being
|
||||
/// initialized.
|
||||
pub const unsafe fn new(init: fn() -> Arc<T>) -> Lazy<T> {
|
||||
impl<T> Lazy<T> {
|
||||
pub const fn new() -> Lazy<T> {
|
||||
Lazy {
|
||||
lock: Mutex::new(),
|
||||
ptr: Cell::new(ptr::null_mut()),
|
||||
init,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&'static self) -> Option<Arc<T>> {
|
||||
unsafe {
|
||||
let _guard = self.lock.lock();
|
||||
let ptr = self.ptr.get();
|
||||
if ptr.is_null() {
|
||||
Some(self.init())
|
||||
} else if ptr == done() {
|
||||
None
|
||||
} else {
|
||||
Some((*ptr).clone())
|
||||
}
|
||||
impl<T: Send + Sync + 'static> Lazy<T> {
|
||||
/// Safety: `init` must not call `get` on the variable that is being
|
||||
/// initialized.
|
||||
pub unsafe fn get(&'static self, init: fn() -> Arc<T>) -> Option<Arc<T>> {
|
||||
let _guard = self.lock.lock();
|
||||
let ptr = self.ptr.get();
|
||||
if ptr.is_null() {
|
||||
Some(self.init(init))
|
||||
} else if ptr == done() {
|
||||
None
|
||||
} else {
|
||||
Some((*ptr).clone())
|
||||
}
|
||||
}
|
||||
|
||||
// Must only be called with `lock` held
|
||||
unsafe fn init(&'static self) -> Arc<T> {
|
||||
unsafe fn init(&'static self, init: fn() -> Arc<T>) -> Arc<T> {
|
||||
// If we successfully register an at exit handler, then we cache the
|
||||
// `Arc` allocation in our own internal box (it will get deallocated by
|
||||
// the at exit handler). Otherwise we just return the freshly allocated
|
||||
@ -66,8 +64,8 @@ impl<T: Send + Sync + 'static> Lazy<T> {
|
||||
});
|
||||
// This could reentrantly call `init` again, which is a problem
|
||||
// because our `lock` allows reentrancy!
|
||||
// That's why `new` is unsafe and requires the caller to ensure no reentrancy happens.
|
||||
let ret = (self.init)();
|
||||
// That's why `get` is unsafe and requires the caller to ensure no reentrancy happens.
|
||||
let ret = init();
|
||||
if registered.is_ok() {
|
||||
self.ptr.set(Box::into_raw(Box::new(ret.clone())));
|
||||
}
|
||||
|
@ -197,9 +197,11 @@ pub struct StdinLock<'a> {
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn stdin() -> Stdin {
|
||||
static INSTANCE: Lazy<Mutex<BufReader<Maybe<StdinRaw>>>> = unsafe { Lazy::new(stdin_init) };
|
||||
static INSTANCE: Lazy<Mutex<BufReader<Maybe<StdinRaw>>>> = Lazy::new();
|
||||
return Stdin {
|
||||
inner: INSTANCE.get().expect("cannot access stdin during shutdown"),
|
||||
inner: unsafe {
|
||||
INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown")
|
||||
},
|
||||
};
|
||||
|
||||
fn stdin_init() -> Arc<Mutex<BufReader<Maybe<StdinRaw>>>> {
|
||||
@ -396,10 +398,11 @@ pub struct StdoutLock<'a> {
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn stdout() -> Stdout {
|
||||
static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>>
|
||||
= unsafe { Lazy::new(stdout_init) };
|
||||
static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>> = Lazy::new();
|
||||
return Stdout {
|
||||
inner: INSTANCE.get().expect("cannot access stdout during shutdown"),
|
||||
inner: unsafe {
|
||||
INSTANCE.get(stdout_init).expect("cannot access stdout during shutdown")
|
||||
},
|
||||
};
|
||||
|
||||
fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>> {
|
||||
@ -533,10 +536,11 @@ pub struct StderrLock<'a> {
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn stderr() -> Stderr {
|
||||
static INSTANCE: Lazy<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> =
|
||||
unsafe { Lazy::new(stderr_init) };
|
||||
static INSTANCE: Lazy<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> = Lazy::new();
|
||||
return Stderr {
|
||||
inner: INSTANCE.get().expect("cannot access stderr during shutdown"),
|
||||
inner: unsafe {
|
||||
INSTANCE.get(stderr_init).expect("cannot access stderr during shutdown")
|
||||
},
|
||||
};
|
||||
|
||||
fn stderr_init() -> Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> {
|
||||
|
@ -107,7 +107,11 @@ pub struct Stability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
pub rustc_depr: Option<RustcDeprecation>,
|
||||
pub rustc_const_unstable: Option<RustcConstUnstable>,
|
||||
/// `None` means the function is stable but needs to be allowed by the
|
||||
/// `min_const_fn` feature
|
||||
/// `Some` contains the feature gate required to be able to use the function
|
||||
/// as const fn
|
||||
pub const_stability: Option<Symbol>,
|
||||
}
|
||||
|
||||
/// The available stability levels.
|
||||
@ -141,11 +145,6 @@ pub struct RustcDeprecation {
|
||||
pub reason: Symbol,
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
|
||||
pub struct RustcConstUnstable {
|
||||
pub feature: Symbol,
|
||||
}
|
||||
|
||||
/// Check if `attrs` contains an attribute like `#![feature(feature_name)]`.
|
||||
/// This will not perform any "sanity checks" on the form of the attributes.
|
||||
pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
|
||||
@ -176,7 +175,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||
|
||||
let mut stab: Option<Stability> = None;
|
||||
let mut rustc_depr: Option<RustcDeprecation> = None;
|
||||
let mut rustc_const_unstable: Option<RustcConstUnstable> = None;
|
||||
let mut rustc_const_unstable: Option<Symbol> = None;
|
||||
|
||||
'outer: for attr in attrs_iter {
|
||||
if ![
|
||||
@ -191,6 +190,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||
mark_used(attr);
|
||||
|
||||
let meta = attr.meta();
|
||||
// attributes with data
|
||||
if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
|
||||
let meta = meta.as_ref().unwrap();
|
||||
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
||||
@ -272,9 +272,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||
|
||||
get_meta!(feature);
|
||||
if let Some(feature) = feature {
|
||||
rustc_const_unstable = Some(RustcConstUnstable {
|
||||
feature
|
||||
});
|
||||
rustc_const_unstable = Some(feature);
|
||||
} else {
|
||||
span_err!(diagnostic, attr.span(), E0629, "missing 'feature'");
|
||||
continue
|
||||
@ -330,7 +328,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||
},
|
||||
feature,
|
||||
rustc_depr: None,
|
||||
rustc_const_unstable: None,
|
||||
const_stability: None,
|
||||
})
|
||||
}
|
||||
(None, _, _) => {
|
||||
@ -379,7 +377,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||
},
|
||||
feature,
|
||||
rustc_depr: None,
|
||||
rustc_const_unstable: None,
|
||||
const_stability: None,
|
||||
})
|
||||
}
|
||||
(None, _) => {
|
||||
@ -412,9 +410,9 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||
}
|
||||
|
||||
// Merge the const-unstable info into the stability info
|
||||
if let Some(rustc_const_unstable) = rustc_const_unstable {
|
||||
if let Some(feature) = rustc_const_unstable {
|
||||
if let Some(ref mut stab) = stab {
|
||||
stab.rustc_const_unstable = Some(rustc_const_unstable);
|
||||
stab.const_stability = Some(feature);
|
||||
} else {
|
||||
span_err!(diagnostic, item_sp, E0630,
|
||||
"rustc_const_unstable attribute must be paired with \
|
||||
|
@ -15,7 +15,7 @@ mod builtin;
|
||||
pub use self::builtin::{
|
||||
cfg_matches, contains_feature_attr, eval_condition, find_crate_name, find_deprecation,
|
||||
find_repr_attrs, find_stability, find_unwind_attr, Deprecation, InlineAttr, IntType, ReprAttr,
|
||||
RustcConstUnstable, RustcDeprecation, Stability, StabilityLevel, UnwindAttr,
|
||||
RustcDeprecation, Stability, StabilityLevel, UnwindAttr,
|
||||
};
|
||||
pub use self::IntType::*;
|
||||
pub use self::ReprAttr::*;
|
||||
|
@ -40,6 +40,16 @@ use symbol::{keywords, Symbol};
|
||||
use std::{env, path};
|
||||
|
||||
macro_rules! set {
|
||||
// The const_fn feature also enables the min_const_fn feature, because `min_const_fn` allows
|
||||
// the declaration `const fn`, but the `const_fn` feature gate enables things inside those
|
||||
// functions that we do not want to expose to the user for now.
|
||||
(const_fn) => {{
|
||||
fn f(features: &mut Features, _: Span) {
|
||||
features.const_fn = true;
|
||||
features.min_const_fn = true;
|
||||
}
|
||||
f as fn(&mut Features, Span)
|
||||
}};
|
||||
($field: ident) => {{
|
||||
fn f(features: &mut Features, _: Span) {
|
||||
features.$field = true;
|
||||
@ -206,25 +216,28 @@ declare_features! (
|
||||
// #23121. Array patterns have some hazards yet.
|
||||
(active, slice_patterns, "1.0.0", Some(23121), None),
|
||||
|
||||
// Allows the definition of `const fn` functions.
|
||||
// Allows the definition of `const fn` functions with some advanced features.
|
||||
(active, const_fn, "1.2.0", Some(24111), None),
|
||||
|
||||
// Allows the definition of `const fn` functions.
|
||||
(active, min_const_fn, "1.30.0", Some(53555), None),
|
||||
|
||||
// Allows let bindings and destructuring in `const fn` functions and constants.
|
||||
(active, const_let, "1.22.1", Some(48821), None),
|
||||
|
||||
// Allows accessing fields of unions inside const fn
|
||||
// Allows accessing fields of unions inside const fn.
|
||||
(active, const_fn_union, "1.27.0", Some(51909), None),
|
||||
|
||||
// Allows casting raw pointers to `usize` during const eval
|
||||
// Allows casting raw pointers to `usize` during const eval.
|
||||
(active, const_raw_ptr_to_usize_cast, "1.27.0", Some(51910), None),
|
||||
|
||||
// Allows dereferencing raw pointers during const eval
|
||||
// Allows dereferencing raw pointers during const eval.
|
||||
(active, const_raw_ptr_deref, "1.27.0", Some(51911), None),
|
||||
|
||||
// Allows reinterpretation of the bits of a value of one type as another type during const eval
|
||||
// Allows reinterpretation of the bits of a value of one type as another type during const eval.
|
||||
(active, const_transmute, "1.29.0", Some(53605), None),
|
||||
|
||||
// Allows comparing raw pointers during const eval
|
||||
// Allows comparing raw pointers during const eval.
|
||||
(active, const_compare_raw_pointers, "1.27.0", Some(53020), None),
|
||||
|
||||
// Allows panicking during const eval (produces compile-time errors)
|
||||
@ -1786,7 +1799,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
gate_feature_post!(&self, async_await, span, "async fn is unstable");
|
||||
}
|
||||
if header.constness.node == ast::Constness::Const {
|
||||
gate_feature_post!(&self, const_fn, span, "const fn is unstable");
|
||||
gate_feature_post!(&self, min_const_fn, span, "const fn is unstable");
|
||||
}
|
||||
// stability of const fn methods are covered in
|
||||
// visit_trait_item and visit_impl_item below; this is
|
||||
@ -1844,7 +1857,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
match ii.node {
|
||||
ast::ImplItemKind::Method(ref sig, _) => {
|
||||
if sig.header.constness.node == ast::Constness::Const {
|
||||
gate_feature_post!(&self, const_fn, ii.span, "const fn is unstable");
|
||||
gate_feature_post!(&self, min_const_fn, ii.span, "const fn is unstable");
|
||||
}
|
||||
}
|
||||
ast::ImplItemKind::Existential(..) => {
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
// ignore-emscripten
|
||||
|
||||
// compile-flags: -Z lower_128bit_ops=yes -C debug_assertions=no
|
||||
// compile-flags: -Z lower_128bit_ops=yes -C debug_assertions=no -O
|
||||
|
||||
#![feature(const_fn)]
|
||||
|
||||
@ -63,103 +63,65 @@ fn main() {
|
||||
// END RUST SOURCE
|
||||
|
||||
// START rustc.const_signed.Lower128Bit.after.mir
|
||||
// _8 = _1;
|
||||
// _9 = const compiler_builtins::int::addsub::rust_i128_addo(move _8, const 1i128) -> bb10;
|
||||
// ...
|
||||
// _7 = move (_9.0: i128);
|
||||
// ...
|
||||
// _10 = const compiler_builtins::int::addsub::rust_i128_subo(move _7, const 2i128) -> bb11;
|
||||
// ...
|
||||
// _6 = move (_10.0: i128);
|
||||
// ...
|
||||
// _11 = const compiler_builtins::int::mul::rust_i128_mulo(move _6, const 3i128) -> bb12;
|
||||
// ...
|
||||
// _5 = move (_11.0: i128);
|
||||
// ...
|
||||
// _12 = Eq(const 4i128, const 0i128);
|
||||
// assert(!move _12, "attempt to divide by zero") -> bb4;
|
||||
// ...
|
||||
// _13 = Eq(const 4i128, const -1i128);
|
||||
// _14 = Eq(_5, const -170141183460469231731687303715884105728i128);
|
||||
// _15 = BitAnd(move _13, move _14);
|
||||
// assert(!move _15, "attempt to divide with overflow") -> bb5;
|
||||
// ...
|
||||
// _4 = const compiler_builtins::int::sdiv::rust_i128_div(move _5, const 4i128) -> bb13;
|
||||
// ...
|
||||
// _17 = Eq(const 5i128, const -1i128);
|
||||
// _18 = Eq(_4, const -170141183460469231731687303715884105728i128);
|
||||
// _19 = BitAnd(move _17, move _18);
|
||||
// assert(!move _19, "attempt to calculate the remainder with overflow") -> bb7;
|
||||
// ...
|
||||
// _3 = const compiler_builtins::int::sdiv::rust_i128_rem(move _4, const 5i128) -> bb15;
|
||||
// ...
|
||||
// _2 = move (_20.0: i128);
|
||||
// ...
|
||||
// _23 = const 7i32 as u128 (Misc);
|
||||
// _21 = const compiler_builtins::int::shift::rust_i128_shro(move _2, move _23) -> bb16;
|
||||
// ...
|
||||
// _0 = move (_21.0: i128);
|
||||
// ...
|
||||
// assert(!move (_9.1: bool), "attempt to add with overflow") -> bb1;
|
||||
// ...
|
||||
// assert(!move (_10.1: bool), "attempt to subtract with overflow") -> bb2;
|
||||
// ...
|
||||
// assert(!move (_11.1: bool), "attempt to multiply with overflow") -> bb3;
|
||||
// ...
|
||||
// _16 = Eq(const 5i128, const 0i128);
|
||||
// assert(!move _16, "attempt to calculate the remainder with a divisor of zero") -> bb6;
|
||||
// ...
|
||||
// assert(!move (_20.1: bool), "attempt to shift left with overflow") -> bb8;
|
||||
// ...
|
||||
// _22 = const 6i32 as u128 (Misc);
|
||||
// _20 = const compiler_builtins::int::shift::rust_i128_shlo(move _3, move _22) -> bb14;
|
||||
// ...
|
||||
// assert(!move (_21.1: bool), "attempt to shift right with overflow") -> bb9;
|
||||
// _7 = const compiler_builtins::int::addsub::rust_i128_add(move _8, const 1i128) -> bb7;
|
||||
// ...
|
||||
// _10 = Eq(const 4i128, const -1i128);
|
||||
// _11 = Eq(_5, const -170141183460469231731687303715884105728i128);
|
||||
// _12 = BitAnd(move _10, move _11);
|
||||
// assert(!move _12, "attempt to divide with overflow") -> bb2;
|
||||
// ...
|
||||
// _4 = const compiler_builtins::int::sdiv::rust_i128_div(move _5, const 4i128) -> bb8;
|
||||
// ...
|
||||
// _14 = Eq(const 5i128, const -1i128);
|
||||
// _15 = Eq(_4, const -170141183460469231731687303715884105728i128);
|
||||
// _16 = BitAnd(move _14, move _15);
|
||||
// assert(!move _16, "attempt to calculate the remainder with overflow") -> bb4;
|
||||
// ...
|
||||
// _3 = const compiler_builtins::int::sdiv::rust_i128_rem(move _4, const 5i128) -> bb11;
|
||||
// ...
|
||||
// _9 = Eq(const 4i128, const 0i128);
|
||||
// assert(!move _9, "attempt to divide by zero") -> bb1;
|
||||
// ...
|
||||
// _5 = const compiler_builtins::int::mul::rust_i128_mul(move _6, const 3i128) -> bb5;
|
||||
// ...
|
||||
// _6 = const compiler_builtins::int::addsub::rust_i128_sub(move _7, const 2i128) -> bb6;
|
||||
// ...
|
||||
// _13 = Eq(const 5i128, const 0i128);
|
||||
// assert(!move _13, "attempt to calculate the remainder with a divisor of zero") -> bb3;
|
||||
// ...
|
||||
// _17 = const 7i32 as u32 (Misc);
|
||||
// _0 = const compiler_builtins::int::shift::rust_i128_shr(move _2, move _17) -> bb9;
|
||||
// ...
|
||||
// _18 = const 6i32 as u32 (Misc);
|
||||
// _2 = const compiler_builtins::int::shift::rust_i128_shl(move _3, move _18) -> bb10;
|
||||
// END rustc.const_signed.Lower128Bit.after.mir
|
||||
|
||||
// START rustc.const_unsigned.Lower128Bit.after.mir
|
||||
// _8 = _1;
|
||||
// _9 = const compiler_builtins::int::addsub::rust_u128_addo(move _8, const 1u128) -> bb8;
|
||||
// ...
|
||||
// _7 = move (_9.0: u128);
|
||||
// ...
|
||||
// _10 = const compiler_builtins::int::addsub::rust_u128_subo(move _7, const 2u128) -> bb9;
|
||||
// ...
|
||||
// _6 = move (_10.0: u128);
|
||||
// ...
|
||||
// _11 = const compiler_builtins::int::mul::rust_u128_mulo(move _6, const 3u128) -> bb10;
|
||||
// ...
|
||||
// _5 = move (_11.0: u128);
|
||||
// ...
|
||||
// _12 = Eq(const 4u128, const 0u128);
|
||||
// assert(!move _12, "attempt to divide by zero") -> bb4;
|
||||
// ...
|
||||
// _4 = const compiler_builtins::int::udiv::rust_u128_div(move _5, const 4u128) -> bb11;
|
||||
// ...
|
||||
// _3 = const compiler_builtins::int::udiv::rust_u128_rem(move _4, const 5u128) -> bb13;
|
||||
// ...
|
||||
// _2 = move (_14.0: u128);
|
||||
// ...
|
||||
// _17 = const 7i32 as u128 (Misc);
|
||||
// _15 = const compiler_builtins::int::shift::rust_u128_shro(move _2, move _17) -> bb14;
|
||||
// ...
|
||||
// _0 = move (_15.0: u128);
|
||||
// ...
|
||||
// assert(!move (_9.1: bool), "attempt to add with overflow") -> bb1;
|
||||
// ...
|
||||
// assert(!move (_10.1: bool), "attempt to subtract with overflow") -> bb2;
|
||||
// ...
|
||||
// assert(!move (_11.1: bool), "attempt to multiply with overflow") -> bb3;
|
||||
// ...
|
||||
// _13 = Eq(const 5u128, const 0u128);
|
||||
// assert(!move _13, "attempt to calculate the remainder with a divisor of zero") -> bb5;
|
||||
// ...
|
||||
// assert(!move (_14.1: bool), "attempt to shift left with overflow") -> bb6;
|
||||
// ...
|
||||
// _16 = const 6i32 as u128 (Misc);
|
||||
// _14 = const compiler_builtins::int::shift::rust_u128_shlo(move _3, move _16) -> bb12;
|
||||
// ...
|
||||
// assert(!move (_15.1: bool), "attempt to shift right with overflow") -> bb7;
|
||||
// _8 = _1;
|
||||
// _7 = const compiler_builtins::int::addsub::rust_u128_add(move _8, const 1u128) -> bb5;
|
||||
// ...
|
||||
// _4 = const compiler_builtins::int::udiv::rust_u128_div(move _5, const 4u128) -> bb6;
|
||||
// ...
|
||||
// _3 = const compiler_builtins::int::udiv::rust_u128_rem(move _4, const 5u128) -> bb9;
|
||||
// ...
|
||||
// _9 = Eq(const 4u128, const 0u128);
|
||||
// assert(!move _9, "attempt to divide by zero") -> bb1;
|
||||
// ...
|
||||
// _5 = const compiler_builtins::int::mul::rust_u128_mul(move _6, const 3u128) -> bb3;
|
||||
// ...
|
||||
// _6 = const compiler_builtins::int::addsub::rust_u128_sub(move _7, const 2u128) -> bb4;
|
||||
// ...
|
||||
// _10 = Eq(const 5u128, const 0u128);
|
||||
// assert(!move _10, "attempt to calculate the remainder with a divisor of zero") -> bb2;
|
||||
// ...
|
||||
// return;
|
||||
// ...
|
||||
// _11 = const 7i32 as u32 (Misc);
|
||||
// _0 = const compiler_builtins::int::shift::rust_u128_shr(move _2, move _11) -> bb7;
|
||||
// ...
|
||||
// _12 = const 6i32 as u32 (Misc);
|
||||
// _2 = const compiler_builtins::int::shift::rust_u128_shl(move _3, move _12) -> bb8;
|
||||
|
||||
// END rustc.const_unsigned.Lower128Bit.after.mir
|
||||
|
||||
// START rustc.test_signed.Lower128Bit.after.mir
|
||||
|
@ -11,6 +11,8 @@
|
||||
// ignore-wasm32
|
||||
// ignore-emscripten
|
||||
|
||||
// compile-flags: -C debug_assertions=yes
|
||||
|
||||
#![feature(const_fn, libc)]
|
||||
#![allow(const_err)]
|
||||
|
||||
@ -19,7 +21,7 @@ extern crate libc;
|
||||
use std::env;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
// this will panic in debug mode
|
||||
// this will panic in debug mode and overflow in release mode
|
||||
const fn bar() -> usize { 0 - 1 }
|
||||
|
||||
fn foo() {
|
||||
|
@ -4,14 +4,14 @@ note: ...which requires normalizing `ParamEnvAnd { param_env: ParamEnv { caller_
|
||||
note: ...which requires const-evaluating `Foo::bytes::{{constant}}`...
|
||||
--> $SRC_DIR/libcore/mem.rs:LL:COL
|
||||
|
|
||||
LL | unsafe { intrinsics::size_of::<T>() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | intrinsics::size_of::<T>()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: ...which again requires computing layout of `Foo`, completing the cycle
|
||||
note: cycle used when const-evaluating `Foo::bytes::{{constant}}`
|
||||
--> $SRC_DIR/libcore/mem.rs:LL:COL
|
||||
|
|
||||
LL | unsafe { intrinsics::size_of::<T>() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | intrinsics::size_of::<T>()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
224
src/test/ui/consts/min_const_fn/min_const_fn.nll.stderr
Normal file
224
src/test/ui/consts/min_const_fn/min_const_fn.nll.stderr
Normal file
@ -0,0 +1,224 @@
|
||||
error[E0493]: destructors cannot be evaluated at compile-time
|
||||
--> $DIR/min_const_fn.rs:49:25
|
||||
|
|
||||
LL | const fn into_inner(self) -> T { self.0 } //~ destructors cannot be evaluated
|
||||
| ^^^^ constant functions cannot evaluate destructors
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:51:5
|
||||
|
|
||||
LL | const fn get_mut(&mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0493]: destructors cannot be evaluated at compile-time
|
||||
--> $DIR/min_const_fn.rs:56:28
|
||||
|
|
||||
LL | const fn into_inner_lt(self) -> T { self.0 } //~ destructors cannot be evaluated
|
||||
| ^^^^ constant functions cannot evaluate destructors
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:58:5
|
||||
|
|
||||
LL | const fn get_mut_lt(&'a mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0493]: destructors cannot be evaluated at compile-time
|
||||
--> $DIR/min_const_fn.rs:63:27
|
||||
|
|
||||
LL | const fn into_inner_s(self) -> T { self.0 } //~ ERROR destructors
|
||||
| ^^^^ constant functions cannot evaluate destructors
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:65:5
|
||||
|
|
||||
LL | const fn get_mut_s(&mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:70:5
|
||||
|
|
||||
LL | const fn get_mut_sq(&mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:88:16
|
||||
|
|
||||
LL | const fn foo11<T: std::fmt::Display>(t: T) -> T { t }
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:90:18
|
||||
|
|
||||
LL | const fn foo11_2<T: Send>(t: T) -> T { t }
|
||||
| ^
|
||||
|
||||
error: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:92:33
|
||||
|
|
||||
LL | const fn foo19(f: f32) -> f32 { f * 2.0 }
|
||||
| ^^^^^^^
|
||||
|
||||
error: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:94:35
|
||||
|
|
||||
LL | const fn foo19_2(f: f32) -> f32 { 2.0 - f }
|
||||
| ^^^^^^^
|
||||
|
||||
error: only int and `bool` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:96:35
|
||||
|
|
||||
LL | const fn foo19_3(f: f32) -> f32 { -f }
|
||||
| ^^
|
||||
|
||||
error: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:98:43
|
||||
|
|
||||
LL | const fn foo19_4(f: f32, g: f32) -> f32 { f / g }
|
||||
| ^^^^^
|
||||
|
||||
error: cannot access `static` items in const fn
|
||||
--> $DIR/min_const_fn.rs:102:27
|
||||
|
|
||||
LL | const fn foo25() -> u32 { BAR } //~ ERROR cannot access `static` items in const fn
|
||||
| ^^^
|
||||
|
||||
error: cannot access `static` items in const fn
|
||||
--> $DIR/min_const_fn.rs:103:36
|
||||
|
|
||||
LL | const fn foo26() -> &'static u32 { &BAR } //~ ERROR cannot access `static` items
|
||||
| ^^^^
|
||||
|
||||
error: casting pointers to ints is unstable in const fn
|
||||
--> $DIR/min_const_fn.rs:104:42
|
||||
|
|
||||
LL | const fn foo30(x: *const u32) -> usize { x as usize }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: casting pointers to ints is unstable in const fn
|
||||
--> $DIR/min_const_fn.rs:106:42
|
||||
|
|
||||
LL | const fn foo30_2(x: *mut u32) -> usize { x as usize }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:108:38
|
||||
|
|
||||
LL | const fn foo30_4(b: bool) -> usize { if b { 1 } else { 42 } }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:110:29
|
||||
|
|
||||
LL | const fn foo30_5(b: bool) { while b { } } //~ ERROR not stable in const fn
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: local variables in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:111:34
|
||||
|
|
||||
LL | const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn are unstable
|
||||
| ^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:112:44
|
||||
|
|
||||
LL | const fn foo36(a: bool, b: bool) -> bool { a && b }
|
||||
| ^^^^^^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:114:44
|
||||
|
|
||||
LL | const fn foo37(a: bool, b: bool) -> bool { a || b }
|
||||
| ^^^^^^
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:116:14
|
||||
|
|
||||
LL | const fn inc(x: &mut i32) { *x += 1 }
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:121:6
|
||||
|
|
||||
LL | impl<T: std::fmt::Debug> Foo<T> {
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:126:6
|
||||
|
|
||||
LL | impl<T: std::fmt::Debug + Sized> Foo<T> {
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:131:6
|
||||
|
|
||||
LL | impl<T: Sync + Sized> Foo<T> {
|
||||
| ^
|
||||
|
||||
error: `impl Trait` in const fn is unstable
|
||||
--> $DIR/min_const_fn.rs:137:1
|
||||
|
|
||||
LL | const fn no_rpit2() -> AlanTuring<impl std::fmt::Debug> { AlanTuring(0) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:139:34
|
||||
|
|
||||
LL | const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:141:22
|
||||
|
|
||||
LL | const fn no_apit(_x: impl std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `impl Trait` in const fn is unstable
|
||||
--> $DIR/min_const_fn.rs:142:1
|
||||
|
|
||||
LL | const fn no_rpit() -> impl std::fmt::Debug {} //~ ERROR `impl Trait` in const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:143:23
|
||||
|
|
||||
LL | const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
||||
| ^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:144:1
|
||||
|
|
||||
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0597]: borrowed value does not live long enough
|
||||
--> $DIR/min_const_fn.rs:144:64
|
||||
|
|
||||
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
||||
| ^^ - temporary value only lives until here
|
||||
| |
|
||||
| temporary value does not live long enough
|
||||
|
|
||||
= note: borrowed value must be valid for the static lifetime...
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:149:41
|
||||
|
|
||||
LL | const fn really_no_traits_i_mean_it() { (&() as &std::fmt::Debug, ()).1 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: function pointers in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:152:21
|
||||
|
|
||||
LL | const fn no_fn_ptrs(_x: fn()) {}
|
||||
| ^^
|
||||
|
||||
error: function pointers in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:154:1
|
||||
|
|
||||
LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 36 previous errors
|
||||
|
||||
Some errors occurred: E0493, E0597.
|
||||
For more information about an error, try `rustc --explain E0493`.
|
156
src/test/ui/consts/min_const_fn/min_const_fn.rs
Normal file
156
src/test/ui/consts/min_const_fn/min_const_fn.rs
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(min_const_fn)]
|
||||
|
||||
// ok
|
||||
const fn foo1() {}
|
||||
const fn foo2(x: i32) -> i32 { x }
|
||||
const fn foo3<T>(x: T) -> T { x }
|
||||
const fn foo7() {
|
||||
(
|
||||
foo1(),
|
||||
foo2(420),
|
||||
foo3(69),
|
||||
).0
|
||||
}
|
||||
const fn foo12<T: Sized>(t: T) -> T { t }
|
||||
const fn foo13<T: ?Sized>(t: &T) -> &T { t }
|
||||
const fn foo14<'a, T: 'a>(t: &'a T) -> &'a T { t }
|
||||
const fn foo15<T>(t: T) -> T where T: Sized { t }
|
||||
const fn foo15_2<T>(t: &T) -> &T where T: ?Sized { t }
|
||||
const fn foo16(f: f32) -> f32 { f }
|
||||
const fn foo17(f: f32) -> u32 { f as u32 }
|
||||
const fn foo18(i: i32) -> i32 { i * 3 }
|
||||
const fn foo20(b: bool) -> bool { !b }
|
||||
const fn foo21<T, U>(t: T, u: U) -> (T, U) { (t, u) }
|
||||
const fn foo22(s: &[u8], i: usize) -> u8 { s[i] }
|
||||
const FOO: u32 = 42;
|
||||
const fn foo23() -> u32 { FOO }
|
||||
const fn foo24() -> &'static u32 { &FOO }
|
||||
const fn foo27(x: &u32) -> u32 { *x }
|
||||
const fn foo28(x: u32) -> u32 { *&x }
|
||||
const fn foo29(x: u32) -> i32 { x as i32 }
|
||||
const fn foo31(a: bool, b: bool) -> bool { a & b }
|
||||
const fn foo32(a: bool, b: bool) -> bool { a | b }
|
||||
const fn foo33(a: bool, b: bool) -> bool { a & b }
|
||||
const fn foo34(a: bool, b: bool) -> bool { a | b }
|
||||
const fn foo35(a: bool, b: bool) -> bool { a ^ b }
|
||||
struct Foo<T: ?Sized>(T);
|
||||
impl<T> Foo<T> {
|
||||
const fn new(t: T) -> Self { Foo(t) }
|
||||
const fn into_inner(self) -> T { self.0 } //~ destructors cannot be evaluated
|
||||
const fn get(&self) -> &T { &self.0 }
|
||||
const fn get_mut(&mut self) -> &mut T { &mut self.0 }
|
||||
//~^ mutable references in const fn are unstable
|
||||
}
|
||||
impl<'a, T> Foo<T> {
|
||||
const fn new_lt(t: T) -> Self { Foo(t) }
|
||||
const fn into_inner_lt(self) -> T { self.0 } //~ destructors cannot be evaluated
|
||||
const fn get_lt(&'a self) -> &T { &self.0 }
|
||||
const fn get_mut_lt(&'a mut self) -> &mut T { &mut self.0 }
|
||||
//~^ mutable references in const fn are unstable
|
||||
}
|
||||
impl<T: Sized> Foo<T> {
|
||||
const fn new_s(t: T) -> Self { Foo(t) }
|
||||
const fn into_inner_s(self) -> T { self.0 } //~ ERROR destructors
|
||||
const fn get_s(&self) -> &T { &self.0 }
|
||||
const fn get_mut_s(&mut self) -> &mut T { &mut self.0 }
|
||||
//~^ mutable references in const fn are unstable
|
||||
}
|
||||
impl<T: ?Sized> Foo<T> {
|
||||
const fn get_sq(&self) -> &T { &self.0 }
|
||||
const fn get_mut_sq(&mut self) -> &mut T { &mut self.0 }
|
||||
//~^ mutable references in const fn are unstable
|
||||
}
|
||||
|
||||
|
||||
const fn char_ops(c: char, d: char) -> bool { c == d }
|
||||
const fn char_ops2(c: char, d: char) -> bool { c < d }
|
||||
const fn char_ops3(c: char, d: char) -> bool { c != d }
|
||||
const fn i32_ops(c: i32, d: i32) -> bool { c == d }
|
||||
const fn i32_ops2(c: i32, d: i32) -> bool { c < d }
|
||||
const fn i32_ops3(c: i32, d: i32) -> bool { c != d }
|
||||
const fn i32_ops4(c: i32, d: i32) -> i32 { c + d }
|
||||
const fn char_cast(u: u8) -> char { u as char }
|
||||
const unsafe fn foo4() -> i32 { 42 }
|
||||
const unsafe fn foo5<T>() -> *const T { 0 as *const T }
|
||||
const unsafe fn foo6<T>() -> *mut T { 0 as *mut T }
|
||||
|
||||
// not ok
|
||||
const fn foo11<T: std::fmt::Display>(t: T) -> T { t }
|
||||
//~^ ERROR trait bounds other than `Sized` on const fn parameters are unstable
|
||||
const fn foo11_2<T: Send>(t: T) -> T { t }
|
||||
//~^ ERROR trait bounds other than `Sized` on const fn parameters are unstable
|
||||
const fn foo19(f: f32) -> f32 { f * 2.0 }
|
||||
//~^ ERROR only int, `bool` and `char` operations are stable in const fn
|
||||
const fn foo19_2(f: f32) -> f32 { 2.0 - f }
|
||||
//~^ ERROR only int, `bool` and `char` operations are stable in const fn
|
||||
const fn foo19_3(f: f32) -> f32 { -f }
|
||||
//~^ ERROR only int and `bool` operations are stable in const fn
|
||||
const fn foo19_4(f: f32, g: f32) -> f32 { f / g }
|
||||
//~^ ERROR only int, `bool` and `char` operations are stable in const fn
|
||||
|
||||
static BAR: u32 = 42;
|
||||
const fn foo25() -> u32 { BAR } //~ ERROR cannot access `static` items in const fn
|
||||
const fn foo26() -> &'static u32 { &BAR } //~ ERROR cannot access `static` items
|
||||
const fn foo30(x: *const u32) -> usize { x as usize }
|
||||
//~^ ERROR casting pointers to int
|
||||
const fn foo30_2(x: *mut u32) -> usize { x as usize }
|
||||
//~^ ERROR casting pointers to int
|
||||
const fn foo30_4(b: bool) -> usize { if b { 1 } else { 42 } }
|
||||
//~^ ERROR `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
const fn foo30_5(b: bool) { while b { } } //~ ERROR not stable in const fn
|
||||
const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn are unstable
|
||||
const fn foo36(a: bool, b: bool) -> bool { a && b }
|
||||
//~^ ERROR `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
const fn foo37(a: bool, b: bool) -> bool { a || b }
|
||||
//~^ ERROR `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
const fn inc(x: &mut i32) { *x += 1 }
|
||||
//~^ ERROR mutable references in const fn are unstable
|
||||
|
||||
fn main() {}
|
||||
|
||||
impl<T: std::fmt::Debug> Foo<T> {
|
||||
//~^ ERROR trait bounds other than `Sized` on const fn parameters are unstable
|
||||
const fn foo(&self) {}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug + Sized> Foo<T> {
|
||||
//~^ ERROR trait bounds other than `Sized` on const fn parameters are unstable
|
||||
const fn foo2(&self) {}
|
||||
}
|
||||
|
||||
impl<T: Sync + Sized> Foo<T> {
|
||||
//~^ ERROR trait bounds other than `Sized` on const fn parameters are unstable
|
||||
const fn foo3(&self) {}
|
||||
}
|
||||
|
||||
struct AlanTuring<T>(T);
|
||||
const fn no_rpit2() -> AlanTuring<impl std::fmt::Debug> { AlanTuring(0) }
|
||||
//~^ ERROR `impl Trait` in const fn is unstable
|
||||
const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
|
||||
//~^ ERROR trait bounds other than `Sized`
|
||||
const fn no_apit(_x: impl std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
||||
const fn no_rpit() -> impl std::fmt::Debug {} //~ ERROR `impl Trait` in const fn is unstable
|
||||
const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
||||
const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
||||
//~^ ERROR trait bounds other than `Sized`
|
||||
|
||||
const fn no_unsafe() { unsafe {} }
|
||||
|
||||
const fn really_no_traits_i_mean_it() { (&() as &std::fmt::Debug, ()).1 }
|
||||
//~^ ERROR trait bounds other than `Sized`
|
||||
|
||||
const fn no_fn_ptrs(_x: fn()) {}
|
||||
//~^ ERROR function pointers in const fn are unstable
|
||||
const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
|
||||
//~^ ERROR function pointers in const fn are unstable
|
||||
|
213
src/test/ui/consts/min_const_fn/min_const_fn.stderr
Normal file
213
src/test/ui/consts/min_const_fn/min_const_fn.stderr
Normal file
@ -0,0 +1,213 @@
|
||||
error[E0493]: destructors cannot be evaluated at compile-time
|
||||
--> $DIR/min_const_fn.rs:49:25
|
||||
|
|
||||
LL | const fn into_inner(self) -> T { self.0 } //~ destructors cannot be evaluated
|
||||
| ^^^^ constant functions cannot evaluate destructors
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:51:5
|
||||
|
|
||||
LL | const fn get_mut(&mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0493]: destructors cannot be evaluated at compile-time
|
||||
--> $DIR/min_const_fn.rs:56:28
|
||||
|
|
||||
LL | const fn into_inner_lt(self) -> T { self.0 } //~ destructors cannot be evaluated
|
||||
| ^^^^ constant functions cannot evaluate destructors
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:58:5
|
||||
|
|
||||
LL | const fn get_mut_lt(&'a mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0493]: destructors cannot be evaluated at compile-time
|
||||
--> $DIR/min_const_fn.rs:63:27
|
||||
|
|
||||
LL | const fn into_inner_s(self) -> T { self.0 } //~ ERROR destructors
|
||||
| ^^^^ constant functions cannot evaluate destructors
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:65:5
|
||||
|
|
||||
LL | const fn get_mut_s(&mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:70:5
|
||||
|
|
||||
LL | const fn get_mut_sq(&mut self) -> &mut T { &mut self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:88:16
|
||||
|
|
||||
LL | const fn foo11<T: std::fmt::Display>(t: T) -> T { t }
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:90:18
|
||||
|
|
||||
LL | const fn foo11_2<T: Send>(t: T) -> T { t }
|
||||
| ^
|
||||
|
||||
error: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:92:33
|
||||
|
|
||||
LL | const fn foo19(f: f32) -> f32 { f * 2.0 }
|
||||
| ^^^^^^^
|
||||
|
||||
error: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:94:35
|
||||
|
|
||||
LL | const fn foo19_2(f: f32) -> f32 { 2.0 - f }
|
||||
| ^^^^^^^
|
||||
|
||||
error: only int and `bool` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:96:35
|
||||
|
|
||||
LL | const fn foo19_3(f: f32) -> f32 { -f }
|
||||
| ^^
|
||||
|
||||
error: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/min_const_fn.rs:98:43
|
||||
|
|
||||
LL | const fn foo19_4(f: f32, g: f32) -> f32 { f / g }
|
||||
| ^^^^^
|
||||
|
||||
error: cannot access `static` items in const fn
|
||||
--> $DIR/min_const_fn.rs:102:27
|
||||
|
|
||||
LL | const fn foo25() -> u32 { BAR } //~ ERROR cannot access `static` items in const fn
|
||||
| ^^^
|
||||
|
||||
error: cannot access `static` items in const fn
|
||||
--> $DIR/min_const_fn.rs:103:36
|
||||
|
|
||||
LL | const fn foo26() -> &'static u32 { &BAR } //~ ERROR cannot access `static` items
|
||||
| ^^^^
|
||||
|
||||
error: casting pointers to ints is unstable in const fn
|
||||
--> $DIR/min_const_fn.rs:104:42
|
||||
|
|
||||
LL | const fn foo30(x: *const u32) -> usize { x as usize }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: casting pointers to ints is unstable in const fn
|
||||
--> $DIR/min_const_fn.rs:106:42
|
||||
|
|
||||
LL | const fn foo30_2(x: *mut u32) -> usize { x as usize }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:108:38
|
||||
|
|
||||
LL | const fn foo30_4(b: bool) -> usize { if b { 1 } else { 42 } }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:110:29
|
||||
|
|
||||
LL | const fn foo30_5(b: bool) { while b { } } //~ ERROR not stable in const fn
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: local variables in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:111:34
|
||||
|
|
||||
LL | const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn are unstable
|
||||
| ^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:112:44
|
||||
|
|
||||
LL | const fn foo36(a: bool, b: bool) -> bool { a && b }
|
||||
| ^^^^^^
|
||||
|
||||
error: `if`, `match`, `&&` and `||` are not stable in const fn
|
||||
--> $DIR/min_const_fn.rs:114:44
|
||||
|
|
||||
LL | const fn foo37(a: bool, b: bool) -> bool { a || b }
|
||||
| ^^^^^^
|
||||
|
||||
error: mutable references in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:116:14
|
||||
|
|
||||
LL | const fn inc(x: &mut i32) { *x += 1 }
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:121:6
|
||||
|
|
||||
LL | impl<T: std::fmt::Debug> Foo<T> {
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:126:6
|
||||
|
|
||||
LL | impl<T: std::fmt::Debug + Sized> Foo<T> {
|
||||
| ^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:131:6
|
||||
|
|
||||
LL | impl<T: Sync + Sized> Foo<T> {
|
||||
| ^
|
||||
|
||||
error: `impl Trait` in const fn is unstable
|
||||
--> $DIR/min_const_fn.rs:137:1
|
||||
|
|
||||
LL | const fn no_rpit2() -> AlanTuring<impl std::fmt::Debug> { AlanTuring(0) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:139:34
|
||||
|
|
||||
LL | const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:141:22
|
||||
|
|
||||
LL | const fn no_apit(_x: impl std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `impl Trait` in const fn is unstable
|
||||
--> $DIR/min_const_fn.rs:142:1
|
||||
|
|
||||
LL | const fn no_rpit() -> impl std::fmt::Debug {} //~ ERROR `impl Trait` in const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:143:23
|
||||
|
|
||||
LL | const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
||||
| ^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:144:1
|
||||
|
|
||||
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn.rs:149:41
|
||||
|
|
||||
LL | const fn really_no_traits_i_mean_it() { (&() as &std::fmt::Debug, ()).1 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: function pointers in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:152:21
|
||||
|
|
||||
LL | const fn no_fn_ptrs(_x: fn()) {}
|
||||
| ^^
|
||||
|
||||
error: function pointers in const fn are unstable
|
||||
--> $DIR/min_const_fn.rs:154:1
|
||||
|
|
||||
LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 35 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0493`.
|
25
src/test/ui/consts/min_const_fn/min_const_fn_dyn.nll.stderr
Normal file
25
src/test/ui/consts/min_const_fn/min_const_fn_dyn.nll.stderr
Normal file
@ -0,0 +1,25 @@
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn_dyn.rs:21:5
|
||||
|
|
||||
LL | x.0.field;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn_dyn.rs:24:66
|
||||
|
|
||||
LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
||||
| ^^
|
||||
|
||||
error[E0597]: borrowed value does not live long enough
|
||||
--> $DIR/min_const_fn_dyn.rs:24:67
|
||||
|
|
||||
LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
||||
| ^ - temporary value only lives until here
|
||||
| |
|
||||
| temporary value does not live long enough
|
||||
|
|
||||
= note: borrowed value must be valid for the static lifetime...
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
27
src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs
Normal file
27
src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(min_const_fn)]
|
||||
|
||||
struct HasDyn {
|
||||
field: &'static dyn std::fmt::Debug,
|
||||
}
|
||||
|
||||
struct Hide(HasDyn);
|
||||
|
||||
const fn no_inner_dyn_trait(_x: Hide) {}
|
||||
const fn no_inner_dyn_trait2(x: Hide) {
|
||||
x.0.field;
|
||||
//~^ ERROR trait bounds other than `Sized`
|
||||
}
|
||||
const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
||||
//~^ ERROR trait bounds other than `Sized`
|
||||
|
||||
fn main() {}
|
14
src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr
Normal file
14
src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn_dyn.rs:21:5
|
||||
|
|
||||
LL | x.0.field;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: trait bounds other than `Sized` on const fn parameters are unstable
|
||||
--> $DIR/min_const_fn_dyn.rs:24:66
|
||||
|
|
||||
LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
||||
| ^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
29
src/test/ui/consts/min_const_fn/min_const_fn_fn_ptr.rs
Normal file
29
src/test/ui/consts/min_const_fn/min_const_fn_fn_ptr.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(min_const_fn)]
|
||||
|
||||
struct HasPtr {
|
||||
field: fn(),
|
||||
}
|
||||
|
||||
struct Hide(HasPtr);
|
||||
|
||||
fn field() {}
|
||||
|
||||
const fn no_inner_dyn_trait(_x: Hide) {}
|
||||
const fn no_inner_dyn_trait2(x: Hide) {
|
||||
x.0.field;
|
||||
//~^ ERROR function pointers in const fn
|
||||
}
|
||||
const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasPtr { field }) }
|
||||
//~^ ERROR function pointers in const fn
|
||||
|
||||
fn main() {}
|
14
src/test/ui/consts/min_const_fn/min_const_fn_fn_ptr.stderr
Normal file
14
src/test/ui/consts/min_const_fn/min_const_fn_fn_ptr.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error: function pointers in const fn are unstable
|
||||
--> $DIR/min_const_fn_fn_ptr.rs:23:5
|
||||
|
|
||||
LL | x.0.field;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: function pointers in const fn are unstable
|
||||
--> $DIR/min_const_fn_fn_ptr.rs:26:59
|
||||
|
|
||||
LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasPtr { field }) }
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -0,0 +1,38 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![unstable(feature = "humans",
|
||||
reason = "who ever let humans program computers,
|
||||
we're apparently really bad at it",
|
||||
issue = "0")]
|
||||
|
||||
#![feature(rustc_const_unstable, const_fn, foo)]
|
||||
#![feature(staged_api)]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature="foo")]
|
||||
const fn foo() -> u32 { 42 }
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
// can't call non-min_const_fn
|
||||
const fn bar() -> u32 { foo() } //~ ERROR can only call other `min_const_fn`
|
||||
|
||||
#[unstable(feature = "rust1", issue="0")]
|
||||
const fn foo2() -> u32 { 42 }
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
// can't call non-min_const_fn
|
||||
const fn bar2() -> u32 { foo2() } //~ ERROR can only call other `min_const_fn`
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
// conformity is required, even with `const_fn` feature gate
|
||||
const fn bar3() -> u32 { (5f32 + 6f32) as u32 } //~ ERROR only int, `bool` and `char` operations
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,20 @@
|
||||
error: can only call other `min_const_fn` within a `min_const_fn`
|
||||
--> $DIR/min_const_fn_libstd_stability.rs:25:25
|
||||
|
|
||||
LL | const fn bar() -> u32 { foo() } //~ ERROR can only call other `min_const_fn`
|
||||
| ^^^^^
|
||||
|
||||
error: can only call other `min_const_fn` within a `min_const_fn`
|
||||
--> $DIR/min_const_fn_libstd_stability.rs:32:26
|
||||
|
|
||||
LL | const fn bar2() -> u32 { foo2() } //~ ERROR can only call other `min_const_fn`
|
||||
| ^^^^^^
|
||||
|
||||
error: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/min_const_fn_libstd_stability.rs:36:26
|
||||
|
|
||||
LL | const fn bar3() -> u32 { (5f32 + 6f32) as u32 } //~ ERROR only int, `bool` and `char` operations
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
38
src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs
Normal file
38
src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(min_const_fn)]
|
||||
|
||||
// ok
|
||||
const unsafe fn foo4() -> i32 { 42 }
|
||||
const unsafe fn foo5<T>() -> *const T { 0 as *const T }
|
||||
const unsafe fn foo6<T>() -> *mut T { 0 as *mut T }
|
||||
const fn no_unsafe() { unsafe {} }
|
||||
|
||||
// not ok
|
||||
const fn foo8() -> i32 {
|
||||
unsafe { foo4() } //~ ERROR unsafe operations are not allowed in const fn
|
||||
}
|
||||
const fn foo9() -> *const String {
|
||||
unsafe { foo5::<String>() } //~ ERROR unsafe operations are not allowed in const fn
|
||||
}
|
||||
const fn foo10() -> *const Vec<std::cell::Cell<u32>> {
|
||||
unsafe { foo6::<Vec<std::cell::Cell<u32>>>() } //~ ERROR not allowed in const fn
|
||||
}
|
||||
const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
|
||||
//~^ dereferencing raw pointers in constant functions
|
||||
|
||||
fn main() {}
|
||||
|
||||
const unsafe fn no_union() {
|
||||
union Foo { x: (), y: () }
|
||||
Foo { x: () }.y //~ ERROR not allowed in const fn
|
||||
//~^ unions in const fn
|
||||
}
|
59
src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr
Normal file
59
src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr
Normal file
@ -0,0 +1,59 @@
|
||||
error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911)
|
||||
--> $DIR/min_const_fn_unsafe.rs:29:51
|
||||
|
|
||||
LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
|
||||
| ^^
|
||||
|
|
||||
= help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: unions in const fn are unstable (see issue #51909)
|
||||
--> $DIR/min_const_fn_unsafe.rs:36:5
|
||||
|
|
||||
LL | Foo { x: () }.y //~ ERROR not allowed in const fn
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(const_fn_union)] to the crate attributes to enable
|
||||
|
||||
error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
|
||||
--> $DIR/min_const_fn_unsafe.rs:21:14
|
||||
|
|
||||
LL | unsafe { foo4() } //~ ERROR unsafe operations are not allowed in const fn
|
||||
| ^^^^^^ call to unsafe function
|
||||
|
|
||||
= note: consult the function's documentation for information on how to avoid undefined behavior
|
||||
|
||||
error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
|
||||
--> $DIR/min_const_fn_unsafe.rs:24:14
|
||||
|
|
||||
LL | unsafe { foo5::<String>() } //~ ERROR unsafe operations are not allowed in const fn
|
||||
| ^^^^^^^^^^^^^^^^ call to unsafe function
|
||||
|
|
||||
= note: consult the function's documentation for information on how to avoid undefined behavior
|
||||
|
||||
error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
|
||||
--> $DIR/min_const_fn_unsafe.rs:27:14
|
||||
|
|
||||
LL | unsafe { foo6::<Vec<std::cell::Cell<u32>>>() } //~ ERROR not allowed in const fn
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
||||
|
|
||||
= note: consult the function's documentation for information on how to avoid undefined behavior
|
||||
|
||||
error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn
|
||||
--> $DIR/min_const_fn_unsafe.rs:29:51
|
||||
|
|
||||
LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
|
||||
| ^^ dereference of raw pointer
|
||||
|
|
||||
= note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
|
||||
|
||||
error: access to union field is unsafe and unsafe operations are not allowed in const fn
|
||||
--> $DIR/min_const_fn_unsafe.rs:36:5
|
||||
|
|
||||
LL | Foo { x: () }.y //~ ERROR not allowed in const fn
|
||||
| ^^^^^^^^^^^^^^^ access to union field
|
||||
|
|
||||
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
@ -4,8 +4,8 @@ error[E0308]: intrinsic has wrong type
|
||||
LL | fn size_of<T>(); //~ ERROR E0308
|
||||
| ^^^^^^^^^^^^^^^^ expected (), found usize
|
||||
|
|
||||
= note: expected type `unsafe extern "rust-intrinsic" fn()`
|
||||
found type `unsafe extern "rust-intrinsic" fn() -> usize`
|
||||
= note: expected type `extern "rust-intrinsic" fn()`
|
||||
found type `extern "rust-intrinsic" fn() -> usize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -8,9 +8,11 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test use of const fn without feature gate.
|
||||
// Test use of const fn without the `const_fn` feature gate.
|
||||
// `min_const_fn` is checked in its own file
|
||||
#![feature(min_const_fn)]
|
||||
|
||||
const fn foo() -> usize { 0 } //~ ERROR const fn is unstable
|
||||
const fn foo() -> usize { 0 } // ok
|
||||
|
||||
trait Foo {
|
||||
const fn foo() -> u32; //~ ERROR const fn is unstable
|
||||
@ -20,12 +22,11 @@ trait Foo {
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
const fn baz() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
const fn baz() -> u32 { 0 } // ok
|
||||
}
|
||||
|
||||
impl Foo for u32 {
|
||||
const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
//~| ERROR trait fns cannot be declared const
|
||||
const fn foo() -> u32 { 0 } //~ ERROR trait fns cannot be declared const
|
||||
}
|
||||
|
||||
static FOO: usize = foo();
|
||||
|
@ -1,31 +1,23 @@
|
||||
error[E0379]: trait fns cannot be declared const
|
||||
--> $DIR/feature-gate-const_fn.rs:16:5
|
||||
--> $DIR/feature-gate-const_fn.rs:18:5
|
||||
|
|
||||
LL | const fn foo() -> u32; //~ ERROR const fn is unstable
|
||||
| ^^^^^ trait fns cannot be const
|
||||
|
||||
error[E0379]: trait fns cannot be declared const
|
||||
--> $DIR/feature-gate-const_fn.rs:18:5
|
||||
--> $DIR/feature-gate-const_fn.rs:20:5
|
||||
|
|
||||
LL | const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^ trait fns cannot be const
|
||||
|
||||
error[E0379]: trait fns cannot be declared const
|
||||
--> $DIR/feature-gate-const_fn.rs:27:5
|
||||
--> $DIR/feature-gate-const_fn.rs:29:5
|
||||
|
|
||||
LL | const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
LL | const fn foo() -> u32 { 0 } //~ ERROR trait fns cannot be declared const
|
||||
| ^^^^^ trait fns cannot be const
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #24111)
|
||||
--> $DIR/feature-gate-const_fn.rs:13:1
|
||||
|
|
||||
LL | const fn foo() -> usize { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #24111)
|
||||
--> $DIR/feature-gate-const_fn.rs:16:5
|
||||
--> $DIR/feature-gate-const_fn.rs:18:5
|
||||
|
|
||||
LL | const fn foo() -> u32; //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -33,30 +25,14 @@ LL | const fn foo() -> u32; //~ ERROR const fn is unstable
|
||||
= help: add #![feature(const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #24111)
|
||||
--> $DIR/feature-gate-const_fn.rs:18:5
|
||||
--> $DIR/feature-gate-const_fn.rs:20:5
|
||||
|
|
||||
LL | const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #24111)
|
||||
--> $DIR/feature-gate-const_fn.rs:23:5
|
||||
|
|
||||
LL | const fn baz() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #24111)
|
||||
--> $DIR/feature-gate-const_fn.rs:27:5
|
||||
|
|
||||
LL | const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(const_fn)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors occurred: E0379, E0658.
|
||||
For more information about an error, try `rustc --explain E0379`.
|
||||
|
46
src/test/ui/feature-gates/feature-gate-min_const_fn.rs
Normal file
46
src/test/ui/feature-gates/feature-gate-min_const_fn.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test use of min_const_fn without feature gate.
|
||||
|
||||
const fn foo() -> usize { 0 } //~ ERROR const fn is unstable
|
||||
|
||||
trait Foo {
|
||||
const fn foo() -> u32; //~ ERROR const fn is unstable
|
||||
//~| ERROR trait fns cannot be declared const
|
||||
const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
//~| ERROR trait fns cannot be declared const
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
const fn baz() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
}
|
||||
|
||||
impl Foo for u32 {
|
||||
const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
//~| ERROR trait fns cannot be declared const
|
||||
}
|
||||
|
||||
static FOO: usize = foo();
|
||||
const BAR: usize = foo();
|
||||
|
||||
macro_rules! constant {
|
||||
($n:ident: $t:ty = $v:expr) => {
|
||||
const $n: $t = $v;
|
||||
}
|
||||
}
|
||||
|
||||
constant! {
|
||||
BAZ: usize = foo()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x: [usize; foo()] = [];
|
||||
}
|
62
src/test/ui/feature-gates/feature-gate-min_const_fn.stderr
Normal file
62
src/test/ui/feature-gates/feature-gate-min_const_fn.stderr
Normal file
@ -0,0 +1,62 @@
|
||||
error[E0379]: trait fns cannot be declared const
|
||||
--> $DIR/feature-gate-min_const_fn.rs:16:5
|
||||
|
|
||||
LL | const fn foo() -> u32; //~ ERROR const fn is unstable
|
||||
| ^^^^^ trait fns cannot be const
|
||||
|
||||
error[E0379]: trait fns cannot be declared const
|
||||
--> $DIR/feature-gate-min_const_fn.rs:18:5
|
||||
|
|
||||
LL | const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^ trait fns cannot be const
|
||||
|
||||
error[E0379]: trait fns cannot be declared const
|
||||
--> $DIR/feature-gate-min_const_fn.rs:27:5
|
||||
|
|
||||
LL | const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^ trait fns cannot be const
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #53555)
|
||||
--> $DIR/feature-gate-min_const_fn.rs:13:1
|
||||
|
|
||||
LL | const fn foo() -> usize { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(min_const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #24111)
|
||||
--> $DIR/feature-gate-min_const_fn.rs:16:5
|
||||
|
|
||||
LL | const fn foo() -> u32; //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #24111)
|
||||
--> $DIR/feature-gate-min_const_fn.rs:18:5
|
||||
|
|
||||
LL | const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #53555)
|
||||
--> $DIR/feature-gate-min_const_fn.rs:23:5
|
||||
|
|
||||
LL | const fn baz() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(min_const_fn)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: const fn is unstable (see issue #53555)
|
||||
--> $DIR/feature-gate-min_const_fn.rs:27:5
|
||||
|
|
||||
LL | const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(min_const_fn)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
Some errors occurred: E0379, E0658.
|
||||
For more information about an error, try `rustc --explain E0379`.
|
Loading…
Reference in New Issue
Block a user