mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #132831 - workingjubilee:rollup-6fdif44, r=workingjubilee
Rollup of 6 pull requests Successful merges: - #131258 (Stabilize s390x inline assembly) - #132801 (interpret: get_alloc_info: also return mutability) - #132823 (require const_impl_trait gate for all conditional and trait const calls) - #132824 (Update grammar in wasm-c-abi's compiler flag documentation) - #132825 (Exclude relnotes-tracking-issue from needs-triage) - #132828 (Additional tests to ensure let is rejected during parsing) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
668959740f
@ -48,6 +48,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
| asm::InlineAsmArch::RiscV32
|
||||
| asm::InlineAsmArch::RiscV64
|
||||
| asm::InlineAsmArch::LoongArch64
|
||||
| asm::InlineAsmArch::S390x
|
||||
);
|
||||
if !is_stable && !self.tcx.features().asm_experimental_arch() {
|
||||
feature_err(
|
||||
|
@ -25,6 +25,10 @@ const_eval_closure_fndef_not_const =
|
||||
function defined here, but it is not `const`
|
||||
const_eval_closure_non_const =
|
||||
cannot call non-const closure in {const_eval_const_context}s
|
||||
|
||||
const_eval_conditionally_const_call =
|
||||
cannot call conditionally-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s
|
||||
|
||||
const_eval_consider_dereferencing =
|
||||
consider dereferencing here
|
||||
|
||||
|
@ -15,7 +15,7 @@ use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_mir_dataflow::Analysis;
|
||||
use rustc_mir_dataflow::impls::MaybeStorageLive;
|
||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||
@ -361,31 +361,21 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
||||
!is_transient
|
||||
}
|
||||
|
||||
/// Returns whether there are const-conditions.
|
||||
fn revalidate_conditional_constness(
|
||||
&mut self,
|
||||
callee: DefId,
|
||||
callee_args: ty::GenericArgsRef<'tcx>,
|
||||
call_source: CallSource,
|
||||
call_span: Span,
|
||||
) {
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
if !tcx.is_conditionally_const(callee) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let const_conditions = tcx.const_conditions(callee).instantiate(tcx, callee_args);
|
||||
// If there are any const conditions on this fn and `const_trait_impl`
|
||||
// is not enabled, simply bail. We shouldn't be able to call conditionally
|
||||
// const functions on stable.
|
||||
if !const_conditions.is_empty() && !tcx.features().const_trait_impl() {
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
callee,
|
||||
args: callee_args,
|
||||
span: call_span,
|
||||
call_source,
|
||||
feature: Some(sym::const_trait_impl),
|
||||
});
|
||||
return;
|
||||
if const_conditions.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let infcx = tcx.infer_ctxt().build(self.body.typing_mode(tcx));
|
||||
@ -421,6 +411,8 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
||||
tcx.dcx()
|
||||
.span_delayed_bug(call_span, "this should have reported a ~const error in HIR");
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,11 +619,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
|
||||
let ConstCx { tcx, body, .. } = *self.ccx;
|
||||
|
||||
let fn_ty = func.ty(body, tcx);
|
||||
|
||||
let (mut callee, mut fn_args) = match *fn_ty.kind() {
|
||||
let (callee, fn_args) = match *fn_ty.kind() {
|
||||
ty::FnDef(def_id, fn_args) => (def_id, fn_args),
|
||||
|
||||
ty::FnPtr(..) => {
|
||||
@ -645,57 +637,38 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
self.revalidate_conditional_constness(callee, fn_args, call_source, *fn_span);
|
||||
let has_const_conditions =
|
||||
self.revalidate_conditional_constness(callee, fn_args, *fn_span);
|
||||
|
||||
let mut is_trait = false;
|
||||
// Attempting to call a trait method?
|
||||
if let Some(trait_did) = tcx.trait_of_item(callee) {
|
||||
// We can't determine the actual callee here, so we have to do different checks
|
||||
// than usual.
|
||||
|
||||
trace!("attempting to call a trait method");
|
||||
|
||||
let trait_is_const = tcx.is_const_trait(trait_did);
|
||||
// trait method calls are only permitted when `effects` is enabled.
|
||||
// typeck ensures the conditions for calling a const trait method are met,
|
||||
// so we only error if the trait isn't const. We try to resolve the trait
|
||||
// into the concrete method, and uses that for const stability checks.
|
||||
// FIXME(const_trait_impl) we might consider moving const stability checks
|
||||
// to typeck as well.
|
||||
if tcx.features().const_trait_impl() && trait_is_const {
|
||||
// This skips the check below that ensures we only call `const fn`.
|
||||
is_trait = true;
|
||||
|
||||
if let Ok(Some(instance)) =
|
||||
Instance::try_resolve(tcx, param_env, callee, fn_args)
|
||||
&& let InstanceKind::Item(def) = instance.def
|
||||
{
|
||||
// Resolve a trait method call to its concrete implementation, which may be in a
|
||||
// `const` trait impl. This is only used for the const stability check below, since
|
||||
// we want to look at the concrete impl's stability.
|
||||
fn_args = instance.args;
|
||||
callee = def;
|
||||
}
|
||||
if trait_is_const {
|
||||
// Trait calls are always conditionally-const.
|
||||
self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
|
||||
// FIXME(const_trait_impl): do a more fine-grained check whether this
|
||||
// particular trait can be const-stably called.
|
||||
} else {
|
||||
// if the trait is const but the user has not enabled the feature(s),
|
||||
// suggest them.
|
||||
let feature = if trait_is_const {
|
||||
Some(if tcx.features().const_trait_impl() {
|
||||
sym::effects
|
||||
} else {
|
||||
sym::const_trait_impl
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Not even a const trait.
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
callee,
|
||||
args: fn_args,
|
||||
span: *fn_span,
|
||||
call_source,
|
||||
feature,
|
||||
});
|
||||
// If we allowed this, we're in miri-unleashed mode, so we might
|
||||
// as well skip the remaining checks.
|
||||
return;
|
||||
}
|
||||
// That's all we can check here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Even if we know the callee, ensure we can use conditionally-const calls.
|
||||
if has_const_conditions {
|
||||
self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
|
||||
}
|
||||
|
||||
// At this point, we are calling a function, `callee`, whose `DefId` is known...
|
||||
@ -783,14 +756,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trait functions are not `const fn` so we have to skip them here.
|
||||
if !tcx.is_const_fn(callee) && !is_trait {
|
||||
if !tcx.is_const_fn(callee) {
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
callee,
|
||||
args: fn_args,
|
||||
span: *fn_span,
|
||||
call_source,
|
||||
feature: None,
|
||||
});
|
||||
// If we allowed this, we're in miri-unleashed mode, so we might
|
||||
// as well skip the remaining checks.
|
||||
|
@ -70,6 +70,37 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
|
||||
}
|
||||
}
|
||||
|
||||
/// A call to a function that is in a trait, or has trait bounds that make it conditionally-const.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ConditionallyConstCall<'tcx> {
|
||||
pub callee: DefId,
|
||||
pub args: GenericArgsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
|
||||
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
|
||||
// We use the `const_trait_impl` gate for all conditionally-const calls.
|
||||
Status::Unstable {
|
||||
gate: sym::const_trait_impl,
|
||||
safe_to_expose_on_stable: false,
|
||||
// We don't want the "mark the callee as `#[rustc_const_stable_indirect]`" hint
|
||||
is_function_call: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
ccx.tcx.sess.create_feature_err(
|
||||
errors::ConditionallyConstCall {
|
||||
span,
|
||||
def_path_str: ccx.tcx.def_path_str_with_args(self.callee, self.args),
|
||||
def_descr: ccx.tcx.def_descr(self.callee),
|
||||
kind: ccx.const_kind(),
|
||||
},
|
||||
sym::const_trait_impl,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A function call where the callee is not marked as `const`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct FnCallNonConst<'tcx> {
|
||||
@ -77,7 +108,6 @@ pub(crate) struct FnCallNonConst<'tcx> {
|
||||
pub args: GenericArgsRef<'tcx>,
|
||||
pub span: Span,
|
||||
pub call_source: CallSource,
|
||||
pub feature: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
||||
@ -85,7 +115,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
|
||||
let FnCallNonConst { callee, args, span, call_source, feature } = *self;
|
||||
let FnCallNonConst { callee, args, span, call_source } = *self;
|
||||
let ConstCx { tcx, param_env, .. } = *ccx;
|
||||
let caller = ccx.def_id();
|
||||
|
||||
@ -285,14 +315,6 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
||||
ccx.const_kind(),
|
||||
));
|
||||
|
||||
if let Some(feature) = feature {
|
||||
ccx.tcx.disabled_nightly_features(
|
||||
&mut err,
|
||||
Some(ccx.tcx.local_def_id_to_hir_id(caller)),
|
||||
[(String::new(), feature)],
|
||||
);
|
||||
}
|
||||
|
||||
if let ConstContext::Static(_) = ccx.const_kind() {
|
||||
err.note(fluent_generated::const_eval_lazy_lock);
|
||||
}
|
||||
@ -398,15 +420,8 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine {
|
||||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
|
||||
if let hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineSource::Block,
|
||||
) = self.0
|
||||
{
|
||||
ccx.tcx.sess.create_feature_err(
|
||||
errors::UnallowedOpInConstContext { span, msg },
|
||||
sym::const_async_blocks,
|
||||
)
|
||||
if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
|
||||
ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
|
||||
} else {
|
||||
ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
|
||||
}
|
||||
|
@ -472,8 +472,9 @@ fn report_validation_error<'tcx>(
|
||||
backtrace.print_backtrace();
|
||||
|
||||
let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
|
||||
let (size, align, _) = ecx.get_alloc_info(alloc_id);
|
||||
let raw_bytes = errors::RawBytesNote { size: size.bytes(), align: align.bytes(), bytes };
|
||||
let info = ecx.get_alloc_info(alloc_id);
|
||||
let raw_bytes =
|
||||
errors::RawBytesNote { size: info.size.bytes(), align: info.align.bytes(), bytes };
|
||||
|
||||
crate::const_eval::report(
|
||||
*ecx.tcx,
|
||||
|
@ -176,6 +176,16 @@ pub(crate) struct NonConstFmtMacroCall {
|
||||
pub kind: ConstContext,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_conditionally_const_call)]
|
||||
pub(crate) struct ConditionallyConstCall {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub def_path_str: String,
|
||||
pub def_descr: &'static str,
|
||||
pub kind: ConstContext,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_non_const_fn_call, code = E0015)]
|
||||
pub(crate) struct NonConstFnCall {
|
||||
|
@ -14,10 +14,9 @@ use std::{fmt, mem, ptr};
|
||||
use rustc_abi::{Align, HasDataLayout, Size};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::display_allocation;
|
||||
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use super::{
|
||||
@ -72,6 +71,21 @@ pub enum AllocKind {
|
||||
Dead,
|
||||
}
|
||||
|
||||
/// Metadata about an `AllocId`.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct AllocInfo {
|
||||
pub size: Size,
|
||||
pub align: Align,
|
||||
pub kind: AllocKind,
|
||||
pub mutbl: Mutability,
|
||||
}
|
||||
|
||||
impl AllocInfo {
|
||||
fn new(size: Size, align: Align, kind: AllocKind, mutbl: Mutability) -> Self {
|
||||
Self { size, align, kind, mutbl }
|
||||
}
|
||||
}
|
||||
|
||||
/// The value of a function pointer.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum FnVal<'tcx, Other> {
|
||||
@ -524,17 +538,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
match self.ptr_try_get_alloc_id(ptr, 0) {
|
||||
Err(addr) => is_offset_misaligned(addr, align),
|
||||
Ok((alloc_id, offset, _prov)) => {
|
||||
let (_size, alloc_align, kind) = self.get_alloc_info(alloc_id);
|
||||
if let Some(misalign) =
|
||||
M::alignment_check(self, alloc_id, alloc_align, kind, offset, align)
|
||||
{
|
||||
let alloc_info = self.get_alloc_info(alloc_id);
|
||||
if let Some(misalign) = M::alignment_check(
|
||||
self,
|
||||
alloc_id,
|
||||
alloc_info.align,
|
||||
alloc_info.kind,
|
||||
offset,
|
||||
align,
|
||||
) {
|
||||
Some(misalign)
|
||||
} else if M::Provenance::OFFSET_IS_ADDR {
|
||||
is_offset_misaligned(ptr.addr().bytes(), align)
|
||||
} else {
|
||||
// Check allocation alignment and offset alignment.
|
||||
if alloc_align.bytes() < align.bytes() {
|
||||
Some(Misalignment { has: alloc_align, required: align })
|
||||
if alloc_info.align.bytes() < align.bytes() {
|
||||
Some(Misalignment { has: alloc_info.align, required: align })
|
||||
} else {
|
||||
is_offset_misaligned(offset.bytes(), align)
|
||||
}
|
||||
@ -818,82 +837,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
||||
/// Obtain the size and alignment of an allocation, even if that allocation has
|
||||
/// been deallocated.
|
||||
pub fn get_alloc_info(&self, id: AllocId) -> (Size, Align, AllocKind) {
|
||||
pub fn get_alloc_info(&self, id: AllocId) -> AllocInfo {
|
||||
// # Regular allocations
|
||||
// Don't use `self.get_raw` here as that will
|
||||
// a) cause cycles in case `id` refers to a static
|
||||
// b) duplicate a global's allocation in miri
|
||||
if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
|
||||
return (alloc.size(), alloc.align, AllocKind::LiveData);
|
||||
return AllocInfo::new(
|
||||
alloc.size(),
|
||||
alloc.align,
|
||||
AllocKind::LiveData,
|
||||
alloc.mutability,
|
||||
);
|
||||
}
|
||||
|
||||
// # Function pointers
|
||||
// (both global from `alloc_map` and local from `extra_fn_ptr_map`)
|
||||
if self.get_fn_alloc(id).is_some() {
|
||||
return (Size::ZERO, Align::ONE, AllocKind::Function);
|
||||
return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::Function, Mutability::Not);
|
||||
}
|
||||
|
||||
// # Statics
|
||||
// Can't do this in the match argument, we may get cycle errors since the lock would
|
||||
// be held throughout the match.
|
||||
match self.tcx.try_get_global_alloc(id) {
|
||||
Some(GlobalAlloc::Static(def_id)) => {
|
||||
// Thread-local statics do not have a constant address. They *must* be accessed via
|
||||
// `ThreadLocalRef`; we can never have a pointer to them as a regular constant value.
|
||||
assert!(!self.tcx.is_thread_local_static(def_id));
|
||||
|
||||
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else {
|
||||
bug!("GlobalAlloc::Static is not a static")
|
||||
};
|
||||
|
||||
let (size, align) = if nested {
|
||||
// Nested anonymous statics are untyped, so let's get their
|
||||
// size and alignment from the allocation itself. This always
|
||||
// succeeds, as the query is fed at DefId creation time, so no
|
||||
// evaluation actually occurs.
|
||||
let alloc = self.tcx.eval_static_initializer(def_id).unwrap();
|
||||
(alloc.0.size(), alloc.0.align)
|
||||
} else {
|
||||
// Use size and align of the type for everything else. We need
|
||||
// to do that to
|
||||
// * avoid cycle errors in case of self-referential statics,
|
||||
// * be able to get information on extern statics.
|
||||
let ty = self
|
||||
.tcx
|
||||
.type_of(def_id)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters");
|
||||
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
|
||||
assert!(layout.is_sized());
|
||||
(layout.size, layout.align.abi)
|
||||
};
|
||||
(size, align, AllocKind::LiveData)
|
||||
}
|
||||
Some(GlobalAlloc::Memory(alloc)) => {
|
||||
// Need to duplicate the logic here, because the global allocations have
|
||||
// different associated types than the interpreter-local ones.
|
||||
let alloc = alloc.inner();
|
||||
(alloc.size(), alloc.align, AllocKind::LiveData)
|
||||
}
|
||||
Some(GlobalAlloc::Function { .. }) => {
|
||||
bug!("We already checked function pointers above")
|
||||
}
|
||||
Some(GlobalAlloc::VTable(..)) => {
|
||||
// No data to be accessed here. But vtables are pointer-aligned.
|
||||
return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable);
|
||||
}
|
||||
// The rest must be dead.
|
||||
None => {
|
||||
// Deallocated pointers are allowed, we should be able to find
|
||||
// them in the map.
|
||||
let (size, align) = *self
|
||||
.memory
|
||||
.dead_alloc_map
|
||||
.get(&id)
|
||||
.expect("deallocated pointers should all be recorded in `dead_alloc_map`");
|
||||
(size, align, AllocKind::Dead)
|
||||
}
|
||||
// # Global allocations
|
||||
if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) {
|
||||
let (size, align) = global_alloc.size_and_align(*self.tcx, self.param_env);
|
||||
let mutbl = global_alloc.mutability(*self.tcx, self.param_env);
|
||||
let kind = match global_alloc {
|
||||
GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
|
||||
GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
|
||||
GlobalAlloc::VTable { .. } => AllocKind::VTable,
|
||||
};
|
||||
return AllocInfo::new(size, align, kind, mutbl);
|
||||
}
|
||||
|
||||
// # Dead pointers
|
||||
let (size, align) = *self
|
||||
.memory
|
||||
.dead_alloc_map
|
||||
.get(&id)
|
||||
.expect("deallocated pointers should all be recorded in `dead_alloc_map`");
|
||||
AllocInfo::new(size, align, AllocKind::Dead, Mutability::Not)
|
||||
}
|
||||
|
||||
/// Obtain the size and alignment of a *live* allocation.
|
||||
@ -902,11 +884,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
id: AllocId,
|
||||
msg: CheckInAllocMsg,
|
||||
) -> InterpResult<'tcx, (Size, Align)> {
|
||||
let (size, align, kind) = self.get_alloc_info(id);
|
||||
if matches!(kind, AllocKind::Dead) {
|
||||
let info = self.get_alloc_info(id);
|
||||
if matches!(info.kind, AllocKind::Dead) {
|
||||
throw_ub!(PointerUseAfterFree(id, msg))
|
||||
}
|
||||
interp_ok((size, align))
|
||||
interp_ok((info.size, info.align))
|
||||
}
|
||||
|
||||
fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> {
|
||||
@ -1458,7 +1440,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
let ptr = scalar.to_pointer(self)?;
|
||||
match self.ptr_try_get_alloc_id(ptr, 0) {
|
||||
Ok((alloc_id, offset, _)) => {
|
||||
let (size, _align, _kind) = self.get_alloc_info(alloc_id);
|
||||
let size = self.get_alloc_info(alloc_id).size;
|
||||
// If the pointer is out-of-bounds, it may be null.
|
||||
// Note that one-past-the-end (offset == size) is still inbounds, and never null.
|
||||
offset > size
|
||||
|
@ -31,7 +31,7 @@ pub use self::intern::{
|
||||
};
|
||||
pub(crate) use self::intrinsics::eval_nullary_intrinsic;
|
||||
pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine};
|
||||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
pub use self::memory::{AllocInfo, AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
use self::operand::Operand;
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy};
|
||||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||
|
@ -31,8 +31,8 @@ use tracing::trace;
|
||||
|
||||
use super::machine::AllocMap;
|
||||
use super::{
|
||||
AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult,
|
||||
MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub,
|
||||
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
|
||||
Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub,
|
||||
format_interp_error,
|
||||
};
|
||||
|
||||
@ -557,9 +557,20 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
if let Ok((alloc_id, _offset, _prov)) =
|
||||
self.ecx.ptr_try_get_alloc_id(place.ptr(), 0)
|
||||
{
|
||||
if let Some(GlobalAlloc::Static(did)) =
|
||||
self.ecx.tcx.try_get_global_alloc(alloc_id)
|
||||
{
|
||||
// Everything should be already interned.
|
||||
let Some(global_alloc) = self.ecx.tcx.try_get_global_alloc(alloc_id) else {
|
||||
assert!(self.ecx.memory.alloc_map.get(alloc_id).is_none());
|
||||
// We can't have *any* references to non-existing allocations in const-eval
|
||||
// as the rest of rustc isn't happy with them... so we throw an error, even
|
||||
// though for zero-sized references this isn't really UB.
|
||||
// A potential future alternative would be to resurrect this as a zero-sized allocation
|
||||
// (which codegen will then compile to an aligned dummy pointer anyway).
|
||||
throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind });
|
||||
};
|
||||
let (size, _align) =
|
||||
global_alloc.size_and_align(*self.ecx.tcx, self.ecx.param_env);
|
||||
|
||||
if let GlobalAlloc::Static(did) = global_alloc {
|
||||
let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
|
||||
bug!()
|
||||
};
|
||||
@ -593,17 +604,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
}
|
||||
}
|
||||
|
||||
// Dangling and Mutability check.
|
||||
let (size, _align, alloc_kind) = self.ecx.get_alloc_info(alloc_id);
|
||||
if alloc_kind == AllocKind::Dead {
|
||||
// This can happen for zero-sized references. We can't have *any* references to
|
||||
// non-existing allocations in const-eval though, interning rejects them all as
|
||||
// the rest of rustc isn't happy with them... so we throw an error, even though
|
||||
// this isn't really UB.
|
||||
// A potential future alternative would be to resurrect this as a zero-sized allocation
|
||||
// (which codegen will then compile to an aligned dummy pointer anyway).
|
||||
throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind });
|
||||
}
|
||||
// If this allocation has size zero, there is no actual mutability here.
|
||||
if size != Size::ZERO {
|
||||
// Determine whether this pointer expects to be pointing to something mutable.
|
||||
@ -618,7 +618,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
}
|
||||
};
|
||||
// Determine what it actually points to.
|
||||
let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
|
||||
let alloc_actual_mutbl =
|
||||
global_alloc.mutability(*self.ecx.tcx, self.ecx.param_env);
|
||||
// Mutable pointer to immutable memory is no good.
|
||||
if ptr_expected_mutbl == Mutability::Mut
|
||||
&& alloc_actual_mutbl == Mutability::Not
|
||||
@ -842,9 +843,16 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
}
|
||||
|
||||
fn in_mutable_memory(&self, val: &PlaceTy<'tcx, M::Provenance>) -> bool {
|
||||
debug_assert!(self.ctfe_mode.is_some());
|
||||
if let Some(mplace) = val.as_mplace_or_local().left() {
|
||||
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
|
||||
mutability(self.ecx, alloc_id).is_mut()
|
||||
let tcx = *self.ecx.tcx;
|
||||
// Everything must be already interned.
|
||||
let mutbl = tcx.global_alloc(alloc_id).mutability(tcx, self.ecx.param_env);
|
||||
if let Some((_, alloc)) = self.ecx.memory.alloc_map.get(alloc_id) {
|
||||
assert_eq!(alloc.mutability, mutbl);
|
||||
}
|
||||
mutbl.is_mut()
|
||||
} else {
|
||||
// No memory at all.
|
||||
false
|
||||
@ -1016,53 +1024,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the allocation is mutable, and whether it's actually a static.
|
||||
/// For "root" statics we look at the type to account for interior
|
||||
/// mutability; for nested statics we have no type and directly use the annotated mutability.
|
||||
fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId) -> Mutability {
|
||||
// Let's see what kind of memory this points to.
|
||||
// We're not using `try_global_alloc` since dangling pointers have already been handled.
|
||||
match ecx.tcx.global_alloc(alloc_id) {
|
||||
GlobalAlloc::Static(did) => {
|
||||
let DefKind::Static { safety: _, mutability, nested } = ecx.tcx.def_kind(did) else {
|
||||
bug!()
|
||||
};
|
||||
if nested {
|
||||
assert!(
|
||||
ecx.memory.alloc_map.get(alloc_id).is_none(),
|
||||
"allocations of nested statics are already interned: {alloc_id:?}, {did:?}"
|
||||
);
|
||||
// Nested statics in a `static` are never interior mutable,
|
||||
// so just use the declared mutability.
|
||||
mutability
|
||||
} else {
|
||||
let mutability = match mutability {
|
||||
Mutability::Not
|
||||
if !ecx
|
||||
.tcx
|
||||
.type_of(did)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters")
|
||||
.is_freeze(*ecx.tcx, ty::ParamEnv::reveal_all()) =>
|
||||
{
|
||||
Mutability::Mut
|
||||
}
|
||||
_ => mutability,
|
||||
};
|
||||
if let Some((_, alloc)) = ecx.memory.alloc_map.get(alloc_id) {
|
||||
assert_eq!(alloc.mutability, mutability);
|
||||
}
|
||||
mutability
|
||||
}
|
||||
}
|
||||
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
|
||||
GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => {
|
||||
// These are immutable, we better don't allow mutable pointers here.
|
||||
Mutability::Not
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, 'tcx, M> {
|
||||
type V = PlaceTy<'tcx, M::Provenance>;
|
||||
|
||||
|
@ -12,11 +12,12 @@ use std::io::{Read, Write};
|
||||
use std::num::NonZero;
|
||||
use std::{fmt, io};
|
||||
|
||||
use rustc_abi::{AddressSpace, Endian, HasDataLayout};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_abi::{AddressSpace, Align, Endian, HasDataLayout, Size};
|
||||
use rustc_ast::{LitKind, Mutability};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
@ -45,7 +46,7 @@ pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
|
||||
pub use self::value::Scalar;
|
||||
use crate::mir;
|
||||
use crate::ty::codec::{TyDecoder, TyEncoder};
|
||||
use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||
use crate::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
|
||||
/// Uniquely identifies one of the following:
|
||||
/// - A constant
|
||||
@ -310,6 +311,85 @@ impl<'tcx> GlobalAlloc<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutability(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Mutability {
|
||||
// Let's see what kind of memory we are.
|
||||
match self {
|
||||
GlobalAlloc::Static(did) => {
|
||||
let DefKind::Static { safety: _, mutability, nested } = tcx.def_kind(did) else {
|
||||
bug!()
|
||||
};
|
||||
if nested {
|
||||
// Nested statics in a `static` are never interior mutable,
|
||||
// so just use the declared mutability.
|
||||
if cfg!(debug_assertions) {
|
||||
let alloc = tcx.eval_static_initializer(did).unwrap();
|
||||
assert_eq!(alloc.0.mutability, mutability);
|
||||
}
|
||||
mutability
|
||||
} else {
|
||||
let mutability = match mutability {
|
||||
Mutability::Not
|
||||
if !tcx
|
||||
.type_of(did)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters")
|
||||
.is_freeze(tcx, param_env) =>
|
||||
{
|
||||
Mutability::Mut
|
||||
}
|
||||
_ => mutability,
|
||||
};
|
||||
mutability
|
||||
}
|
||||
}
|
||||
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
|
||||
GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => {
|
||||
// These are immutable.
|
||||
Mutability::Not
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size_and_align(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> (Size, Align) {
|
||||
match self {
|
||||
GlobalAlloc::Static(def_id) => {
|
||||
let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else {
|
||||
bug!("GlobalAlloc::Static is not a static")
|
||||
};
|
||||
|
||||
if nested {
|
||||
// Nested anonymous statics are untyped, so let's get their
|
||||
// size and alignment from the allocation itself. This always
|
||||
// succeeds, as the query is fed at DefId creation time, so no
|
||||
// evaluation actually occurs.
|
||||
let alloc = tcx.eval_static_initializer(def_id).unwrap();
|
||||
(alloc.0.size(), alloc.0.align)
|
||||
} else {
|
||||
// Use size and align of the type for everything else. We need
|
||||
// to do that to
|
||||
// * avoid cycle errors in case of self-referential statics,
|
||||
// * be able to get information on extern statics.
|
||||
let ty = tcx
|
||||
.type_of(def_id)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters");
|
||||
let layout = tcx.layout_of(param_env.and(ty)).unwrap();
|
||||
assert!(layout.is_sized());
|
||||
(layout.size, layout.align.abi)
|
||||
}
|
||||
}
|
||||
GlobalAlloc::Memory(alloc) => {
|
||||
let alloc = alloc.inner();
|
||||
(alloc.size(), alloc.align)
|
||||
}
|
||||
GlobalAlloc::Function { .. } => (Size::ZERO, Align::ONE),
|
||||
GlobalAlloc::VTable(..) => {
|
||||
// No data to be accessed here. But vtables are pointer-aligned.
|
||||
return (Size::ZERO, tcx.data_layout.pointer_align.abi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const CTFE_ALLOC_SALT: usize = 0;
|
||||
|
@ -155,6 +155,7 @@ pub fn feature_warn_issue(
|
||||
}
|
||||
|
||||
/// Adds the diagnostics for a feature to an existing error.
|
||||
/// Must be a language feature!
|
||||
pub fn add_feature_diagnostics<G: EmissionGuarantee>(
|
||||
err: &mut Diag<'_, G>,
|
||||
sess: &Session,
|
||||
|
@ -300,6 +300,7 @@ impl Session {
|
||||
self.opts.test
|
||||
}
|
||||
|
||||
/// `feature` must be a language feature.
|
||||
#[track_caller]
|
||||
pub fn create_feature_err<'a>(&'a self, err: impl Diagnostic<'a>, feature: Symbol) -> Diag<'a> {
|
||||
let mut err = self.dcx().create_err(err);
|
||||
|
@ -3,8 +3,8 @@
|
||||
This option controls whether Rust uses the spec-compliant C ABI when compiling
|
||||
for the `wasm32-unknown-unknown` target.
|
||||
|
||||
This makes it possible to be ABI-compatible with all other spec-compliant Wasm
|
||||
like Rusts `wasm32-wasip1`.
|
||||
This makes it possible to be ABI-compatible with all other spec-compliant Wasm targets
|
||||
like `wasm32-wasip1`.
|
||||
|
||||
This compiler flag is perma-unstable, as it will be enabled by default in the
|
||||
future with no option to fall back to the old non-spec-compliant ABI.
|
||||
|
@ -18,7 +18,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
|
||||
- MSP430
|
||||
- M68k
|
||||
- CSKY
|
||||
- s390x
|
||||
- Arm64EC
|
||||
- SPARC
|
||||
|
||||
@ -52,11 +51,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
|
||||
| M68k | `reg_addr` | `a[0-3]` | `a` |
|
||||
| CSKY | `reg` | `r[0-31]` | `r` |
|
||||
| CSKY | `freg` | `f[0-31]` | `f` |
|
||||
| s390x | `reg` | `r[0-10]`, `r[12-14]` | `r` |
|
||||
| s390x | `reg_addr` | `r[1-10]`, `r[12-14]` | `a` |
|
||||
| s390x | `freg` | `f[0-15]` | `f` |
|
||||
| s390x | `vreg` | `v[0-31]` | Only clobbers |
|
||||
| s390x | `areg` | `a[2-15]` | Only clobbers |
|
||||
| SPARC | `reg` | `r[2-29]` | `r` |
|
||||
| SPARC | `yreg` | `y` | Only clobbers |
|
||||
| Arm64EC | `reg` | `x[0-12]`, `x[15-22]`, `x[25-27]`, `x30` | `r` |
|
||||
@ -96,10 +90,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
|
||||
| M68k | `reg_data` | None | `i8`, `i16`, `i32` |
|
||||
| CSKY | `reg` | None | `i8`, `i16`, `i32` |
|
||||
| CSKY | `freg` | None | `f32`, |
|
||||
| s390x | `reg`, `reg_addr` | None | `i8`, `i16`, `i32`, `i64` |
|
||||
| s390x | `freg` | None | `f32`, `f64` |
|
||||
| s390x | `vreg` | N/A | Only clobbers |
|
||||
| s390x | `areg` | N/A | Only clobbers |
|
||||
| SPARC | `reg` | None | `i8`, `i16`, `i32`, `i64` (SPARC64 only) |
|
||||
| SPARC | `yreg` | N/A | Only clobbers |
|
||||
| Arm64EC | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |
|
||||
@ -159,8 +149,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
|
||||
|
||||
| Architecture | Unsupported register | Reason |
|
||||
| ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| All | `sp`, `r15` (s390x), `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. |
|
||||
| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r11` (s390x), `r30`/`i6` (SPARC), `x29` (Arm64EC) | The frame pointer cannot be used as an input or output. |
|
||||
| All | `sp`, `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. |
|
||||
| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC), `x29` (Arm64EC) | The frame pointer cannot be used as an input or output. |
|
||||
| All | `r19` (Hexagon), `r29` (PowerPC), `r30` (PowerPC), `x19` (Arm64EC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. |
|
||||
| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |
|
||||
| MIPS | `$1` or `$at` | Reserved for assembler. |
|
||||
@ -181,8 +171,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
|
||||
| CSKY | `r15` | This is the link register. |
|
||||
| CSKY | `r[26-30]` | Reserved by its ABI. |
|
||||
| CSKY | `r31` | This is the TLS register. |
|
||||
| s390x | `c[0-15]` | Reserved by the kernel. |
|
||||
| s390x | `a[0-1]` | Reserved for system use. |
|
||||
| SPARC | `r0`/`g0` | This is always zero and cannot be used as inputs or outputs. |
|
||||
| SPARC | `r1`/`g1` | Used internally by LLVM. |
|
||||
| SPARC | `r5`/`g5` | Reserved for system. (SPARC32 only) |
|
||||
@ -206,9 +194,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
|
||||
| PowerPC | `reg` | None | `0` | None |
|
||||
| PowerPC | `reg_nonzero` | None | `3` | None |
|
||||
| PowerPC | `freg` | None | `0` | None |
|
||||
| s390x | `reg` | None | `%r0` | None |
|
||||
| s390x | `reg_addr` | None | `%r1` | None |
|
||||
| s390x | `freg` | None | `%f0` | None |
|
||||
| SPARC | `reg` | None | `%o0` | None |
|
||||
| CSKY | `reg` | None | `r0` | None |
|
||||
| CSKY | `freg` | None | `f0` | None |
|
||||
@ -232,8 +217,6 @@ These flags registers must be restored upon exiting the asm block if the `preser
|
||||
- The status register `r2`.
|
||||
- M68k
|
||||
- The condition code register `ccr`.
|
||||
- s390x
|
||||
- The condition code register `cc`.
|
||||
- SPARC
|
||||
- Integer condition codes (`icc` and `xcc`)
|
||||
- Floating-point condition codes (`fcc[0-3]`)
|
||||
|
@ -134,7 +134,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// entered for addresses that are not the base address, so even zero-sized
|
||||
// allocations will get recognized at their base address -- but all other
|
||||
// allocations will *not* be recognized at their "end" address.
|
||||
let size = ecx.get_alloc_info(alloc_id).0;
|
||||
let size = ecx.get_alloc_info(alloc_id).size;
|
||||
if offset < size.bytes() { Some(alloc_id) } else { None }
|
||||
}
|
||||
}?;
|
||||
@ -157,25 +157,25 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
) -> InterpResult<'tcx, u64> {
|
||||
let ecx = self.eval_context_ref();
|
||||
let mut rng = ecx.machine.rng.borrow_mut();
|
||||
let (size, align, kind) = ecx.get_alloc_info(alloc_id);
|
||||
let info = ecx.get_alloc_info(alloc_id);
|
||||
// This is either called immediately after allocation (and then cached), or when
|
||||
// adjusting `tcx` pointers (which never get freed). So assert that we are looking
|
||||
// at a live allocation. This also ensures that we never re-assign an address to an
|
||||
// allocation that previously had an address, but then was freed and the address
|
||||
// information was removed.
|
||||
assert!(!matches!(kind, AllocKind::Dead));
|
||||
assert!(!matches!(info.kind, AllocKind::Dead));
|
||||
|
||||
// This allocation does not have a base address yet, pick or reuse one.
|
||||
if ecx.machine.native_lib.is_some() {
|
||||
// In native lib mode, we use the "real" address of the bytes for this allocation.
|
||||
// This ensures the interpreted program and native code have the same view of memory.
|
||||
let base_ptr = match kind {
|
||||
let base_ptr = match info.kind {
|
||||
AllocKind::LiveData => {
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
||||
// For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
|
||||
let prepared_bytes = MiriAllocBytes::zeroed(size, align)
|
||||
let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align)
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes")
|
||||
panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes", size = info.size)
|
||||
});
|
||||
let ptr = prepared_bytes.as_ptr();
|
||||
// Store prepared allocation space to be picked up for use later.
|
||||
@ -204,7 +204,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
// We are not in native lib mode, so we control the addresses ourselves.
|
||||
if let Some((reuse_addr, clock)) =
|
||||
global_state.reuse.take_addr(&mut *rng, size, align, memory_kind, ecx.active_thread())
|
||||
global_state.reuse.take_addr(&mut *rng, info.size, info.align, memory_kind, ecx.active_thread())
|
||||
{
|
||||
if let Some(clock) = clock {
|
||||
ecx.acquire_clock(&clock);
|
||||
@ -220,14 +220,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
.next_base_addr
|
||||
.checked_add(slack)
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
let base_addr = align_addr(base_addr, align.bytes());
|
||||
let base_addr = align_addr(base_addr, info.align.bytes());
|
||||
|
||||
// Remember next base address. If this allocation is zero-sized, leave a gap of at
|
||||
// least 1 to avoid two allocations having the same base address. (The logic in
|
||||
// `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers
|
||||
// need to be distinguishable!)
|
||||
global_state.next_base_addr = base_addr
|
||||
.checked_add(max(size.bytes(), 1))
|
||||
.checked_add(max(info.size.bytes(), 1))
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
// Even if `Size` didn't overflow, we might still have filled up the address space.
|
||||
if global_state.next_base_addr > ecx.target_usize_max() {
|
||||
|
@ -363,7 +363,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// If it does exist, then we have the guarantee that the
|
||||
// pointer is readable, and the implicit read access inserted
|
||||
// will never cause UB on the pointer itself.
|
||||
let (_, _, kind) = this.get_alloc_info(*alloc_id);
|
||||
let kind = this.get_alloc_info(*alloc_id).kind;
|
||||
if matches!(kind, AllocKind::LiveData) {
|
||||
let alloc_extra = this.get_alloc_extra(*alloc_id)?; // can still fail for `extern static`
|
||||
let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
|
||||
|
@ -626,7 +626,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
|
||||
return interp_ok(())
|
||||
};
|
||||
|
||||
let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
|
||||
let alloc_kind = this.get_alloc_info(alloc_id).kind;
|
||||
match alloc_kind {
|
||||
AllocKind::LiveData => {
|
||||
// This should have alloc_extra data, but `get_alloc_extra` can still fail
|
||||
@ -1017,7 +1017,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Function pointers and dead objects don't have an alloc_extra so we ignore them.
|
||||
// This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
|
||||
// NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
|
||||
let (_size, _align, kind) = this.get_alloc_info(alloc_id);
|
||||
let kind = this.get_alloc_info(alloc_id).kind;
|
||||
match kind {
|
||||
AllocKind::LiveData => {
|
||||
// This should have alloc_extra data, but `get_alloc_extra` can still fail
|
||||
|
@ -274,7 +274,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
.insert(new_tag, protect);
|
||||
}
|
||||
|
||||
let alloc_kind = this.get_alloc_info(alloc_id).2;
|
||||
let alloc_kind = this.get_alloc_info(alloc_id).kind;
|
||||
if !matches!(alloc_kind, AllocKind::LiveData) {
|
||||
assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
|
||||
// There's not actually any bytes here where accesses could even be tracked.
|
||||
@ -538,7 +538,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Function pointers and dead objects don't have an alloc_extra so we ignore them.
|
||||
// This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
|
||||
// NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
|
||||
let (_size, _align, kind) = this.get_alloc_info(alloc_id);
|
||||
let kind = this.get_alloc_info(alloc_id).kind;
|
||||
match kind {
|
||||
AllocKind::LiveData => {
|
||||
// This should have alloc_extra data, but `get_alloc_extra` can still fail
|
||||
|
@ -1125,10 +1125,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
||||
let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
|
||||
panic!("extern_statics cannot contain wildcards")
|
||||
};
|
||||
let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id);
|
||||
let info = ecx.get_alloc_info(alloc_id);
|
||||
let def_ty = ecx.tcx.type_of(def_id).instantiate_identity();
|
||||
let extern_decl_layout = ecx.tcx.layout_of(ty::ParamEnv::empty().and(def_ty)).unwrap();
|
||||
if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align {
|
||||
if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
|
||||
throw_unsup_format!(
|
||||
"extern static `{link_name}` has been declared as `{krate}::{name}` \
|
||||
with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
|
||||
@ -1138,8 +1138,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
||||
krate = ecx.tcx.crate_name(def_id.krate),
|
||||
decl_size = extern_decl_layout.size.bytes(),
|
||||
decl_align = extern_decl_layout.align.abi.bytes(),
|
||||
shim_size = shim_size.bytes(),
|
||||
shim_align = shim_align.bytes(),
|
||||
shim_size = info.size.bytes(),
|
||||
shim_align = info.align.bytes(),
|
||||
)
|
||||
}
|
||||
interp_ok(ptr)
|
||||
|
@ -300,7 +300,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let id = this.read_scalar(id)?.to_u64()?;
|
||||
let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
|
||||
if let Some(id) = std::num::NonZero::new(id).map(AllocId)
|
||||
&& this.get_alloc_info(id).2 == AllocKind::LiveData
|
||||
&& this.get_alloc_info(id).kind == AllocKind::LiveData
|
||||
{
|
||||
this.print_borrow_state(id, show_unnamed)?;
|
||||
} else {
|
||||
@ -409,7 +409,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
);
|
||||
}
|
||||
if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
|
||||
let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id);
|
||||
let alloc_align = this.get_alloc_info(alloc_id).align;
|
||||
// If the newly promised alignment is bigger than the native alignment of this
|
||||
// allocation, and bigger than the previously promised alignment, then set it.
|
||||
if align > alloc_align
|
||||
|
@ -4,7 +4,7 @@
|
||||
//@[s390x] needs-llvm-components: systemz
|
||||
//@ compile-flags: -Zmerge-functions=disabled
|
||||
|
||||
#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)]
|
||||
#![feature(no_core, lang_items, rustc_attrs, repr_simd)]
|
||||
#![crate_type = "rlib"]
|
||||
#![no_core]
|
||||
#![allow(asm_sub_register, non_camel_case_types)]
|
||||
|
@ -3,7 +3,7 @@
|
||||
//@[s390x] needs-llvm-components: systemz
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)]
|
||||
#![feature(no_core, rustc_attrs, lang_items)]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "sized"]
|
||||
|
@ -1,340 +0,0 @@
|
||||
// Check that we don't suggest enabling a feature for code that's
|
||||
// not accepted even with that feature.
|
||||
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn _if() {
|
||||
if (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if (((let 0 = 1))) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if (let 0 = 1) && true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if true && (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if (let 0 = 1) && (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
|
||||
if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
fn _while() {
|
||||
while (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while (((let 0 = 1))) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while (let 0 = 1) && true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while true && (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while (let 0 = 1) && (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
|
||||
while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
fn _macros() {
|
||||
macro_rules! use_expr {
|
||||
($e:expr) => {
|
||||
if $e {}
|
||||
while $e {}
|
||||
}
|
||||
}
|
||||
use_expr!((let 0 = 1 && 0 == 0));
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
use_expr!((let 0 = 1));
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
fn nested_within_if_expr() {
|
||||
if &let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if !let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
if *let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
if -let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
fn _check_try_binds_tighter() -> Result<(), ()> {
|
||||
if let 0 = 0? {}
|
||||
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
Ok(())
|
||||
}
|
||||
if (let 0 = 0)? {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if true || let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
if (true || let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
if true && (true || let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
if true || (true && let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
let mut x = true;
|
||||
if x = let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if true..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
if ..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
if (let 0 = 0).. {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
// Binds as `(let ... = true)..true &&/|| false`.
|
||||
if let Range { start: _, end: _ } = true..true && false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
if let Range { start: _, end: _ } = true..true || false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: F, end } = F)..(|| true)`.
|
||||
const F: fn() -> bool = || true;
|
||||
if let Range { start: F, end } = F..|| true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: true, end } = t)..(&&false)`.
|
||||
let t = &&true;
|
||||
if let Range { start: true, end } = t..&&false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
|
||||
if let true = let true = true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
fn nested_within_while_expr() {
|
||||
while &let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while !let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
while *let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
while -let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
fn _check_try_binds_tighter() -> Result<(), ()> {
|
||||
while let 0 = 0? {}
|
||||
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
Ok(())
|
||||
}
|
||||
while (let 0 = 0)? {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while true || let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
while (true || let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
while true && (true || let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
while true || (true && let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
let mut x = true;
|
||||
while x = let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while true..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
while ..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
while (let 0 = 0).. {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
// Binds as `(let ... = true)..true &&/|| false`.
|
||||
while let Range { start: _, end: _ } = true..true && false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
while let Range { start: _, end: _ } = true..true || false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: F, end } = F)..(|| true)`.
|
||||
const F: fn() -> bool = || true;
|
||||
while let Range { start: F, end } = F..|| true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: true, end } = t)..(&&false)`.
|
||||
let t = &&true;
|
||||
while let Range { start: true, end } = t..&&false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
|
||||
while let true = let true = true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
fn not_error_because_clarified_intent() {
|
||||
if let Range { start: _, end: _ } = (true..true || false) { }
|
||||
|
||||
if let Range { start: _, end: _ } = (true..true && false) { }
|
||||
|
||||
while let Range { start: _, end: _ } = (true..true || false) { }
|
||||
|
||||
while let Range { start: _, end: _ } = (true..true && false) { }
|
||||
}
|
||||
|
||||
fn outside_if_and_while_expr() {
|
||||
&let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
!let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
*let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
-let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
let _ = let _ = 3;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
fn _check_try_binds_tighter() -> Result<(), ()> {
|
||||
let 0 = 0?;
|
||||
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
Ok(())
|
||||
}
|
||||
(let 0 = 0)?;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
true || let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
(true || let 0 = 0);
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
true && (true || let 0 = 0);
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
let mut x = true;
|
||||
x = let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
true..(let 0 = 0);
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
..(let 0 = 0);
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
(let 0 = 0)..;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
(let Range { start: _, end: _ } = true..true || false);
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
|
||||
(let true = let true = true);
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
|
||||
{
|
||||
#[cfg(FALSE)]
|
||||
let x = true && let y = 1;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
{
|
||||
[1, 2, 3][let _ = ()]
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
// Check function tail position.
|
||||
&let 0 = 0
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
// Let's make sure that `let` inside const generic arguments are considered.
|
||||
fn inside_const_generic_arguments() {
|
||||
struct A<const B: bool>;
|
||||
impl<const B: bool> A<{B}> { const O: u32 = 5; }
|
||||
|
||||
if let A::<{
|
||||
true && let 1 = 1
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}>::O = 5 {}
|
||||
|
||||
while let A::<{
|
||||
true && let 1 = 1
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}>::O = 5 {}
|
||||
|
||||
if A::<{
|
||||
true && let 1 = 1
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}>::O == 5 {}
|
||||
|
||||
// In the cases above we have `ExprKind::Block` to help us out.
|
||||
// Below however, we would not have a block and so an implementation might go
|
||||
// from visiting expressions to types without banning `let` expressions down the tree.
|
||||
// This tests ensures that we are not caught by surprise should the parser
|
||||
// admit non-IDENT expressions in const generic arguments.
|
||||
|
||||
if A::<
|
||||
true && let 1 = 1
|
||||
//~^ ERROR expressions must be enclosed in braces
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
>::O == 5 {}
|
||||
}
|
||||
|
||||
fn with_parenthesis() {
|
||||
let opt = Some(Some(1i32));
|
||||
|
||||
if (let Some(a) = opt && true) {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
if (let Some(a) = opt) && true {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
if (let Some(a) = opt) && (let Some(b) = a) {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
}
|
||||
if (let Some(a) = opt && (let Some(b) = a)) && true {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
}
|
||||
if (let Some(a) = opt && (true)) && true {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
let x = (true && let y = 1);
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
#[cfg(FALSE)]
|
||||
{
|
||||
([1, 2, 3][let _ = ()])
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,990 @@
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:33:9
|
||||
|
|
||||
LL | if (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:33:9
|
||||
|
|
||||
LL | if (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:36:11
|
||||
|
|
||||
LL | if (((let 0 = 1))) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:36:11
|
||||
|
|
||||
LL | if (((let 0 = 1))) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:39:9
|
||||
|
|
||||
LL | if (let 0 = 1) && true {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:39:9
|
||||
|
|
||||
LL | if (let 0 = 1) && true {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:42:17
|
||||
|
|
||||
LL | if true && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:42:17
|
||||
|
|
||||
LL | if true && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:45:9
|
||||
|
|
||||
LL | if (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:45:9
|
||||
|
|
||||
LL | if (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:45:24
|
||||
|
|
||||
LL | if (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:45:24
|
||||
|
|
||||
LL | if (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:49:35
|
||||
|
|
||||
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:49:35
|
||||
|
|
||||
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:49:48
|
||||
|
|
||||
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:49:35
|
||||
|
|
||||
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:49:61
|
||||
|
|
||||
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:49:35
|
||||
|
|
||||
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:59:12
|
||||
|
|
||||
LL | while (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:59:12
|
||||
|
|
||||
LL | while (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:62:14
|
||||
|
|
||||
LL | while (((let 0 = 1))) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:62:14
|
||||
|
|
||||
LL | while (((let 0 = 1))) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:65:12
|
||||
|
|
||||
LL | while (let 0 = 1) && true {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:65:12
|
||||
|
|
||||
LL | while (let 0 = 1) && true {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:68:20
|
||||
|
|
||||
LL | while true && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:68:20
|
||||
|
|
||||
LL | while true && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:71:12
|
||||
|
|
||||
LL | while (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:71:12
|
||||
|
|
||||
LL | while (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:71:27
|
||||
|
|
||||
LL | while (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:71:27
|
||||
|
|
||||
LL | while (let 0 = 1) && (let 0 = 1) {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:75:38
|
||||
|
|
||||
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:75:38
|
||||
|
|
||||
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:75:51
|
||||
|
|
||||
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:75:38
|
||||
|
|
||||
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:75:64
|
||||
|
|
||||
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:75:38
|
||||
|
|
||||
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:99:9
|
||||
|
|
||||
LL | if &let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:102:9
|
||||
|
|
||||
LL | if !let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:104:9
|
||||
|
|
||||
LL | if *let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:106:9
|
||||
|
|
||||
LL | if -let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:114:9
|
||||
|
|
||||
LL | if (let 0 = 0)? {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:117:16
|
||||
|
|
||||
LL | if true || let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `||` operators are not supported in let chain expressions
|
||||
--> $DIR/disallowed-positions.rs:117:13
|
||||
|
|
||||
LL | if true || let 0 = 0 {}
|
||||
| ^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:119:17
|
||||
|
|
||||
LL | if (true || let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:121:25
|
||||
|
|
||||
LL | if true && (true || let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:123:25
|
||||
|
|
||||
LL | if true || (true && let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:127:12
|
||||
|
|
||||
LL | if x = let 0 = 0 {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:130:15
|
||||
|
|
||||
LL | if true..(let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:133:11
|
||||
|
|
||||
LL | if ..(let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:135:9
|
||||
|
|
||||
LL | if (let 0 = 0).. {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:139:8
|
||||
|
|
||||
LL | if let Range { start: _, end: _ } = true..true && false {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:142:8
|
||||
|
|
||||
LL | if let Range { start: _, end: _ } = true..true || false {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:148:8
|
||||
|
|
||||
LL | if let Range { start: F, end } = F..|| true {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:154:8
|
||||
|
|
||||
LL | if let Range { start: true, end } = t..&&false {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:158:19
|
||||
|
|
||||
LL | if let true = let true = true {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:161:15
|
||||
|
|
||||
LL | if return let 0 = 0 {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:164:21
|
||||
|
|
||||
LL | loop { if break let 0 = 0 {} }
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:167:15
|
||||
|
|
||||
LL | if (match let 0 = 0 { _ => { false } }) {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:170:9
|
||||
|
|
||||
LL | if (let 0 = 0, false).1 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:173:9
|
||||
|
|
||||
LL | if (let 0 = 0,) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:177:13
|
||||
|
|
||||
LL | if (let 0 = 0).await {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:181:12
|
||||
|
|
||||
LL | if (|| let 0 = 0) {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:184:9
|
||||
|
|
||||
LL | if (let 0 = 0)() {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:190:12
|
||||
|
|
||||
LL | while &let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:193:12
|
||||
|
|
||||
LL | while !let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:195:12
|
||||
|
|
||||
LL | while *let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:197:12
|
||||
|
|
||||
LL | while -let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:205:12
|
||||
|
|
||||
LL | while (let 0 = 0)? {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:208:19
|
||||
|
|
||||
LL | while true || let 0 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `||` operators are not supported in let chain expressions
|
||||
--> $DIR/disallowed-positions.rs:208:16
|
||||
|
|
||||
LL | while true || let 0 = 0 {}
|
||||
| ^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:210:20
|
||||
|
|
||||
LL | while (true || let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:212:28
|
||||
|
|
||||
LL | while true && (true || let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:214:28
|
||||
|
|
||||
LL | while true || (true && let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:218:15
|
||||
|
|
||||
LL | while x = let 0 = 0 {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:221:18
|
||||
|
|
||||
LL | while true..(let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:224:14
|
||||
|
|
||||
LL | while ..(let 0 = 0) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:226:12
|
||||
|
|
||||
LL | while (let 0 = 0).. {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:230:11
|
||||
|
|
||||
LL | while let Range { start: _, end: _ } = true..true && false {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:233:11
|
||||
|
|
||||
LL | while let Range { start: _, end: _ } = true..true || false {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:239:11
|
||||
|
|
||||
LL | while let Range { start: F, end } = F..|| true {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:245:11
|
||||
|
|
||||
LL | while let Range { start: true, end } = t..&&false {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:249:22
|
||||
|
|
||||
LL | while let true = let true = true {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:252:18
|
||||
|
|
||||
LL | while return let 0 = 0 {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:255:39
|
||||
|
|
||||
LL | 'outer: loop { while break 'outer let 0 = 0 {} }
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:258:18
|
||||
|
|
||||
LL | while (match let 0 = 0 { _ => { false } }) {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:261:12
|
||||
|
|
||||
LL | while (let 0 = 0, false).1 {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:264:12
|
||||
|
|
||||
LL | while (let 0 = 0,) {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:268:16
|
||||
|
|
||||
LL | while (let 0 = 0).await {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:272:15
|
||||
|
|
||||
LL | while (|| let 0 = 0) {}
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:275:12
|
||||
|
|
||||
LL | while (let 0 = 0)() {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:292:6
|
||||
|
|
||||
LL | &let 0 = 0;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:295:6
|
||||
|
|
||||
LL | !let 0 = 0;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:297:6
|
||||
|
|
||||
LL | *let 0 = 0;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:299:6
|
||||
|
|
||||
LL | -let 0 = 0;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:301:13
|
||||
|
|
||||
LL | let _ = let _ = 3;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:309:6
|
||||
|
|
||||
LL | (let 0 = 0)?;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:312:13
|
||||
|
|
||||
LL | true || let 0 = 0;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:314:14
|
||||
|
|
||||
LL | (true || let 0 = 0);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:316:22
|
||||
|
|
||||
LL | true && (true || let 0 = 0);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:320:9
|
||||
|
|
||||
LL | x = let 0 = 0;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:323:12
|
||||
|
|
||||
LL | true..(let 0 = 0);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:325:8
|
||||
|
|
||||
LL | ..(let 0 = 0);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:327:6
|
||||
|
|
||||
LL | (let 0 = 0)..;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:330:6
|
||||
|
|
||||
LL | (let Range { start: _, end: _ } = true..true || false);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:334:6
|
||||
|
|
||||
LL | (let true = let true = true);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:334:17
|
||||
|
|
||||
LL | (let true = let true = true);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:340:25
|
||||
|
|
||||
LL | let x = true && let y = 1;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:346:19
|
||||
|
|
||||
LL | [1, 2, 3][let _ = ()]
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:351:6
|
||||
|
|
||||
LL | &let 0 = 0
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:362:17
|
||||
|
|
||||
LL | true && let 1 = 1
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:367:17
|
||||
|
|
||||
LL | true && let 1 = 1
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:372:17
|
||||
|
|
||||
LL | true && let 1 = 1
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:383:17
|
||||
|
|
||||
LL | true && let 1 = 1
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expressions must be enclosed in braces to be used as const generic arguments
|
||||
--> $DIR/disallowed-positions.rs:383:9
|
||||
|
|
||||
LL | true && let 1 = 1
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: enclose the `const` expression in braces
|
||||
|
|
||||
LL | { true && let 1 = 1 }
|
||||
| + +
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:393:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && true) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:393:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && true) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:397:9
|
||||
|
|
||||
LL | if (let Some(a) = opt) && true {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:397:9
|
||||
|
|
||||
LL | if (let Some(a) = opt) && true {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:400:9
|
||||
|
|
||||
LL | if (let Some(a) = opt) && (let Some(b) = a) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:400:9
|
||||
|
|
||||
LL | if (let Some(a) = opt) && (let Some(b) = a) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:400:32
|
||||
|
|
||||
LL | if (let Some(a) = opt) && (let Some(b) = a) {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:400:32
|
||||
|
|
||||
LL | if (let Some(a) = opt) && (let Some(b) = a) {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:408:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:408:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:408:31
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:408:31
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:412:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:412:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:412:31
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:412:31
|
||||
|
|
||||
LL | if (let Some(a) = opt && (let Some(b) = a)) && true {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:416:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && (true)) && true {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
note: `let`s wrapped in parentheses are not supported in a context with let chains
|
||||
--> $DIR/disallowed-positions.rs:416:9
|
||||
|
|
||||
LL | if (let Some(a) = opt && (true)) && true {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:436:22
|
||||
|
|
||||
LL | let x = (true && let y = 1);
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/disallowed-positions.rs:441:20
|
||||
|
|
||||
LL | ([1, 2, 3][let _ = ()])
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
|
||||
error: aborting due to 105 previous errors
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@ revisions: no_feature feature nothing
|
||||
//@ edition: 2021
|
||||
// Here we test that `lowering` behaves correctly wrt. `let $pats = $expr` expressions.
|
||||
//
|
||||
// We want to make sure that `let` is banned in situations other than:
|
||||
@ -17,7 +19,8 @@
|
||||
//
|
||||
// To that end, we check some positions which is not part of the language above.
|
||||
|
||||
#![feature(let_chains)] // Avoid inflating `.stderr` with overzealous gates in this test.
|
||||
// Avoid inflating `.stderr` with overzealous gates (or test what happens if you disable the gate)
|
||||
#![cfg_attr(not(no_feature), feature(let_chains))]
|
||||
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
|
||||
@ -25,6 +28,7 @@ use std::ops::Range;
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn _if() {
|
||||
if (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
@ -46,8 +50,11 @@ fn _if() {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
//[no_feature]~| ERROR `let` expressions in this position are unstable
|
||||
//[no_feature]~| ERROR `let` expressions in this position are unstable
|
||||
}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn _while() {
|
||||
while (let 0 = 1) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
@ -69,8 +76,11 @@ fn _while() {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
//[no_feature]~| ERROR `let` expressions in this position are unstable
|
||||
//[no_feature]~| ERROR `let` expressions in this position are unstable
|
||||
}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn _macros() {
|
||||
macro_rules! use_expr {
|
||||
($e:expr) => {
|
||||
@ -79,11 +89,12 @@ fn _macros() {
|
||||
}
|
||||
}
|
||||
use_expr!((let 0 = 1 && 0 == 0));
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//[feature,no_feature]~^ ERROR expected expression, found `let` statement
|
||||
use_expr!((let 0 = 1));
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//[feature,no_feature]~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn nested_within_if_expr() {
|
||||
if &let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
@ -97,7 +108,7 @@ fn nested_within_if_expr() {
|
||||
|
||||
fn _check_try_binds_tighter() -> Result<(), ()> {
|
||||
if let 0 = 0? {}
|
||||
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
//[feature,no_feature]~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
Ok(())
|
||||
}
|
||||
if (let 0 = 0)? {}
|
||||
@ -118,7 +129,7 @@ fn nested_within_if_expr() {
|
||||
|
||||
if true..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
if ..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
if (let 0 = 0).. {}
|
||||
@ -127,27 +138,54 @@ fn nested_within_if_expr() {
|
||||
// Binds as `(let ... = true)..true &&/|| false`.
|
||||
if let Range { start: _, end: _ } = true..true && false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
if let Range { start: _, end: _ } = true..true || false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: F, end } = F)..(|| true)`.
|
||||
const F: fn() -> bool = || true;
|
||||
if let Range { start: F, end } = F..|| true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: true, end } = t)..(&&false)`.
|
||||
let t = &&true;
|
||||
if let Range { start: true, end } = t..&&false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
|
||||
if let true = let true = true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if return let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
loop { if break let 0 = 0 {} }
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if (match let 0 = 0 { _ => { false } }) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if (let 0 = 0, false).1 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if (let 0 = 0,) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
async fn foo() {
|
||||
if (let 0 = 0).await {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
if (|| let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
if (let 0 = 0)() {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn nested_within_while_expr() {
|
||||
while &let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
@ -161,7 +199,7 @@ fn nested_within_while_expr() {
|
||||
|
||||
fn _check_try_binds_tighter() -> Result<(), ()> {
|
||||
while let 0 = 0? {}
|
||||
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
//[feature,no_feature]~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
Ok(())
|
||||
}
|
||||
while (let 0 = 0)? {}
|
||||
@ -182,7 +220,7 @@ fn nested_within_while_expr() {
|
||||
|
||||
while true..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
while ..(let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
while (let 0 = 0).. {}
|
||||
@ -191,27 +229,54 @@ fn nested_within_while_expr() {
|
||||
// Binds as `(let ... = true)..true &&/|| false`.
|
||||
while let Range { start: _, end: _ } = true..true && false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
while let Range { start: _, end: _ } = true..true || false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: F, end } = F)..(|| true)`.
|
||||
const F: fn() -> bool = || true;
|
||||
while let Range { start: F, end } = F..|| true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
|
||||
// Binds as `(let Range { start: true, end } = t)..(&&false)`.
|
||||
let t = &&true;
|
||||
while let Range { start: true, end } = t..&&false {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//~| ERROR mismatched types
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
|
||||
while let true = let true = true {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while return let 0 = 0 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
'outer: loop { while break 'outer let 0 = 0 {} }
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while (match let 0 = 0 { _ => { false } }) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while (let 0 = 0, false).1 {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while (let 0 = 0,) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
async fn foo() {
|
||||
while (let 0 = 0).await {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
while (|| let 0 = 0) {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
while (let 0 = 0)() {}
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn not_error_because_clarified_intent() {
|
||||
if let Range { start: _, end: _ } = (true..true || false) { }
|
||||
|
||||
@ -222,6 +287,7 @@ fn not_error_because_clarified_intent() {
|
||||
while let Range { start: _, end: _ } = (true..true && false) { }
|
||||
}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn outside_if_and_while_expr() {
|
||||
&let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
@ -232,10 +298,12 @@ fn outside_if_and_while_expr() {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
-let 0 = 0;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
let _ = let _ = 3;
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
fn _check_try_binds_tighter() -> Result<(), ()> {
|
||||
let 0 = 0?;
|
||||
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
//[feature,no_feature]~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
Ok(())
|
||||
}
|
||||
(let 0 = 0)?;
|
||||
@ -260,8 +328,8 @@ fn outside_if_and_while_expr() {
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
|
||||
(let Range { start: _, end: _ } = true..true || false);
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
//[feature,no_feature]~| ERROR mismatched types
|
||||
|
||||
(let true = let true = true);
|
||||
//~^ ERROR expected expression, found `let` statement
|
||||
@ -285,6 +353,7 @@ fn outside_if_and_while_expr() {
|
||||
}
|
||||
|
||||
// Let's make sure that `let` inside const generic arguments are considered.
|
||||
#[cfg(not(nothing))]
|
||||
fn inside_const_generic_arguments() {
|
||||
struct A<const B: bool>;
|
||||
impl<const B: bool> A<{B}> { const O: u32 = 5; }
|
||||
@ -317,6 +386,7 @@ fn inside_const_generic_arguments() {
|
||||
>::O == 5 {}
|
||||
}
|
||||
|
||||
#[cfg(not(nothing))]
|
||||
fn with_parenthesis() {
|
||||
let opt = Some(Some(1i32));
|
||||
|
||||
@ -332,6 +402,7 @@ fn with_parenthesis() {
|
||||
//~| ERROR expected expression, found `let` statement
|
||||
}
|
||||
if let Some(a) = opt && (true && true) {
|
||||
//[no_feature]~^ ERROR `let` expressions in this position are unstable
|
||||
}
|
||||
|
||||
if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
|
||||
@ -347,14 +418,18 @@ fn with_parenthesis() {
|
||||
}
|
||||
|
||||
if (true && (true)) && let Some(a) = opt {
|
||||
//[no_feature]~^ ERROR `let` expressions in this position are unstable
|
||||
}
|
||||
if (true) && let Some(a) = opt {
|
||||
//[no_feature]~^ ERROR `let` expressions in this position are unstable
|
||||
}
|
||||
if true && let Some(a) = opt {
|
||||
//[no_feature]~^ ERROR `let` expressions in this position are unstable
|
||||
}
|
||||
|
||||
let fun = || true;
|
||||
if let true = (true && fun()) && (true) {
|
||||
//[no_feature]~^ ERROR `let` expressions in this position are unstable
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
|
@ -18,11 +18,9 @@ const fn const_context() {
|
||||
#[cfg(any(stocknc, gatednc))]
|
||||
NonConst.func();
|
||||
//[stocknc]~^ ERROR: cannot call
|
||||
//[stocknc]~| ERROR: cannot call
|
||||
//[gatednc]~^^^ ERROR: the trait bound
|
||||
//[gatednc]~^^ ERROR: the trait bound
|
||||
Const.func();
|
||||
//[stock,stocknc]~^ ERROR: cannot call
|
||||
//[stock,stocknc]~| ERROR: cannot call
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,28 +1,13 @@
|
||||
error[E0015]: cannot call non-const fn `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:23:11
|
||||
error[E0658]: cannot call conditionally-const method `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:22:5
|
||||
|
|
||||
LL | Const.func();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
|
||||
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0015]: cannot call non-const fn `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:23:11
|
||||
|
|
||||
LL | Const.func();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
|
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0015`.
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
@ -1,53 +1,23 @@
|
||||
error[E0015]: cannot call non-const fn `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:19:14
|
||||
error[E0658]: cannot call conditionally-const method `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:19:5
|
||||
|
|
||||
LL | NonConst.func();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
|
||||
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0015]: cannot call non-const fn `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:19:14
|
||||
|
|
||||
LL | NonConst.func();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
|
|
||||
|
||||
error[E0015]: cannot call non-const fn `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:23:11
|
||||
error[E0658]: cannot call conditionally-const method `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:22:5
|
||||
|
|
||||
LL | Const.func();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
|
||||
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0015]: cannot call non-const fn `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
|
||||
--> $DIR/cross-crate.rs:23:11
|
||||
|
|
||||
LL | Const.func();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
|
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0015`.
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
@ -10,8 +10,7 @@ fn non_const_context() {
|
||||
|
||||
const fn stable_const_context() {
|
||||
Unstable::func();
|
||||
//~^ ERROR cannot call non-const fn `<staged_api::Unstable as staged_api::MyTrait>::func` in constant functions
|
||||
//~| ERROR cannot call non-const fn `<staged_api::Unstable as staged_api::MyTrait>::func` in constant functions
|
||||
//~^ ERROR cannot call conditionally-const associated function `<staged_api::Unstable as staged_api::MyTrait>::func` in constant functions
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,28 +1,13 @@
|
||||
error[E0015]: cannot call non-const fn `<staged_api::Unstable as staged_api::MyTrait>::func` in constant functions
|
||||
error[E0658]: cannot call conditionally-const associated function `<staged_api::Unstable as staged_api::MyTrait>::func` in constant functions
|
||||
--> $DIR/staged-api-user-crate.rs:12:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
|
|
||||
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
|
||||
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0015]: cannot call non-const fn `<staged_api::Unstable as staged_api::MyTrait>::func` in constant functions
|
||||
--> $DIR/staged-api-user-crate.rs:12:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
|
||||
LL + #![feature(const_trait_impl)]
|
||||
|
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0015`.
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
@ -1,10 +1,11 @@
|
||||
//@ revisions: stable unstable
|
||||
//! Checks whether we are properly enforcing recursive const stability for trait calls.
|
||||
//@ compile-flags: -Znext-solver
|
||||
|
||||
#![cfg_attr(unstable, feature(unstable))] // The feature from the ./auxiliary/staged-api.rs file.
|
||||
#![cfg_attr(unstable, feature(local_feature))]
|
||||
#![feature(unstable)] // The feature from the ./auxiliary/staged-api.rs file.
|
||||
#![feature(local_feature)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(rustc_allow_const_fn_unstable)]
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
//@ aux-build: staged-api.rs
|
||||
@ -16,13 +17,16 @@ use staged_api::*;
|
||||
pub struct Foo;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(unstable, rustc_const_unstable(feature = "local_feature", issue = "none"))]
|
||||
#[cfg_attr(stable, rustc_const_stable(feature = "local_feature", since = "1.0.0"))]
|
||||
#[rustc_const_unstable(feature = "local_feature", issue = "none")]
|
||||
impl const MyTrait for Foo {
|
||||
//[stable]~^ ERROR trait implementations cannot be const stable yet
|
||||
fn func() {}
|
||||
}
|
||||
|
||||
#[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
const fn conditionally_const<T: ~const MyTrait>() {
|
||||
T::func();
|
||||
}
|
||||
|
||||
// Const stability has no impact on usage in non-const contexts.
|
||||
fn non_const_context() {
|
||||
Unstable::func();
|
||||
@ -32,43 +36,35 @@ fn non_const_context() {
|
||||
#[unstable(feature = "none", issue = "none")]
|
||||
const fn const_context() {
|
||||
Unstable::func();
|
||||
//[unstable]~^ ERROR cannot use `#[feature(unstable)]`
|
||||
//[stable]~^^ ERROR not yet stable as a const fn
|
||||
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
|
||||
Foo::func();
|
||||
//[unstable]~^ ERROR cannot use `#[feature(local_feature)]`
|
||||
//[stable]~^^ cannot be (indirectly) exposed to stable
|
||||
// We get the error on `stable` since this is a trait function.
|
||||
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
|
||||
Unstable2::func();
|
||||
//~^ ERROR not yet stable as a const fn
|
||||
// ^ fails, because the `unstable2` feature is not active
|
||||
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
|
||||
conditionally_const::<Foo>();
|
||||
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(unstable, rustc_const_unstable(feature = "local_feature", issue = "none"))]
|
||||
#[rustc_const_unstable(feature = "local_feature", issue = "none")]
|
||||
pub const fn const_context_not_const_stable() {
|
||||
//[stable]~^ ERROR function has missing const stability attribute
|
||||
Unstable::func();
|
||||
//[stable]~^ ERROR not yet stable as a const fn
|
||||
Foo::func();
|
||||
//[stable]~^ cannot be (indirectly) exposed to stable
|
||||
// We get the error on `stable` since this is a trait function.
|
||||
Unstable2::func();
|
||||
//~^ ERROR not yet stable as a const fn
|
||||
// ^ fails, because the `unstable2` feature is not active
|
||||
conditionally_const::<Foo>();
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "cheese", since = "1.0.0")]
|
||||
const fn stable_const_context() {
|
||||
Unstable::func();
|
||||
//[unstable]~^ ERROR cannot use `#[feature(unstable)]`
|
||||
//[stable]~^^ ERROR not yet stable as a const fn
|
||||
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
|
||||
Foo::func();
|
||||
//[unstable]~^ ERROR cannot use `#[feature(local_feature)]`
|
||||
//[stable]~^^ cannot be (indirectly) exposed to stable
|
||||
// We get the error on `stable` since this is a trait function.
|
||||
const_context_not_const_stable()
|
||||
//[unstable]~^ ERROR cannot use `#[feature(local_feature)]`
|
||||
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
|
||||
const_context_not_const_stable();
|
||||
//~^ ERROR cannot use `#[feature(local_feature)]`
|
||||
conditionally_const::<Foo>();
|
||||
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,89 +0,0 @@
|
||||
error: trait implementations cannot be const stable yet
|
||||
--> $DIR/staged-api.rs:21:1
|
||||
|
|
||||
LL | / impl const MyTrait for Foo {
|
||||
LL | |
|
||||
LL | | fn func() {}
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
||||
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
|
||||
|
||||
error: function has missing const stability attribute
|
||||
--> $DIR/staged-api.rs:48:1
|
||||
|
|
||||
LL | / pub const fn const_context_not_const_stable() {
|
||||
LL | |
|
||||
LL | | Unstable::func();
|
||||
LL | |
|
||||
... |
|
||||
LL | | // ^ fails, because the `unstable2` feature is not active
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: `<staged_api::Unstable as staged_api::MyTrait>::func` is not yet stable as a const fn
|
||||
--> $DIR/staged-api.rs:34:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable)]` to the crate attributes to enable
|
||||
|
||||
error: `<Foo as staged_api::MyTrait>::func` cannot be (indirectly) exposed to stable
|
||||
--> $DIR/staged-api.rs:37:5
|
||||
|
|
||||
LL | Foo::func();
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]`
|
||||
|
||||
error: `<staged_api::Unstable2 as staged_api::MyTrait>::func` is not yet stable as a const fn
|
||||
--> $DIR/staged-api.rs:41:5
|
||||
|
|
||||
LL | Unstable2::func();
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable2)]` to the crate attributes to enable
|
||||
|
||||
error: `<staged_api::Unstable as staged_api::MyTrait>::func` is not yet stable as a const fn
|
||||
--> $DIR/staged-api.rs:50:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable)]` to the crate attributes to enable
|
||||
|
||||
error: `<Foo as staged_api::MyTrait>::func` cannot be (indirectly) exposed to stable
|
||||
--> $DIR/staged-api.rs:52:5
|
||||
|
|
||||
LL | Foo::func();
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]`
|
||||
|
||||
error: `<staged_api::Unstable2 as staged_api::MyTrait>::func` is not yet stable as a const fn
|
||||
--> $DIR/staged-api.rs:55:5
|
||||
|
|
||||
LL | Unstable2::func();
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable2)]` to the crate attributes to enable
|
||||
|
||||
error: `<staged_api::Unstable as staged_api::MyTrait>::func` is not yet stable as a const fn
|
||||
--> $DIR/staged-api.rs:63:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable)]` to the crate attributes to enable
|
||||
|
||||
error: `<Foo as staged_api::MyTrait>::func` cannot be (indirectly) exposed to stable
|
||||
--> $DIR/staged-api.rs:66:5
|
||||
|
|
||||
LL | Foo::func();
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
139
tests/ui/traits/const-traits/staged-api.stderr
Normal file
139
tests/ui/traits/const-traits/staged-api.stderr
Normal file
@ -0,0 +1,139 @@
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
|
||||
--> $DIR/staged-api.rs:38:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
|
||||
--> $DIR/staged-api.rs:40:5
|
||||
|
|
||||
LL | Foo::func();
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
|
||||
--> $DIR/staged-api.rs:42:5
|
||||
|
|
||||
LL | Unstable2::func();
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
|
||||
--> $DIR/staged-api.rs:44:5
|
||||
|
|
||||
LL | conditionally_const::<Foo>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
|
||||
--> $DIR/staged-api.rs:60:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
|
||||
--> $DIR/staged-api.rs:62:5
|
||||
|
|
||||
LL | Foo::func();
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]`
|
||||
--> $DIR/staged-api.rs:64:5
|
||||
|
|
||||
LL | const_context_not_const_stable();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features
|
||||
help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(local_feature)]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
|
||||
--> $DIR/staged-api.rs:66:5
|
||||
|
|
||||
LL | conditionally_const::<Foo>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
@ -1,108 +0,0 @@
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]`
|
||||
--> $DIR/staged-api.rs:34:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features
|
||||
help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(unstable)]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]`
|
||||
--> $DIR/staged-api.rs:37:5
|
||||
|
|
||||
LL | Foo::func();
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features
|
||||
help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(local_feature)]
|
||||
LL | const fn const_context() {
|
||||
|
|
||||
|
||||
error: `<staged_api::Unstable2 as staged_api::MyTrait>::func` is not yet stable as a const fn
|
||||
--> $DIR/staged-api.rs:41:5
|
||||
|
|
||||
LL | Unstable2::func();
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable2)]` to the crate attributes to enable
|
||||
|
||||
error: `<staged_api::Unstable2 as staged_api::MyTrait>::func` is not yet stable as a const fn
|
||||
--> $DIR/staged-api.rs:55:5
|
||||
|
|
||||
LL | Unstable2::func();
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable2)]` to the crate attributes to enable
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]`
|
||||
--> $DIR/staged-api.rs:63:5
|
||||
|
|
||||
LL | Unstable::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features
|
||||
help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(unstable)]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]`
|
||||
--> $DIR/staged-api.rs:66:5
|
||||
|
|
||||
LL | Foo::func();
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features
|
||||
help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(local_feature)]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]`
|
||||
--> $DIR/staged-api.rs:70:5
|
||||
|
|
||||
LL | const_context_not_const_stable()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features
|
||||
help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
|
|
||||
LL + #[rustc_const_unstable(feature = "...", issue = "...")]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
|
||||
LL + #[rustc_allow_const_fn_unstable(local_feature)]
|
||||
LL | const fn stable_const_context() {
|
||||
|
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
@ -432,6 +432,7 @@ new_issue = true
|
||||
exclude_labels = [
|
||||
"C-tracking-issue",
|
||||
"A-diagnostics",
|
||||
"relnotes-tracking-issue",
|
||||
]
|
||||
|
||||
[autolabel."WG-trait-system-refactor"]
|
||||
|
Loading…
Reference in New Issue
Block a user