Auto merge of #3294 - rust-lang:rustup-2024-02-11, r=saethlin

Automatic Rustup
This commit is contained in:
bors 2024-02-11 05:13:02 +00:00
commit a40a100e4b
171 changed files with 2304 additions and 2675 deletions

View file

@ -15,7 +15,7 @@
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(negative_impls)]
#![feature(stmt_expr_attributes)]

View file

@ -8,6 +8,10 @@ ast_lowering_arbitrary_expression_in_pattern =
ast_lowering_argument = argument
ast_lowering_assoc_ty_binding_in_dyn =
associated type bounds are not allowed in `dyn` types
.suggestion = use `impl Trait` to introduce a type instead
ast_lowering_assoc_ty_parentheses =
parenthesized generic arguments cannot be used in associated type constraints
@ -100,9 +104,6 @@ ast_lowering_match_arm_with_no_body =
`match` arm with no body
.suggestion = add a body after the pattern
ast_lowering_misplaced_assoc_ty_binding =
associated type bounds are only allowed in where clauses and function signatures, not in {$position}
ast_lowering_misplaced_double_dot =
`..` patterns are not allowed here
.note = only allowed in tuple, tuple struct, and slice patterns

View file

@ -94,11 +94,12 @@ pub struct MisplacedImplTrait<'a> {
}
#[derive(Diagnostic)]
#[diag(ast_lowering_misplaced_assoc_ty_binding)]
pub struct MisplacedAssocTyBinding<'a> {
#[diag(ast_lowering_assoc_ty_binding_in_dyn)]
pub struct MisplacedAssocTyBinding {
#[primary_span]
pub span: Span,
pub position: DiagnosticArgFromDisplay<'a>,
#[suggestion(code = " = impl", applicability = "maybe-incorrect", style = "verbose")]
pub suggestion: Option<Span>,
}
#[derive(Diagnostic, Clone, Copy)]

View file

@ -197,7 +197,6 @@ trait ResolverAstLoweringExt {
fn get_label_res(&self, id: NodeId) -> Option<NodeId>;
fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
fn remap_extra_lifetime_params(&mut self, from: NodeId, to: NodeId);
}
impl ResolverAstLoweringExt for ResolverAstLowering {
@ -256,11 +255,6 @@ impl ResolverAstLoweringExt for ResolverAstLowering {
fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
self.extra_lifetime_params_map.remove(&id).unwrap_or_default()
}
fn remap_extra_lifetime_params(&mut self, from: NodeId, to: NodeId) {
let lifetimes = self.extra_lifetime_params_map.remove(&from).unwrap_or_default();
self.extra_lifetime_params_map.insert(to, lifetimes);
}
}
/// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree,
@ -1084,88 +1078,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::TypeBindingKind::Equality { term }
}
AssocConstraintKind::Bound { bounds } => {
enum DesugarKind {
ImplTrait,
Error(ImplTraitPosition),
Bound,
}
// Disallow ATB in dyn types
if self.is_in_dyn_type {
let suggestion = match itctx {
ImplTraitContext::ReturnPositionOpaqueTy { .. }
| ImplTraitContext::TypeAliasesOpaqueTy { .. }
| ImplTraitContext::Universal => {
let bound_end_span = constraint
.gen_args
.as_ref()
.map_or(constraint.ident.span, |args| args.span());
if bound_end_span.eq_ctxt(constraint.span) {
Some(self.tcx.sess.source_map().next_point(bound_end_span))
} else {
None
}
}
_ => None,
};
// Piggy-back on the `impl Trait` context to figure out the correct behavior.
let desugar_kind = match itctx {
// in an argument, RPIT, or TAIT, if we are within a dyn type:
//
// fn foo(x: dyn Iterator<Item: Debug>)
//
// then desugar to:
//
// fn foo(x: dyn Iterator<Item = impl Debug>)
//
// This is because dyn traits must have all of their associated types specified.
ImplTraitContext::ReturnPositionOpaqueTy { .. }
| ImplTraitContext::TypeAliasesOpaqueTy { .. }
| ImplTraitContext::Universal
if self.is_in_dyn_type =>
{
DesugarKind::ImplTrait
}
let guar = self.dcx().emit_err(errors::MisplacedAssocTyBinding {
span: constraint.span,
suggestion,
});
let err_ty =
&*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar)));
hir::TypeBindingKind::Equality { term: err_ty.into() }
} else {
// Desugar `AssocTy: Bounds` into a type binding where the
// later desugars into a trait predicate.
let bounds = self.lower_param_bounds(bounds, itctx);
ImplTraitContext::Disallowed(position) if self.is_in_dyn_type => {
DesugarKind::Error(position)
}
// We are in the parameter position, but not within a dyn type:
//
// fn foo(x: impl Iterator<Item: Debug>)
//
// so we leave it as is and this gets expanded in astconv to a bound like
// `<T as Iterator>::Item: Debug` where `T` is the type parameter for the
// `impl Iterator`.
_ => DesugarKind::Bound,
};
match desugar_kind {
DesugarKind::ImplTrait => {
// Desugar `AssocTy: Bounds` into `AssocTy = impl Bounds`. We do this by
// constructing the HIR for `impl bounds...` and then lowering that.
let impl_trait_node_id = self.next_node_id();
// Shift `impl Trait` lifetime captures from the associated type bound's
// node id to the opaque node id, so that the opaque can actually use
// these lifetime bounds.
self.resolver
.remap_extra_lifetime_params(constraint.id, impl_trait_node_id);
self.with_dyn_type_scope(false, |this| {
let node_id = this.next_node_id();
let ty = this.lower_ty(
&Ty {
id: node_id,
kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
span: this.lower_span(constraint.span),
tokens: None,
},
itctx,
);
hir::TypeBindingKind::Equality { term: ty.into() }
})
}
DesugarKind::Bound => {
// Desugar `AssocTy: Bounds` into a type binding where the
// later desugars into a trait predicate.
let bounds = self.lower_param_bounds(bounds, itctx);
hir::TypeBindingKind::Constraint { bounds }
}
DesugarKind::Error(position) => {
let guar = self.dcx().emit_err(errors::MisplacedAssocTyBinding {
span: constraint.span,
position: DiagnosticArgFromDisplay(&position),
});
let err_ty =
&*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar)));
hir::TypeBindingKind::Equality { term: err_ty.into() }
}
hir::TypeBindingKind::Constraint { bounds }
}
}
};

View file

@ -13,7 +13,6 @@
#![feature(hash_raw_entry)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(impl_trait_in_assoc_type)]
#[macro_use]

View file

@ -190,6 +190,8 @@ codegen_ssa_no_module_named =
codegen_ssa_no_natvis_directory = error enumerating natvis directory: {$error}
codegen_ssa_no_saved_object_file = cached cgu {$cgu_name} should have an object file, but doesn't
codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status}
.note = {$output}

View file

