mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
Support tail calls in mir via TerminatorKind::TailCall
This commit is contained in:
parent
e2cf31a614
commit
484152d562
@ -728,6 +728,12 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
|
|||||||
}
|
}
|
||||||
self.mutate_place(loc, (*destination, span), Deep, flow_state);
|
self.mutate_place(loc, (*destination, span), Deep, flow_state);
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { func, args, fn_span: _ } => {
|
||||||
|
self.consume_operand(loc, (func, span), flow_state);
|
||||||
|
for arg in args {
|
||||||
|
self.consume_operand(loc, (&arg.node, arg.span), flow_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
|
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
|
||||||
self.consume_operand(loc, (cond, span), flow_state);
|
self.consume_operand(loc, (cond, span), flow_state);
|
||||||
if let AssertKind::BoundsCheck { len, index } = &**msg {
|
if let AssertKind::BoundsCheck { len, index } = &**msg {
|
||||||
@ -814,9 +820,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
|
|||||||
|
|
||||||
TerminatorKind::UnwindResume
|
TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::CoroutineDrop => {
|
| TerminatorKind::CoroutineDrop => {
|
||||||
// Returning from the function implicitly kills storage for all locals and statics.
|
|
||||||
// Often, the storage will already have been killed by an explicit
|
|
||||||
// StorageDead, but we don't always emit those (notably on unwind paths),
|
// StorageDead, but we don't always emit those (notably on unwind paths),
|
||||||
// so this "extra check" serves as a kind of backup.
|
// so this "extra check" serves as a kind of backup.
|
||||||
let borrow_set = self.borrow_set.clone();
|
let borrow_set = self.borrow_set.clone();
|
||||||
|
@ -125,6 +125,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
|
|||||||
}
|
}
|
||||||
self.mutate_place(location, *destination, Deep);
|
self.mutate_place(location, *destination, Deep);
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { func, args, .. } => {
|
||||||
|
self.consume_operand(location, func);
|
||||||
|
for arg in args {
|
||||||
|
self.consume_operand(location, &arg.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
|
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
|
||||||
self.consume_operand(location, cond);
|
self.consume_operand(location, cond);
|
||||||
use rustc_middle::mir::AssertKind;
|
use rustc_middle::mir::AssertKind;
|
||||||
|
@ -1352,7 +1352,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
// FIXME: check the values
|
// FIXME: check the values
|
||||||
}
|
}
|
||||||
TerminatorKind::Call { func, args, destination, call_source, target, .. } => {
|
TerminatorKind::Call { func, args, .. }
|
||||||
|
| TerminatorKind::TailCall { func, args, .. } => {
|
||||||
|
let call_source = match term.kind {
|
||||||
|
TerminatorKind::Call { call_source, .. } => call_source,
|
||||||
|
TerminatorKind::TailCall { .. } => CallSource::Normal,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
self.check_operand(func, term_location);
|
self.check_operand(func, term_location);
|
||||||
for arg in args {
|
for arg in args {
|
||||||
self.check_operand(&arg.node, term_location);
|
self.check_operand(&arg.node, term_location);
|
||||||
@ -1425,7 +1432,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_call_dest(body, term, &sig, *destination, *target, term_location);
|
if let TerminatorKind::Call { destination, target, .. } = term.kind {
|
||||||
|
self.check_call_dest(body, term, &sig, destination, target, term_location);
|
||||||
|
}
|
||||||
|
|
||||||
// The ordinary liveness rules will ensure that all
|
// The ordinary liveness rules will ensure that all
|
||||||
// regions in the type of the callee are live here. We
|
// regions in the type of the callee are live here. We
|
||||||
@ -1443,7 +1452,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
.add_location(region_vid, term_location);
|
.add_location(region_vid, term_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source);
|
self.check_call_inputs(body, term, func, &sig, args, term_location, call_source);
|
||||||
}
|
}
|
||||||
TerminatorKind::Assert { cond, msg, .. } => {
|
TerminatorKind::Assert { cond, msg, .. } => {
|
||||||
self.check_operand(cond, term_location);
|
self.check_operand(cond, term_location);
|
||||||
@ -1675,6 +1684,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
span_mirbug!(self, block_data, "return on cleanup block")
|
span_mirbug!(self, block_data, "return on cleanup block")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { .. } => {
|
||||||
|
if is_cleanup {
|
||||||
|
span_mirbug!(self, block_data, "tailcall on cleanup block")
|
||||||
|
}
|
||||||
|
}
|
||||||
TerminatorKind::CoroutineDrop { .. } => {
|
TerminatorKind::CoroutineDrop { .. } => {
|
||||||
if is_cleanup {
|
if is_cleanup {
|
||||||
span_mirbug!(self, block_data, "coroutine_drop in cleanup block")
|
span_mirbug!(self, block_data, "coroutine_drop in cleanup block")
|
||||||
|
@ -491,6 +491,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// FIXME(explicit_tail_calls): add support for tail calls to the cranelift backend, once cranelift supports tail calls
|
||||||
|
TerminatorKind::TailCall { fn_span, .. } => span_bug!(
|
||||||
|
*fn_span,
|
||||||
|
"tail calls are not yet supported in `rustc_codegen_cranelift` backend"
|
||||||
|
),
|
||||||
TerminatorKind::InlineAsm {
|
TerminatorKind::InlineAsm {
|
||||||
template,
|
template,
|
||||||
operands,
|
operands,
|
||||||
|
@ -565,6 +565,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
|
|||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { .. } => return None,
|
||||||
TerminatorKind::Call { .. } => {}
|
TerminatorKind::Call { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,6 +281,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
|
|||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::CoroutineDrop
|
| TerminatorKind::CoroutineDrop
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::SwitchInt { .. }
|
| TerminatorKind::SwitchInt { .. }
|
||||||
|
@ -1389,6 +1389,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
fn_span,
|
fn_span,
|
||||||
mergeable_succ(),
|
mergeable_succ(),
|
||||||
),
|
),
|
||||||
|
mir::TerminatorKind::TailCall { .. } => {
|
||||||
|
// FIXME(explicit_tail_calls): implement tail calls in ssa backend
|
||||||
|
span_bug!(
|
||||||
|
terminator.source_info.span,
|
||||||
|
"`TailCall` terminator is not yet supported by `rustc_codegen_ssa`"
|
||||||
|
)
|
||||||
|
}
|
||||||
mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => {
|
mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => {
|
||||||
bug!("coroutine ops in codegen")
|
bug!("coroutine ops in codegen")
|
||||||
}
|
}
|
||||||
|
@ -135,6 +135,8 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
|
|||||||
ccx: &'mir ConstCx<'mir, 'tcx>,
|
ccx: &'mir ConstCx<'mir, 'tcx>,
|
||||||
tainted_by_errors: Option<ErrorGuaranteed>,
|
tainted_by_errors: Option<ErrorGuaranteed>,
|
||||||
) -> ConstQualifs {
|
) -> ConstQualifs {
|
||||||
|
// FIXME(explicit_tail_calls): uhhhh I think we can return without return now, does it change anything
|
||||||
|
|
||||||
// Find the `Return` terminator if one exists.
|
// Find the `Return` terminator if one exists.
|
||||||
//
|
//
|
||||||
// If no `Return` terminator exists, this MIR is divergent. Just return the conservative
|
// If no `Return` terminator exists, this MIR is divergent. Just return the conservative
|
||||||
@ -711,7 +713,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||||||
self.super_terminator(terminator, location);
|
self.super_terminator(terminator, location);
|
||||||
|
|
||||||
match &terminator.kind {
|
match &terminator.kind {
|
||||||
TerminatorKind::Call { func, args, fn_span, call_source, .. } => {
|
TerminatorKind::Call { func, args, fn_span, .. }
|
||||||
|
| TerminatorKind::TailCall { func, args, fn_span, .. } => {
|
||||||
|
let call_source = match terminator.kind {
|
||||||
|
TerminatorKind::Call { call_source, .. } => call_source,
|
||||||
|
TerminatorKind::TailCall { .. } => CallSource::Normal,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
|
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
|
||||||
let caller = self.def_id();
|
let caller = self.def_id();
|
||||||
|
|
||||||
@ -783,7 +792,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||||||
callee,
|
callee,
|
||||||
args: fn_args,
|
args: fn_args,
|
||||||
span: *fn_span,
|
span: *fn_span,
|
||||||
call_source: *call_source,
|
call_source,
|
||||||
feature: Some(if tcx.features().const_trait_impl {
|
feature: Some(if tcx.features().const_trait_impl {
|
||||||
sym::effects
|
sym::effects
|
||||||
} else {
|
} else {
|
||||||
@ -830,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||||||
callee,
|
callee,
|
||||||
args: fn_args,
|
args: fn_args,
|
||||||
span: *fn_span,
|
span: *fn_span,
|
||||||
call_source: *call_source,
|
call_source,
|
||||||
feature: None,
|
feature: None,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -108,6 +108,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
|
|||||||
|
|
||||||
mir::TerminatorKind::UnwindTerminate(_)
|
mir::TerminatorKind::UnwindTerminate(_)
|
||||||
| mir::TerminatorKind::Call { .. }
|
| mir::TerminatorKind::Call { .. }
|
||||||
|
| mir::TerminatorKind::TailCall { .. }
|
||||||
| mir::TerminatorKind::Assert { .. }
|
| mir::TerminatorKind::Assert { .. }
|
||||||
| mir::TerminatorKind::FalseEdge { .. }
|
| mir::TerminatorKind::FalseEdge { .. }
|
||||||
| mir::TerminatorKind::FalseUnwind { .. }
|
| mir::TerminatorKind::FalseUnwind { .. }
|
||||||
|
@ -172,6 +172,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TailCall { func: _, args: _, fn_span: _ } => todo!(),
|
||||||
|
|
||||||
Drop { place, target, unwind, replace: _ } => {
|
Drop { place, target, unwind, replace: _ } => {
|
||||||
let place = self.eval_place(place)?;
|
let place = self.eval_place(place)?;
|
||||||
let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||||
|
@ -845,6 +845,16 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||||||
}
|
}
|
||||||
write!(fmt, ")")
|
write!(fmt, ")")
|
||||||
}
|
}
|
||||||
|
TailCall { func, args, .. } => {
|
||||||
|
write!(fmt, "tailcall {func:?}(")?;
|
||||||
|
for (index, arg) in args.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{:?}", arg)?;
|
||||||
|
}
|
||||||
|
write!(fmt, ")")
|
||||||
|
}
|
||||||
Assert { cond, expected, msg, .. } => {
|
Assert { cond, expected, msg, .. } => {
|
||||||
write!(fmt, "assert(")?;
|
write!(fmt, "assert(")?;
|
||||||
if !expected {
|
if !expected {
|
||||||
@ -912,7 +922,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||||||
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
||||||
use self::TerminatorKind::*;
|
use self::TerminatorKind::*;
|
||||||
match *self {
|
match *self {
|
||||||
Return | UnwindResume | UnwindTerminate(_) | Unreachable | CoroutineDrop => vec![],
|
Return
|
||||||
|
| TailCall { .. }
|
||||||
|
| UnwindResume
|
||||||
|
| UnwindTerminate(_)
|
||||||
|
| Unreachable
|
||||||
|
| CoroutineDrop => vec![],
|
||||||
Goto { .. } => vec!["".into()],
|
Goto { .. } => vec!["".into()],
|
||||||
SwitchInt { ref targets, .. } => targets
|
SwitchInt { ref targets, .. } => targets
|
||||||
.values
|
.values
|
||||||
|
@ -744,6 +744,36 @@ pub enum TerminatorKind<'tcx> {
|
|||||||
fn_span: Span,
|
fn_span: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Tail call.
|
||||||
|
///
|
||||||
|
/// Roughly speaking this is a chimera of [`Call`] and [`Return`], with some caveats.
|
||||||
|
/// Semantically tail calls consists of two actions:
|
||||||
|
/// - pop of the current stack frame
|
||||||
|
/// - a call to the `func`, with the return address of the **current** caller
|
||||||
|
/// - so that a `return` inside `func` returns to the caller of the caller
|
||||||
|
/// of the function that is currently being executed
|
||||||
|
///
|
||||||
|
/// Note that in difference with [`Call`] this is missing
|
||||||
|
/// - `destination` (because it's always the return place)
|
||||||
|
/// - `target` (because it's always taken from the current stack frame)
|
||||||
|
/// - `unwind` (because it's always taken from the current stack frame)
|
||||||
|
///
|
||||||
|
/// [`Call`]: TerminatorKind::Call
|
||||||
|
/// [`Return`]: TerminatorKind::Return
|
||||||
|
TailCall {
|
||||||
|
/// The function that’s being called.
|
||||||
|
func: Operand<'tcx>,
|
||||||
|
/// Arguments the function is called with.
|
||||||
|
/// These are owned by the callee, which is free to modify them.
|
||||||
|
/// This allows the memory occupied by "by-value" arguments to be
|
||||||
|
/// reused across function calls without duplicating the contents.
|
||||||
|
args: Vec<Spanned<Operand<'tcx>>>,
|
||||||
|
// FIXME(explicit_tail_calls): should we have the span for `become`? is this span accurate? do we need it?
|
||||||
|
/// This `Span` is the span of the function, without the dot and receiver
|
||||||
|
/// (e.g. `foo(a, b)` in `x.foo(a, b)`
|
||||||
|
fn_span: Span,
|
||||||
|
},
|
||||||
|
|
||||||
/// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
|
/// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
|
||||||
/// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
|
/// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
|
||||||
/// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
|
/// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
|
||||||
@ -870,6 +900,7 @@ impl TerminatorKind<'_> {
|
|||||||
TerminatorKind::Unreachable => "Unreachable",
|
TerminatorKind::Unreachable => "Unreachable",
|
||||||
TerminatorKind::Drop { .. } => "Drop",
|
TerminatorKind::Drop { .. } => "Drop",
|
||||||
TerminatorKind::Call { .. } => "Call",
|
TerminatorKind::Call { .. } => "Call",
|
||||||
|
TerminatorKind::TailCall { .. } => "TailCall",
|
||||||
TerminatorKind::Assert { .. } => "Assert",
|
TerminatorKind::Assert { .. } => "Assert",
|
||||||
TerminatorKind::Yield { .. } => "Yield",
|
TerminatorKind::Yield { .. } => "Yield",
|
||||||
TerminatorKind::CoroutineDrop => "CoroutineDrop",
|
TerminatorKind::CoroutineDrop => "CoroutineDrop",
|
||||||
|
@ -439,6 +439,7 @@ mod helper {
|
|||||||
| CoroutineDrop
|
| CoroutineDrop
|
||||||
| Return
|
| Return
|
||||||
| Unreachable
|
| Unreachable
|
||||||
|
| TailCall { .. }
|
||||||
| Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
|
| Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
|
||||||
InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
|
InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
|
||||||
targets.iter().copied().chain(Some(u))
|
targets.iter().copied().chain(Some(u))
|
||||||
@ -479,6 +480,7 @@ mod helper {
|
|||||||
| CoroutineDrop
|
| CoroutineDrop
|
||||||
| Return
|
| Return
|
||||||
| Unreachable
|
| Unreachable
|
||||||
|
| TailCall { .. }
|
||||||
| Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
|
| Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
|
||||||
InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
|
InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
|
||||||
targets.iter_mut().chain(Some(u))
|
targets.iter_mut().chain(Some(u))
|
||||||
@ -501,6 +503,7 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::CoroutineDrop
|
| TerminatorKind::CoroutineDrop
|
||||||
| TerminatorKind::Yield { .. }
|
| TerminatorKind::Yield { .. }
|
||||||
@ -521,6 +524,7 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::CoroutineDrop
|
| TerminatorKind::CoroutineDrop
|
||||||
| TerminatorKind::Yield { .. }
|
| TerminatorKind::Yield { .. }
|
||||||
@ -606,9 +610,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||||||
pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
|
pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
|
||||||
use TerminatorKind::*;
|
use TerminatorKind::*;
|
||||||
match *self {
|
match *self {
|
||||||
Return | UnwindResume | UnwindTerminate(_) | CoroutineDrop | Unreachable => {
|
Return
|
||||||
TerminatorEdges::None
|
| TailCall { .. }
|
||||||
}
|
| UnwindResume
|
||||||
|
| UnwindTerminate(_)
|
||||||
|
| CoroutineDrop
|
||||||
|
| Unreachable => TerminatorEdges::None,
|
||||||
|
|
||||||
Goto { target } => TerminatorEdges::Single(target),
|
Goto { target } => TerminatorEdges::Single(target),
|
||||||
|
|
||||||
|
@ -540,6 +540,17 @@ macro_rules! make_mir_visitor {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TerminatorKind::TailCall {
|
||||||
|
func,
|
||||||
|
args,
|
||||||
|
fn_span: _,
|
||||||
|
} => {
|
||||||
|
self.visit_operand(func, location);
|
||||||
|
for arg in args {
|
||||||
|
self.visit_operand(&$($mutability)? arg.node, location);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
TerminatorKind::Assert {
|
TerminatorKind::Assert {
|
||||||
cond,
|
cond,
|
||||||
expected: _,
|
expected: _,
|
||||||
|
@ -2,7 +2,9 @@ use crate::build::scope::BreakableTarget;
|
|||||||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
||||||
use rustc_middle::middle::region;
|
use rustc_middle::middle::region;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
|
use rustc_middle::span_bug;
|
||||||
use rustc_middle::thir::*;
|
use rustc_middle::thir::*;
|
||||||
|
use rustc_span::source_map::Spanned;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
@ -91,9 +93,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
ExprKind::Return { value } => {
|
ExprKind::Return { value } => {
|
||||||
this.break_scope(block, value, BreakableTarget::Return, source_info)
|
this.break_scope(block, value, BreakableTarget::Return, source_info)
|
||||||
}
|
}
|
||||||
// FIXME(explicit_tail_calls): properly lower tail calls here
|
|
||||||
ExprKind::Become { value } => {
|
ExprKind::Become { value } => {
|
||||||
this.break_scope(block, Some(value), BreakableTarget::Return, source_info)
|
let v = &this.thir[value];
|
||||||
|
let ExprKind::Scope { value, .. } = v.kind else {
|
||||||
|
span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
|
||||||
|
};
|
||||||
|
|
||||||
|
let v = &this.thir[value];
|
||||||
|
let ExprKind::Call { ref args, fun, fn_span, .. } = v.kind else {
|
||||||
|
span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
|
||||||
|
};
|
||||||
|
|
||||||
|
let fun = unpack!(block = this.as_local_operand(block, fun));
|
||||||
|
let args: Vec<_> = args
|
||||||
|
.into_iter()
|
||||||
|
.copied()
|
||||||
|
.map(|arg| Spanned {
|
||||||
|
node: unpack!(block = this.as_local_call_operand(block, arg)),
|
||||||
|
span: this.thir.exprs[arg].span,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
this.record_operands_moved(&args);
|
||||||
|
|
||||||
|
debug!("expr_into_dest: fn_span={:?}", fn_span);
|
||||||
|
|
||||||
|
this.cfg.terminate(
|
||||||
|
block,
|
||||||
|
source_info,
|
||||||
|
TerminatorKind::TailCall { func: fun, args, fn_span },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.cfg.start_new_block().unit()
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -1523,6 +1523,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
|
|||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::Yield { .. }
|
| TerminatorKind::Yield { .. }
|
||||||
| TerminatorKind::CoroutineDrop
|
| TerminatorKind::CoroutineDrop
|
||||||
|
@ -196,6 +196,8 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx
|
|||||||
| TerminatorKind::CoroutineDrop
|
| TerminatorKind::CoroutineDrop
|
||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
// FIXME(explicit_tail_calls) Is this right??
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive),
|
| TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive),
|
||||||
|
|
||||||
|
@ -145,6 +145,7 @@ where
|
|||||||
| TerminatorKind::InlineAsm { .. }
|
| TerminatorKind::InlineAsm { .. }
|
||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::SwitchInt { .. }
|
| TerminatorKind::SwitchInt { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::Yield { .. } => {}
|
| TerminatorKind::Yield { .. } => {}
|
||||||
|
@ -288,6 +288,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||||||
| TerminatorKind::Goto { .. }
|
| TerminatorKind::Goto { .. }
|
||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::SwitchInt { .. }
|
| TerminatorKind::SwitchInt { .. }
|
||||||
| TerminatorKind::Unreachable => {}
|
| TerminatorKind::Unreachable => {}
|
||||||
}
|
}
|
||||||
@ -325,6 +326,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||||||
| TerminatorKind::Goto { .. }
|
| TerminatorKind::Goto { .. }
|
||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::SwitchInt { .. }
|
| TerminatorKind::SwitchInt { .. }
|
||||||
| TerminatorKind::Unreachable => {}
|
| TerminatorKind::Unreachable => {}
|
||||||
}
|
}
|
||||||
|
@ -489,6 +489,12 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
|
|||||||
self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly);
|
self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { ref func, ref args, .. } => {
|
||||||
|
self.gather_operand(func);
|
||||||
|
for arg in args {
|
||||||
|
self.gather_operand(&arg.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
TerminatorKind::InlineAsm {
|
TerminatorKind::InlineAsm {
|
||||||
template: _,
|
template: _,
|
||||||
ref operands,
|
ref operands,
|
||||||
|
@ -269,6 +269,9 @@ pub trait ValueAnalysis<'tcx> {
|
|||||||
TerminatorKind::SwitchInt { discr, targets } => {
|
TerminatorKind::SwitchInt { discr, targets } => {
|
||||||
return self.handle_switch_int(discr, targets, state);
|
return self.handle_switch_int(discr, targets, state);
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { .. } => {
|
||||||
|
// FIXME(explicit_tail_calls): determine if we need to do something here (probably not)
|
||||||
|
}
|
||||||
TerminatorKind::Goto { .. }
|
TerminatorKind::Goto { .. }
|
||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
|
@ -1367,6 +1367,10 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
|
|||||||
| TerminatorKind::Call { .. }
|
| TerminatorKind::Call { .. }
|
||||||
| TerminatorKind::InlineAsm { .. }
|
| TerminatorKind::InlineAsm { .. }
|
||||||
| TerminatorKind::Assert { .. } => return true,
|
| TerminatorKind::Assert { .. } => return true,
|
||||||
|
|
||||||
|
TerminatorKind::TailCall { .. } => {
|
||||||
|
unreachable!("tail calls can't be present in generators")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1916,6 +1920,7 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> {
|
|||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::Drop { .. }
|
| TerminatorKind::Drop { .. }
|
||||||
| TerminatorKind::Assert { .. }
|
| TerminatorKind::Assert { .. }
|
||||||
|
@ -358,9 +358,12 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
|
|||||||
}
|
}
|
||||||
|
|
||||||
// These terminators have no coverage-relevant successors.
|
// These terminators have no coverage-relevant successors.
|
||||||
CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate(_) => {
|
CoroutineDrop
|
||||||
CoverageSuccessors::NotChainable(&[])
|
| Return
|
||||||
}
|
| TailCall { .. }
|
||||||
|
| Unreachable
|
||||||
|
| UnwindResume
|
||||||
|
| UnwindTerminate(_) => CoverageSuccessors::NotChainable(&[]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +193,8 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
|
|||||||
| TerminatorKind::Goto { .. } => None,
|
| TerminatorKind::Goto { .. } => None,
|
||||||
|
|
||||||
// Call `func` operand can have a more specific span when part of a chain of calls
|
// Call `func` operand can have a more specific span when part of a chain of calls
|
||||||
| TerminatorKind::Call { ref func, .. } => {
|
TerminatorKind::Call { ref func, .. }
|
||||||
|
| TerminatorKind::TailCall { ref func, .. } => {
|
||||||
let mut span = terminator.source_info.span;
|
let mut span = terminator.source_info.span;
|
||||||
if let mir::Operand::Constant(box constant) = func {
|
if let mir::Operand::Constant(box constant) = func {
|
||||||
if constant.span.lo() > span.lo() {
|
if constant.span.lo() > span.lo() {
|
||||||
|
@ -628,6 +628,12 @@ impl WriteInfo {
|
|||||||
self.add_operand(&arg.node);
|
self.add_operand(&arg.node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { func, args, .. } => {
|
||||||
|
self.add_operand(func);
|
||||||
|
for arg in args {
|
||||||
|
self.add_operand(&arg.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
TerminatorKind::InlineAsm { operands, .. } => {
|
TerminatorKind::InlineAsm { operands, .. } => {
|
||||||
for asm_operand in operands {
|
for asm_operand in operands {
|
||||||
match asm_operand {
|
match asm_operand {
|
||||||
|
@ -383,6 +383,8 @@ impl<'tcx> Inliner<'tcx> {
|
|||||||
) -> Option<CallSite<'tcx>> {
|
) -> Option<CallSite<'tcx>> {
|
||||||
// Only consider direct calls to functions
|
// Only consider direct calls to functions
|
||||||
let terminator = bb_data.terminator();
|
let terminator = bb_data.terminator();
|
||||||
|
|
||||||
|
// FIXME(explicit_tail_calls): figure out if we can inline tail calls
|
||||||
if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind {
|
if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind {
|
||||||
let func_ty = func.ty(caller_body, self.tcx);
|
let func_ty = func.ty(caller_body, self.tcx);
|
||||||
if let ty::FnDef(def_id, args) = *func_ty.kind() {
|
if let ty::FnDef(def_id, args) = *func_ty.kind() {
|
||||||
@ -550,6 +552,9 @@ impl<'tcx> Inliner<'tcx> {
|
|||||||
// inline-asm is detected. LLVM will still possibly do an inline later on
|
// inline-asm is detected. LLVM will still possibly do an inline later on
|
||||||
// if the no-attribute function ends up with the same instruction set anyway.
|
// if the no-attribute function ends up with the same instruction set anyway.
|
||||||
return Err("Cannot move inline-asm across instruction sets");
|
return Err("Cannot move inline-asm across instruction sets");
|
||||||
|
} else if let TerminatorKind::TailCall { .. } = term.kind {
|
||||||
|
// FIXME(explicit_tail_calls): figure out how exactly functions containing tail calls can be inlined (and if they even should)
|
||||||
|
return Err("can't inline functions with tail calls");
|
||||||
} else {
|
} else {
|
||||||
work_list.extend(term.successors())
|
work_list.extend(term.successors())
|
||||||
}
|
}
|
||||||
@ -1038,6 +1043,10 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
|
|||||||
*target = self.map_block(*target);
|
*target = self.map_block(*target);
|
||||||
*unwind = self.map_unwind(*unwind);
|
*unwind = self.map_unwind(*unwind);
|
||||||
}
|
}
|
||||||
|
TerminatorKind::TailCall { .. } => {
|
||||||
|
// check_mir_body forbids tail calls
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
TerminatorKind::Call { ref mut target, ref mut unwind, .. } => {
|
TerminatorKind::Call { ref mut target, ref mut unwind, .. } => {
|
||||||
if let Some(ref mut tgt) = *target {
|
if let Some(ref mut tgt) = *target {
|
||||||
*tgt = self.map_block(*tgt);
|
*tgt = self.map_block(*tgt);
|
||||||
|
@ -596,6 +596,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
|
|||||||
TerminatorKind::UnwindResume
|
TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"),
|
| TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"),
|
||||||
// Disallowed during optimizations.
|
// Disallowed during optimizations.
|
||||||
|
@ -799,6 +799,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
|||||||
| TerminatorKind::UnwindResume
|
| TerminatorKind::UnwindResume
|
||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::Drop { .. }
|
| TerminatorKind::Drop { .. }
|
||||||
| TerminatorKind::Yield { .. }
|
| TerminatorKind::Yield { .. }
|
||||||
|
@ -38,7 +38,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
|
|||||||
self.super_terminator(terminator, location);
|
self.super_terminator(terminator, location);
|
||||||
let span = || self.body.source_info(location).span;
|
let span = || self.body.source_info(location).span;
|
||||||
match &terminator.kind {
|
match &terminator.kind {
|
||||||
mir::TerminatorKind::Call { func, .. } => {
|
mir::TerminatorKind::Call { func, .. } | mir::TerminatorKind::TailCall { func, .. } => {
|
||||||
let callee_ty = func.ty(self.body, self.tcx);
|
let callee_ty = func.ty(self.body, self.tcx);
|
||||||
self.mentioned_items
|
self.mentioned_items
|
||||||
.push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() });
|
.push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() });
|
||||||
|
@ -75,6 +75,7 @@ impl RemoveNoopLandingPads {
|
|||||||
| TerminatorKind::UnwindTerminate(_)
|
| TerminatorKind::UnwindTerminate(_)
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| TerminatorKind::Call { .. }
|
| TerminatorKind::Call { .. }
|
||||||
|
| TerminatorKind::TailCall { .. }
|
||||||
| TerminatorKind::Assert { .. }
|
| TerminatorKind::Assert { .. }
|
||||||
| TerminatorKind::Drop { .. }
|
| TerminatorKind::Drop { .. }
|
||||||
| TerminatorKind::InlineAsm { .. } => false,
|
| TerminatorKind::InlineAsm { .. } => false,
|
||||||
|
@ -400,40 +400,44 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
|||||||
self.check_edge(location, *target, EdgeKind::Normal);
|
self.check_edge(location, *target, EdgeKind::Normal);
|
||||||
self.check_unwind_edge(location, *unwind);
|
self.check_unwind_edge(location, *unwind);
|
||||||
}
|
}
|
||||||
TerminatorKind::Call { args, destination, target, unwind, .. } => {
|
TerminatorKind::Call { args, .. } | TerminatorKind::TailCall { args, .. } => {
|
||||||
if let Some(target) = target {
|
// FIXME(explicit_tail_calls): refactor this & add tail-call specific checks
|
||||||
self.check_edge(location, *target, EdgeKind::Normal);
|
if let TerminatorKind::Call { target, unwind, destination, .. } = terminator.kind {
|
||||||
}
|
if let Some(target) = target {
|
||||||
self.check_unwind_edge(location, *unwind);
|
self.check_edge(location, target, EdgeKind::Normal);
|
||||||
|
}
|
||||||
|
self.check_unwind_edge(location, unwind);
|
||||||
|
|
||||||
// The code generation assumes that there are no critical call edges. The assumption
|
// The code generation assumes that there are no critical call edges. The assumption
|
||||||
// is used to simplify inserting code that should be executed along the return edge
|
// is used to simplify inserting code that should be executed along the return edge
|
||||||
// from the call. FIXME(tmiasko): Since this is a strictly code generation concern,
|
// from the call. FIXME(tmiasko): Since this is a strictly code generation concern,
|
||||||
// the code generation should be responsible for handling it.
|
// the code generation should be responsible for handling it.
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized)
|
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized)
|
||||||
&& self.is_critical_call_edge(*target, *unwind)
|
&& self.is_critical_call_edge(target, unwind)
|
||||||
{
|
{
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
format!(
|
format!(
|
||||||
"encountered critical edge in `Call` terminator {:?}",
|
"encountered critical edge in `Call` terminator {:?}",
|
||||||
terminator.kind,
|
terminator.kind,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The call destination place and Operand::Move place used as an argument might be
|
||||||
|
// passed by a reference to the callee. Consequently they cannot be packed.
|
||||||
|
if is_within_packed(self.tcx, &self.body.local_decls, destination).is_some() {
|
||||||
|
// This is bad! The callee will expect the memory to be aligned.
|
||||||
|
self.fail(
|
||||||
|
location,
|
||||||
|
format!(
|
||||||
|
"encountered packed place in `Call` terminator destination: {:?}",
|
||||||
|
terminator.kind,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The call destination place and Operand::Move place used as an argument might be
|
|
||||||
// passed by a reference to the callee. Consequently they cannot be packed.
|
|
||||||
if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() {
|
|
||||||
// This is bad! The callee will expect the memory to be aligned.
|
|
||||||
self.fail(
|
|
||||||
location,
|
|
||||||
format!(
|
|
||||||
"encountered packed place in `Call` terminator destination: {:?}",
|
|
||||||
terminator.kind,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if let Operand::Move(place) = &arg.node {
|
if let Operand::Move(place) = &arg.node {
|
||||||
if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
|
if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
|
||||||
@ -1498,15 +1502,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TerminatorKind::Call { func, .. } => {
|
TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => {
|
||||||
let func_ty = func.ty(&self.body.local_decls, self.tcx);
|
let func_ty = func.ty(&self.body.local_decls, self.tcx);
|
||||||
match func_ty.kind() {
|
match func_ty.kind() {
|
||||||
ty::FnPtr(..) | ty::FnDef(..) => {}
|
ty::FnPtr(..) | ty::FnDef(..) => {}
|
||||||
_ => self.fail(
|
_ => self.fail(
|
||||||
location,
|
location,
|
||||||
format!("encountered non-callable type {func_ty} in `Call` terminator"),
|
format!(
|
||||||
|
"encountered non-callable type {func_ty} in `{}` terminator",
|
||||||
|
terminator.kind.name()
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TerminatorKind::TailCall { .. } = terminator.kind {
|
||||||
|
// FIXME(explicit_tail_calls): implement tail-call specific checks here (such as signature matching, forbidding closures, etc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TerminatorKind::Assert { cond, .. } => {
|
TerminatorKind::Assert { cond, .. } => {
|
||||||
let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
|
let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
|
||||||
|
@ -755,7 +755,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match terminator.kind {
|
match terminator.kind {
|
||||||
mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => {
|
mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. }
|
||||||
|
| mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => {
|
||||||
let callee_ty = func.ty(self.body, tcx);
|
let callee_ty = func.ty(self.body, tcx);
|
||||||
// *Before* monomorphizing, record that we already handled this mention.
|
// *Before* monomorphizing, record that we already handled this mention.
|
||||||
self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty));
|
self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty));
|
||||||
|
@ -644,6 +644,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
|
|||||||
target: target.map(|t| t.as_usize()),
|
target: target.map(|t| t.as_usize()),
|
||||||
unwind: unwind.stable(tables),
|
unwind: unwind.stable(tables),
|
||||||
},
|
},
|
||||||
|
mir::TerminatorKind::TailCall { func: _, args: _, fn_span: _ } => todo!(),
|
||||||
mir::TerminatorKind::Assert { cond, expected, msg, target, unwind } => {
|
mir::TerminatorKind::Assert { cond, expected, msg, target, unwind } => {
|
||||||
TerminatorKind::Assert {
|
TerminatorKind::Assert {
|
||||||
cond: cond.stable(tables),
|
cond: cond.stable(tables),
|
||||||
|
22
tests/ui/explicit-tail-calls/constck.rs
Normal file
22
tests/ui/explicit-tail-calls/constck.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(explicit_tail_calls)]
|
||||||
|
|
||||||
|
const fn f() {
|
||||||
|
if false {
|
||||||
|
become not_const();
|
||||||
|
//~^ error: cannot call non-const fn `not_const` in constant functions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn g((): ()) {
|
||||||
|
if false {
|
||||||
|
become yes_const(not_const());
|
||||||
|
//~^ error: cannot call non-const fn `not_const` in constant functions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not_const() {}
|
||||||
|
|
||||||
|
const fn yes_const((): ()) {}
|
||||||
|
|
||||||
|
fn main() {}
|
19
tests/ui/explicit-tail-calls/constck.stderr
Normal file
19
tests/ui/explicit-tail-calls/constck.stderr
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
error[E0015]: cannot call non-const fn `not_const` in constant functions
|
||||||
|
--> $DIR/constck.rs:6:16
|
||||||
|
|
|
||||||
|
LL | become not_const();
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||||
|
|
||||||
|
error[E0015]: cannot call non-const fn `not_const` in constant functions
|
||||||
|
--> $DIR/constck.rs:13:26
|
||||||
|
|
|
||||||
|
LL | become yes_const(not_const());
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0015`.
|
@ -13,7 +13,7 @@ fn _f1() {
|
|||||||
become _g1(); //~ error: mismatched types
|
become _g1(); //~ error: mismatched types
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _g1() -> ! { //~ WARN: cannot return without recursing
|
fn _g1() -> ! {
|
||||||
become _g1();
|
become _g1();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,17 +22,6 @@ error[E0308]: mismatched types
|
|||||||
LL | become _g2();
|
LL | become _g2();
|
||||||
| ^^^^^^^^^^^^ expected `u32`, found `u16`
|
| ^^^^^^^^^^^^ expected `u32`, found `u16`
|
||||||
|
|
||||||
warning: function cannot return without recursing
|
error: aborting due to 3 previous errors
|
||||||
--> $DIR/return-mismatches.rs:16:1
|
|
||||||
|
|
|
||||||
LL | fn _g1() -> ! {
|
|
||||||
| ^^^^^^^^^^^^^ cannot return without recursing
|
|
||||||
LL | become _g1();
|
|
||||||
| ----- recursive call site
|
|
||||||
|
|
|
||||||
= help: a `loop` may express intention better if this is on purpose
|
|
||||||
= note: `#[warn(unconditional_recursion)]` on by default
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors; 1 warning emitted
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
11
tests/ui/explicit-tail-calls/unsafeck.rs
Normal file
11
tests/ui/explicit-tail-calls/unsafeck.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(explicit_tail_calls)]
|
||||||
|
|
||||||
|
const fn f() {
|
||||||
|
become dangerous();
|
||||||
|
//~^ error: call to unsafe function `dangerous` is unsafe and requires unsafe function or block
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsafe fn dangerous() {}
|
||||||
|
|
||||||
|
fn main() {}
|
11
tests/ui/explicit-tail-calls/unsafeck.stderr
Normal file
11
tests/ui/explicit-tail-calls/unsafeck.stderr
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
error[E0133]: call to unsafe function `dangerous` is unsafe and requires unsafe function or block
|
||||||
|
--> $DIR/unsafeck.rs:5:12
|
||||||
|
|
|
||||||
|
LL | become dangerous();
|
||||||
|
| ^^^^^^^^^^^ call to unsafe function
|
||||||
|
|
|
||||||
|
= note: consult the function's documentation for information on how to avoid undefined behavior
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0133`.
|
@ -147,14 +147,14 @@ fn o() -> Result<(), ()> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn p() {
|
// fn p() { // FIXME(explicit_tail_calls): this currently trips an assertion...
|
||||||
let 0 = become {
|
// let 0 = become {
|
||||||
()
|
// ()
|
||||||
} else {
|
// } else {
|
||||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
// // ~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||||
return;
|
// return;
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn q() {
|
fn q() {
|
||||||
let foo = |x: i32| {
|
let foo = |x: i32| {
|
||||||
|
@ -203,19 +203,6 @@ LL | ()
|
|||||||
LL ~ }) else {
|
LL ~ }) else {
|
||||||
|
|
|
|
||||||
|
|
||||||
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
|
||||||
--> $DIR/bad-let-else-statement.rs:153:5
|
|
||||||
|
|
|
||||||
LL | } else {
|
|
||||||
| ^
|
|
||||||
|
|
|
||||||
help: wrap the expression in parentheses
|
|
||||||
|
|
|
||||||
LL ~ let 0 = become ({
|
|
||||||
LL | ()
|
|
||||||
LL ~ }) else {
|
|
||||||
|
|
|
||||||
|
|
||||||
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||||
--> $DIR/bad-let-else-statement.rs:163:5
|
--> $DIR/bad-let-else-statement.rs:163:5
|
||||||
|
|
|
|
||||||
@ -325,5 +312,5 @@ LL | | } else {
|
|||||||
= note: this pattern will always match, so the `else` clause is useless
|
= note: this pattern will always match, so the `else` clause is useless
|
||||||
= help: consider removing the `else` clause
|
= help: consider removing the `else` clause
|
||||||
|
|
||||||
error: aborting due to 20 previous errors; 5 warnings emitted
|
error: aborting due to 19 previous errors; 5 warnings emitted
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user