@ -913,7 +913,9 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
let object = load_from_incr_comp_dir(
cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)),
module.source.saved_files.get("o").expect("no saved object file in work product"),
module.source.saved_files.get("o").unwrap_or_else(|| {
cgcx.create_dcx().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name })
}),
);
let dwarf_object =
module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {

View file

@ -121,6 +121,12 @@ pub struct NoNatvisDirectory {
pub error: Error,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_no_saved_object_file)]
pub struct NoSavedObjectFile<'a> {
pub cgu_name: &'a str,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_copy_path_buf)]
pub struct CopyPathBuf {

View file

@ -30,7 +30,9 @@ const_eval_closure_non_const =
cannot call non-const closure in {const_eval_const_context}s
const_eval_consider_dereferencing =
consider dereferencing here
const_eval_const_accesses_static = constant accesses static
const_eval_const_accesses_mut_global =
constant accesses mutable global memory
const_eval_const_context = {$kind ->
[const] constant
@ -319,12 +321,6 @@ const_eval_size_overflow =
const_eval_stack_frame_limit_reached =
reached the configured maximum number of stack frames
const_eval_static_access =
{const_eval_const_context}s cannot refer to statics
.help = consider extracting the value of the `static` to a `const`, and referring to that
.teach_note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
.teach_help = To fix this, the value can be extracted to a `const` and then used.
const_eval_thread_local_access =
thread-local statics cannot be accessed at compile-time
@ -415,6 +411,10 @@ const_eval_upcast_mismatch =
## (We'd love to sort this differently to make that more clear but tidy won't let us...)
const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
const_eval_validation_const_ref_to_extern = {$front_matter}: encountered reference to `extern` static in `const`
const_eval_validation_const_ref_to_mutable = {$front_matter}: encountered reference to mutable memory in `const`
const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)

View file

@ -17,7 +17,7 @@ use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopTy
/// The CTFE machine has some custom error kinds.
#[derive(Clone, Debug)]
pub enum ConstEvalErrKind {
ConstAccessesStatic,
ConstAccessesMutGlobal,
ModifiedGlobal,
AssertFailure(AssertKind<ConstInt>),
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
@ -28,7 +28,7 @@ impl MachineStopType for ConstEvalErrKind {
use crate::fluent_generated::*;
use ConstEvalErrKind::*;
match self {
ConstAccessesStatic => const_eval_const_accesses_static,
ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
ModifiedGlobal => const_eval_modified_global,
Panic { .. } => const_eval_panic,
AssertFailure(x) => x.diagnostic_message(),
@ -37,7 +37,7 @@ impl MachineStopType for ConstEvalErrKind {
fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) {
use ConstEvalErrKind::*;
match *self {
ConstAccessesStatic | ModifiedGlobal => {}
ConstAccessesMutGlobal | ModifiedGlobal => {}
AssertFailure(kind) => kind.add_args(adder),
Panic { msg, line, col, file } => {
adder("msg".into(), msg.into_diagnostic_arg());

View file

@ -11,7 +11,7 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Span;
use rustc_target::abi::{self, Abi};
use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter};
use crate::const_eval::CheckAlignment;
use crate::errors;
use crate::errors::ConstEvalError;
@ -90,14 +90,14 @@ pub(crate) fn mk_eval_cx<'mir, 'tcx>(
tcx: TyCtxt<'tcx>,
root_span: Span,
param_env: ty::ParamEnv<'tcx>,
can_access_statics: CanAccessStatics,
can_access_mut_global: CanAccessMutGlobal,
) -> CompileTimeEvalContext<'mir, 'tcx> {
debug!("mk_eval_cx: {:?}", param_env);
InterpCx::new(
tcx,
root_span,
param_env,
CompileTimeInterpreter::new(can_access_statics, CheckAlignment::No),
CompileTimeInterpreter::new(can_access_mut_global, CheckAlignment::No),
)
}
@ -200,7 +200,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
tcx,
tcx.def_span(key.value.instance.def_id()),
key.param_env,
CanAccessStatics::from(is_static),
CanAccessMutGlobal::from(is_static),
);
let mplace = ecx.raw_const_to_mplace(constant).expect(
@ -277,9 +277,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
tcx,
tcx.def_span(def),
key.param_env,
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
// For consts however we want to ensure they behave "as if" they were evaluated at runtime,
// so we have to reject reading mutable global memory.
CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
);
eval_in_interpreter(ecx, cid, is_static)
}
@ -358,7 +360,7 @@ pub fn const_validate_mplace<'mir, 'tcx>(
// Promoteds in statics are consts that re allowed to point to statics.
CtfeValidationMode::Const {
allow_immutable_unsafe_cell: false,
allow_static_ptrs: true,
allow_extern_static_ptrs: true,
}
}
Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static`
@ -366,7 +368,10 @@ pub fn const_validate_mplace<'mir, 'tcx>(
// In normal `const` (not promoted), the outermost allocation is always only copied,
// so having `UnsafeCell` in there is okay despite them being in immutable memory.
let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner;
CtfeValidationMode::Const { allow_immutable_unsafe_cell, allow_static_ptrs: false }
CtfeValidationMode::Const {
allow_immutable_unsafe_cell,
allow_extern_static_ptrs: false,
}
}
};
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;

View file

@ -51,13 +51,10 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
/// The virtual call stack.
pub(super) stack: Vec<Frame<'mir, 'tcx>>,
/// We need to make sure consts never point to anything mutable, even recursively. That is
/// relied on for pattern matching on consts with references.
/// To achieve this, two pieces have to work together:
/// * Interning makes everything outside of statics immutable.
/// * Pointers to allocations inside of statics can never leak outside, to a non-static global.
/// This boolean here controls the second part.
pub(super) can_access_statics: CanAccessStatics,
/// Pattern matching on consts with references would be unsound if those references
/// could point to anything mutable. Therefore, when evaluating consts and when constructing valtrees,
/// we ensure that only immutable global memory can be accessed.
pub(super) can_access_mut_global: CanAccessMutGlobal,
/// Whether to check alignment during evaluation.
pub(super) check_alignment: CheckAlignment,
@ -73,12 +70,12 @@ pub enum CheckAlignment {
}
#[derive(Copy, Clone, PartialEq)]
pub(crate) enum CanAccessStatics {
pub(crate) enum CanAccessMutGlobal {
No,
Yes,
}
impl From<bool> for CanAccessStatics {
impl From<bool> for CanAccessMutGlobal {
fn from(value: bool) -> Self {
if value { Self::Yes } else { Self::No }
}
@ -86,13 +83,13 @@ impl From<bool> for CanAccessStatics {
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(crate) fn new(
can_access_statics: CanAccessStatics,
can_access_mut_global: CanAccessMutGlobal,
check_alignment: CheckAlignment,
) -> Self {
CompileTimeInterpreter {
num_evaluated_steps: 0,
stack: Vec::new(),
can_access_statics,
can_access_mut_global,
check_alignment,
}
}
@ -680,7 +677,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
machine: &Self,
alloc_id: AllocId,
alloc: ConstAllocation<'tcx>,
static_def_id: Option<DefId>,
_static_def_id: Option<DefId>,
is_write: bool,
) -> InterpResult<'tcx> {
let alloc = alloc.inner();
@ -692,22 +689,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
}
} else {
// Read access. These are usually allowed, with some exceptions.
if machine.can_access_statics == CanAccessStatics::Yes {
if machine.can_access_mut_global == CanAccessMutGlobal::Yes {
// Machine configuration allows us read from anything (e.g., `static` initializer).
Ok(())
} else if static_def_id.is_some() {
// Machine configuration does not allow us to read statics
// (e.g., `const` initializer).
// See const_eval::machine::MemoryExtra::can_access_statics for why
// this check is so important: if we could read statics, we could read pointers
// to mutable allocations *inside* statics. These allocations are not themselves
// statics, so pointers to them can get around the check in `validity.rs`.
Err(ConstEvalErrKind::ConstAccessesStatic.into())
} else if alloc.mutability == Mutability::Mut {
// Machine configuration does not allow us to read statics (e.g., `const`
// initializer).
Err(ConstEvalErrKind::ConstAccessesMutGlobal.into())
} else {
// Immutable global, this read is fine.
// But make sure we never accept a read from something mutable, that would be
// unsound. The reason is that as the content of this allocation may be different
// now and at run-time, so if we permit reading now we might return the wrong value.
assert_eq!(alloc.mutability, Mutability::Not);
Ok(())
}

View file

@ -1,12 +1,11 @@
// Not in interpret to make sure we do not use private implementation details
use crate::errors::MaxNumNodesInConstErr;
use crate::interpret::InterpCx;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
use rustc_middle::mir::interpret::InterpErrorInfo;
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
use rustc_middle::ty::{self, Ty};
use crate::interpret::{format_interp_error, InterpCx};
mod error;
mod eval_queries;
@ -18,56 +17,26 @@ pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;
pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value};
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
const VALTREE_MAX_NODES: usize = 100000;
pub(crate) enum ValTreeCreationError {
NodesOverflow,
/// Values of this type, or this particular value, are not supported as valtrees.
NonSupportedType,
Other,
}
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>;
/// Evaluates a constant and turns it into a type-level constant value.
pub(crate) fn eval_to_valtree<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
cid: GlobalId<'tcx>,
) -> EvalToValTreeResult<'tcx> {
let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
// FIXME Need to provide a span to `eval_to_valtree`
let ecx = mk_eval_cx(
tcx,
DUMMY_SP,
param_env,
// It is absolutely crucial for soundness that
// we do not read from static items or other mutable memory.
CanAccessStatics::No,
);
let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
debug!(?place);
let mut num_nodes = 0;
let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
match valtree_result {
Ok(valtree) => Ok(Some(valtree)),
Err(err) => {
let did = cid.instance.def_id();
let global_const_id = cid.display(tcx);
match err {
ValTreeCreationError::NodesOverflow => {
let span = tcx.hir().span_if_local(did);
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
Ok(None)
}
ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None),
}
}
impl From<InterpErrorInfo<'_>> for ValTreeCreationError {
fn from(err: InterpErrorInfo<'_>) -> Self {
ty::tls::with(|tcx| {
bug!(
"Unexpected Undefined Behavior error during valtree construction: {}",
format_interp_error(tcx.dcx(), err),
)
})
}
}
@ -78,7 +47,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
ty: Ty<'tcx>,
) -> Option<mir::DestructuredConstant<'tcx>> {
let param_env = ty::ParamEnv::reveal_all();
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessStatics::No);
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
let op = ecx.const_val_to_op(val, ty, None).ok()?;
// We go to `usize` as we cannot allocate anything bigger anyway.

View file

@ -1,17 +1,20 @@
use rustc_middle::mir;
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Abi, VariantIdx};
use super::eval_queries::{mk_eval_cx, op_to_const};
use super::machine::CompileTimeEvalContext;
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
use crate::const_eval::CanAccessStatics;
use crate::const_eval::CanAccessMutGlobal;
use crate::errors::MaxNumNodesInConstErr;
use crate::interpret::MPlaceTy;
use crate::interpret::{
intern_const_alloc_recursive, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy,
Projectable, Scalar,
};
use rustc_middle::mir;
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Abi, VariantIdx};
#[instrument(skip(ecx), level = "debug")]
fn branches<'tcx>(
@ -70,7 +73,7 @@ fn slice_branches<'tcx>(
}
#[instrument(skip(ecx), level = "debug")]
pub(crate) fn const_to_valtree_inner<'tcx>(
fn const_to_valtree_inner<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
num_nodes: &mut usize,
@ -88,9 +91,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
Ok(ty::ValTree::zst())
}
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
let Ok(val) = ecx.read_immediate(place) else {
return Err(ValTreeCreationError::Other);
};
let val = ecx.read_immediate(place)?;
let val = val.to_scalar();
*num_nodes += 1;
@ -102,19 +103,17 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
// equality at compile-time (see `ptr_guaranteed_cmp`).
// However we allow those that are just integers in disguise.
// First, get the pointer. Remember it might be wide!
let Ok(val) = ecx.read_immediate(place) else {
return Err(ValTreeCreationError::Other);
};
let val = ecx.read_immediate(place)?;
// We could allow wide raw pointers where both sides are integers in the future,
// but for now we reject them.
if matches!(val.layout.abi, Abi::ScalarPair(..)) {
return Err(ValTreeCreationError::Other);
return Err(ValTreeCreationError::NonSupportedType);
}
let val = val.to_scalar();
// We are in the CTFE machine, so ptr-to-int casts will fail.
// This can only be `Ok` if `val` already is an integer.
let Ok(val) = val.try_to_int() else {
return Err(ValTreeCreationError::Other);
return Err(ValTreeCreationError::NonSupportedType);
};
// It's just a ScalarInt!
Ok(ty::ValTree::Leaf(val))
@ -125,11 +124,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType),
ty::Ref(_, _, _) => {
let Ok(derefd_place)= ecx.deref_pointer(place) else {
return Err(ValTreeCreationError::Other);
};
debug!(?derefd_place);
let derefd_place = ecx.deref_pointer(place)?;
const_to_valtree_inner(ecx, &derefd_place, num_nodes)
}
@ -153,9 +148,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
bug!("uninhabited types should have errored and never gotten converted to valtree")
}
let Ok(variant) = ecx.read_discriminant(place) else {
return Err(ValTreeCreationError::Other);
};
let variant = ecx.read_discriminant(place)?;
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
}
@ -221,6 +214,47 @@ fn create_valtree_place<'tcx>(
ecx.allocate_dyn(layout, MemoryKind::Stack, meta).unwrap()
}
/// Evaluates a constant and turns it into a type-level constant value.
pub(crate) fn eval_to_valtree<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
cid: GlobalId<'tcx>,
) -> EvalToValTreeResult<'tcx> {
let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
// FIXME Need to provide a span to `eval_to_valtree`
let ecx = mk_eval_cx(
tcx,
DUMMY_SP,
param_env,
// It is absolutely crucial for soundness that
// we do not read from mutable memory.
CanAccessMutGlobal::No,
);
let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
debug!(?place);
let mut num_nodes = 0;
let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
match valtree_result {
Ok(valtree) => Ok(Some(valtree)),
Err(err) => {
let did = cid.instance.def_id();
let global_const_id = cid.display(tcx);
let span = tcx.hir().span_if_local(did);
match err {
ValTreeCreationError::NodesOverflow => {
let handled =
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
Err(handled.into())
}
ValTreeCreationError::NonSupportedType => Ok(None),
}
}
}
}
/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
/// construction has finished.
// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
@ -253,7 +287,7 @@ pub fn valtree_to_const_value<'tcx>(
}
}
ty::Ref(_, inner_ty, _) => {
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty);
let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap());
op_to_const(&ecx, &imm.into(), /* for diagnostics */ false)
@ -280,7 +314,7 @@ pub fn valtree_to_const_value<'tcx>(
bug!("could not find non-ZST field during in {layout:#?}");
}
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
// Need to create a place for this valtree.
let place = create_valtree_place(&mut ecx, layout, valtree);

View file

@ -56,23 +56,11 @@ pub(crate) struct UnstableInStable {
#[derive(Diagnostic)]
#[diag(const_eval_thread_local_access, code = E0625)]
pub(crate) struct NonConstOpErr {
pub(crate) struct ThreadLocalAccessErr {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_static_access, code = E0013)]
#[help]
pub(crate) struct StaticAccessErr {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
#[note(const_eval_teach_note)]
#[help(const_eval_teach_help)]
pub teach: Option<()>,
}
#[derive(Diagnostic)]
#[diag(const_eval_raw_ptr_to_int)]
#[note]
@ -623,6 +611,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
PartialPointer => const_eval_validation_partial_pointer,
ConstRefToMutable => const_eval_validation_const_ref_to_mutable,
ConstRefToExtern => const_eval_validation_const_ref_to_extern,
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
NullFnPtr => const_eval_validation_null_fn_ptr,
@ -777,6 +767,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
NullPtr { .. }
| PtrToStatic { .. }
| MutableRefInConst
| ConstRefToMutable
| ConstRefToExtern
| MutableRefToImmutable
| NullFnPtr
| NeverVal

View file

@ -4,6 +4,7 @@ use std::{fmt, mem};
use either::{Either, Left, Right};
use hir::CRATE_HIR_ID;
use rustc_errors::DiagCtxt;
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec;
use rustc_middle::mir;
@ -430,6 +431,26 @@ pub(super) fn from_known_layout<'tcx>(
}
}
/// Turn the given error into a human-readable string. Expects the string to be printed, so if
/// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that
/// triggered the error.
///
/// This is NOT the preferred way to render an error; use `report` from `const_eval` instead.
/// However, this is useful when error messages appear in ICEs.
pub fn format_interp_error<'tcx>(dcx: &DiagCtxt, e: InterpErrorInfo<'tcx>) -> String {
let (e, backtrace) = e.into_parts();
backtrace.print_backtrace();
// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
// label and arguments from the InterpError.
#[allow(rustc::untranslatable_diagnostic)]
let mut diag = dcx.struct_allow("");
let msg = e.diagnostic_message();
e.add_args(dcx, &mut diag);
let s = dcx.eagerly_translate_to_string(msg, diag.args());
diag.cancel();
s
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn new(
tcx: TyCtxt<'tcx>,
@ -462,27 +483,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.map_or(CRATE_HIR_ID, |def_id| self.tcx.local_def_id_to_hir_id(def_id))
}
/// Turn the given error into a human-readable string. Expects the string to be printed, so if
/// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that
/// triggered the error.
///
/// This is NOT the preferred way to render an error; use `report` from `const_eval` instead.
/// However, this is useful when error messages appear in ICEs.
pub fn format_error(&self, e: InterpErrorInfo<'tcx>) -> String {
let (e, backtrace) = e.into_parts();
backtrace.print_backtrace();
// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
// label and arguments from the InterpError.
let dcx = self.tcx.dcx();
#[allow(rustc::untranslatable_diagnostic)]
let mut diag = dcx.struct_allow("");
let msg = e.diagnostic_message();
e.add_args(dcx, &mut diag);
let s = dcx.eagerly_translate_to_string(msg, diag.args());
diag.cancel();
s
}
#[inline(always)]
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
M::stack(self)

View file

@ -20,7 +20,7 @@ mod visitor;
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
pub use self::eval_context::{Frame, FrameInfo, InterpCx, StackPopCleanup};
pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup};
pub use self::intern::{
intern_const_alloc_for_constprop, intern_const_alloc_recursive, InternKind,
};

View file

@ -27,8 +27,9 @@ use rustc_target::abi::{
use std::hash::Hash;
use super::{
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor,
format_interp_error, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx,
InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar,
ValueVisitor,
};
// for the validation errors
@ -132,8 +133,7 @@ pub enum CtfeValidationMode {
/// `allow_immutable_unsafe_cell` says whether we allow `UnsafeCell` in immutable memory (which is the
/// case for the top-level allocation of a `const`, where this is fine because the allocation will be
/// copied at each use site).
/// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics).
Const { allow_immutable_unsafe_cell: bool, allow_static_ptrs: bool },
Const { allow_immutable_unsafe_cell: bool, allow_extern_static_ptrs: bool },
}
impl CtfeValidationMode {
@ -146,13 +146,6 @@ impl CtfeValidationMode {
}
}
fn allow_static_ptrs(self) -> bool {
match self {
CtfeValidationMode::Static { .. } => true, // statics can point to statics
CtfeValidationMode::Const { allow_static_ptrs, .. } => allow_static_ptrs,
}
}
fn may_contain_mutable_ref(self) -> bool {
match self {
CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut,
@ -468,53 +461,59 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// Special handling for pointers to statics (irrespective of their type).
assert!(!self.ecx.tcx.is_thread_local_static(did));
assert!(self.ecx.tcx.is_static(did));
if self.ctfe_mode.is_some_and(|c| !c.allow_static_ptrs()) {
// See const_eval::machine::MemoryExtra::can_access_statics for why
// this check is so important.
// This check is reachable when the const just referenced the static,
// but never read it (so we never entered `before_access_global`).
throw_validation_failure!(self.path, PtrToStatic { ptr_kind });
}
let is_mut =
matches!(self.ecx.tcx.def_kind(did), DefKind::Static(Mutability::Mut))
|| !self
.ecx
.tcx
.type_of(did)
.no_bound_vars()
.expect("statics should not have generic parameters")
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all());
// Mutability check.
if ptr_expected_mutbl == Mutability::Mut {
if matches!(
self.ecx.tcx.def_kind(did),
DefKind::Static(Mutability::Not)
) && self
.ecx
.tcx
.type_of(did)
.no_bound_vars()
.expect("statics should not have generic parameters")
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all())
{
if !is_mut {
throw_validation_failure!(self.path, MutableRefToImmutable);
}
}
// We skip recursively checking other statics. These statics must be sound by
// themselves, and the only way to get broken statics here is by using
// unsafe code.
// The reasons we don't check other statics is twofold. For one, in all
// sound cases, the static was already validated on its own, and second, we
// trigger cycle errors if we try to compute the value of the other static
// and that static refers back to us.
// We might miss const-invalid data,
// but things are still sound otherwise (in particular re: consts
// referring to statics).
return Ok(());
match self.ctfe_mode {
Some(CtfeValidationMode::Static { .. }) => {
// We skip recursively checking other statics. These statics must be sound by
// themselves, and the only way to get broken statics here is by using
// unsafe code.
// The reasons we don't check other statics is twofold. For one, in all
// sound cases, the static was already validated on its own, and second, we
// trigger cycle errors if we try to compute the value of the other static
// and that static refers back to us.
// This could miss some UB, but that's fine.
return Ok(());
}
Some(CtfeValidationMode::Const {
allow_extern_static_ptrs, ..
}) => {
// For consts on the other hand we have to recursively check;
// pattern matching assumes a valid value. However we better make
// sure this is not mutable.
if is_mut {
throw_validation_failure!(self.path, ConstRefToMutable);
}
if self.ecx.tcx.is_foreign_item(did) {
if !allow_extern_static_ptrs {
throw_validation_failure!(self.path, ConstRefToExtern);
} else {
// We can't validate this...
return Ok(());
}
}
}
None => {}
}
}
GlobalAlloc::Memory(alloc) => {
if alloc.inner().mutability == Mutability::Mut
&& matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
{
// This is impossible: this can only be some inner allocation of a
// `static mut` (everything else either hits the `GlobalAlloc::Static`
// case or is interned immutably). To get such a pointer we'd have to
// load it from a static, but such loads lead to a CTFE error.
span_bug!(
self.ecx.tcx.span,
"encountered reference to mutable memory inside a `const`"
);
throw_validation_failure!(self.path, ConstRefToMutable);
}
if ptr_expected_mutbl == Mutability::Mut
&& alloc.inner().mutability == Mutability::Not
@ -993,7 +992,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Complain about any other kind of error -- those are bad because we'd like to
// report them in a way that shows *where* in the value the issue lies.
Err(err) => {
bug!("Unexpected error during validation: {}", self.format_error(err));
bug!(
"Unexpected error during validation: {}",
format_interp_error(self.tcx.dcx(), err)
);
}
}
}

View file

@ -449,35 +449,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
Rvalue::Ref(_, BorrowKind::Mut { .. }, place) => {
let ty = place.ty(self.body, self.tcx).ty;
let is_allowed = match ty.kind() {
// Inside a `static mut`, `&mut [...]` is allowed.
ty::Array(..) | ty::Slice(_)
if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) =>
{
true
}
// FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
// that this is merely a ZST and it is already eligible for promotion.
// This may require an RFC?
/*
ty::Array(_, len) if len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(0)
=> true,
*/
_ => false,
};
Rvalue::Ref(_, BorrowKind::Mut { .. }, place)
| Rvalue::AddressOf(Mutability::Mut, place) => {
// Inside mutable statics, we allow arbitrary mutable references.
// We've allowed `static mut FOO = &mut [elements];` for a long time (the exact
// reasons why are lost to history), and there is no reason to restrict that to
// arrays and slices.
let is_allowed =
self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut);
if !is_allowed {
self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
self.check_mut_borrow(
place.local,
if matches!(rvalue, Rvalue::Ref(..)) {
hir::BorrowKind::Ref
} else {
hir::BorrowKind::Raw
},
);
}
}
Rvalue::AddressOf(Mutability::Mut, place) => {
self.check_mut_borrow(place.local, hir::BorrowKind::Raw)
}
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake, place)
| Rvalue::AddressOf(Mutability::Not, place) => {
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(

View file

@ -580,16 +580,21 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
if let hir::ConstContext::Static(_) = ccx.const_kind() {
Status::Allowed
} else {
Status::Forbidden
Status::Unstable(sym::const_refs_to_static)
}
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
ccx.dcx().create_err(errors::StaticAccessErr {
let mut err = feature_err(
&ccx.tcx.sess,
sym::const_refs_to_static,
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0013).then_some(()),
})
format!("referencing statics in {}s is unstable", ccx.const_kind(),),
);
err
.note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.")
.help("to fix this, the value can be extracted to a `const` and then used.");
err
}
}
@ -598,7 +603,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
pub struct ThreadLocalAccess;
impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
ccx.dcx().create_err(errors::NonConstOpErr { span })
ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
}
}

View file

@ -6,7 +6,7 @@ use rustc_middle::ty::layout::LayoutOf;
use rustc_span::symbol::Symbol;
use rustc_type_ir::Mutability;
use crate::const_eval::{mk_eval_cx, CanAccessStatics, CompileTimeEvalContext};
use crate::const_eval::{mk_eval_cx, CanAccessMutGlobal, CompileTimeEvalContext};
use crate::interpret::*;
/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
@ -57,7 +57,7 @@ pub(crate) fn const_caller_location_provider(
col: u32,
) -> mir::ConstValue<'_> {
trace!("const_caller_location: {}:{}:{}", file, line, col);
let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessMutGlobal::No);
let loc_place = alloc_caller_location(&mut ecx, file, line, col);
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {

View file

@ -2,7 +2,7 @@ use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, Val
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
use crate::const_eval::{CanAccessStatics, CheckAlignment, CompileTimeInterpreter};
use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeInterpreter};
use crate::interpret::{InterpCx, MemoryKind, OpTy};
/// Determines if this type permits "raw" initialization by just transmuting some memory into an
@ -44,7 +44,7 @@ fn might_permit_raw_init_strict<'tcx>(
tcx: TyCtxt<'tcx>,
kind: ValidityRequirement,
) -> Result<bool, &'tcx LayoutError<'tcx>> {
let machine = CompileTimeInterpreter::new(CanAccessStatics::No, CheckAlignment::Error);
let machine = CompileTimeInterpreter::new(CanAccessMutGlobal::No, CheckAlignment::Error);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);

View file

@ -1,9 +1,11 @@
#### Note: this error code is no longer emitted by the compiler
Static and const variables can refer to other const variables. But a const
variable cannot refer to a static variable.
Erroneous code example:
```compile_fail,E0013
```compile_fail,E0658
static X: i32 = 42;
const Y: i32 = X;
```

View file

@ -7,6 +7,7 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(array_windows)]
@ -16,7 +17,6 @@
#![feature(error_reporter)]
#![feature(extract_if)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(rustc_attrs)]

View file

@ -409,6 +409,8 @@ declare_features! (
(unstable, const_precise_live_drops, "1.46.0", Some(73255)),
/// Allows references to types with interior mutability within constants
(unstable, const_refs_to_cell, "1.51.0", Some(80384)),
/// Allows creating pointers and references to `static` items in constants.
(unstable, const_refs_to_static, "CURRENT_RUSTC_VERSION", Some(119618)),
/// Allows `impl const Trait for T` syntax.
(unstable, const_trait_impl, "1.42.0", Some(67792)),
/// Allows the `?` operator in const contexts.

View file

@ -5,7 +5,7 @@
#![feature(associated_type_defaults)]
#![feature(closure_track_caller)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(never_type)]
#![feature(rustc_attrs)]
#![feature(variant_count)]

View file

@ -1283,7 +1283,8 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD
let ty = tcx.type_of(def_id).instantiate_identity();
if ty.references_error() {
// If there is already another error, do not emit an error for not using a type parameter.
assert!(tcx.dcx().has_errors().is_some());
// Without the `stashed_err_count` part this can fail (#120856).
assert!(tcx.dcx().has_errors().is_some() || tcx.dcx().stashed_err_count() > 0);
return;
}

View file

@ -10,7 +10,7 @@ use rustc_errors::{codes::*, struct_span_code_err};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use rustc_span::{sym, ErrorGuaranteed};
use rustc_trait_selection::traits;
mod builtin;
@ -70,7 +70,11 @@ fn enforce_trait_manually_implementable(
if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable =
tcx.trait_def(trait_def_id).specialization_kind
{
if !tcx.features().specialization && !tcx.features().min_specialization {
if !tcx.features().specialization
&& !tcx.features().min_specialization
&& !impl_header_span.allows_unstable(sym::specialization)
&& !impl_header_span.allows_unstable(sym::min_specialization)
{
return Err(tcx.dcx().emit_err(errors::SpecializationTrait { span: impl_header_span }));
}
}

View file

@ -67,7 +67,7 @@ This API is completely unstable and subject to change.
#![feature(is_sorted)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(never_type)]
#![feature(lazy_cell)]
#![feature(slice_partition_dedup)]

View file

@ -53,7 +53,7 @@ pub fn check_legal_trait_for_method_call(
};
return Err(tcx.dcx().emit_err(errors::ExplicitDestructorCall { span, sugg }));
}
tcx.coherent_trait(trait_id)
tcx.ensure().coherent_trait(trait_id)
}
#[derive(Debug)]
@ -261,23 +261,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
adjusted_ty: Ty<'tcx>,
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
// HACK(async_closures): For async closures, prefer `AsyncFn*`
// over `Fn*`, since all async closures implement `FnOnce`, but
// choosing that over `AsyncFn`/`AsyncFnMut` would be more restrictive.
// For other callables, just prefer `Fn*` for perf reasons.
//
// The order of trait choices here is not that big of a deal,
// since it just guides inference (and our choice of autoref).
// Though in the future, I'd like typeck to choose:
// `Fn > AsyncFn > FnMut > AsyncFnMut > FnOnce > AsyncFnOnce`
// ...or *ideally*, we just have `LendingFn`/`LendingFnMut`, which
// would naturally unify these two trait hierarchies in the most
// general way.
let call_trait_choices = if self.shallow_resolve(adjusted_ty).is_coroutine_closure() {
[
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
(self.tcx.lang_items().fn_trait(), sym::call, true),
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
]
} else {
[
(self.tcx.lang_items().fn_trait(), sym::call, true),
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
]
};
// Try the options that are least restrictive on the caller first.
for (opt_trait_def_id, method_name, borrow) in [
(self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true),
(self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true),
(self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false),
(self.tcx.lang_items().async_fn_trait(), Ident::with_dummy_span(sym::async_call), true),
(
self.tcx.lang_items().async_fn_mut_trait(),
Ident::with_dummy_span(sym::async_call_mut),
true,
),
(
self.tcx.lang_items().async_fn_once_trait(),
Ident::with_dummy_span(sym::async_call_once),
false,
),
] {
for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
let Some(trait_def_id) = opt_trait_def_id else { continue };
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
@ -294,7 +311,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(ok) = self.lookup_method_in_trait(
self.misc(call_expr.span),
method_name,
Ident::with_dummy_span(method_name),
trait_def_id,
adjusted_ty,
opt_input_type.as_ref().map(slice::from_ref),

View file

@ -56,11 +56,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
let (expected_sig, expected_kind) = match expected.to_option(self) {
Some(ty) => {
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
}
None => (None, None),
let (expected_sig, expected_kind) = match closure.kind {
hir::ClosureKind::Closure => match expected.to_option(self) {
Some(ty) => {
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
}
None => (None, None),
},
// We don't want to deduce a signature from `Fn` bounds for coroutines
// or coroutine-closures, because the former does not implement `Fn`
// ever, and the latter's signature doesn't correspond to the coroutine
// type that it returns.
hir::ClosureKind::Coroutine(_) | hir::ClosureKind::CoroutineClosure(_) => (None, None),
};
let ClosureSignatures { bound_sig, mut liberated_sig } =

View file

@ -5,7 +5,7 @@
#![feature(try_blocks)]
#![feature(never_type)]
#![feature(box_patterns)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(control_flow_enum)]
#[macro_use]

View file

@ -24,7 +24,7 @@
#![feature(let_chains)]
#![feature(if_let_guard)]
#![feature(iterator_try_collect)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(try_blocks)]
#![recursion_limit = "512"] // For rustdoc

View file

@ -177,12 +177,13 @@ pub fn report_object_safety_error<'tcx>(
)));
}
impls => {
let types = impls
let mut types = impls
.iter()
.map(|t| {
with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),))
})
.collect::<Vec<_>>();
types.sort();
err.help(format!(
"the following types implement the trait, consider defining an enum where each \
variant holds one of these types, implementing `{}` for this new enum and using \

View file

@ -35,7 +35,6 @@
#![feature(iter_order_by)]
#![feature(let_chains)]
#![feature(trait_upcasting)]
#![feature(min_specialization)]
#![feature(rustc_attrs)]
#![allow(internal_features)]

View file

@ -1,5 +1,3 @@
#![feature(min_specialization)]
#[macro_use]
extern crate rustc_macros;

View file

@ -417,6 +417,8 @@ pub enum ValidationErrorKind<'tcx> {
PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
PtrToStatic { ptr_kind: PointerKind },
MutableRefInConst,
ConstRefToMutable,
ConstRefToExtern,
MutableRefToImmutable,
UnsafeCellInImmutable,
NullFnPtr,

View file

@ -9,7 +9,7 @@
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(try_blocks)]
#[macro_use]

View file

@ -2,7 +2,7 @@
#![feature(box_patterns)]
#![feature(exact_size_is_empty)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(try_blocks)]
#[macro_use]

View file

@ -1,128 +0,0 @@
//! This pass optimizes the following sequence
//! ```rust,ignore (example)
//! bb2: {
//! _2 = const true;
//! goto -> bb3;
//! }
//!
//! bb3: {
//! switchInt(_2) -> [false: bb4, otherwise: bb5];
//! }
//! ```
//! into
//! ```rust,ignore (example)
//! bb2: {
//! _2 = const true;
//! goto -> bb5;
//! }
//! ```
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_middle::{mir::visit::Visitor, ty::ParamEnv};
use super::simplify::{simplify_cfg, simplify_locals};
pub struct ConstGoto;
impl<'tcx> MirPass<'tcx> for ConstGoto {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
// This pass participates in some as-of-yet untested unsoundness found
// in https://github.com/rust-lang/rust/issues/112460
sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
trace!("Running ConstGoto on {:?}", body.source);
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
let mut opt_finder =
ConstGotoOptimizationFinder { tcx, body, optimizations: vec![], param_env };
opt_finder.visit_body(body);
let should_simplify = !opt_finder.optimizations.is_empty();
for opt in opt_finder.optimizations {
let block = &mut body.basic_blocks_mut()[opt.bb_with_goto];
block.statements.extend(opt.stmts_move_up);
let terminator = block.terminator_mut();
let new_goto = TerminatorKind::Goto { target: opt.target_to_use_in_goto };
debug!("SUCCESS: replacing `{:?}` with `{:?}`", terminator.kind, new_goto);
terminator.kind = new_goto;
}
// if we applied optimizations, we potentially have some cfg to cleanup to
// make it easier for further passes
if should_simplify {
simplify_cfg(body);
simplify_locals(body, tcx);
}
}
}
impl<'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'_, 'tcx> {
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
if data.is_cleanup {
// Because of the restrictions around control flow in cleanup blocks, we don't perform
// this optimization at all in such blocks.
return;
}
self.super_basic_block_data(block, data);
}
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
let _: Option<_> = try {
let target = terminator.kind.as_goto()?;
// We only apply this optimization if the last statement is a const assignment
let last_statement = self.body.basic_blocks[location.block].statements.last()?;
if let (place, Rvalue::Use(Operand::Constant(_const))) =
last_statement.kind.as_assign()?
{
// We found a constant being assigned to `place`.
// Now check that the target of this Goto switches on this place.
let target_bb = &self.body.basic_blocks[target];
// The `StorageDead(..)` statement does not affect the functionality of mir.
// We can move this part of the statement up to the predecessor.
let mut stmts_move_up = Vec::new();
for stmt in &target_bb.statements {
if let StatementKind::StorageDead(..) = stmt.kind {
stmts_move_up.push(stmt.clone())
} else {
None?;
}
}
let target_bb_terminator = target_bb.terminator();
let (discr, targets) = target_bb_terminator.kind.as_switch()?;
if discr.place() == Some(*place) {
let switch_ty = place.ty(self.body.local_decls(), self.tcx).ty;
debug_assert_eq!(switch_ty, _const.ty());
// We now know that the Switch matches on the const place, and it is statementless
// Now find which value in the Switch matches the const value.
let const_value = _const.const_.try_eval_bits(self.tcx, self.param_env)?;
let target_to_use_in_goto = targets.target_for_value(const_value);
self.optimizations.push(OptimizationToApply {
bb_with_goto: location.block,
target_to_use_in_goto,
stmts_move_up,
});
}
}
Some(())
};
self.super_terminator(terminator, location);
}
}
struct OptimizationToApply<'tcx> {
bb_with_goto: BasicBlock,
target_to_use_in_goto: BasicBlock,
stmts_move_up: Vec<Statement<'tcx>>,
}
pub struct ConstGotoOptimizationFinder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
param_env: ParamEnv<'tcx>,
optimizations: Vec<OptimizationToApply<'tcx>>,
}

View file

@ -3,8 +3,9 @@
use std::fmt::Debug;
use rustc_const_eval::interpret::{ImmTy, Projectable};
use rustc_const_eval::interpret::{InterpCx, InterpResult, Scalar};
use rustc_const_eval::interpret::{
format_interp_error, ImmTy, InterpCx, InterpResult, Projectable, Scalar,
};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_hir::HirId;
@ -246,7 +247,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
assert!(
!error.kind().formatted_string(),
"const-prop encountered formatting error: {}",
self.ecx.format_error(error),
format_interp_error(self.ecx.tcx.dcx(), error),
);
None
}

View file

@ -60,7 +60,7 @@ const MAX_PLACES: usize = 100;
impl<'tcx> MirPass<'tcx> for JumpThreading {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
sess.mir_opt_level() >= 4
sess.mir_opt_level() >= 2
}
#[instrument(skip_all level = "debug")]

View file

@ -8,7 +8,7 @@
#![feature(is_sorted)]
#![feature(let_chains)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(never_type)]
#![feature(option_get_or_insert_default)]
#![feature(round_char_boundary)]
@ -59,7 +59,6 @@ mod remove_place_mention;
mod add_subtyping_projections;
pub mod cleanup_post_borrowck;
mod const_debuginfo;
mod const_goto;
mod const_prop;
mod const_prop_lint;
mod copy_prop;
@ -103,7 +102,6 @@ mod remove_unneeded_drops;
mod remove_zsts;
mod required_consts;
mod reveal_all;
mod separate_const_switch;
mod shim;
mod ssa;
// This pass is public to allow external drivers to perform MIR cleanup
@ -590,7 +588,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// Has to run after `slice::len` lowering
&normalize_array_len::NormalizeArrayLen,
&const_goto::ConstGoto,
&ref_prop::ReferencePropagation,
&sroa::ScalarReplacementOfAggregates,
&match_branches::MatchBranchSimplification,
@ -601,10 +598,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&dead_store_elimination::DeadStoreElimination::Initial,
&gvn::GVN,
&simplify::SimplifyLocals::AfterGVN,
// Perform `SeparateConstSwitch` after SSA-based analyses, as cloning blocks may
// destroy the SSA property. It should still happen before const-propagation, so the
// latter pass will leverage the created opportunities.
&separate_const_switch::SeparateConstSwitch,
&dataflow_const_prop::DataflowConstProp,
&const_debuginfo::ConstDebugInfo,
&o1(simplify_branches::SimplifyConstCondition::AfterConstProp),

View file

@ -1,343 +0,0 @@
//! A pass that duplicates switch-terminated blocks
//! into a new copy for each predecessor, provided
//! the predecessor sets the value being switched
//! over to a constant.
//!
//! The purpose of this pass is to help constant
//! propagation passes to simplify the switch terminator
//! of the copied blocks into gotos when some predecessors
//! statically determine the output of switches.
//!
//! ```text
//! x = 12 --- ---> something
//! \ / 12
//! --> switch x
//! / \ otherwise
//! x = y --- ---> something else
//! ```
//! becomes
//! ```text
//! x = 12 ---> switch x ------> something
//! \ / 12
//! X
//! / \ otherwise
//! x = y ---> switch x ------> something else
//! ```
//! so it can hopefully later be turned by another pass into
//! ```text
//! x = 12 --------------------> something
//! / 12
//! /
//! / otherwise
//! x = y ---- switch x ------> something else
//! ```
//!
//! This optimization is meant to cover simple cases
//! like `?` desugaring. For now, it thus focuses on
//! simplicity rather than completeness (it notably
//! sometimes duplicates abusively).
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use smallvec::SmallVec;
pub struct SeparateConstSwitch;
impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
// This pass participates in some as-of-yet untested unsoundness found
// in https://github.com/rust-lang/rust/issues/112460
sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
}
fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// If execution did something, applying a simplification layer
// helps later passes optimize the copy away.
if separate_const_switch(body) > 0 {
super::simplify::simplify_cfg(body);
}
}
}
/// Returns the amount of blocks that were duplicated
pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new();
let predecessors = body.basic_blocks.predecessors();
'block_iter: for (block_id, block) in body.basic_blocks.iter_enumerated() {
if let TerminatorKind::SwitchInt {
discr: Operand::Copy(switch_place) | Operand::Move(switch_place),
..
} = block.terminator().kind
{
// If the block is on an unwind path, do not
// apply the optimization as unwind paths
// rely on a unique parent invariant
if block.is_cleanup {
continue 'block_iter;
}
// If the block has fewer than 2 predecessors, ignore it
// we could maybe chain blocks that have exactly one
// predecessor, but for now we ignore that
if predecessors[block_id].len() < 2 {
continue 'block_iter;
}
// First, let's find a non-const place
// that determines the result of the switch
if let Some(switch_place) = find_determining_place(switch_place, block) {
// We now have an input place for which it would
// be interesting if predecessors assigned it from a const
let mut predecessors_left = predecessors[block_id].len();
'predec_iter: for predecessor_id in predecessors[block_id].iter().copied() {
let predecessor = &body.basic_blocks[predecessor_id];
// First we make sure the predecessor jumps
// in a reasonable way
match &predecessor.terminator().kind {
// The following terminators are
// unconditionally valid
TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } => {}
TerminatorKind::FalseEdge { real_target, .. } => {
if *real_target != block_id {
continue 'predec_iter;
}
}
// The following terminators are not allowed
TerminatorKind::UnwindResume
| TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::CoroutineDrop => {
continue 'predec_iter;
}
}
if is_likely_const(switch_place, predecessor) {
new_blocks.push((predecessor_id, block_id));
predecessors_left -= 1;
if predecessors_left < 2 {
// If the original block only has one predecessor left,
// we have nothing left to do
break 'predec_iter;
}
}
}
}
}
}
// Once the analysis is done, perform the duplication
let body_span = body.span;
let copied_blocks = new_blocks.len();
let blocks = body.basic_blocks_mut();
for (pred_id, target_id) in new_blocks {
let new_block = blocks[target_id].clone();
let new_block_id = blocks.push(new_block);
let terminator = blocks[pred_id].terminator_mut();
match terminator.kind {
TerminatorKind::Goto { ref mut target } => {
*target = new_block_id;
}
TerminatorKind::FalseEdge { ref mut real_target, .. } => {
if *real_target == target_id {
*real_target = new_block_id;
}
}
TerminatorKind::SwitchInt { ref mut targets, .. } => {
targets.all_targets_mut().iter_mut().for_each(|x| {
if *x == target_id {
*x = new_block_id;
}
});
}
TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::CoroutineDrop
| TerminatorKind::Assert { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::Yield { .. } => {
span_bug!(
body_span,
"basic block terminator had unexpected kind {:?}",
&terminator.kind
)
}
}
}
copied_blocks
}
/// This function describes a rough heuristic guessing
/// whether a place is last set with a const within the block.
/// Notably, it will be overly pessimistic in cases that are already
/// not handled by `separate_const_switch`.
fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData<'tcx>) -> bool {
for statement in block.statements.iter().rev() {
match &statement.kind {
StatementKind::Assign(assign) => {
if assign.0 == tracked_place {
match assign.1 {
// These rvalues are definitely constant
Rvalue::Use(Operand::Constant(_))
| Rvalue::Ref(_, _, _)
| Rvalue::AddressOf(_, _)
| Rvalue::Cast(_, Operand::Constant(_), _)
| Rvalue::NullaryOp(_, _)
| Rvalue::ShallowInitBox(_, _)
| Rvalue::UnaryOp(_, Operand::Constant(_)) => return true,
// These rvalues make things ambiguous
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::Len(_)
| Rvalue::BinaryOp(_, _)
| Rvalue::CheckedBinaryOp(_, _)
| Rvalue::Aggregate(_, _) => return false,
// These rvalues move the place to track
Rvalue::Cast(_, Operand::Copy(place) | Operand::Move(place), _)
| Rvalue::Use(Operand::Copy(place) | Operand::Move(place))
| Rvalue::CopyForDeref(place)
| Rvalue::UnaryOp(_, Operand::Copy(place) | Operand::Move(place))
| Rvalue::Discriminant(place) => tracked_place = place,
}
}
}
// If the discriminant is set, it is always set
// as a constant, so the job is done.
// As we are **ignoring projections**, if the place
// we are tracking sees its discriminant be set,
// that means we had to be tracking the discriminant
// specifically (as it is impossible to switch over
// an enum directly, and if we were switching over
// its content, we would have had to at least cast it to
// some variant first)
StatementKind::SetDiscriminant { place, .. } => {
if **place == tracked_place {
return true;
}
}
// These statements have no influence on the place
// we are interested in
StatementKind::FakeRead(_)
| StatementKind::Deinit(_)
| StatementKind::StorageLive(_)
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _)
| StatementKind::PlaceMention(..)
| StatementKind::Coverage(_)
| StatementKind::StorageDead(_)
| StatementKind::Intrinsic(_)
| StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
}
}
// If no good reason for the place to be const is found,
// give up. We could maybe go up predecessors, but in
// most cases giving up now should be sufficient.
false
}
/// Finds a unique place that entirely determines the value
/// of `switch_place`, if it exists. This is only a heuristic.
/// Ideally we would like to track multiple determining places
/// for some edge cases, but one is enough for a lot of situations.
fn find_determining_place<'tcx>(
mut switch_place: Place<'tcx>,
block: &BasicBlockData<'tcx>,
) -> Option<Place<'tcx>> {
for statement in block.statements.iter().rev() {
match &statement.kind {
StatementKind::Assign(op) => {
if op.0 != switch_place {
continue;
}
match op.1 {
// The following rvalues move the place
// that may be const in the predecessor
Rvalue::Use(Operand::Move(new) | Operand::Copy(new))
| Rvalue::UnaryOp(_, Operand::Copy(new) | Operand::Move(new))
| Rvalue::CopyForDeref(new)
| Rvalue::Cast(_, Operand::Move(new) | Operand::Copy(new), _)
| Rvalue::Repeat(Operand::Move(new) | Operand::Copy(new), _)
| Rvalue::Discriminant(new)
=> switch_place = new,
// The following rvalues might still make the block
// be valid but for now we reject them
Rvalue::Len(_)
| Rvalue::Ref(_, _, _)
| Rvalue::BinaryOp(_, _)
| Rvalue::CheckedBinaryOp(_, _)
| Rvalue::Aggregate(_, _)
// The following rvalues definitely mean we cannot
// or should not apply this optimization
| Rvalue::Use(Operand::Constant(_))
| Rvalue::Repeat(Operand::Constant(_), _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::AddressOf(_, _)
| Rvalue::NullaryOp(_, _)
| Rvalue::ShallowInitBox(_, _)
| Rvalue::UnaryOp(_, Operand::Constant(_))
| Rvalue::Cast(_, Operand::Constant(_), _) => return None,
}
}
// These statements have no influence on the place
// we are interested in
StatementKind::FakeRead(_)
| StatementKind::Deinit(_)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _)
| StatementKind::PlaceMention(..)
| StatementKind::Coverage(_)
| StatementKind::Intrinsic(_)
| StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
// If the discriminant is set, it is always set
// as a constant, so the job is already done.
// As we are **ignoring projections**, if the place
// we are tracking sees its discriminant be set,
// that means we had to be tracking the discriminant
// specifically (as it is impossible to switch over
// an enum directly, and if we were switching over
// its content, we would have had to at least cast it to
// some variant first)
StatementKind::SetDiscriminant { place, .. } => {
if **place == switch_place {
return None;
}
}
}
}
Some(switch_place)
}

View file

@ -10,7 +10,6 @@
#![allow(internal_features)]
#![feature(let_chains)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
#![feature(try_blocks)]
#[macro_use]

View file

@ -582,6 +582,7 @@ symbols! {
const_raw_ptr_deref,
const_raw_ptr_to_usize_cast,
const_refs_to_cell,
const_refs_to_static,
const_trait,
const_trait_bound_opt_out,
const_trait_impl,

View file

@ -14,7 +14,7 @@
#![feature(exhaustive_patterns)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(rustc_attrs)]
#![feature(step_trait)]
#![allow(internal_features)]

View file

@ -24,7 +24,7 @@
#![feature(option_take_if)]
#![feature(never_type)]
#![feature(type_alias_impl_trait)]
#![feature(min_specialization)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![recursion_limit = "512"] // For rustdoc
#[macro_use]

View file

@ -323,34 +323,27 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
self_ty: Ty<'tcx>,
goal_kind: ty::ClosureKind,
env_region: ty::Region<'tcx>,
) -> Result<
(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Option<ty::Predicate<'tcx>>),
NoSolution,
> {
) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
{
match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
let sig = args.coroutine_closure_sig().skip_binder();
let mut nested = vec![];
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
Ok((
args.coroutine_closure_sig().map_bound(|sig| {
let coroutine_ty = sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
);
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
}),
None,
))
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
@ -367,42 +360,117 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
Ok((
args.coroutine_closure_sig().map_bound(|sig| {
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
let coroutine_ty = sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
}),
Some(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
),
))
}
nested.push(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
);
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
};
Ok((
args.coroutine_closure_sig().rebind((
sig.tupled_inputs_ty,
sig.return_ty,
coroutine_ty,
)),
nested,
))
}
ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) => Err(NoSolution),
ty::FnDef(..) | ty::FnPtr(..) => {
let bound_sig = self_ty.fn_sig(tcx);
let sig = bound_sig.skip_binder();
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
// return type implements `Future`.
let nested = vec![
bound_sig
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
.to_predicate(tcx),
];
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
Ok((
bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
nested,
))
}
ty::Closure(_, args) => {
let args = args.as_closure();
let bound_sig = args.sig();
let sig = bound_sig.skip_binder();
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
// `Closure`s only implement `AsyncFn*` when their return type
// implements `Future`.
let mut nested = vec![
bound_sig
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
.to_predicate(tcx),
];
// Additionally, we need to check that the closure kind
// is still compatible.
let kind_ty = args.kind_ty();
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
// When we don't know the closure kind (and therefore also the closure's upvars,
// which are computed at the same time), we must delay the computation of the
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
nested.push(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
);
}
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
}
ty::Bool
| ty::Char

View file

@ -3042,7 +3042,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
this = "the implicit `Sized` requirement on this type parameter";
}
if let Some(hir::Node::TraitItem(hir::TraitItem {
ident,
generics,
kind: hir::TraitItemKind::Type(bounds, None),
..
})) = tcx.hir().get_if_local(item_def_id)
@ -3054,7 +3054,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let (span, separator) = if let [.., last] = bounds {
(last.span().shrink_to_hi(), " +")
} else {
(ident.span.shrink_to_hi(), ":")
(generics.span.shrink_to_hi(), ":")
};
err.span_suggestion_verbose(
span,

View file

@ -2993,7 +2993,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
(s, " +")
} else {
(span.shrink_to_hi(), ":")
(param.name.ident().span.shrink_to_hi(), ":")
};
err.span_suggestion_verbose(
span,

View file

@ -2087,7 +2087,9 @@ fn confirm_select_candidate<'cx, 'tcx>(
} else if lang_items.async_iterator_trait() == Some(trait_def_id) {
confirm_async_iterator_candidate(selcx, obligation, data)
} else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
if obligation.predicate.self_ty().is_closure() {
if obligation.predicate.self_ty().is_closure()
|| obligation.predicate.self_ty().is_coroutine_closure()
{
confirm_closure_candidate(selcx, obligation, data)
} else {
confirm_fn_pointer_candidate(selcx, obligation, data)
@ -2410,11 +2412,75 @@ fn confirm_closure_candidate<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
let tcx = selcx.tcx();
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let ty::Closure(_, args) = self_ty.kind() else {
unreachable!("expected closure self type for closure candidate, found {self_ty}")
let closure_sig = match *self_ty.kind() {
ty::Closure(_, args) => args.as_closure().sig(),
// Construct a "normal" `FnOnce` signature for coroutine-closure. This is
// basically duplicated with the `AsyncFnOnce::CallOnce` confirmation, but
// I didn't see a good way to unify those.
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
args.coroutine_closure_sig().map_bound(|sig| {
// If we know the kind and upvars, use that directly.
// Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
// the projection, like the `AsyncFn*` traits do.
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() {
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
ty::ClosureKind::FnOnce,
tcx.lifetimes.re_static,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
let upvars_projection_def_id = tcx
.associated_items(async_fn_kind_trait_def_id)
.filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(),
tcx.lifetimes.re_static.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
};
tcx.mk_fn_sig(
[sig.tupled_inputs_ty],
output_ty,
sig.c_variadic,
sig.unsafety,
sig.abi,
)
})
}
_ => {
unreachable!("expected closure self type for closure candidate, found {self_ty}");
}
};
let closure_sig = args.as_closure().sig();
let Normalized { value: closure_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@ -2470,126 +2536,171 @@ fn confirm_callable_candidate<'cx, 'tcx>(
fn confirm_async_closure_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
mut nested: Vec<PredicateObligation<'tcx>>,
nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let ty::CoroutineClosure(def_id, args) = *self_ty.kind() else {
unreachable!(
"expected coroutine-closure self type for coroutine-closure candidate, found {self_ty}"
)
};
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
let tcx = selcx.tcx();
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let goal_kind =
tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap();
let async_fn_kind_helper_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
nested.push(obligation.with(
tcx,
ty::TraitRef::new(
tcx,
async_fn_kind_helper_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
),
));
let env_region = match goal_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2),
ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
};
let item_name = tcx.item_name(obligation.predicate.def_id);
let upvars_projection_def_id = tcx
.associated_items(async_fn_kind_helper_trait_def_id)
.filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
let poly_cache_entry = match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();
// FIXME(async_closures): Confirmation is kind of a mess here. Ideally,
// we'd short-circuit when we know that the goal_kind >= closure_kind, and not
// register a nested predicate or create a new projection ty here. But I'm too
// lazy to make this more efficient atm, and we can always tweak it later,
// since all this does is make the solver do more work.
//
// The code duplication due to the different length args is kind of weird, too.
//
// See the logic in `structural_traits` in the new solver to understand a bit
// more clearly how this *should* look.
let poly_cache_entry = args.coroutine_closure_sig().map_bound(|sig| {
let (projection_ty, term) = match tcx.item_name(obligation.predicate.def_id) {
sym::CallOnceFuture => {
let tupled_upvars_ty = Ty::new_projection(
let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
bug!("we should not be confirming if the closure kind is not met");
}
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
let upvars_projection_def_id = tcx
.associated_items(async_fn_kind_trait_def_id)
.filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
// When we don't know the closure kind (and therefore also the closure's upvars,
// which are computed at the same time), we must delay the computation of the
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
// N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
}
}
sym::Output => sig.return_ty,
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
tcx,
upvars_projection_def_id,
obligation.predicate.def_id,
[self_ty, sig.tupled_inputs_ty],
),
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
),
name => bug!("no such associated type: {name}"),
};
args.coroutine_closure_sig()
.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
ty::FnDef(..) | ty::FnPtr(..) => {
let bound_sig = self_ty.fn_sig(tcx);
let sig = bound_sig.skip_binder();
let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
sym::Output => {
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
}
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[self_ty, Ty::new_tup(tcx, sig.inputs())],
),
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
ty::GenericArg::from(self_ty),
Ty::new_tup(tcx, sig.inputs()).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
let coroutine_ty = sig.to_coroutine(
),
name => bug!("no such associated type: {name}"),
};
bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
ty::Closure(_, args) => {
let args = args.as_closure();
let bound_sig = args.sig();
let sig = bound_sig.skip_binder();
let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
sym::Output => {
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
}
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => {
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]])
}
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(
ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[self_ty, sig.tupled_inputs_ty],
),
coroutine_ty.into(),
)
}
sym::CallMutFuture | sym::CallFuture => {
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
let coroutine_ty = sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(
ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[
ty::GenericArg::from(self_ty),
sig.tupled_inputs_ty.into(),
env_region.into(),
],
),
coroutine_ty.into(),
)
}
sym::Output => (
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty]),
sig.return_ty.into(),
),
name => bug!("no such associated type: {name}"),
};
ty::ProjectionPredicate { projection_ty, term }
});
obligation.predicate.def_id,
[ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()],
),
name => bug!("no such associated type: {name}"),
};
bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
_ => bug!("expected callable type for AsyncFn candidate"),
};
confirm_param_env_candidate(selcx, obligation, poly_cache_entry, true)
.with_addl_obligations(nested)

View file

@ -374,6 +374,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
}
ty::CoroutineClosure(def_id, args) => {
let is_const = self.tcx().is_const_fn_raw(def_id);
match self.infcx.closure_kind(self_ty) {
Some(closure_kind) => {
let no_borrows = match self
.infcx
.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
.kind()
{
ty::Tuple(tys) => tys.is_empty(),
ty::Error(_) => false,
_ => bug!("tuple_fields called on non-tuple"),
};
// A coroutine-closure implements `FnOnce` *always*, since it may
// always be called once. It additionally implements `Fn`/`FnMut`
// only if it has no upvars (therefore no borrows from the closure
// that would need to be represented with a lifetime) and if the
// closure kind permits it.
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
// if it takes all of its upvars by copy, and none by ref. This would
// require us to record a bit more information during upvar analysis.
if no_borrows && closure_kind.extends(kind) {
candidates.vec.push(ClosureCandidate { is_const });
} else if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });
}
}
None => {
if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });
} else {
// This stays ambiguous until kind+upvars are determined.
candidates.ambiguous = true;
}
}
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_unboxed_closure_candidates: ambiguous self-type");
candidates.ambiguous = true;
@ -403,8 +440,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
candidates.vec.push(AsyncClosureCandidate);
}
ty::Infer(ty::TyVar(_)) => {
candidates.ambiguous = true;
// Closures and fn pointers implement `AsyncFn*` if their return types
// implement `Future`, which is checked later.
ty::Closure(_, args) => {
if let Some(closure_kind) = args.as_closure().kind_ty().to_opt_closure_kind()
&& !closure_kind.extends(goal_kind)
{
return;
}
candidates.vec.push(AsyncClosureCandidate);
}
ty::FnDef(..) | ty::FnPtr(..) => {
candidates.vec.push(AsyncClosureCandidate);
}
_ => {}
}

View file

@ -872,17 +872,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let ty::Closure(closure_def_id, args) = *self_ty.kind() else {
bug!("closure candidate for non-closure {:?}", obligation);
let trait_ref = match *self_ty.kind() {
ty::Closure(_, args) => {
self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_)
}
ty::CoroutineClosure(_, args) => {
args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.tupled_inputs_ty],
)
})
}
_ => {
bug!("closure candidate for non-closure {:?}", obligation);
}
};
let trait_ref =
self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_);
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
Ok(nested)
self.confirm_poly_trait_refs(obligation, trait_ref)
}
#[instrument(skip(self), level = "debug")]
@ -890,40 +898,86 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the args on closure types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let tcx = self.tcx();
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let ty::CoroutineClosure(closure_def_id, args) = *self_ty.kind() else {
bug!("async closure candidate for non-coroutine-closure {:?}", obligation);
let mut nested = vec![];
let (trait_ref, kind_ty) = match *self_ty.kind() {
ty::CoroutineClosure(_, args) => {
let args = args.as_coroutine_closure();
let trait_ref = args.coroutine_closure_sig().map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.tupled_inputs_ty],
)
});
(trait_ref, args.kind_ty())
}
ty::FnDef(..) | ty::FnPtr(..) => {
let sig = self_ty.fn_sig(tcx);
let trait_ref = sig.map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, Ty::new_tup(tcx, sig.inputs())],
)
});
// We must additionally check that the return type impls `Future`.
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
nested.push(obligation.with(
tcx,
sig.map_bound(|sig| {
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
}),
));
(trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
}
ty::Closure(_, args) => {
let sig = args.as_closure().sig();
let trait_ref = sig.map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.inputs()[0]],
)
});
// We must additionally check that the return type impls `Future`.
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
nested.push(obligation.with(
tcx,
sig.map_bound(|sig| {
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
}),
));
(trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
}
_ => bug!("expected callable type for AsyncFn candidate"),
};
let trait_ref = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.tupled_inputs_ty],
)
});
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?);
let goal_kind =
self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
nested.push(obligation.with(
self.tcx(),
ty::TraitRef::from_lang_item(
self.tcx(),
LangItem::AsyncFnKindHelper,
obligation.cause.span,
[
args.as_coroutine_closure().kind_ty(),
Ty::from_closure_kind(self.tcx(), goal_kind),
],
),
));
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
// If we have not yet determiend the `ClosureKind` of the closure or coroutine-closure,
// then additionally register an `AsyncFnKindHelper` goal which will fail if the kind
// is constrained to an insufficient type later on.
if let Some(closure_kind) = self.infcx.shallow_resolve(kind_ty).to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(SelectionError::Unimplemented);
}
} else {
nested.push(obligation.with(
self.tcx(),
ty::TraitRef::from_lang_item(
self.tcx(),
LangItem::AsyncFnKindHelper,
obligation.cause.span,
[kind_ty, Ty::from_closure_kind(self.tcx(), goal_kind)],
),
));
}
Ok(nested)
}

View file

@ -446,7 +446,7 @@ fn report_conflicting_impls<'tcx>(
match used_to_be_allowed {
None => {
let reported = if overlap.with_impl.is_local()
|| tcx.orphan_check_impl(impl_def_id).is_ok()
|| tcx.ensure().orphan_check_impl(impl_def_id).is_ok()
{
let mut err = tcx.dcx().struct_span_err(impl_span, msg);
err.code(E0119);

View file

@ -278,6 +278,24 @@ fn resolve_associated_item<'tcx>(
def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
args: rcvr_args,
}),
ty::CoroutineClosure(coroutine_closure_def_id, args) => {
// When a coroutine-closure implements the `Fn` traits, then it
// always dispatches to the `FnOnce` implementation. This is to
// ensure that the `closure_kind` of the resulting closure is in
// sync with the built-in trait implementations (since all of the
// implementations return `FnOnce::Output`).
if ty::ClosureKind::FnOnce == args.as_coroutine_closure().kind() {
Some(Instance::new(coroutine_closure_def_id, args))
} else {
Some(Instance {
def: ty::InstanceDef::ConstructCoroutineInClosureShim {
coroutine_closure_def_id,
target_kind: ty::ClosureKind::FnOnce,
},
args,
})
}
}
_ => bug!(
"no built-in definition for `{trait_ref}::{}` for non-fn type",
tcx.item_name(trait_item_id)
@ -306,6 +324,19 @@ fn resolve_associated_item<'tcx>(
Some(Instance::new(coroutine_closure_def_id, args))
}
}
ty::Closure(closure_def_id, args) => {
let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
Some(Instance::resolve_closure(
tcx,
closure_def_id,
args,
trait_closure_kind,
))
}
ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
args: rcvr_args,
}),
_ => bug!(
"no built-in definition for `{trait_ref}::{}` for non-lending-closure type",
tcx.item_name(trait_item_id)

View file

@ -159,6 +159,7 @@ use core::iter::FusedIterator;
use core::marker::Tuple;
use core::marker::Unsize;
use core::mem::{self, SizedTypeProperties};
use core::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce};
use core::ops::{
CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DispatchFromDyn, Receiver,
};
@ -2030,6 +2031,34 @@ impl<Args: Tuple, F: Fn<Args> + ?Sized, A: Allocator> Fn<Args> for Box<F, A> {
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<Args: Tuple, F: AsyncFnOnce<Args> + ?Sized, A: Allocator> AsyncFnOnce<Args> for Box<F, A> {
type Output = F::Output;
type CallOnceFuture = F::CallOnceFuture;
extern "rust-call" fn async_call_once(self, args: Args) -> Self::CallOnceFuture {
F::async_call_once(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<Args: Tuple, F: AsyncFnMut<Args> + ?Sized, A: Allocator> AsyncFnMut<Args> for Box<F, A> {
type CallMutFuture<'a> = F::CallMutFuture<'a> where Self: 'a;
extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallMutFuture<'_> {
F::async_call_mut(self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<Args: Tuple, F: AsyncFn<Args> + ?Sized, A: Allocator> AsyncFn<Args> for Box<F, A> {
type CallFuture<'a> = F::CallFuture<'a> where Self: 'a;
extern "rust-call" fn async_call(&self, args: Args) -> Self::CallFuture<'_> {
F::async_call(self, args)
}
}
#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}

View file

@ -106,6 +106,7 @@
#![feature(array_windows)]
#![feature(ascii_char)]
#![feature(assert_matches)]
#![feature(async_fn_traits)]
#![feature(async_iterator)]
#![feature(coerce_unsized)]
#![feature(const_align_of_val)]

View file

@ -65,44 +65,67 @@ pub trait AsyncFnOnce<Args: Tuple> {
mod impls {
use super::{AsyncFn, AsyncFnMut, AsyncFnOnce};
use crate::future::Future;
use crate::marker::Tuple;
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<F: Fn<A>, A: Tuple> AsyncFn<A> for F
impl<A: Tuple, F: ?Sized> AsyncFn<A> for &F
where
<F as FnOnce<A>>::Output: Future,
F: AsyncFn<A>,
{
type CallFuture<'a> = <F as FnOnce<A>>::Output where Self: 'a;
type CallFuture<'a> = F::CallFuture<'a> where Self: 'a;
extern "rust-call" fn async_call(&self, args: A) -> Self::CallFuture<'_> {
self.call(args)
F::async_call(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<F: FnMut<A>, A: Tuple> AsyncFnMut<A> for F
impl<A: Tuple, F: ?Sized> AsyncFnMut<A> for &F
where
<F as FnOnce<A>>::Output: Future,
F: AsyncFn<A>,
{
type CallMutFuture<'a> = <F as FnOnce<A>>::Output where Self: 'a;
type CallMutFuture<'a> = F::CallFuture<'a> where Self: 'a;
extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> {
self.call_mut(args)
F::async_call(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<F: FnOnce<A>, A: Tuple> AsyncFnOnce<A> for F
impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce<A> for &'a F
where
<F as FnOnce<A>>::Output: Future,
F: AsyncFn<A>,
{
type CallOnceFuture = <F as FnOnce<A>>::Output;
type Output = <<F as FnOnce<A>>::Output as Future>::Output;
type Output = F::Output;
type CallOnceFuture = F::CallFuture<'a>;
extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture {
self.call_once(args)
F::async_call(self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<A: Tuple, F: ?Sized> AsyncFnMut<A> for &mut F
where
F: AsyncFnMut<A>,
{
type CallMutFuture<'a> = F::CallMutFuture<'a> where Self: 'a;
extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> {
F::async_call_mut(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce<A> for &'a mut F
where
F: AsyncFnMut<A>,
{
type Output = F::Output;
type CallOnceFuture = F::CallMutFuture<'a>;
extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture {
F::async_call_mut(self, args)
}
}
}

View file

@ -2119,7 +2119,16 @@ macro_rules! atomic_int {
/// This type has the same in-memory representation as the underlying
/// integer type, [`
#[doc = $s_int_type]
/// `]. For more about the differences between atomic types and
/// `].
#[doc = if_not_8_bit! {
$int_type,
concat!(
"However, the alignment of this type is always equal to its ",
"size, even on targets where [`", $s_int_type, "`] has a ",
"lesser alignment."
)
}]
/// For more about the differences between atomic types and
/// non-atomic types as well as information about the portability of
/// this type, please see the [module-level documentation].
///

View file

@ -12,7 +12,8 @@ fn main() {
return;
}
let target = env::var("TARGET").expect("TARGET was not set");
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set");
let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set");
let cfg = &mut cc::Build::new();
// FIXME: `rerun-if-changed` directives are not currently emitted and the build script
@ -40,7 +41,7 @@ fn main() {
"InstrProfilingBiasVar.c",
];
if target.contains("msvc") {
if target_env == "msvc" {
// Don't pull in extra libraries on MSVC
cfg.flag("/Zl");
profile_sources.push("WindowsMMap.c");
@ -55,7 +56,7 @@ fn main() {
cfg.flag("-fno-builtin");
cfg.flag("-fomit-frame-pointer");
cfg.define("VISIBILITY_HIDDEN", None);
if !target.contains("windows") {
if target_os != "windows" {
cfg.flag("-fvisibility=hidden");
cfg.define("COMPILER_RT_HAS_UNAME", Some("1"));
} else {

View file

@ -2,41 +2,47 @@ use std::env;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let target = env::var("TARGET").expect("TARGET was not set");
if target.contains("linux")
|| target.contains("netbsd")
|| target.contains("dragonfly")
|| target.contains("openbsd")
|| target.contains("freebsd")
|| target.contains("solaris")
|| target.contains("illumos")
|| target.contains("apple-darwin")
|| target.contains("apple-ios")
|| target.contains("apple-tvos")
|| target.contains("apple-watchos")
|| target.contains("uwp")
|| target.contains("windows")
|| target.contains("fuchsia")
|| (target.contains("sgx") && target.contains("fortanix"))
|| target.contains("hermit")
|| target.contains("l4re")
|| target.contains("redox")
|| target.contains("haiku")
|| target.contains("vxworks")
|| target.contains("wasm32")
|| target.contains("wasm64")
|| target.contains("espidf")
|| target.contains("solid")
|| target.contains("nintendo-3ds")
|| target.contains("vita")
|| target.contains("aix")
|| target.contains("nto")
|| target.contains("xous")
|| target.contains("hurd")
|| target.contains("uefi")
|| target.contains("teeos")
|| target.contains("zkvm")
// See src/bootstrap/synthetic_targets.rs
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH was not set");
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set");
let target_vendor =
env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set");
let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set");
if target_os == "linux"
|| target_os == "android"
|| target_os == "netbsd"
|| target_os == "dragonfly"
|| target_os == "openbsd"
|| target_os == "freebsd"
|| target_os == "solaris"
|| target_os == "illumos"
|| target_os == "macos"
|| target_os == "ios"
|| target_os == "tvos"
|| target_os == "watchos"
|| target_os == "windows"
|| target_os == "fuchsia"
|| (target_vendor == "fortanix" && target_env == "sgx")
|| target_os == "hermit"
|| target_os == "l4re"
|| target_os == "redox"
|| target_os == "haiku"
|| target_os == "vxworks"
|| target_arch == "wasm32"
|| target_arch == "wasm64"
|| target_os == "espidf"
|| target_os.starts_with("solid")
|| (target_vendor == "nintendo" && target_env == "newlib")
|| target_os == "vita"
|| target_os == "aix"
|| target_os == "nto"
|| target_os == "xous"
|| target_os == "hurd"
|| target_os == "uefi"
|| target_os == "teeos"
|| target_os == "zkvm"
// See src/bootstrap/src/core/build_steps/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
{
// These platforms don't have any special requirements.

View file

@ -98,7 +98,7 @@ pub fn prebuilt_llvm_config(
let out_dir = builder.llvm_out(target);
let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build);
if !builder.config.build.is_msvc() || builder.ninja() {
if (!builder.config.build.is_msvc() || builder.ninja()) && !builder.config.llvm_from_ci {
llvm_config_ret_dir.push("build");
}
llvm_config_ret_dir.push("bin");

View file

@ -524,6 +524,23 @@ mod dist {
);
}
#[test]
fn llvm_out_behaviour() {
let mut config = configure(&["A"], &["B"]);
config.llvm_from_ci = true;
let build = Build::new(config.clone());
let target = TargetSelection::from_user("A");
assert!(build.llvm_out(target).ends_with("ci-llvm"));
let target = TargetSelection::from_user("B");
assert!(build.llvm_out(target).ends_with("llvm"));
config.llvm_from_ci = false;
let build = Build::new(config.clone());
let target = TargetSelection::from_user("A");
assert!(build.llvm_out(target).ends_with("llvm"));
}
#[test]
fn build_with_empty_host() {
let config = configure(&[], &["C"]);

View file

@ -792,12 +792,16 @@ impl Build {
self.stage_out(compiler, mode).join(&*target.triple).join(self.cargo_dir())
}
/// Root output directory for LLVM compiled for `target`
/// Root output directory of LLVM for `target`
///
/// Note that if LLVM is configured externally then the directory returned
/// will likely be empty.
fn llvm_out(&self, target: TargetSelection) -> PathBuf {
self.out.join(&*target.triple).join("llvm")
if self.config.llvm_from_ci && self.config.build == target {
self.config.ci_llvm_root()
} else {
self.out.join(&*target.triple).join("llvm")
}
}
fn lld_out(&self, target: TargetSelection) -> PathBuf {

View file

@ -1 +1 @@
b5c46dc5426038a49c95398bce30eeb20ec421e2
0cbef48150e1fab161b5fd147b57ceb3f9272a52

View file

@ -285,7 +285,10 @@ pub fn report_error<'tcx, 'mir>(
) =>
{
ecx.handle_ice(); // print interpreter backtrace
bug!("This validation error should be impossible in Miri: {}", ecx.format_error(e));
bug!(
"This validation error should be impossible in Miri: {}",
format_interp_error(ecx.tcx.dcx(), e)
);
}
UndefinedBehavior(_) => "Undefined Behavior",
ResourceExhaustion(_) => "resource exhaustion",
@ -299,7 +302,10 @@ pub fn report_error<'tcx, 'mir>(
) => "post-monomorphization error",
_ => {
ecx.handle_ice(); // print interpreter backtrace
bug!("This error should be impossible in Miri: {}", ecx.format_error(e));
bug!(
"This error should be impossible in Miri: {}",
format_interp_error(ecx.tcx.dcx(), e)
);
}
};
#[rustfmt::skip]
@ -365,7 +371,7 @@ pub fn report_error<'tcx, 'mir>(
_ => {}
}
msg.insert(0, ecx.format_error(e));
msg.insert(0, format_interp_error(ecx.tcx.dcx(), e));
report_msg(
DiagLevel::Error,

View file

@ -33,20 +33,16 @@ Number of file 0 mappings: 24
- Code(Expression(1, Add)) at (prev + 1, 5) to (start + 3, 2)
= (c1 + (c0 - c1))
Function name: closure::main::{closure#0}
Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 28, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 07, 01, 09, 01, 06]
Function name: closure::main::{closure#0} (unused)
Raw bytes (24): 0x[01, 01, 00, 04, 00, 28, 05, 02, 14, 00, 02, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 40, 5) to (start + 2, 20)
- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10)
- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11)
= (c0 - c1)
- Code(Expression(1, Add)) at (prev + 1, 9) to (start + 1, 6)
= (c1 + (c0 - c1))
- Code(Zero) at (prev + 40, 5) to (start + 2, 20)
- Code(Zero) at (prev + 2, 21) to (start + 2, 10)
- Code(Zero) at (prev + 2, 10) to (start + 0, 11)
- Code(Zero) at (prev + 1, 9) to (start + 1, 6)
Function name: closure::main::{closure#10} (unused)
Raw bytes (10): 0x[01, 01, 00, 01, 00, 9b, 01, 07, 00, 21]
@ -148,20 +144,16 @@ Number of file 0 mappings: 6
- Code(Expression(0, Add)) at (prev + 2, 9) to (start + 0, 10)
= (c1 + (c0 - c1))
Function name: closure::main::{closure#18}
Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 19, 0d, 02, 1c, 05, 02, 1d, 02, 12, 02, 02, 12, 00, 13, 07, 01, 11, 01, 0e]
Function name: closure::main::{closure#18} (unused)
Raw bytes (24): 0x[01, 01, 00, 04, 00, 19, 0d, 02, 1c, 00, 02, 1d, 02, 12, 00, 02, 12, 00, 13, 00, 01, 11, 01, 0e]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 25, 13) to (start + 2, 28)
- Code(Counter(1)) at (prev + 2, 29) to (start + 2, 18)
- Code(Expression(0, Sub)) at (prev + 2, 18) to (start + 0, 19)
= (c0 - c1)
- Code(Expression(1, Add)) at (prev + 1, 17) to (start + 1, 14)
= (c1 + (c0 - c1))
- Code(Zero) at (prev + 25, 13) to (start + 2, 28)
- Code(Zero) at (prev + 2, 29) to (start + 2, 18)
- Code(Zero) at (prev + 2, 18) to (start + 0, 19)
- Code(Zero) at (prev + 1, 17) to (start + 1, 14)
Function name: closure::main::{closure#19}
Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 43, 0d, 02, 1c, 05, 02, 1d, 02, 12, 02, 02, 12, 00, 13, 07, 01, 11, 01, 0e]

View file

@ -77,7 +77,7 @@ Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 167, 9) to (start + 2, 10)
Function name: issue_84561::test3
Raw bytes (436): 0x[01, 01, 41, 05, 09, 0d, 00, 15, 19, 12, 00, 15, 19, 21, 00, 1e, 00, 21, 00, 31, 00, 3d, 41, 2e, 45, 3d, 41, 42, 49, 45, 00, 3f, 51, 42, 49, 45, 00, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 92, 01, 55, 51, 00, 8f, 01, 5d, 92, 01, 55, 51, 00, 87, 01, 61, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 82, 01, 65, 87, 01, 61, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 75, f6, 01, fb, 01, 79, 71, fe, 01, 82, 02, 71, 69, 6d, 71, fe, 01, 82, 02, 71, 69, 6d, 69, 6d, 82, 02, 71, 69, 6d, fb, 01, 79, 71, fe, 01, 82, 02, 71, 69, 6d, f3, 01, 7d, 75, f6, 01, fb, 01, 79, 71, fe, 01, 82, 02, 71, 69, 6d, ee, 01, 00, f3, 01, 7d, 75, f6, 01, fb, 01, 79, 71, fe, 01, 82, 02, 71, 69, 6d, 33, 01, 08, 01, 03, 1c, 05, 04, 09, 01, 1c, 02, 02, 05, 04, 1f, 0d, 05, 05, 00, 1f, 06, 01, 05, 00, 1f, 15, 01, 09, 01, 1c, 12, 02, 05, 00, 1f, 0e, 01, 05, 00, 0f, 00, 00, 20, 00, 30, 21, 01, 05, 03, 0f, 00, 03, 20, 00, 30, 00, 00, 33, 00, 41, 00, 00, 4b, 00, 5a, 1e, 01, 05, 00, 0f, 00, 05, 09, 03, 10, 00, 05, 0d, 00, 1b, 00, 02, 0d, 00, 1c, 1a, 04, 09, 05, 06, 31, 06, 05, 03, 06, 22, 04, 05, 03, 06, 3d, 04, 09, 04, 06, 2e, 05, 08, 00, 0f, 45, 01, 09, 03, 0a, 2a, 05, 09, 03, 0a, 3f, 05, 08, 00, 0f, 51, 01, 09, 00, 13, 00, 03, 0d, 00, 1d, 3a, 03, 09, 00, 13, 00, 03, 0d, 00, 1d, 87, 01, 03, 05, 00, 0f, 8f, 01, 01, 0c, 00, 13, 5d, 01, 0d, 00, 13, 8a, 01, 02, 0d, 00, 13, 82, 01, 04, 05, 02, 13, 65, 03, 0d, 00, 13, 7e, 02, 0d, 00, 13, f3, 01, 03, 05, 00, 0f, 69, 01, 0c, 00, 13, 6d, 01, 0d, 03, 0e, 75, 04, 0d, 00, 13, fb, 01, 02, 0d, 00, 17, 82, 02, 01, 14, 00, 1b, 71, 01, 15, 00, 1b, fe, 01, 02, 15, 00, 1b, f6, 01, 04, 0d, 00, 13, 7d, 03, 09, 00, 19, ee, 01, 02, 05, 00, 0f, ea, 01, 03, 09, 00, 22, 00, 02, 05, 00, 0f, 00, 03, 09, 00, 2c, 00, 02, 01, 00, 02]
Raw bytes (436): 0x[01, 01, 41, 05, 09, 0d, 00, 15, 19, 12, 00, 15, 19, 21, 00, 1e, 00, 21, 00, 31, 00, 3d, 41, 2e, 45, 3d, 41, 42, 49, 45, 00, 3f, 51, 42, 49, 45, 00, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 92, 01, 55, 51, 00, 8f, 01, 5d, 92, 01, 55, 51, 00, 87, 01, 61, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 82, 01, 65, 87, 01, 61, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 75, f6, 01, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, 00, fe, 01, 82, 02, 00, 69, 6d, 69, 6d, 82, 02, 00, 69, 6d, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, f3, 01, 7d, 75, f6, 01, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, ee, 01, 00, f3, 01, 7d, 75, f6, 01, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, 33, 01, 08, 01, 03, 1c, 05, 04, 09, 01, 1c, 02, 02, 05, 04, 1f, 0d, 05, 05, 00, 1f, 06, 01, 05, 00, 1f, 15, 01, 09, 01, 1c, 12, 02, 05, 00, 1f, 0e, 01, 05, 00, 0f, 00, 00, 20, 00, 30, 21, 01, 05, 03, 0f, 00, 03, 20, 00, 30, 00, 00, 33, 00, 41, 00, 00, 4b, 00, 5a, 1e, 01, 05, 00, 0f, 00, 05, 09, 03, 10, 00, 05, 0d, 00, 1b, 00, 02, 0d, 00, 1c, 1a, 04, 09, 05, 06, 31, 06, 05, 03, 06, 22, 04, 05, 03, 06, 3d, 04, 09, 04, 06, 2e, 05, 08, 00, 0f, 45, 01, 09, 03, 0a, 2a, 05, 09, 03, 0a, 3f, 05, 08, 00, 0f, 51, 01, 09, 00, 13, 00, 03, 0d, 00, 1d, 3a, 03, 09, 00, 13, 00, 03, 0d, 00, 1d, 87, 01, 03, 05, 00, 0f, 8f, 01, 01, 0c, 00, 13, 5d, 01, 0d, 00, 13, 8a, 01, 02, 0d, 00, 13, 82, 01, 04, 05, 02, 13, 65, 03, 0d, 00, 13, 7e, 02, 0d, 00, 13, f3, 01, 03, 05, 00, 0f, 69, 01, 0c, 00, 13, 6d, 01, 0d, 03, 0e, 75, 04, 0d, 00, 13, fb, 01, 02, 0d, 00, 17, 82, 02, 01, 14, 00, 1b, 00, 01, 15, 00, 1b, fe, 01, 02, 15, 00, 1b, f6, 01, 04, 0d, 00, 13, 7d, 03, 09, 00, 19, ee, 01, 02, 05, 00, 0f, ea, 01, 03, 09, 00, 22, 00, 02, 05, 00, 0f, 00, 03, 09, 00, 2c, 00, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 65
@ -120,31 +120,31 @@ Number of expressions: 65
- expression 36 operands: lhs = Counter(20), rhs = Zero
- expression 37 operands: lhs = Counter(29), rhs = Expression(61, Sub)
- expression 38 operands: lhs = Expression(62, Add), rhs = Counter(30)
- expression 39 operands: lhs = Counter(28), rhs = Expression(63, Sub)
- expression 40 operands: lhs = Expression(64, Sub), rhs = Counter(28)
- expression 39 operands: lhs = Zero, rhs = Expression(63, Sub)
- expression 40 operands: lhs = Expression(64, Sub), rhs = Zero
- expression 41 operands: lhs = Counter(26), rhs = Counter(27)
- expression 42 operands: lhs = Counter(28), rhs = Expression(63, Sub)
- expression 43 operands: lhs = Expression(64, Sub), rhs = Counter(28)
- expression 42 operands: lhs = Zero, rhs = Expression(63, Sub)
- expression 43 operands: lhs = Expression(64, Sub), rhs = Zero
- expression 44 operands: lhs = Counter(26), rhs = Counter(27)
- expression 45 operands: lhs = Counter(26), rhs = Counter(27)
- expression 46 operands: lhs = Expression(64, Sub), rhs = Counter(28)
- expression 46 operands: lhs = Expression(64, Sub), rhs = Zero
- expression 47 operands: lhs = Counter(26), rhs = Counter(27)
- expression 48 operands: lhs = Expression(62, Add), rhs = Counter(30)
- expression 49 operands: lhs = Counter(28), rhs = Expression(63, Sub)
- expression 50 operands: lhs = Expression(64, Sub), rhs = Counter(28)
- expression 49 operands: lhs = Zero, rhs = Expression(63, Sub)
- expression 50 operands: lhs = Expression(64, Sub), rhs = Zero
- expression 51 operands: lhs = Counter(26), rhs = Counter(27)
- expression 52 operands: lhs = Expression(60, Add), rhs = Counter(31)
- expression 53 operands: lhs = Counter(29), rhs = Expression(61, Sub)
- expression 54 operands: lhs = Expression(62, Add), rhs = Counter(30)
- expression 55 operands: lhs = Counter(28), rhs = Expression(63, Sub)
- expression 56 operands: lhs = Expression(64, Sub), rhs = Counter(28)
- expression 55 operands: lhs = Zero, rhs = Expression(63, Sub)
- expression 56 operands: lhs = Expression(64, Sub), rhs = Zero
- expression 57 operands: lhs = Counter(26), rhs = Counter(27)
- expression 58 operands: lhs = Expression(59, Sub), rhs = Zero
- expression 59 operands: lhs = Expression(60, Add), rhs = Counter(31)
- expression 60 operands: lhs = Counter(29), rhs = Expression(61, Sub)
- expression 61 operands: lhs = Expression(62, Add), rhs = Counter(30)
- expression 62 operands: lhs = Counter(28), rhs = Expression(63, Sub)
- expression 63 operands: lhs = Expression(64, Sub), rhs = Counter(28)
- expression 62 operands: lhs = Zero, rhs = Expression(63, Sub)
- expression 63 operands: lhs = Expression(64, Sub), rhs = Zero
- expression 64 operands: lhs = Counter(26), rhs = Counter(27)
Number of file 0 mappings: 51
- Code(Counter(0)) at (prev + 8, 1) to (start + 3, 28)
@ -200,24 +200,24 @@ Number of file 0 mappings: 51
- Code(Expression(31, Sub)) at (prev + 2, 13) to (start + 0, 19)
= (((c23 + (((c20 - Zero) + c21) - c23)) - c24) - c25)
- Code(Expression(60, Add)) at (prev + 3, 5) to (start + 0, 15)
= (c29 + ((c28 + ((c26 - c27) - c28)) - c30))
= (c29 + ((Zero + ((c26 - c27) - Zero)) - c30))
- Code(Counter(26)) at (prev + 1, 12) to (start + 0, 19)
- Code(Counter(27)) at (prev + 1, 13) to (start + 3, 14)
- Code(Counter(29)) at (prev + 4, 13) to (start + 0, 19)
- Code(Expression(62, Add)) at (prev + 2, 13) to (start + 0, 23)
= (c28 + ((c26 - c27) - c28))
= (Zero + ((c26 - c27) - Zero))
- Code(Expression(64, Sub)) at (prev + 1, 20) to (start + 0, 27)
= (c26 - c27)
- Code(Counter(28)) at (prev + 1, 21) to (start + 0, 27)
- Code(Zero) at (prev + 1, 21) to (start + 0, 27)
- Code(Expression(63, Sub)) at (prev + 2, 21) to (start + 0, 27)
= ((c26 - c27) - c28)
= ((c26 - c27) - Zero)
- Code(Expression(61, Sub)) at (prev + 4, 13) to (start + 0, 19)
= ((c28 + ((c26 - c27) - c28)) - c30)
= ((Zero + ((c26 - c27) - Zero)) - c30)
- Code(Counter(31)) at (prev + 3, 9) to (start + 0, 25)
- Code(Expression(59, Sub)) at (prev + 2, 5) to (start + 0, 15)
= ((c29 + ((c28 + ((c26 - c27) - c28)) - c30)) - c31)
= ((c29 + ((Zero + ((c26 - c27) - Zero)) - c30)) - c31)
- Code(Expression(58, Sub)) at (prev + 3, 9) to (start + 0, 34)
= (((c29 + ((c28 + ((c26 - c27) - c28)) - c30)) - c31) - Zero)
= (((c29 + ((Zero + ((c26 - c27) - Zero)) - c30)) - c31) - Zero)
- Code(Zero) at (prev + 2, 5) to (start + 0, 15)
- Code(Zero) at (prev + 3, 9) to (start + 0, 44)
- Code(Zero) at (prev + 2, 1) to (start + 0, 2)

View file

@ -59,162 +59,131 @@ Number of file 0 mappings: 4
= (c1 + (c0 - c1))
Function name: try_error_result::test1
Raw bytes (77): 0x[01, 01, 09, 01, 07, 05, 09, 03, 0d, 1d, 11, 16, 1d, 03, 0d, 1f, 0d, 23, 19, 11, 15, 0b, 01, 0d, 01, 02, 17, 03, 07, 09, 00, 0e, 16, 02, 09, 04, 1a, 1d, 06, 0d, 00, 29, 11, 00, 29, 00, 2a, 0e, 01, 0d, 00, 2a, 15, 00, 2a, 00, 2b, 12, 04, 0d, 00, 2a, 19, 00, 2a, 00, 2b, 0d, 03, 05, 00, 0b, 1b, 01, 01, 00, 02]
Raw bytes (75): 0x[01, 01, 08, 01, 07, 00, 09, 03, 0d, 12, 1d, 03, 0d, 1b, 0d, 1f, 00, 11, 00, 0b, 01, 0d, 01, 02, 17, 03, 07, 09, 00, 0e, 12, 02, 09, 04, 1a, 1d, 06, 0d, 00, 29, 11, 00, 29, 00, 2a, 00, 01, 0d, 00, 2a, 00, 00, 2a, 00, 2b, 0e, 04, 0d, 00, 2a, 00, 00, 2a, 00, 2b, 0d, 03, 05, 00, 0b, 17, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 9
Number of expressions: 8
- expression 0 operands: lhs = Counter(0), rhs = Expression(1, Add)
- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
- expression 1 operands: lhs = Zero, rhs = Counter(2)
- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 3 operands: lhs = Counter(7), rhs = Counter(4)
- expression 4 operands: lhs = Expression(5, Sub), rhs = Counter(7)
- expression 5 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(3)
- expression 7 operands: lhs = Expression(8, Add), rhs = Counter(6)
- expression 8 operands: lhs = Counter(4), rhs = Counter(5)
- expression 3 operands: lhs = Expression(4, Sub), rhs = Counter(7)
- expression 4 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(3)
- expression 6 operands: lhs = Expression(7, Add), rhs = Zero
- expression 7 operands: lhs = Counter(4), rhs = Zero
Number of file 0 mappings: 11
- Code(Counter(0)) at (prev + 13, 1) to (start + 2, 23)
- Code(Expression(0, Add)) at (prev + 7, 9) to (start + 0, 14)
= (c0 + (c1 + c2))
- Code(Expression(5, Sub)) at (prev + 2, 9) to (start + 4, 26)
= ((c0 + (c1 + c2)) - c3)
= (c0 + (Zero + c2))
- Code(Expression(4, Sub)) at (prev + 2, 9) to (start + 4, 26)
= ((c0 + (Zero + c2)) - c3)
- Code(Counter(7)) at (prev + 6, 13) to (start + 0, 41)
- Code(Counter(4)) at (prev + 0, 41) to (start + 0, 42)
- Code(Expression(3, Sub)) at (prev + 1, 13) to (start + 0, 42)
= (c7 - c4)
- Code(Counter(5)) at (prev + 0, 42) to (start + 0, 43)
- Code(Expression(4, Sub)) at (prev + 4, 13) to (start + 0, 42)
= (((c0 + (c1 + c2)) - c3) - c7)
- Code(Counter(6)) at (prev + 0, 42) to (start + 0, 43)
- Code(Zero) at (prev + 1, 13) to (start + 0, 42)
- Code(Zero) at (prev + 0, 42) to (start + 0, 43)
- Code(Expression(3, Sub)) at (prev + 4, 13) to (start + 0, 42)
= (((c0 + (Zero + c2)) - c3) - c7)
- Code(Zero) at (prev + 0, 42) to (start + 0, 43)
- Code(Counter(3)) at (prev + 3, 5) to (start + 0, 11)
- Code(Expression(6, Add)) at (prev + 1, 1) to (start + 0, 2)
= (((c4 + c5) + c6) + c3)
- Code(Expression(5, Add)) at (prev + 1, 1) to (start + 0, 2)
= (((c4 + Zero) + Zero) + c3)
Function name: try_error_result::test2
Raw bytes (358): 0x[01, 01, 3b, 01, 07, 05, 09, 03, 0d, 41, 11, 4a, 15, 41, 11, 42, 1d, 46, 19, 4a, 15, 41, 11, 4a, 15, 41, 11, 46, 19, 4a, 15, 41, 11, 42, 1d, 46, 19, 4a, 15, 41, 11, 5e, 25, 49, 21, 49, 21, 5e, 25, 49, 21, 8a, 01, 2d, 8e, 01, 29, 92, 01, 41, 03, 0d, 92, 01, 41, 03, 0d, 8e, 01, 29, 92, 01, 41, 03, 0d, 8a, 01, 2d, 8e, 01, 29, 92, 01, 41, 03, 0d, a6, 01, 35, 45, 31, 45, 31, a6, 01, 35, 45, 31, ba, 01, 3d, 4d, 39, 4d, 39, ba, 01, 3d, 4d, 39, c3, 01, 0d, c7, 01, db, 01, cb, 01, cf, 01, 11, 15, d3, 01, d7, 01, 19, 1d, 21, 25, df, 01, e3, 01, 29, 2d, e7, 01, eb, 01, 31, 35, 39, 3d, 28, 01, 3e, 01, 03, 17, 03, 08, 09, 00, 0e, 92, 01, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 11, 00, 2f, 00, 30, 4a, 00, 31, 03, 35, 15, 04, 11, 00, 12, 46, 02, 11, 04, 12, 3e, 05, 11, 00, 14, 46, 00, 17, 00, 41, 19, 00, 41, 00, 42, 42, 00, 43, 00, 5f, 1d, 00, 5f, 00, 60, 3e, 01, 0d, 00, 20, 5a, 01, 11, 00, 14, 49, 00, 17, 00, 41, 21, 00, 41, 00, 42, 5e, 00, 43, 00, 60, 25, 00, 60, 00, 61, 5a, 01, 0d, 00, 20, 86, 01, 04, 11, 00, 14, 8e, 01, 00, 17, 00, 42, 29, 00, 42, 00, 43, 8a, 01, 00, 44, 00, 61, 2d, 00, 61, 00, 62, 86, 01, 01, 0d, 00, 20, a2, 01, 01, 11, 00, 14, 45, 00, 17, 01, 36, 31, 01, 36, 00, 37, a6, 01, 01, 12, 00, 2f, 35, 00, 2f, 00, 30, a2, 01, 01, 0d, 00, 20, b6, 01, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 39, 02, 11, 00, 12, ba, 01, 01, 12, 00, 2f, 3d, 01, 11, 00, 12, b6, 01, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, bf, 01, 01, 01, 00, 02]
Raw bytes (280): 0x[01, 01, 24, 01, 07, 00, 09, 03, 0d, 41, 00, 1e, 00, 41, 00, 1e, 00, 41, 00, 4a, 00, 4e, 00, 52, 41, 03, 0d, 52, 41, 03, 0d, 4e, 00, 52, 41, 03, 0d, 4a, 00, 4e, 00, 52, 41, 03, 0d, 66, 00, 45, 00, 45, 00, 66, 00, 45, 00, 7a, 00, 4d, 00, 4d, 00, 7a, 00, 4d, 00, 83, 01, 0d, 87, 01, 00, 00, 8b, 01, 8f, 01, 00, 19, 00, 28, 01, 3e, 01, 03, 17, 03, 08, 09, 00, 0e, 52, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 00, 00, 2f, 00, 30, 1e, 00, 31, 03, 35, 00, 04, 11, 00, 12, 1a, 02, 11, 04, 12, 00, 05, 11, 00, 14, 1a, 00, 17, 00, 41, 19, 00, 41, 00, 42, 00, 00, 43, 00, 5f, 00, 00, 5f, 00, 60, 00, 01, 0d, 00, 20, 00, 01, 11, 00, 14, 00, 00, 17, 00, 41, 00, 00, 41, 00, 42, 00, 00, 43, 00, 60, 00, 00, 60, 00, 61, 00, 01, 0d, 00, 20, 46, 04, 11, 00, 14, 4e, 00, 17, 00, 42, 00, 00, 42, 00, 43, 4a, 00, 44, 00, 61, 00, 00, 61, 00, 62, 46, 01, 0d, 00, 20, 62, 01, 11, 00, 14, 45, 00, 17, 01, 36, 00, 01, 36, 00, 37, 66, 01, 12, 00, 2f, 00, 00, 2f, 00, 30, 62, 01, 0d, 00, 20, 76, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 00, 02, 11, 00, 12, 7a, 01, 12, 00, 2f, 00, 01, 11, 00, 12, 76, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, 7f, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 59
Number of expressions: 36
- expression 0 operands: lhs = Counter(0), rhs = Expression(1, Add)
- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
- expression 1 operands: lhs = Zero, rhs = Counter(2)
- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 3 operands: lhs = Counter(16), rhs = Counter(4)
- expression 4 operands: lhs = Expression(18, Sub), rhs = Counter(5)
- expression 5 operands: lhs = Counter(16), rhs = Counter(4)
- expression 6 operands: lhs = Expression(16, Sub), rhs = Counter(7)
- expression 7 operands: lhs = Expression(17, Sub), rhs = Counter(6)
- expression 8 operands: lhs = Expression(18, Sub), rhs = Counter(5)
- expression 9 operands: lhs = Counter(16), rhs = Counter(4)
- expression 10 operands: lhs = Expression(18, Sub), rhs = Counter(5)
- expression 11 operands: lhs = Counter(16), rhs = Counter(4)
- expression 12 operands: lhs = Expression(17, Sub), rhs = Counter(6)
- expression 13 operands: lhs = Expression(18, Sub), rhs = Counter(5)
- expression 14 operands: lhs = Counter(16), rhs = Counter(4)
- expression 15 operands: lhs = Expression(16, Sub), rhs = Counter(7)
- expression 16 operands: lhs = Expression(17, Sub), rhs = Counter(6)
- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(5)
- expression 18 operands: lhs = Counter(16), rhs = Counter(4)
- expression 19 operands: lhs = Expression(23, Sub), rhs = Counter(9)
- expression 20 operands: lhs = Counter(18), rhs = Counter(8)
- expression 21 operands: lhs = Counter(18), rhs = Counter(8)
- expression 22 operands: lhs = Expression(23, Sub), rhs = Counter(9)
- expression 23 operands: lhs = Counter(18), rhs = Counter(8)
- expression 24 operands: lhs = Expression(34, Sub), rhs = Counter(11)
- expression 25 operands: lhs = Expression(35, Sub), rhs = Counter(10)
- expression 26 operands: lhs = Expression(36, Sub), rhs = Counter(16)
- expression 27 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 28 operands: lhs = Expression(36, Sub), rhs = Counter(16)
- expression 29 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 30 operands: lhs = Expression(35, Sub), rhs = Counter(10)
- expression 31 operands: lhs = Expression(36, Sub), rhs = Counter(16)
- expression 32 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 33 operands: lhs = Expression(34, Sub), rhs = Counter(11)
- expression 34 operands: lhs = Expression(35, Sub), rhs = Counter(10)
- expression 35 operands: lhs = Expression(36, Sub), rhs = Counter(16)
- expression 36 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 37 operands: lhs = Expression(41, Sub), rhs = Counter(13)
- expression 38 operands: lhs = Counter(17), rhs = Counter(12)
- expression 39 operands: lhs = Counter(17), rhs = Counter(12)
- expression 40 operands: lhs = Expression(41, Sub), rhs = Counter(13)
- expression 41 operands: lhs = Counter(17), rhs = Counter(12)
- expression 42 operands: lhs = Expression(46, Sub), rhs = Counter(15)
- expression 43 operands: lhs = Counter(19), rhs = Counter(14)
- expression 44 operands: lhs = Counter(19), rhs = Counter(14)
- expression 45 operands: lhs = Expression(46, Sub), rhs = Counter(15)
- expression 46 operands: lhs = Counter(19), rhs = Counter(14)
- expression 47 operands: lhs = Expression(48, Add), rhs = Counter(3)
- expression 48 operands: lhs = Expression(49, Add), rhs = Expression(54, Add)
- expression 49 operands: lhs = Expression(50, Add), rhs = Expression(51, Add)
- expression 50 operands: lhs = Counter(4), rhs = Counter(5)
- expression 51 operands: lhs = Expression(52, Add), rhs = Expression(53, Add)
- expression 52 operands: lhs = Counter(6), rhs = Counter(7)
- expression 53 operands: lhs = Counter(8), rhs = Counter(9)
- expression 54 operands: lhs = Expression(55, Add), rhs = Expression(56, Add)
- expression 55 operands: lhs = Counter(10), rhs = Counter(11)
- expression 56 operands: lhs = Expression(57, Add), rhs = Expression(58, Add)
- expression 57 operands: lhs = Counter(12), rhs = Counter(13)
- expression 58 operands: lhs = Counter(14), rhs = Counter(15)
- expression 3 operands: lhs = Counter(16), rhs = Zero
- expression 4 operands: lhs = Expression(7, Sub), rhs = Zero
- expression 5 operands: lhs = Counter(16), rhs = Zero
- expression 6 operands: lhs = Expression(7, Sub), rhs = Zero
- expression 7 operands: lhs = Counter(16), rhs = Zero
- expression 8 operands: lhs = Expression(18, Sub), rhs = Zero
- expression 9 operands: lhs = Expression(19, Sub), rhs = Zero
- expression 10 operands: lhs = Expression(20, Sub), rhs = Counter(16)
- expression 11 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 12 operands: lhs = Expression(20, Sub), rhs = Counter(16)
- expression 13 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 14 operands: lhs = Expression(19, Sub), rhs = Zero
- expression 15 operands: lhs = Expression(20, Sub), rhs = Counter(16)
- expression 16 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 17 operands: lhs = Expression(18, Sub), rhs = Zero
- expression 18 operands: lhs = Expression(19, Sub), rhs = Zero
- expression 19 operands: lhs = Expression(20, Sub), rhs = Counter(16)
- expression 20 operands: lhs = Expression(0, Add), rhs = Counter(3)
- expression 21 operands: lhs = Expression(25, Sub), rhs = Zero
- expression 22 operands: lhs = Counter(17), rhs = Zero
- expression 23 operands: lhs = Counter(17), rhs = Zero
- expression 24 operands: lhs = Expression(25, Sub), rhs = Zero
- expression 25 operands: lhs = Counter(17), rhs = Zero
- expression 26 operands: lhs = Expression(30, Sub), rhs = Zero
- expression 27 operands: lhs = Counter(19), rhs = Zero
- expression 28 operands: lhs = Counter(19), rhs = Zero
- expression 29 operands: lhs = Expression(30, Sub), rhs = Zero
- expression 30 operands: lhs = Counter(19), rhs = Zero
- expression 31 operands: lhs = Expression(32, Add), rhs = Counter(3)
- expression 32 operands: lhs = Expression(33, Add), rhs = Zero
- expression 33 operands: lhs = Zero, rhs = Expression(34, Add)
- expression 34 operands: lhs = Expression(35, Add), rhs = Zero
- expression 35 operands: lhs = Counter(6), rhs = Zero
Number of file 0 mappings: 40
- Code(Counter(0)) at (prev + 62, 1) to (start + 3, 23)
- Code(Expression(0, Add)) at (prev + 8, 9) to (start + 0, 14)
= (c0 + (c1 + c2))
- Code(Expression(36, Sub)) at (prev + 2, 9) to (start + 4, 26)
= ((c0 + (c1 + c2)) - c3)
= (c0 + (Zero + c2))
- Code(Expression(20, Sub)) at (prev + 2, 9) to (start + 4, 26)
= ((c0 + (Zero + c2)) - c3)
- Code(Counter(16)) at (prev + 6, 13) to (start + 0, 47)
- Code(Counter(4)) at (prev + 0, 47) to (start + 0, 48)
- Code(Expression(18, Sub)) at (prev + 0, 49) to (start + 3, 53)
= (c16 - c4)
- Code(Counter(5)) at (prev + 4, 17) to (start + 0, 18)
- Code(Expression(17, Sub)) at (prev + 2, 17) to (start + 4, 18)
= ((c16 - c4) - c5)
- Code(Expression(15, Sub)) at (prev + 5, 17) to (start + 0, 20)
= ((((c16 - c4) - c5) - c6) - c7)
- Code(Expression(17, Sub)) at (prev + 0, 23) to (start + 0, 65)
= ((c16 - c4) - c5)
- Code(Zero) at (prev + 0, 47) to (start + 0, 48)
- Code(Expression(7, Sub)) at (prev + 0, 49) to (start + 3, 53)
= (c16 - Zero)
- Code(Zero) at (prev + 4, 17) to (start + 0, 18)
- Code(Expression(6, Sub)) at (prev + 2, 17) to (start + 4, 18)
= ((c16 - Zero) - Zero)
- Code(Zero) at (prev + 5, 17) to (start + 0, 20)
- Code(Expression(6, Sub)) at (prev + 0, 23) to (start + 0, 65)
= ((c16 - Zero) - Zero)
- Code(Counter(6)) at (prev + 0, 65) to (start + 0, 66)
- Code(Expression(16, Sub)) at (prev + 0, 67) to (start + 0, 95)
= (((c16 - c4) - c5) - c6)
- Code(Counter(7)) at (prev + 0, 95) to (start + 0, 96)
- Code(Expression(15, Sub)) at (prev + 1, 13) to (start + 0, 32)
= ((((c16 - c4) - c5) - c6) - c7)
- Code(Expression(22, Sub)) at (prev + 1, 17) to (start + 0, 20)
= ((c18 - c8) - c9)
- Code(Counter(18)) at (prev + 0, 23) to (start + 0, 65)
- Code(Counter(8)) at (prev + 0, 65) to (start + 0, 66)
- Code(Expression(23, Sub)) at (prev + 0, 67) to (start + 0, 96)
= (c18 - c8)
- Code(Counter(9)) at (prev + 0, 96) to (start + 0, 97)
- Code(Expression(22, Sub)) at (prev + 1, 13) to (start + 0, 32)
= ((c18 - c8) - c9)
- Code(Expression(33, Sub)) at (prev + 4, 17) to (start + 0, 20)
= (((((c0 + (c1 + c2)) - c3) - c16) - c10) - c11)
- Code(Expression(35, Sub)) at (prev + 0, 23) to (start + 0, 66)
= (((c0 + (c1 + c2)) - c3) - c16)
- Code(Counter(10)) at (prev + 0, 66) to (start + 0, 67)
- Code(Expression(34, Sub)) at (prev + 0, 68) to (start + 0, 97)
= ((((c0 + (c1 + c2)) - c3) - c16) - c10)
- Code(Counter(11)) at (prev + 0, 97) to (start + 0, 98)
- Code(Expression(33, Sub)) at (prev + 1, 13) to (start + 0, 32)
= (((((c0 + (c1 + c2)) - c3) - c16) - c10) - c11)
- Code(Expression(40, Sub)) at (prev + 1, 17) to (start + 0, 20)
= ((c17 - c12) - c13)
- Code(Zero) at (prev + 0, 67) to (start + 0, 95)
- Code(Zero) at (prev + 0, 95) to (start + 0, 96)
- Code(Zero) at (prev + 1, 13) to (start + 0, 32)
- Code(Zero) at (prev + 1, 17) to (start + 0, 20)
- Code(Zero) at (prev + 0, 23) to (start + 0, 65)
- Code(Zero) at (prev + 0, 65) to (start + 0, 66)
- Code(Zero) at (prev + 0, 67) to (start + 0, 96)
- Code(Zero) at (prev + 0, 96) to (start + 0, 97)
- Code(Zero) at (prev + 1, 13) to (start + 0, 32)
- Code(Expression(17, Sub)) at (prev + 4, 17) to (start + 0, 20)
= (((((c0 + (Zero + c2)) - c3) - c16) - Zero) - Zero)
- Code(Expression(19, Sub)) at (prev + 0, 23) to (start + 0, 66)
= (((c0 + (Zero + c2)) - c3) - c16)
- Code(Zero) at (prev + 0, 66) to (start + 0, 67)
- Code(Expression(18, Sub)) at (prev + 0, 68) to (start + 0, 97)
= ((((c0 + (Zero + c2)) - c3) - c16) - Zero)
- Code(Zero) at (prev + 0, 97) to (start + 0, 98)
- Code(Expression(17, Sub)) at (prev + 1, 13) to (start + 0, 32)
= (((((c0 + (Zero + c2)) - c3) - c16) - Zero) - Zero)
- Code(Expression(24, Sub)) at (prev + 1, 17) to (start + 0, 20)
= ((c17 - Zero) - Zero)
- Code(Counter(17)) at (prev + 0, 23) to (start + 1, 54)
- Code(Counter(12)) at (prev + 1, 54) to (start + 0, 55)
- Code(Expression(41, Sub)) at (prev + 1, 18) to (start + 0, 47)
= (c17 - c12)
- Code(Counter(13)) at (prev + 0, 47) to (start + 0, 48)
- Code(Expression(40, Sub)) at (prev + 1, 13) to (start + 0, 32)
= ((c17 - c12) - c13)
- Code(Expression(45, Sub)) at (prev + 1, 17) to (start + 0, 20)
= ((c19 - c14) - c15)
- Code(Zero) at (prev + 1, 54) to (start + 0, 55)
- Code(Expression(25, Sub)) at (prev + 1, 18) to (start + 0, 47)
= (c17 - Zero)
- Code(Zero) at (prev + 0, 47) to (start + 0, 48)
- Code(Expression(24, Sub)) at (prev + 1, 13) to (start + 0, 32)
= ((c17 - Zero) - Zero)
- Code(Expression(29, Sub)) at (prev + 1, 17) to (start + 0, 20)
= ((c19 - Zero) - Zero)
- Code(Counter(19)) at (prev + 0, 23) to (start + 1, 54)
- Code(Counter(14)) at (prev + 2, 17) to (start + 0, 18)
- Code(Expression(46, Sub)) at (prev + 1, 18) to (start + 0, 47)
= (c19 - c14)
- Code(Counter(15)) at (prev + 1, 17) to (start + 0, 18)
- Code(Expression(45, Sub)) at (prev + 2, 13) to (start + 0, 32)
= ((c19 - c14) - c15)
- Code(Zero) at (prev + 2, 17) to (start + 0, 18)
- Code(Expression(30, Sub)) at (prev + 1, 18) to (start + 0, 47)
= (c19 - Zero)
- Code(Zero) at (prev + 1, 17) to (start + 0, 18)
- Code(Expression(29, Sub)) at (prev + 2, 13) to (start + 0, 32)
= ((c19 - Zero) - Zero)
- Code(Counter(3)) at (prev + 3, 5) to (start + 0, 11)
- Code(Expression(47, Add)) at (prev + 1, 1) to (start + 0, 2)
= ((((c4 + c5) + ((c6 + c7) + (c8 + c9))) + ((c10 + c11) + ((c12 + c13) + (c14 + c15)))) + c3)
- Code(Expression(31, Add)) at (prev + 1, 1) to (start + 0, 2)
= (((Zero + ((c6 + Zero) + Zero)) + Zero) + c3)

View file

@ -1,9 +1,9 @@
Function name: yield::main
Raw bytes (106): 0x[01, 01, 0b, 05, 09, 0d, 11, 22, 15, 0d, 11, 11, 15, 22, 15, 0d, 11, 22, 15, 0d, 11, 19, 1d, 25, 29, 10, 01, 07, 01, 01, 16, 01, 06, 0b, 00, 2e, 0d, 01, 27, 00, 29, 03, 01, 0e, 00, 34, 0d, 02, 0b, 00, 2e, 22, 01, 22, 00, 27, 1e, 00, 2c, 00, 2e, 13, 01, 0e, 00, 34, 1e, 03, 09, 00, 16, 1e, 07, 0b, 00, 2e, 21, 01, 27, 00, 29, 27, 01, 0e, 00, 34, 21, 02, 0b, 00, 2e, 2d, 01, 27, 00, 29, 2b, 01, 0e, 00, 34, 2d, 02, 01, 00, 02]
Raw bytes (106): 0x[01, 01, 0b, 05, 00, 0d, 11, 22, 15, 0d, 11, 11, 15, 22, 15, 0d, 11, 22, 15, 0d, 11, 19, 1d, 25, 29, 10, 01, 07, 01, 01, 16, 01, 06, 0b, 00, 2e, 0d, 01, 27, 00, 29, 03, 01, 0e, 00, 34, 0d, 02, 0b, 00, 2e, 22, 01, 22, 00, 27, 1e, 00, 2c, 00, 2e, 13, 01, 0e, 00, 34, 1e, 03, 09, 00, 16, 1e, 07, 0b, 00, 2e, 21, 01, 27, 00, 29, 27, 01, 0e, 00, 34, 21, 02, 0b, 00, 2e, 2d, 01, 27, 00, 29, 2b, 01, 0e, 00, 34, 2d, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 11
- expression 0 operands: lhs = Counter(1), rhs = Counter(2)
- expression 0 operands: lhs = Counter(1), rhs = Zero
- expression 1 operands: lhs = Counter(3), rhs = Counter(4)
- expression 2 operands: lhs = Expression(8, Sub), rhs = Counter(5)
- expression 3 operands: lhs = Counter(3), rhs = Counter(4)
@ -19,7 +19,7 @@ Number of file 0 mappings: 16
- Code(Counter(0)) at (prev + 6, 11) to (start + 0, 46)
- Code(Counter(3)) at (prev + 1, 39) to (start + 0, 41)
- Code(Expression(0, Add)) at (prev + 1, 14) to (start + 0, 52)
= (c1 + c2)
= (c1 + Zero)
- Code(Counter(3)) at (prev + 2, 11) to (start + 0, 46)
- Code(Expression(8, Sub)) at (prev + 1, 34) to (start + 0, 39)
= (c3 - c4)

View file

@ -1,50 +0,0 @@
- // MIR for `issue_77355_opt` before ConstGoto
+ // MIR for `issue_77355_opt` after ConstGoto
fn issue_77355_opt(_1: Foo) -> u64 {
debug num => _1;
let mut _0: u64;
- let mut _2: bool;
- let mut _3: isize;
+ let mut _2: isize;
bb0: {
- StorageLive(_2);
- _3 = discriminant(_1);
- switchInt(move _3) -> [1: bb2, 2: bb2, otherwise: bb1];
+ _2 = discriminant(_1);
+ switchInt(move _2) -> [1: bb2, 2: bb2, otherwise: bb1];
}
bb1: {
- _2 = const false;
+ _0 = const 42_u64;
goto -> bb3;
}
bb2: {
- _2 = const true;
+ _0 = const 23_u64;
goto -> bb3;
}
bb3: {
- switchInt(move _2) -> [0: bb5, otherwise: bb4];
- }
-
- bb4: {
- _0 = const 23_u64;
- goto -> bb6;
- }
-
- bb5: {
- _0 = const 42_u64;
- goto -> bb6;
- }
-
- bb6: {
- StorageDead(_2);
return;
}
}

View file

@ -1,19 +0,0 @@
// skip-filecheck
// unit-test: ConstGoto
pub enum Foo {
A,
B,
C,
D,
E,
F,
}
// EMIT_MIR const_goto.issue_77355_opt.ConstGoto.diff
fn issue_77355_opt(num: Foo) -> u64 {
if matches!(num, Foo::B | Foo::C) { 23 } else { 42 }
}
fn main() {
issue_77355_opt(Foo::A);
}

View file

@ -1,51 +0,0 @@
- // MIR for `f` before ConstGoto
+ // MIR for `f` after ConstGoto
fn f() -> u64 {
let mut _0: u64;
let mut _1: bool;
let mut _2: i32;
bb0: {
StorageLive(_1);
StorageLive(_2);
_2 = const A;
switchInt(_2) -> [1: bb2, 2: bb2, 3: bb2, otherwise: bb1];
}
bb1: {
_1 = const true;
goto -> bb3;
}
bb2: {
_1 = const B;
- goto -> bb3;
+ switchInt(_1) -> [0: bb4, otherwise: bb3];
}
bb3: {
- switchInt(_1) -> [0: bb5, otherwise: bb4];
- }
-
- bb4: {
_0 = const 2_u64;
- goto -> bb6;
+ goto -> bb5;
}
- bb5: {
+ bb4: {
_0 = const 1_u64;
- goto -> bb6;
+ goto -> bb5;
}
- bb6: {
+ bb5: {
StorageDead(_2);
StorageDead(_1);
return;
}
}

View file

@ -0,0 +1,47 @@
- // MIR for `f` before JumpThreading
+ // MIR for `f` after JumpThreading
fn f() -> u64 {
let mut _0: u64;
let mut _1: bool;
bb0: {
StorageLive(_1);
switchInt(const A) -> [1: bb2, 2: bb2, 3: bb2, otherwise: bb1];
}
bb1: {
_1 = const true;
- goto -> bb3;
+ goto -> bb7;
}
bb2: {
_1 = const B;
goto -> bb3;
}
bb3: {
switchInt(_1) -> [0: bb5, otherwise: bb4];
}
bb4: {
_0 = const 2_u64;
goto -> bb6;
}
bb5: {
_0 = const 1_u64;
goto -> bb6;
}
bb6: {
StorageDead(_1);
return;
+ }
+
+ bb7: {
+ goto -> bb4;
}
}

View file

@ -5,7 +5,7 @@
// compile-flags: -Zunsound-mir-opts
// If const eval fails, then don't crash
// EMIT_MIR const_goto_const_eval_fail.f.ConstGoto.diff
// EMIT_MIR const_goto_const_eval_fail.f.JumpThreading.diff
pub fn f<const A: i32, const B: bool>() -> u64 {
match {
match A {

View file

@ -1,102 +0,0 @@
- // MIR for `match_nested_if` before ConstGoto
+ // MIR for `match_nested_if` after ConstGoto
fn match_nested_if() -> bool {
let mut _0: bool;
let _1: bool;
- let mut _2: ();
- let mut _3: bool;
- let mut _4: bool;
- let mut _5: bool;
- let mut _6: bool;
+ let mut _2: bool;
scope 1 {
debug val => _1;
}
bb0: {
StorageLive(_1);
StorageLive(_2);
- _2 = ();
- StorageLive(_3);
- StorageLive(_4);
- StorageLive(_5);
- StorageLive(_6);
- _6 = const true;
- switchInt(move _6) -> [0: bb2, otherwise: bb1];
+ _2 = const true;
+ switchInt(move _2) -> [0: bb2, otherwise: bb1];
}
bb1: {
- _5 = const true;
+ StorageDead(_2);
+ _1 = const true;
goto -> bb3;
}
bb2: {
- _5 = const false;
+ StorageDead(_2);
+ _1 = const false;
goto -> bb3;
}
bb3: {
- switchInt(move _5) -> [0: bb5, otherwise: bb4];
- }
-
- bb4: {
- StorageDead(_6);
- _4 = const true;
- goto -> bb6;
- }
-
- bb5: {
- StorageDead(_6);
- _4 = const false;
- goto -> bb6;
- }
-
- bb6: {
- switchInt(move _4) -> [0: bb8, otherwise: bb7];
- }
-
- bb7: {
- StorageDead(_5);
- _3 = const true;
- goto -> bb9;
- }
-
- bb8: {
- StorageDead(_5);
- _3 = const false;
- goto -> bb9;
- }
-
- bb9: {
- switchInt(move _3) -> [0: bb11, otherwise: bb10];
- }
-
- bb10: {
- StorageDead(_4);
- StorageDead(_3);
- _1 = const true;
- goto -> bb12;
- }
-
- bb11: {
- StorageDead(_4);
- StorageDead(_3);
- _1 = const false;
- goto -> bb12;
- }
-
- bb12: {
- StorageDead(_2);
_0 = _1;
StorageDead(_1);
return;
}
}

View file

@ -1,22 +0,0 @@
// skip-filecheck
// unit-test: ConstGoto
// EMIT_MIR const_goto_storage.match_nested_if.ConstGoto.diff
fn match_nested_if() -> bool {
let val = match () {
() if if if if true { true } else { false } { true } else { false } {
true
} else {
false
} =>
{
true
}
_ => false,
};
val
}
fn main() {
let _ = match_nested_if();
}

View file

@ -4,66 +4,12 @@ fn step_forward(_1: u32, _2: usize) -> u32 {
debug x => _1;
debug n => _2;
let mut _0: u32;
scope 1 (inlined <u32 as Step>::forward) {
debug start => _1;
debug n => _2;
let _3: std::option::Option<u32>;
let mut _4: &std::option::Option<u32>;
let mut _7: bool;
let mut _8: u32;
scope 2 {
}
scope 3 (inlined Option::<u32>::is_none) {
debug self => _4;
let mut _6: bool;
scope 4 (inlined Option::<u32>::is_some) {
debug self => _4;
let mut _5: isize;
}
}
scope 5 (inlined core::num::<impl u32>::wrapping_add) {
debug self => _1;
debug rhs => _8;
}
}
bb0: {
StorageLive(_7);
StorageLive(_4);
StorageLive(_3);
_3 = <u32 as Step>::forward_checked(_1, _2) -> [return: bb1, unwind continue];
_0 = <u32 as Step>::forward(move _1, move _2) -> [return: bb1, unwind continue];
}
bb1: {
_4 = &_3;
StorageLive(_6);
StorageLive(_5);
_5 = discriminant(_3);
_6 = Eq(_5, const 1_isize);
StorageDead(_5);
_7 = Not(move _6);
StorageDead(_6);
switchInt(move _7) -> [0: bb2, otherwise: bb3];
}
bb2: {
StorageDead(_3);
StorageDead(_4);
goto -> bb4;
}
bb3: {
StorageDead(_3);
StorageDead(_4);
assert(!const true, "attempt to compute `{} + {}`, which would overflow", const _, const 1_u32) -> [success: bb4, unwind continue];
}
bb4: {
StorageDead(_7);
StorageLive(_8);
_8 = _2 as u32 (IntToInt);
_0 = Add(_1, _8);
StorageDead(_8);
return;
}
}

View file

@ -7,14 +7,13 @@ fn int_range(_1: usize, _2: usize) -> () {
let mut _3: std::ops::Range<usize>;
let mut _4: std::ops::Range<usize>;
let mut _5: &mut std::ops::Range<usize>;
let mut _11: std::option::Option<usize>;
let mut _14: isize;
let _16: ();
let mut _13: std::option::Option<usize>;
let _15: ();
scope 1 {
debug iter => _4;
let _15: usize;
let _14: usize;
scope 2 {
debug i => _15;
debug i => _14;
}
scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) {
debug self => _5;
@ -23,10 +22,10 @@ fn int_range(_1: usize, _2: usize) -> () {
let mut _6: &usize;
let mut _7: &usize;
let mut _10: bool;
let _12: usize;
let mut _13: usize;
let _11: usize;
let mut _12: usize;
scope 6 {
debug old => _12;
debug old => _11;
scope 7 {
}
}
@ -51,9 +50,9 @@ fn int_range(_1: usize, _2: usize) -> () {
}
bb1: {
StorageLive(_11);
StorageLive(_13);
_5 = &mut _4;
StorageLive(_12);
StorageLive(_11);
StorageLive(_10);
StorageLive(_6);
_6 = &(_4.0: usize);
@ -72,53 +71,33 @@ fn int_range(_1: usize, _2: usize) -> () {
bb2: {
StorageDead(_7);
StorageDead(_6);
_11 = const Option::<usize>::None;
goto -> bb5;
StorageDead(_10);
StorageDead(_11);
StorageDead(_13);
StorageDead(_4);
return;
}
bb3: {
StorageDead(_7);
StorageDead(_6);
_12 = (_4.0: usize);
StorageLive(_13);
_13 = <usize as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb4, unwind continue];
_11 = (_4.0: usize);
StorageLive(_12);
_12 = <usize as Step>::forward_unchecked(_11, const 1_usize) -> [return: bb4, unwind continue];
}
bb4: {
(_4.0: usize) = move _13;
StorageDead(_13);
_11 = Option::<usize>::Some(_12);
goto -> bb5;
(_4.0: usize) = move _12;
StorageDead(_12);
_13 = Option::<usize>::Some(_11);
StorageDead(_10);
StorageDead(_11);
_14 = ((_13 as Some).0: usize);
_15 = opaque::<usize>(move _14) -> [return: bb5, unwind continue];
}
bb5: {
StorageDead(_10);
StorageDead(_12);
_14 = discriminant(_11);
switchInt(move _14) -> [0: bb6, 1: bb7, otherwise: bb9];
}
bb6: {
StorageDead(_11);
StorageDead(_4);
return;
}
bb7: {
_15 = ((_11 as Some).0: usize);
_16 = opaque::<usize>(move _15) -> [return: bb8, unwind continue];
}
bb8: {
StorageDead(_11);
StorageDead(_13);
goto -> bb1;
}
bb9: {
unreachable;
}
}
ALLOC0 (size: 16, align: 8) {
00 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ ........
}

View file

@ -0,0 +1,22 @@
// MIR for `issue_77355_opt` after PreCodegen
fn issue_77355_opt(_1: Foo) -> u64 {
debug num => _1;
let mut _0: u64;
let mut _2: isize;
bb0: {
_2 = discriminant(_1);
switchInt(move _2) -> [1: bb1, 2: bb1, otherwise: bb2];
}
bb1: {
_0 = const 23_u64;
return;
}
bb2: {
_0 = const 42_u64;
return;
}
}

View file

@ -0,0 +1,27 @@
// This test verifies that the MIR we output using the `matches!()` macro is close
// to the MIR for an `if let` branch.
pub enum Foo {
A,
B,
C,
D,
E,
F,
}
// EMIT_MIR matches_macro.issue_77355_opt.PreCodegen.after.mir
fn issue_77355_opt(num: Foo) -> u64 {
// CHECK-LABEL: fn issue_77355_opt(
// CHECK: switchInt({{.*}}) -> [1: bb1, 2: bb1, otherwise: bb2];
// CHECK: bb1: {
// CHECK-NEXT: _0 = const 23_u64;
// CHECK-NEXT: return;
// CHECK: bb2: {
// CHECK-NEXT: _0 = const 42_u64;
// CHECK-NEXT: return;
if matches!(num, Foo::B | Foo::C) { 23 } else { 42 }
}
fn main() {
issue_77355_opt(Foo::A);
}

View file

@ -8,16 +8,15 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
let mut _4: std::ops::Range<u32>;
let mut _5: std::ops::Range<u32>;
let mut _6: &mut std::ops::Range<u32>;
let mut _12: std::option::Option<u32>;
let mut _15: isize;
let mut _17: &impl Fn(u32);
let mut _18: (u32,);
let _19: ();
let mut _14: std::option::Option<u32>;
let mut _16: &impl Fn(u32);
let mut _17: (u32,);
let _18: ();
scope 1 {
debug iter => _5;
let _16: u32;
let _15: u32;
scope 2 {
debug x => _16;
debug x => _15;
}
scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) {
debug self => _6;
@ -26,10 +25,10 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
let mut _7: &u32;
let mut _8: &u32;
let mut _11: bool;
let _13: u32;
let mut _14: u32;
let _12: u32;
let mut _13: u32;
scope 6 {
debug old => _13;
debug old => _12;
scope 7 {
}
}
@ -54,9 +53,9 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
}
bb1: {
StorageLive(_12);
StorageLive(_14);
_6 = &mut _5;
StorageLive(_13);
StorageLive(_12);
StorageLive(_11);
StorageLive(_7);
_7 = &(_5.0: u32);
@ -69,69 +68,49 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
_11 = Lt(move _9, move _10);
StorageDead(_10);
StorageDead(_9);
switchInt(move _11) -> [0: bb2, otherwise: bb3];
switchInt(move _11) -> [0: bb2, otherwise: bb4];
}
bb2: {
StorageDead(_8);
StorageDead(_7);
_12 = const Option::<u32>::None;
goto -> bb5;
StorageDead(_11);
StorageDead(_12);
StorageDead(_14);
StorageDead(_5);
drop(_3) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
_13 = (_5.0: u32);
StorageLive(_14);
_14 = <u32 as Step>::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind unreachable];
}
bb4: {
(_5.0: u32) = move _14;
StorageDead(_14);
_12 = Option::<u32>::Some(_13);
goto -> bb5;
}
bb5: {
StorageDead(_11);
StorageDead(_13);
_15 = discriminant(_12);
switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10];
}
bb6: {
StorageDead(_12);
StorageDead(_5);
drop(_3) -> [return: bb7, unwind unreachable];
}
bb7: {
return;
}
bb8: {
_16 = ((_12 as Some).0: u32);
StorageLive(_17);
_17 = &_3;
StorageLive(_18);
_18 = (_16,);
_19 = <impl Fn(u32) as Fn<(u32,)>>::call(move _17, move _18) -> [return: bb9, unwind unreachable];
bb4: {
StorageDead(_8);
StorageDead(_7);
_12 = (_5.0: u32);
StorageLive(_13);
_13 = <u32 as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind unreachable];
}
bb9: {
StorageDead(_18);
StorageDead(_17);
bb5: {
(_5.0: u32) = move _13;
StorageDead(_13);
_14 = Option::<u32>::Some(_12);
StorageDead(_11);
StorageDead(_12);
_15 = ((_14 as Some).0: u32);
StorageLive(_16);
_16 = &_3;
StorageLive(_17);
_17 = (_15,);
_18 = <impl Fn(u32) as Fn<(u32,)>>::call(move _16, move _17) -> [return: bb6, unwind unreachable];
}
bb6: {
StorageDead(_17);
StorageDead(_16);
StorageDead(_14);
goto -> bb1;
}
bb10: {
unreachable;
}
}
ALLOC0 (size: 8, align: 4) {
00 00 00 00 __ __ __ __ ....
}

View file

@ -8,16 +8,15 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
let mut _4: std::ops::Range<u32>;
let mut _5: std::ops::Range<u32>;
let mut _6: &mut std::ops::Range<u32>;
let mut _12: std::option::Option<u32>;
let mut _15: isize;
let mut _17: &impl Fn(u32);
let mut _18: (u32,);
let _19: ();
let mut _14: std::option::Option<u32>;
let mut _16: &impl Fn(u32);
let mut _17: (u32,);
let _18: ();
scope 1 {
debug iter => _5;
let _16: u32;
let _15: u32;
scope 2 {
debug x => _16;
debug x => _15;
}
scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) {
debug self => _6;
@ -26,10 +25,10 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
let mut _7: &u32;
let mut _8: &u32;
let mut _11: bool;
let _13: u32;
let mut _14: u32;
let _12: u32;
let mut _13: u32;
scope 6 {
debug old => _13;
debug old => _12;
scope 7 {
}
}
@ -54,9 +53,9 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
}
bb1: {
StorageLive(_12);
StorageLive(_14);
_6 = &mut _5;
StorageLive(_13);
StorageLive(_12);
StorageLive(_11);
StorageLive(_7);
_7 = &(_5.0: u32);
@ -69,77 +68,57 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
_11 = Lt(move _9, move _10);
StorageDead(_10);
StorageDead(_9);
switchInt(move _11) -> [0: bb2, otherwise: bb3];
switchInt(move _11) -> [0: bb2, otherwise: bb4];
}
bb2: {
StorageDead(_8);
StorageDead(_7);
_12 = const Option::<u32>::None;
goto -> bb5;
StorageDead(_11);
StorageDead(_12);
StorageDead(_14);
StorageDead(_5);
drop(_3) -> [return: bb3, unwind continue];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
_13 = (_5.0: u32);
StorageLive(_14);
_14 = <u32 as Step>::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind: bb11];
}
bb4: {
(_5.0: u32) = move _14;
StorageDead(_14);
_12 = Option::<u32>::Some(_13);
goto -> bb5;
}
bb5: {
StorageDead(_11);
StorageDead(_13);
_15 = discriminant(_12);
switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10];
}
bb6: {
StorageDead(_12);
StorageDead(_5);
drop(_3) -> [return: bb7, unwind continue];
}
bb7: {
return;
}
bb8: {
_16 = ((_12 as Some).0: u32);
StorageLive(_17);
_17 = &_3;
StorageLive(_18);
_18 = (_16,);
_19 = <impl Fn(u32) as Fn<(u32,)>>::call(move _17, move _18) -> [return: bb9, unwind: bb11];
bb4: {
StorageDead(_8);
StorageDead(_7);
_12 = (_5.0: u32);
StorageLive(_13);
_13 = <u32 as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind: bb7];
}
bb9: {
StorageDead(_18);
StorageDead(_17);
bb5: {
(_5.0: u32) = move _13;
StorageDead(_13);
_14 = Option::<u32>::Some(_12);
StorageDead(_11);
StorageDead(_12);
_15 = ((_14 as Some).0: u32);
StorageLive(_16);
_16 = &_3;
StorageLive(_17);
_17 = (_15,);
_18 = <impl Fn(u32) as Fn<(u32,)>>::call(move _16, move _17) -> [return: bb6, unwind: bb7];
}
bb6: {
StorageDead(_17);
StorageDead(_16);
StorageDead(_14);
goto -> bb1;
}
bb10: {
unreachable;
bb7 (cleanup): {
drop(_3) -> [return: bb8, unwind terminate(cleanup)];
}
bb11 (cleanup): {
drop(_3) -> [return: bb12, unwind terminate(cleanup)];
}
bb12 (cleanup): {
bb8 (cleanup): {
resume;
}
}
ALLOC0 (size: 8, align: 4) {
00 00 00 00 __ __ __ __ ....
}

View file

@ -8,21 +8,20 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
let mut _4: std::ops::Range<usize>;
let mut _5: std::ops::Range<usize>;
let mut _6: &mut std::ops::Range<usize>;
let mut _12: std::option::Option<usize>;
let mut _15: isize;
let mut _17: usize;
let mut _18: bool;
let mut _20: &impl Fn(usize, &T);
let mut _21: (usize, &T);
let _22: ();
let mut _14: std::option::Option<usize>;
let mut _16: usize;
let mut _17: bool;
let mut _19: &impl Fn(usize, &T);
let mut _20: (usize, &T);
let _21: ();
scope 1 {
debug iter => _5;
let _16: usize;
let _15: usize;
scope 2 {
debug i => _16;
let _19: &T;
debug i => _15;
let _18: &T;
scope 3 {
debug x => _19;
debug x => _18;
}
}
scope 5 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) {
@ -32,10 +31,10 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
let mut _7: &usize;
let mut _8: &usize;
let mut _11: bool;
let _13: usize;
let mut _14: usize;
let _12: usize;
let mut _13: usize;
scope 7 {
debug old => _13;
debug old => _12;
scope 8 {
}
}
@ -63,9 +62,9 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
}
bb1: {
StorageLive(_12);
StorageLive(_14);
_6 = &mut _5;
StorageLive(_13);
StorageLive(_12);
StorageLive(_11);
StorageLive(_7);
_7 = &(_5.0: usize);
@ -78,76 +77,56 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
_11 = Lt(move _9, move _10);
StorageDead(_10);
StorageDead(_9);
switchInt(move _11) -> [0: bb2, otherwise: bb3];
switchInt(move _11) -> [0: bb2, otherwise: bb4];
}
bb2: {
StorageDead(_8);
StorageDead(_7);
_12 = const Option::<usize>::None;
goto -> bb5;
StorageDead(_11);
StorageDead(_12);
StorageDead(_14);
StorageDead(_5);
drop(_2) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
_13 = (_5.0: usize);
StorageLive(_14);
_14 = <usize as Step>::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind unreachable];
}
bb4: {
(_5.0: usize) = move _14;
StorageDead(_14);
_12 = Option::<usize>::Some(_13);
goto -> bb5;
}
bb5: {
StorageDead(_11);
StorageDead(_13);
_15 = discriminant(_12);
switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb11];
}
bb6: {
StorageDead(_12);
StorageDead(_5);
drop(_2) -> [return: bb7, unwind unreachable];
}
bb7: {
return;
}
bb8: {
_16 = ((_12 as Some).0: usize);
_17 = Len((*_1));
_18 = Lt(_16, _17);
assert(move _18, "index out of bounds: the length is {} but the index is {}", move _17, _16) -> [success: bb9, unwind unreachable];
bb4: {
StorageDead(_8);
StorageDead(_7);
_12 = (_5.0: usize);
StorageLive(_13);
_13 = <usize as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind unreachable];
}
bb9: {
_19 = &(*_1)[_16];
StorageLive(_20);
_20 = &_2;
StorageLive(_21);
_21 = (_16, _19);
_22 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _20, move _21) -> [return: bb10, unwind unreachable];
}
bb10: {
StorageDead(_21);
StorageDead(_20);
bb5: {
(_5.0: usize) = move _13;
StorageDead(_13);
_14 = Option::<usize>::Some(_12);
StorageDead(_11);
StorageDead(_12);
_15 = ((_14 as Some).0: usize);
_16 = Len((*_1));
_17 = Lt(_15, _16);
assert(move _17, "index out of bounds: the length is {} but the index is {}", move _16, _15) -> [success: bb6, unwind unreachable];
}
bb6: {
_18 = &(*_1)[_15];
StorageLive(_19);
_19 = &_2;
StorageLive(_20);
_20 = (_15, _18);
_21 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _19, move _20) -> [return: bb7, unwind unreachable];
}
bb7: {
StorageDead(_20);
StorageDead(_19);
StorageDead(_14);
goto -> bb1;
}
bb11: {
unreachable;
}
}
ALLOC0 (size: 16, align: 8) {
00 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ ........
}

View file

@ -8,21 +8,20 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
let mut _4: std::ops::Range<usize>;
let mut _5: std::ops::Range<usize>;
let mut _6: &mut std::ops::Range<usize>;
let mut _12: std::option::Option<usize>;
let mut _15: isize;
let mut _17: usize;
let mut _18: bool;
let mut _20: &impl Fn(usize, &T);
let mut _21: (usize, &T);
let _22: ();
let mut _14: std::option::Option<usize>;
let mut _16: usize;
let mut _17: bool;
let mut _19: &impl Fn(usize, &T);
let mut _20: (usize, &T);
let _21: ();
scope 1 {
debug iter => _5;
let _16: usize;
let _15: usize;
scope 2 {
debug i => _16;
let _19: &T;
debug i => _15;
let _18: &T;
scope 3 {
debug x => _19;
debug x => _18;
}
}
scope 5 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) {
@ -32,10 +31,10 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
let mut _7: &usize;
let mut _8: &usize;
let mut _11: bool;
let _13: usize;
let mut _14: usize;
let _12: usize;
let mut _13: usize;
scope 7 {
debug old => _13;
debug old => _12;
scope 8 {
}
}
@ -63,9 +62,9 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
}
bb1: {
StorageLive(_12);
StorageLive(_14);
_6 = &mut _5;
StorageLive(_13);
StorageLive(_12);
StorageLive(_11);
StorageLive(_7);
_7 = &(_5.0: usize);
@ -78,84 +77,64 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
_11 = Lt(move _9, move _10);
StorageDead(_10);
StorageDead(_9);
switchInt(move _11) -> [0: bb2, otherwise: bb3];
switchInt(move _11) -> [0: bb2, otherwise: bb4];
}
bb2: {
StorageDead(_8);
StorageDead(_7);
_12 = const Option::<usize>::None;
goto -> bb5;
StorageDead(_11);
StorageDead(_12);
StorageDead(_14);
StorageDead(_5);
drop(_2) -> [return: bb3, unwind continue];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
_13 = (_5.0: usize);
StorageLive(_14);
_14 = <usize as Step>::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind: bb12];
}
bb4: {
(_5.0: usize) = move _14;
StorageDead(_14);
_12 = Option::<usize>::Some(_13);
goto -> bb5;
}
bb5: {
StorageDead(_11);
StorageDead(_13);
_15 = discriminant(_12);
switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb11];
}
bb6: {
StorageDead(_12);
StorageDead(_5);
drop(_2) -> [return: bb7, unwind continue];
}
bb7: {
return;
}
bb8: {
_16 = ((_12 as Some).0: usize);
_17 = Len((*_1));
_18 = Lt(_16, _17);
assert(move _18, "index out of bounds: the length is {} but the index is {}", move _17, _16) -> [success: bb9, unwind: bb12];
bb4: {
StorageDead(_8);
StorageDead(_7);
_12 = (_5.0: usize);
StorageLive(_13);
_13 = <usize as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind: bb8];
}
bb9: {
_19 = &(*_1)[_16];
StorageLive(_20);
_20 = &_2;
StorageLive(_21);
_21 = (_16, _19);
_22 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _20, move _21) -> [return: bb10, unwind: bb12];
}
bb10: {
StorageDead(_21);
StorageDead(_20);
bb5: {
(_5.0: usize) = move _13;
StorageDead(_13);
_14 = Option::<usize>::Some(_12);
StorageDead(_11);
StorageDead(_12);
_15 = ((_14 as Some).0: usize);
_16 = Len((*_1));
_17 = Lt(_15, _16);
assert(move _17, "index out of bounds: the length is {} but the index is {}", move _16, _15) -> [success: bb6, unwind: bb8];
}
bb6: {
_18 = &(*_1)[_15];
StorageLive(_19);
_19 = &_2;
StorageLive(_20);
_20 = (_15, _18);
_21 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _19, move _20) -> [return: bb7, unwind: bb8];
}
bb7: {
StorageDead(_20);
StorageDead(_19);
StorageDead(_14);
goto -> bb1;
}
bb11: {
unreachable;
bb8 (cleanup): {
drop(_2) -> [return: bb9, unwind terminate(cleanup)];
}
bb12 (cleanup): {
drop(_2) -> [return: bb13, unwind terminate(cleanup)];
}
bb13 (cleanup): {
bb9 (cleanup): {
resume;
}
}
ALLOC0 (size: 16, align: 8) {
00 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ ........
}

View file

@ -6,65 +6,51 @@ fn new(_1: Result<T, E>) -> Result<T, E> {
let mut _2: isize;
let _3: T;
let mut _4: std::ops::ControlFlow<E, T>;
let _5: E;
let mut _6: isize;
let _7: T;
let _8: E;
let _5: T;
let _6: E;
let _7: E;
scope 1 {
debug v => _3;
}
scope 2 {
debug e => _5;
debug e => _6;
}
scope 3 {
debug v => _7;
debug v => _5;
}
scope 4 {
debug e => _8;
debug e => _7;
}
bb0: {
StorageLive(_4);
_2 = discriminant(_1);
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb7];
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4];
}
bb1: {
_3 = move ((_1 as Ok).0: T);
_4 = ControlFlow::<E, T>::Continue(_3);
_5 = move ((_4 as Continue).0: T);
_0 = Result::<T, E>::Ok(_5);
StorageDead(_4);
goto -> bb3;
}
bb2: {
_5 = move ((_1 as Err).0: E);
_4 = ControlFlow::<E, T>::Break(_5);
_6 = move ((_1 as Err).0: E);
_4 = ControlFlow::<E, T>::Break(_6);
_7 = move ((_4 as Break).0: E);
_0 = Result::<T, E>::Err(_7);
StorageDead(_4);
goto -> bb3;
}
bb3: {
_6 = discriminant(_4);
switchInt(move _6) -> [0: bb4, 1: bb5, otherwise: bb7];
}
bb4: {
_7 = move ((_4 as Continue).0: T);
_0 = Result::<T, E>::Ok(_7);
StorageDead(_4);
goto -> bb6;
}
bb5: {
_8 = move ((_4 as Break).0: E);
_0 = Result::<T, E>::Err(_8);
StorageDead(_4);
goto -> bb6;
}
bb6: {
return;
}
bb7: {
bb4: {
unreachable;
}
}

View file

@ -1,5 +1,5 @@
- // MIR for `identity` before SeparateConstSwitch
+ // MIR for `identity` after SeparateConstSwitch
- // MIR for `identity` before JumpThreading
+ // MIR for `identity` after JumpThreading
fn identity(_1: Result<i32, i32>) -> Result<i32, i32> {
debug x => _1;
@ -79,7 +79,8 @@
StorageDead(_8);
StorageDead(_3);
_4 = discriminant(_2);
switchInt(move _4) -> [0: bb1, 1: bb2, otherwise: bb6];
- switchInt(move _4) -> [0: bb1, 1: bb2, otherwise: bb6];
+ goto -> bb1;
}
bb4: {
@ -88,7 +89,8 @@
_11 = Result::<Infallible, i32>::Err(_10);
_2 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _11);
StorageDead(_11);
goto -> bb3;
- goto -> bb3;
+ goto -> bb7;
}
bb5: {
@ -99,6 +101,15 @@
bb6: {
unreachable;
+ }
+
+ bb7: {
+ StorageDead(_10);
+ StorageDead(_9);
+ StorageDead(_8);
+ StorageDead(_3);
+ _4 = discriminant(_2);
+ goto -> bb2;
}
}

View file

@ -6,7 +6,7 @@
use std::ops::ControlFlow;
// EMIT_MIR separate_const_switch.too_complex.SeparateConstSwitch.diff
// EMIT_MIR separate_const_switch.too_complex.JumpThreading.diff
fn too_complex(x: Result<i32, usize>) -> Option<i32> {
// The pass should break the outer match into
// two blocks that only have one parent each.
@ -23,7 +23,7 @@ fn too_complex(x: Result<i32, usize>) -> Option<i32> {
}
}
// EMIT_MIR separate_const_switch.identity.SeparateConstSwitch.diff
// EMIT_MIR separate_const_switch.identity.JumpThreading.diff
fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
Ok(x?)
}

View file

@ -1,5 +1,5 @@
- // MIR for `too_complex` before SeparateConstSwitch
+ // MIR for `too_complex` after SeparateConstSwitch
- // MIR for `too_complex` before JumpThreading
+ // MIR for `too_complex` after JumpThreading
fn too_complex(_1: Result<i32, usize>) -> Option<i32> {
debug x => _1;
@ -33,7 +33,8 @@
bb1: {
_5 = ((_1 as Err).0: usize);
_2 = ControlFlow::<usize, i32>::Break(_5);
goto -> bb3;
- goto -> bb3;
+ goto -> bb8;
}
bb2: {
@ -44,7 +45,8 @@
bb3: {
_6 = discriminant(_2);
switchInt(move _6) -> [0: bb5, 1: bb4, otherwise: bb7];
- switchInt(move _6) -> [0: bb5, 1: bb4, otherwise: bb7];
+ goto -> bb5;
}
bb4: {
@ -68,6 +70,11 @@
bb7: {
unreachable;
+ }
+
+ bb8: {
+ _6 = discriminant(_2);
+ goto -> bb4;
}
}

View file

@ -1,15 +0,0 @@
// run-pass
#![allow(dead_code)]
// Checks that mutable static items can have mutable slices
static mut TEST: &'static mut [isize] = &mut [1];
static mut EMPTY: &'static mut [isize] = &mut [];
pub fn main() {
unsafe {
TEST[0] += 1;
assert_eq!(TEST[0], 2);
}
}

View file

@ -23,10 +23,10 @@ const fn const_bar<T>(x: T) -> T {
x
}
global_asm!("{}", const S);
//~^ ERROR constants cannot refer to statics
//~^ ERROR referencing statics
global_asm!("{}", const const_foo(0));
global_asm!("{}", const const_foo(S));
//~^ ERROR constants cannot refer to statics
//~^ ERROR referencing statics
global_asm!("{}", const const_bar(0));
global_asm!("{}", const const_bar(S));
//~^ ERROR constants cannot refer to statics
//~^ ERROR referencing statics

View file

@ -1,27 +1,39 @@
error[E0013]: constants cannot refer to statics
error[E0658]: referencing statics in constants is unstable
--> $DIR/type-check-4.rs:25:25
|
LL | global_asm!("{}", const S);
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
= note: see issue #119618 <https://github.com/rust-lang/rust/issues/119618> for more information
= help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
= help: to fix this, the value can be extracted to a `const` and then used.
error[E0013]: constants cannot refer to statics
error[E0658]: referencing statics in constants is unstable
--> $DIR/type-check-4.rs:28:35
|
LL | global_asm!("{}", const const_foo(S));
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
= note: see issue #119618 <https://github.com/rust-lang/rust/issues/119618> for more information
= help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
= help: to fix this, the value can be extracted to a `const` and then used.
error[E0013]: constants cannot refer to statics
error[E0658]: referencing statics in constants is unstable
--> $DIR/type-check-4.rs:31:35
|
LL | global_asm!("{}", const const_bar(S));
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
= note: see issue #119618 <https://github.com/rust-lang/rust/issues/119618> for more information
= help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
= help: to fix this, the value can be extracted to a `const` and then used.
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0013`.
For more information about this error, try `rustc --explain E0658`.

View file

@ -19,10 +19,10 @@ const fn const_bar<T>(x: T) -> T {
x
}
global_asm!("{}", const S);
//~^ ERROR constants cannot refer to statics
//~^ ERROR referencing statics
global_asm!("{}", const const_foo(0));
global_asm!("{}", const const_foo(S));
//~^ ERROR constants cannot refer to statics
//~^ ERROR referencing statics
global_asm!("{}", const const_bar(0));
global_asm!("{}", const const_bar(S));
//~^ ERROR constants cannot refer to statics
//~^ ERROR referencing statics

View file

@ -1,27 +1,39 @@
error[E0013]: constants cannot refer to statics
error[E0658]: referencing statics in constants is unstable
--> $DIR/type-check-4.rs:21:25
|
LL | global_asm!("{}", const S);
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
= note: see issue #119618 <https://github.com/rust-lang/rust/issues/119618> for more information
= help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
= help: to fix this, the value can be extracted to a `const` and then used.
error[E0013]: constants cannot refer to statics
error[E0658]: referencing statics in constants is unstable
--> $DIR/type-check-4.rs:24:35
|
LL | global_asm!("{}", const const_foo(S));
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
= note: see issue #119618 <https://github.com/rust-lang/rust/issues/119618> for more information
= help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
= help: to fix this, the value can be extracted to a `const` and then used.
error[E0013]: constants cannot refer to statics
error[E0658]: referencing statics in constants is unstable
--> $DIR/type-check-4.rs:27:35
|
LL | global_asm!("{}", const const_bar(S));
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
= note: see issue #119618 <https://github.com/rust-lang/rust/issues/119618> for more information
= help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
= help: to fix this, the value can be extracted to a `const` and then used.
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0013`.
For more information about this error, try `rustc --explain E0658`.

View file

@ -28,9 +28,9 @@ impl Bar for AssocNoCopy {
impl Thing for AssocNoCopy {
type Out = Box<dyn Bar<Assoc: Copy>>;
//~^ ERROR associated type bounds are not allowed in `dyn` types
fn func() -> Self::Out {
//~^ ERROR the trait bound `String: Copy` is not satisfied
Box::new(AssocNoCopy)
}
}

View file

@ -1,9 +1,13 @@
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/assoc-type-eq-with-dyn-atb-fail.rs:32:18
error: associated type bounds are not allowed in `dyn` types
--> $DIR/assoc-type-eq-with-dyn-atb-fail.rs:30:28
|
LL | fn func() -> Self::Out {
| ^^^^^^^^^ the trait `Copy` is not implemented for `String`
LL | type Out = Box<dyn Bar<Assoc: Copy>>;
| ^^^^^^^^^^^
|
help: use `impl Trait` to introduce a type instead
|
LL | type Out = Box<dyn Bar<Assoc = impl Copy>>;
| ~~~~~~
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -7,7 +7,7 @@ trait B {
fn f()
where
dyn for<'j> B<AssocType: 'j>:,
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
//~^ ERROR associated type bounds are not allowed in `dyn` types
{
}

View file

@ -1,4 +1,4 @@
error: associated type bounds are only allowed in where clauses and function signatures, not in bounds
error: associated type bounds are not allowed in `dyn` types
--> $DIR/bad-universal-in-dyn-in-where-clause.rs:9:19
|
LL | dyn for<'j> B<AssocType: 'j>:,

View file

@ -8,6 +8,6 @@ trait Trait2 {}
// It's not possible to insert a universal `impl Trait` here!
impl dyn Trait<Item: Trait2> {}
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
//~^ ERROR associated type bounds are not allowed in `dyn` types
fn main() {}

Some files were not shown because too many files have changed in this diff Show more