Auto merge of #3793 - rust-lang:rustup-2024-08-07, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
384f8586cb
100 changed files with 2621 additions and 1607 deletions
|
|
@ -646,22 +646,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
|
|||
}
|
||||
}
|
||||
|
||||
// This is a workaround for a LLVM bug that doesn't implicitly enable
|
||||
// `simd128` when `relaxed-simd` is.
|
||||
// See <https://github.com/llvm/llvm-project/pull/99803>, which didn't make
|
||||
// it into a released version of LLVM yet.
|
||||
//
|
||||
// This doesn't use the "implicit target feature" system because it is only
|
||||
// used for function attributes in other targets, which fixes this bug as
|
||||
// well on the function attribute level.
|
||||
if sess.target.families.contains(&"wasm".into()) {
|
||||
if features.iter().any(|f| f == "+relaxed-simd")
|
||||
&& !features.iter().any(|f| f == "+simd128")
|
||||
{
|
||||
features.push("+simd128".into());
|
||||
}
|
||||
}
|
||||
|
||||
if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
|
||||
sess.dcx().emit_err(TargetFeatureDisableOrEnable {
|
||||
features: f,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_ast::ast;
|
||||
use rustc_attr::InstructionSetAttr;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
|
||||
|
|
@ -30,6 +30,7 @@ pub fn from_target_feature(
|
|||
.emit();
|
||||
};
|
||||
let rust_features = tcx.features();
|
||||
let mut added_target_features = Vec::new();
|
||||
for item in list {
|
||||
// Only `enable = ...` is accepted in the meta-item list.
|
||||
if !item.has_name(sym::enable) {
|
||||
|
|
@ -44,7 +45,7 @@ pub fn from_target_feature(
|
|||
};
|
||||
|
||||
// We allow comma separation to enable multiple features.
|
||||
target_features.extend(value.as_str().split(',').filter_map(|feature| {
|
||||
added_target_features.extend(value.as_str().split(',').filter_map(|feature| {
|
||||
let Some(feature_gate) = supported_target_features.get(feature) else {
|
||||
let msg = format!("the feature named `{feature}` is not valid for this target");
|
||||
let mut err = tcx.dcx().struct_span_err(item.span(), msg);
|
||||
|
|
@ -98,13 +99,14 @@ pub fn from_target_feature(
|
|||
}));
|
||||
}
|
||||
|
||||
for (feature, requires) in tcx.sess.target.implicit_target_features() {
|
||||
if target_features.iter().any(|f| f.as_str() == *feature)
|
||||
&& !target_features.iter().any(|f| f.as_str() == *requires)
|
||||
{
|
||||
target_features.push(Symbol::intern(requires));
|
||||
}
|
||||
// Add both explicit and implied target features, using a set to deduplicate
|
||||
let mut target_features_set = UnordSet::new();
|
||||
for feature in added_target_features.iter() {
|
||||
target_features_set
|
||||
.extend_unord(tcx.implied_target_features(*feature).clone().into_items());
|
||||
}
|
||||
target_features_set.extend(added_target_features);
|
||||
target_features.extend(target_features_set.into_sorted_stable_ord())
|
||||
}
|
||||
|
||||
/// Computes the set of target features used in a function for the purposes of
|
||||
|
|
@ -162,6 +164,28 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
.collect()
|
||||
}
|
||||
},
|
||||
implied_target_features: |tcx, feature| {
|
||||
let implied_features = tcx
|
||||
.sess
|
||||
.target
|
||||
.implied_target_features()
|
||||
.iter()
|
||||
.map(|(f, i)| (Symbol::intern(f), i))
|
||||
.collect::<FxHashMap<_, _>>();
|
||||
|
||||
// implied target features have their own implied target features, so we traverse the
|
||||
// map until there are no more features to add
|
||||
let mut features = UnordSet::new();
|
||||
let mut new_features = vec![feature];
|
||||
while let Some(new_feature) = new_features.pop() {
|
||||
if features.insert(new_feature) {
|
||||
if let Some(implied_features) = implied_features.get(&new_feature) {
|
||||
new_features.extend(implied_features.iter().copied().map(Symbol::intern))
|
||||
}
|
||||
}
|
||||
}
|
||||
features
|
||||
},
|
||||
asm_target_features,
|
||||
..*providers
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,9 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
cid.promoted.map_or_else(String::new, |p| format!("::{p:?}"))
|
||||
);
|
||||
|
||||
ecx.push_stack_frame(
|
||||
// This can't use `init_stack_frame` since `body` is not a function,
|
||||
// so computing its ABI would fail. It's also not worth it since there are no arguments to pass.
|
||||
ecx.push_stack_frame_raw(
|
||||
cid.instance,
|
||||
body,
|
||||
&ret.clone().into(),
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ use crate::errors::{LongRunning, LongRunningWarn};
|
|||
use crate::fluent_generated as fluent;
|
||||
use crate::interpret::{
|
||||
self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup,
|
||||
throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame,
|
||||
throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
|
||||
GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
|
||||
StackPopCleanup,
|
||||
};
|
||||
|
||||
/// When hitting this many interpreted terminators we emit a deny by default lint
|
||||
|
|
@ -306,17 +307,15 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
|
|||
let align = ImmTy::from_uint(target_align, args[1].layout).into();
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
|
||||
|
||||
// We replace the entire function call with a "tail call".
|
||||
// Note that this happens before the frame of the original function
|
||||
// is pushed on the stack.
|
||||
self.eval_fn_call(
|
||||
FnVal::Instance(instance),
|
||||
(CallAbi::Rust, fn_abi),
|
||||
// Push the stack frame with our own adjusted arguments.
|
||||
self.init_stack_frame(
|
||||
instance,
|
||||
self.load_mir(instance.def, None)?,
|
||||
fn_abi,
|
||||
&[FnArg::Copy(addr), FnArg::Copy(align)],
|
||||
/* with_caller_location = */ false,
|
||||
dest,
|
||||
ret,
|
||||
mir::UnwindAction::Unreachable,
|
||||
StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable },
|
||||
)?;
|
||||
Ok(ControlFlow::Break(()))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
//! Manages calling a concrete function (with known MIR body) with argument passing,
|
||||
//! and returning the return value to the caller.
|
||||
use std::borrow::Cow;
|
||||
|
||||
use either::Either;
|
||||
use either::{Left, Right};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, AdtDef, Instance, Ty};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
|
||||
use rustc_target::abi::{self, FieldIdx, Integer};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use tracing::trace;
|
||||
use tracing::{info, instrument, trace};
|
||||
|
||||
use super::{
|
||||
throw_ub, throw_ub_custom, throw_unsup_format, CtfeProvenance, FnVal, ImmTy, InterpCx,
|
||||
InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, Scalar,
|
||||
StackPopCleanup,
|
||||
InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, ReturnAction, Scalar,
|
||||
StackPopCleanup, StackPopInfo,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::interpret::eval_context::StackPopInfo;
|
||||
use crate::interpret::ReturnAction;
|
||||
|
||||
/// An argment passed to a function.
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -39,15 +38,6 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> {
|
||||
callee: FnVal<'tcx, M::ExtraFnVal>,
|
||||
args: Vec<FnArg<'tcx, M::Provenance>>,
|
||||
fn_sig: ty::FnSig<'tcx>,
|
||||
fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>,
|
||||
/// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`])
|
||||
with_caller_location: bool,
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the
|
||||
/// original memory occurs.
|
||||
|
|
@ -67,7 +57,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
args.iter().map(|fn_arg| self.copy_fn_arg(fn_arg)).collect()
|
||||
}
|
||||
|
||||
pub fn fn_arg_field(
|
||||
/// Helper function for argument untupling.
|
||||
pub(super) fn fn_arg_field(
|
||||
&self,
|
||||
arg: &FnArg<'tcx, M::Provenance>,
|
||||
field: usize,
|
||||
|
|
@ -78,190 +69,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
})
|
||||
}
|
||||
|
||||
pub(super) fn eval_terminator(
|
||||
&mut self,
|
||||
terminator: &mir::Terminator<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
use rustc_middle::mir::TerminatorKind::*;
|
||||
match terminator.kind {
|
||||
Return => {
|
||||
self.return_from_current_stack_frame(/* unwinding */ false)?
|
||||
}
|
||||
|
||||
Goto { target } => self.go_to_block(target),
|
||||
|
||||
SwitchInt { ref discr, ref targets } => {
|
||||
let discr = self.read_immediate(&self.eval_operand(discr, None)?)?;
|
||||
trace!("SwitchInt({:?})", *discr);
|
||||
|
||||
// Branch to the `otherwise` case by default, if no match is found.
|
||||
let mut target_block = targets.otherwise();
|
||||
|
||||
for (const_int, target) in targets.iter() {
|
||||
// Compare using MIR BinOp::Eq, to also support pointer values.
|
||||
// (Avoiding `self.binary_op` as that does some redundant layout computation.)
|
||||
let res = self.binary_op(
|
||||
mir::BinOp::Eq,
|
||||
&discr,
|
||||
&ImmTy::from_uint(const_int, discr.layout),
|
||||
)?;
|
||||
if res.to_scalar().to_bool()? {
|
||||
target_block = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.go_to_block(target_block);
|
||||
}
|
||||
|
||||
Call {
|
||||
ref func,
|
||||
ref args,
|
||||
destination,
|
||||
target,
|
||||
unwind,
|
||||
call_source: _,
|
||||
fn_span: _,
|
||||
} => {
|
||||
let old_stack = self.frame_idx();
|
||||
let old_loc = self.frame().loc;
|
||||
|
||||
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
|
||||
self.eval_callee_and_args(terminator, func, args)?;
|
||||
|
||||
let destination = self.force_allocation(&self.eval_place(destination)?)?;
|
||||
self.eval_fn_call(
|
||||
callee,
|
||||
(fn_sig.abi, fn_abi),
|
||||
&args,
|
||||
with_caller_location,
|
||||
&destination,
|
||||
target,
|
||||
if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable },
|
||||
)?;
|
||||
// Sanity-check that `eval_fn_call` either pushed a new frame or
|
||||
// did a jump to another block.
|
||||
if self.frame_idx() == old_stack && self.frame().loc == old_loc {
|
||||
span_bug!(terminator.source_info.span, "evaluating this call made no progress");
|
||||
}
|
||||
}
|
||||
|
||||
TailCall { ref func, ref args, fn_span: _ } => {
|
||||
let old_frame_idx = self.frame_idx();
|
||||
|
||||
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
|
||||
self.eval_callee_and_args(terminator, func, args)?;
|
||||
|
||||
self.eval_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?;
|
||||
|
||||
if self.frame_idx() != old_frame_idx {
|
||||
span_bug!(
|
||||
terminator.source_info.span,
|
||||
"evaluating this tail call pushed a new stack frame"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Drop { place, target, unwind, replace: _ } => {
|
||||
let place = self.eval_place(place)?;
|
||||
let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||
if let ty::InstanceKind::DropGlue(_, None) = instance.def {
|
||||
// This is the branch we enter if and only if the dropped type has no drop glue
|
||||
// whatsoever. This can happen as a result of monomorphizing a drop of a
|
||||
// generic. In order to make sure that generic and non-generic code behaves
|
||||
// roughly the same (and in keeping with Mir semantics) we do nothing here.
|
||||
self.go_to_block(target);
|
||||
return Ok(());
|
||||
}
|
||||
trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty);
|
||||
self.drop_in_place(&place, instance, target, unwind)?;
|
||||
}
|
||||
|
||||
Assert { ref cond, expected, ref msg, target, unwind } => {
|
||||
let ignored =
|
||||
M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check();
|
||||
let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?;
|
||||
if ignored || expected == cond_val {
|
||||
self.go_to_block(target);
|
||||
} else {
|
||||
M::assert_panic(self, msg, unwind)?;
|
||||
}
|
||||
}
|
||||
|
||||
UnwindTerminate(reason) => {
|
||||
M::unwind_terminate(self, reason)?;
|
||||
}
|
||||
|
||||
// When we encounter Resume, we've finished unwinding
|
||||
// cleanup for the current stack frame. We pop it in order
|
||||
// to continue unwinding the next frame
|
||||
UnwindResume => {
|
||||
trace!("unwinding: resuming from cleanup");
|
||||
// By definition, a Resume terminator means
|
||||
// that we're unwinding
|
||||
self.return_from_current_stack_frame(/* unwinding */ true)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// It is UB to ever encounter this.
|
||||
Unreachable => throw_ub!(Unreachable),
|
||||
|
||||
// These should never occur for MIR we actually run.
|
||||
FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | CoroutineDrop => span_bug!(
|
||||
terminator.source_info.span,
|
||||
"{:#?} should have been eliminated by MIR pass",
|
||||
terminator.kind
|
||||
),
|
||||
|
||||
InlineAsm { template, ref operands, options, ref targets, .. } => {
|
||||
M::eval_inline_asm(self, template, operands, options, targets)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Evaluate the arguments of a function call
|
||||
pub(super) fn eval_fn_call_arguments(
|
||||
&self,
|
||||
ops: &[Spanned<mir::Operand<'tcx>>],
|
||||
) -> InterpResult<'tcx, Vec<FnArg<'tcx, M::Provenance>>> {
|
||||
ops.iter()
|
||||
.map(|op| {
|
||||
let arg = match &op.node {
|
||||
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
|
||||
// Make a regular copy.
|
||||
let op = self.eval_operand(&op.node, None)?;
|
||||
FnArg::Copy(op)
|
||||
}
|
||||
mir::Operand::Move(place) => {
|
||||
// If this place lives in memory, preserve its location.
|
||||
// We call `place_to_op` which will be an `MPlaceTy` whenever there exists
|
||||
// an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
|
||||
// which can return a local even if that has an mplace.)
|
||||
let place = self.eval_place(*place)?;
|
||||
let op = self.place_to_op(&place)?;
|
||||
|
||||
match op.as_mplace_or_imm() {
|
||||
Either::Left(mplace) => FnArg::InPlace(mplace),
|
||||
Either::Right(_imm) => {
|
||||
// This argument doesn't live in memory, so there's no place
|
||||
// to make inaccessible during the call.
|
||||
// We rely on there not being any stray `PlaceTy` that would let the
|
||||
// caller directly access this local!
|
||||
// This is also crucial for tail calls, where we want the `FnArg` to
|
||||
// stay valid when the old stack frame gets popped.
|
||||
FnArg::Copy(op)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(arg)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Find the wrapped inner type of a transparent wrapper.
|
||||
/// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
|
||||
///
|
||||
|
|
@ -503,46 +310,205 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
|
||||
/// necessary information about callee and arguments to make a call.
|
||||
fn eval_callee_and_args(
|
||||
&self,
|
||||
terminator: &mir::Terminator<'tcx>,
|
||||
func: &mir::Operand<'tcx>,
|
||||
args: &[Spanned<mir::Operand<'tcx>>],
|
||||
) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
|
||||
let func = self.eval_operand(func, None)?;
|
||||
let args = self.eval_fn_call_arguments(args)?;
|
||||
|
||||
let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
|
||||
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder);
|
||||
let extra_args = &args[fn_sig.inputs().len()..];
|
||||
let extra_args =
|
||||
self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty));
|
||||
|
||||
let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() {
|
||||
ty::FnPtr(_sig) => {
|
||||
let fn_ptr = self.read_pointer(&func)?;
|
||||
let fn_val = self.get_ptr_fn(fn_ptr)?;
|
||||
(fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false)
|
||||
}
|
||||
ty::FnDef(def_id, args) => {
|
||||
let instance = self.resolve(def_id, args)?;
|
||||
(
|
||||
FnVal::Instance(instance),
|
||||
self.fn_abi_of_instance(instance, extra_args)?,
|
||||
instance.def.requires_caller_location(*self.tcx),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location })
|
||||
fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> {
|
||||
// Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988
|
||||
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
|
||||
if !self.tcx.sess.target.is_like_wasm
|
||||
&& attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.any(|feature| !self.tcx.sess.target_features.contains(feature))
|
||||
{
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_unavailable_target_features_for_fn,
|
||||
unavailable_feats = attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.filter(|&feature| !self.tcx.sess.target_features.contains(feature))
|
||||
.fold(String::new(), |mut s, feature| {
|
||||
if !s.is_empty() {
|
||||
s.push_str(", ");
|
||||
}
|
||||
s.push_str(feature.as_str());
|
||||
s
|
||||
}),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Call this function -- pushing the stack frame and initializing the arguments.
|
||||
/// The main entry point for creating a new stack frame: performs ABI checks and initializes
|
||||
/// arguments.
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn init_stack_frame(
|
||||
&mut self,
|
||||
instance: Instance<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
args: &[FnArg<'tcx, M::Provenance>],
|
||||
with_caller_location: bool,
|
||||
destination: &MPlaceTy<'tcx, M::Provenance>,
|
||||
mut stack_pop: StackPopCleanup,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Compute callee information.
|
||||
// FIXME: for variadic support, do we have to somehow determine callee's extra_args?
|
||||
let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
|
||||
|
||||
if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic {
|
||||
throw_unsup_format!("calling a c-variadic function is not supported");
|
||||
}
|
||||
|
||||
if M::enforce_abi(self) {
|
||||
if caller_fn_abi.conv != callee_fn_abi.conv {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_incompatible_calling_conventions,
|
||||
callee_conv = format!("{:?}", callee_fn_abi.conv),
|
||||
caller_conv = format!("{:?}", caller_fn_abi.conv),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all target features required by the callee (i.e., from
|
||||
// the attribute `#[target_feature(enable = ...)]`) are enabled at
|
||||
// compile time.
|
||||
self.check_fn_target_features(instance)?;
|
||||
|
||||
if !callee_fn_abi.can_unwind {
|
||||
// The callee cannot unwind, so force the `Unreachable` unwind handling.
|
||||
match &mut stack_pop {
|
||||
StackPopCleanup::Root { .. } => {}
|
||||
StackPopCleanup::Goto { unwind, .. } => {
|
||||
*unwind = mir::UnwindAction::Unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.push_stack_frame_raw(instance, body, destination, stack_pop)?;
|
||||
|
||||
// If an error is raised here, pop the frame again to get an accurate backtrace.
|
||||
// To this end, we wrap it all in a `try` block.
|
||||
let res: InterpResult<'tcx> = try {
|
||||
trace!(
|
||||
"caller ABI: {:#?}, args: {:#?}",
|
||||
caller_fn_abi,
|
||||
args.iter()
|
||||
.map(|arg| (
|
||||
arg.layout().ty,
|
||||
match arg {
|
||||
FnArg::Copy(op) => format!("copy({op:?})"),
|
||||
FnArg::InPlace(mplace) => format!("in-place({mplace:?})"),
|
||||
}
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
trace!(
|
||||
"spread_arg: {:?}, locals: {:#?}",
|
||||
body.spread_arg,
|
||||
body.args_iter()
|
||||
.map(|local| (
|
||||
local,
|
||||
self.layout_of_local(self.frame(), local, None).unwrap().ty,
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// In principle, we have two iterators: Where the arguments come from, and where
|
||||
// they go to.
|
||||
|
||||
// The "where they come from" part is easy, we expect the caller to do any special handling
|
||||
// that might be required here (e.g. for untupling).
|
||||
// If `with_caller_location` is set we pretend there is an extra argument (that
|
||||
// we will not pass; our `caller_location` intrinsic implementation walks the stack instead).
|
||||
assert_eq!(
|
||||
args.len() + if with_caller_location { 1 } else { 0 },
|
||||
caller_fn_abi.args.len(),
|
||||
"mismatch between caller ABI and caller arguments",
|
||||
);
|
||||
let mut caller_args = args
|
||||
.iter()
|
||||
.zip(caller_fn_abi.args.iter())
|
||||
.filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore));
|
||||
|
||||
// Now we have to spread them out across the callee's locals,
|
||||
// taking into account the `spread_arg`. If we could write
|
||||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ignored arguments.
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter();
|
||||
for local in body.args_iter() {
|
||||
// Construct the destination place for this argument. At this point all
|
||||
// locals are still dead, so we cannot construct a `PlaceTy`.
|
||||
let dest = mir::Place::from(local);
|
||||
// `layout_of_local` does more than just the instantiation we need to get the
|
||||
// type, but the result gets cached so this avoids calling the instantiation
|
||||
// query *again* the next time this local is accessed.
|
||||
let ty = self.layout_of_local(self.frame(), local, None)?.ty;
|
||||
if Some(local) == body.spread_arg {
|
||||
// Make the local live once, then fill in the value field by field.
|
||||
self.storage_live(local)?;
|
||||
// Must be a tuple
|
||||
let ty::Tuple(fields) = ty.kind() else {
|
||||
span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}")
|
||||
};
|
||||
for (i, field_ty) in fields.iter().enumerate() {
|
||||
let dest = dest.project_deeper(
|
||||
&[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)],
|
||||
*self.tcx,
|
||||
);
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
&dest,
|
||||
field_ty,
|
||||
/* already_live */ true,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
// Normal argument. Cannot mark it as live yet, it might be unsized!
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
&dest,
|
||||
ty,
|
||||
/* already_live */ false,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
// If the callee needs a caller location, pretend we consume one more argument from the ABI.
|
||||
if instance.def.requires_caller_location(*self.tcx) {
|
||||
callee_args_abis.next().unwrap();
|
||||
}
|
||||
// Now we should have no more caller args or callee arg ABIs
|
||||
assert!(
|
||||
callee_args_abis.next().is_none(),
|
||||
"mismatch between callee ABI and callee body arguments"
|
||||
);
|
||||
if caller_args.next().is_some() {
|
||||
throw_ub_custom!(fluent::const_eval_too_many_caller_args);
|
||||
}
|
||||
// Don't forget to check the return type!
|
||||
if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
|
||||
throw_ub!(AbiMismatchReturn {
|
||||
caller_ty: caller_fn_abi.ret.layout.ty,
|
||||
callee_ty: callee_fn_abi.ret.layout.ty
|
||||
});
|
||||
}
|
||||
|
||||
// Protect return place for in-place return value passing.
|
||||
M::protect_in_place_function_argument(self, &destination)?;
|
||||
|
||||
// Don't forget to mark "initially live" locals as live.
|
||||
self.storage_live_for_always_live_locals()?;
|
||||
};
|
||||
res.inspect_err(|_| {
|
||||
// Don't show the incomplete stack frame in the error stacktrace.
|
||||
self.stack_mut().pop();
|
||||
})
|
||||
}
|
||||
|
||||
/// Initiate a call to this function -- pushing the stack frame and initializing the arguments.
|
||||
///
|
||||
/// `caller_fn_abi` is used to determine if all the arguments are passed the proper way.
|
||||
/// However, we also need `caller_abi` to determine if we need to do untupling of arguments.
|
||||
|
|
@ -550,7 +516,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// `with_caller_location` indicates whether the caller passed a caller location. Miri
|
||||
/// implements caller locations without argument passing, but to match `FnAbi` we need to know
|
||||
/// when those arguments are present.
|
||||
pub(crate) fn eval_fn_call(
|
||||
pub(super) fn init_fn_call(
|
||||
&mut self,
|
||||
fn_val: FnVal<'tcx, M::ExtraFnVal>,
|
||||
(caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>),
|
||||
|
|
@ -558,9 +524,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
with_caller_location: bool,
|
||||
destination: &MPlaceTy<'tcx, M::Provenance>,
|
||||
target: Option<mir::BasicBlock>,
|
||||
mut unwind: mir::UnwindAction,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("eval_fn_call: {:#?}", fn_val);
|
||||
trace!("init_fn_call: {:#?}", fn_val);
|
||||
|
||||
let instance = match fn_val {
|
||||
FnVal::Instance(instance) => instance,
|
||||
|
|
@ -591,7 +557,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
)? {
|
||||
assert!(!self.tcx.intrinsic(fallback.def_id()).unwrap().must_be_overridden);
|
||||
assert!(matches!(fallback.def, ty::InstanceKind::Item(_)));
|
||||
return self.eval_fn_call(
|
||||
return self.init_fn_call(
|
||||
FnVal::Instance(fallback),
|
||||
(caller_abi, caller_fn_abi),
|
||||
args,
|
||||
|
|
@ -630,189 +596,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
return Ok(());
|
||||
};
|
||||
|
||||
// Compute callee information using the `instance` returned by
|
||||
// `find_mir_or_eval_fn`.
|
||||
// FIXME: for variadic support, do we have to somehow determine callee's extra_args?
|
||||
let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
|
||||
|
||||
if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic {
|
||||
throw_unsup_format!("calling a c-variadic function is not supported");
|
||||
}
|
||||
|
||||
if M::enforce_abi(self) {
|
||||
if caller_fn_abi.conv != callee_fn_abi.conv {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_incompatible_calling_conventions,
|
||||
callee_conv = format!("{:?}", callee_fn_abi.conv),
|
||||
caller_conv = format!("{:?}", caller_fn_abi.conv),
|
||||
// Special handling for the closure ABI: untuple the last argument.
|
||||
let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> =
|
||||
if caller_abi == Abi::RustCall && !args.is_empty() {
|
||||
// Untuple
|
||||
let (untuple_arg, args) = args.split_last().unwrap();
|
||||
trace!("init_fn_call: Will pass last argument by untupling");
|
||||
Cow::from(
|
||||
args.iter()
|
||||
.map(|a| Ok(a.clone()))
|
||||
.chain(
|
||||
(0..untuple_arg.layout().fields.count())
|
||||
.map(|i| self.fn_arg_field(untuple_arg, i)),
|
||||
)
|
||||
.collect::<InterpResult<'_, Vec<_>>>()?,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Plain arg passing
|
||||
Cow::from(args)
|
||||
};
|
||||
|
||||
// Check that all target features required by the callee (i.e., from
|
||||
// the attribute `#[target_feature(enable = ...)]`) are enabled at
|
||||
// compile time.
|
||||
self.check_fn_target_features(instance)?;
|
||||
|
||||
if !callee_fn_abi.can_unwind {
|
||||
// The callee cannot unwind, so force the `Unreachable` unwind handling.
|
||||
unwind = mir::UnwindAction::Unreachable;
|
||||
}
|
||||
|
||||
self.push_stack_frame(
|
||||
self.init_stack_frame(
|
||||
instance,
|
||||
body,
|
||||
caller_fn_abi,
|
||||
&args,
|
||||
with_caller_location,
|
||||
destination,
|
||||
StackPopCleanup::Goto { ret: target, unwind },
|
||||
)?;
|
||||
|
||||
// If an error is raised here, pop the frame again to get an accurate backtrace.
|
||||
// To this end, we wrap it all in a `try` block.
|
||||
let res: InterpResult<'tcx> = try {
|
||||
trace!(
|
||||
"caller ABI: {:?}, args: {:#?}",
|
||||
caller_abi,
|
||||
args.iter()
|
||||
.map(|arg| (
|
||||
arg.layout().ty,
|
||||
match arg {
|
||||
FnArg::Copy(op) => format!("copy({op:?})"),
|
||||
FnArg::InPlace(mplace) => format!("in-place({mplace:?})"),
|
||||
}
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
trace!(
|
||||
"spread_arg: {:?}, locals: {:#?}",
|
||||
body.spread_arg,
|
||||
body.args_iter()
|
||||
.map(|local| (
|
||||
local,
|
||||
self.layout_of_local(self.frame(), local, None).unwrap().ty,
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// In principle, we have two iterators: Where the arguments come from, and where
|
||||
// they go to.
|
||||
|
||||
// For where they come from: If the ABI is RustCall, we untuple the
|
||||
// last incoming argument. These two iterators do not have the same type,
|
||||
// so to keep the code paths uniform we accept an allocation
|
||||
// (for RustCall ABI only).
|
||||
let caller_args: Cow<'_, [FnArg<'tcx, M::Provenance>]> =
|
||||
if caller_abi == Abi::RustCall && !args.is_empty() {
|
||||
// Untuple
|
||||
let (untuple_arg, args) = args.split_last().unwrap();
|
||||
trace!("eval_fn_call: Will pass last argument by untupling");
|
||||
Cow::from(
|
||||
args.iter()
|
||||
.map(|a| Ok(a.clone()))
|
||||
.chain(
|
||||
(0..untuple_arg.layout().fields.count())
|
||||
.map(|i| self.fn_arg_field(untuple_arg, i)),
|
||||
)
|
||||
.collect::<InterpResult<'_, Vec<_>>>()?,
|
||||
)
|
||||
} else {
|
||||
// Plain arg passing
|
||||
Cow::from(args)
|
||||
};
|
||||
// If `with_caller_location` is set we pretend there is an extra argument (that
|
||||
// we will not pass).
|
||||
assert_eq!(
|
||||
caller_args.len() + if with_caller_location { 1 } else { 0 },
|
||||
caller_fn_abi.args.len(),
|
||||
"mismatch between caller ABI and caller arguments",
|
||||
);
|
||||
let mut caller_args = caller_args
|
||||
.iter()
|
||||
.zip(caller_fn_abi.args.iter())
|
||||
.filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore));
|
||||
|
||||
// Now we have to spread them out across the callee's locals,
|
||||
// taking into account the `spread_arg`. If we could write
|
||||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ignored arguments.
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter();
|
||||
for local in body.args_iter() {
|
||||
// Construct the destination place for this argument. At this point all
|
||||
// locals are still dead, so we cannot construct a `PlaceTy`.
|
||||
let dest = mir::Place::from(local);
|
||||
// `layout_of_local` does more than just the instantiation we need to get the
|
||||
// type, but the result gets cached so this avoids calling the instantiation
|
||||
// query *again* the next time this local is accessed.
|
||||
let ty = self.layout_of_local(self.frame(), local, None)?.ty;
|
||||
if Some(local) == body.spread_arg {
|
||||
// Make the local live once, then fill in the value field by field.
|
||||
self.storage_live(local)?;
|
||||
// Must be a tuple
|
||||
let ty::Tuple(fields) = ty.kind() else {
|
||||
span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}")
|
||||
};
|
||||
for (i, field_ty) in fields.iter().enumerate() {
|
||||
let dest = dest.project_deeper(
|
||||
&[mir::ProjectionElem::Field(
|
||||
FieldIdx::from_usize(i),
|
||||
field_ty,
|
||||
)],
|
||||
*self.tcx,
|
||||
);
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
&dest,
|
||||
field_ty,
|
||||
/* already_live */ true,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
// Normal argument. Cannot mark it as live yet, it might be unsized!
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
&dest,
|
||||
ty,
|
||||
/* already_live */ false,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
// If the callee needs a caller location, pretend we consume one more argument from the ABI.
|
||||
if instance.def.requires_caller_location(*self.tcx) {
|
||||
callee_args_abis.next().unwrap();
|
||||
}
|
||||
// Now we should have no more caller args or callee arg ABIs
|
||||
assert!(
|
||||
callee_args_abis.next().is_none(),
|
||||
"mismatch between callee ABI and callee body arguments"
|
||||
);
|
||||
if caller_args.next().is_some() {
|
||||
throw_ub_custom!(fluent::const_eval_too_many_caller_args);
|
||||
}
|
||||
// Don't forget to check the return type!
|
||||
if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
|
||||
throw_ub!(AbiMismatchReturn {
|
||||
caller_ty: caller_fn_abi.ret.layout.ty,
|
||||
callee_ty: callee_fn_abi.ret.layout.ty
|
||||
});
|
||||
}
|
||||
|
||||
// Protect return place for in-place return value passing.
|
||||
M::protect_in_place_function_argument(self, &destination)?;
|
||||
|
||||
// Don't forget to mark "initially live" locals as live.
|
||||
self.storage_live_for_always_live_locals()?;
|
||||
};
|
||||
match res {
|
||||
Err(err) => {
|
||||
self.stack_mut().pop();
|
||||
Err(err)
|
||||
}
|
||||
Ok(()) => Ok(()),
|
||||
}
|
||||
)
|
||||
}
|
||||
// `InstanceKind::Virtual` does not have callable MIR. Calls to `Virtual` instances must be
|
||||
// codegen'd / interpreted as virtual calls through the vtable.
|
||||
|
|
@ -935,7 +747,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
caller_fn_abi.args[0].layout.ty = receiver_ty;
|
||||
|
||||
// recurse with concrete function
|
||||
self.eval_fn_call(
|
||||
self.init_fn_call(
|
||||
FnVal::Instance(fn_inst),
|
||||
(caller_abi, &caller_fn_abi),
|
||||
&args,
|
||||
|
|
@ -948,30 +760,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn eval_fn_tail_call(
|
||||
/// Initiate a tail call to this function -- popping the current stack frame, pushing the new
|
||||
/// stack frame and initializing the arguments.
|
||||
pub(super) fn init_fn_tail_call(
|
||||
&mut self,
|
||||
fn_val: FnVal<'tcx, M::ExtraFnVal>,
|
||||
(caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>),
|
||||
args: &[FnArg<'tcx, M::Provenance>],
|
||||
with_caller_location: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("eval_fn_call: {:#?}", fn_val);
|
||||
trace!("init_fn_tail_call: {:#?}", fn_val);
|
||||
|
||||
// This is the "canonical" implementation of tails calls,
|
||||
// a pop of the current stack frame, followed by a normal call
|
||||
// which pushes a new stack frame, with the return address from
|
||||
// the popped stack frame.
|
||||
//
|
||||
// Note that we are using `pop_stack_frame` and not `return_from_current_stack_frame`,
|
||||
// Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`,
|
||||
// as the latter "executes" the goto to the return block, but we don't want to,
|
||||
// only the tail called function should return to the current return block.
|
||||
M::before_stack_pop(self, self.frame())?;
|
||||
|
||||
let StackPopInfo { return_action, return_to_block, return_place } =
|
||||
self.pop_stack_frame(false)?;
|
||||
self.pop_stack_frame_raw(false)?;
|
||||
|
||||
assert_eq!(return_action, ReturnAction::Normal);
|
||||
|
||||
// Take the "stack pop cleanup" info, and use that to initiate the next call.
|
||||
let StackPopCleanup::Goto { ret, unwind } = return_to_block else {
|
||||
bug!("can't tailcall as root");
|
||||
};
|
||||
|
|
@ -980,7 +795,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// we should check if both caller&callee can/n't unwind,
|
||||
// see <https://github.com/rust-lang/rust/pull/113128#issuecomment-1614979803>
|
||||
|
||||
self.eval_fn_call(
|
||||
self.init_fn_call(
|
||||
fn_val,
|
||||
(caller_abi, caller_fn_abi),
|
||||
args,
|
||||
|
|
@ -991,41 +806,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
)
|
||||
}
|
||||
|
||||
fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> {
|
||||
// Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988
|
||||
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
|
||||
if !self.tcx.sess.target.is_like_wasm
|
||||
&& attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.any(|feature| !self.tcx.sess.target_features.contains(feature))
|
||||
{
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_unavailable_target_features_for_fn,
|
||||
unavailable_feats = attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.filter(|&feature| !self.tcx.sess.target_features.contains(feature))
|
||||
.fold(String::new(), |mut s, feature| {
|
||||
if !s.is_empty() {
|
||||
s.push_str(", ");
|
||||
}
|
||||
s.push_str(feature.as_str());
|
||||
s
|
||||
}),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn drop_in_place(
|
||||
pub(super) fn init_drop_in_place_call(
|
||||
&mut self,
|
||||
place: &PlaceTy<'tcx, M::Provenance>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
target: mir::BasicBlock,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("drop_in_place: {:?},\n instance={:?}", place, instance);
|
||||
trace!("init_drop_in_place_call: {:?},\n instance={:?}", place, instance);
|
||||
// We take the address of the object. This may well be unaligned, which is fine
|
||||
// for us here. However, unaligned accesses will probably make the actual drop
|
||||
// implementation fail -- a problem shared by rustc.
|
||||
|
|
@ -1060,7 +848,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let arg = self.mplace_to_ref(&place)?;
|
||||
let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?);
|
||||
|
||||
self.eval_fn_call(
|
||||
self.init_fn_call(
|
||||
FnVal::Instance(instance),
|
||||
(Abi::Rust, fn_abi),
|
||||
&[FnArg::Copy(arg.into())],
|
||||
|
|
@ -1070,4 +858,118 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
unwind,
|
||||
)
|
||||
}
|
||||
|
||||
/// Pops the current frame from the stack, copies the return value to the caller, deallocates
|
||||
/// the memory for allocated locals, and jumps to an appropriate place.
|
||||
///
|
||||
/// If `unwinding` is `false`, then we are performing a normal return
|
||||
/// from a function. In this case, we jump back into the frame of the caller,
|
||||
/// and continue execution as normal.
|
||||
///
|
||||
/// If `unwinding` is `true`, then we are in the middle of a panic,
|
||||
/// and need to unwind this frame. In this case, we jump to the
|
||||
/// `cleanup` block for the function, which is responsible for running
|
||||
/// `Drop` impls for any locals that have been initialized at this point.
|
||||
/// The cleanup block ends with a special `Resume` terminator, which will
|
||||
/// cause us to continue unwinding.
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
pub(super) fn return_from_current_stack_frame(
|
||||
&mut self,
|
||||
unwinding: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
info!(
|
||||
"popping stack frame ({})",
|
||||
if unwinding { "during unwinding" } else { "returning from function" }
|
||||
);
|
||||
|
||||
// Check `unwinding`.
|
||||
assert_eq!(
|
||||
unwinding,
|
||||
match self.frame().loc {
|
||||
Left(loc) => self.body().basic_blocks[loc.block].is_cleanup,
|
||||
Right(_) => true,
|
||||
}
|
||||
);
|
||||
if unwinding && self.frame_idx() == 0 {
|
||||
throw_ub_custom!(fluent::const_eval_unwind_past_top);
|
||||
}
|
||||
|
||||
M::before_stack_pop(self, self.frame())?;
|
||||
|
||||
// Copy return value. Must of course happen *before* we deallocate the locals.
|
||||
// Must be *after* `before_stack_pop` as otherwise the return place might still be protected.
|
||||
let copy_ret_result = if !unwinding {
|
||||
let op = self
|
||||
.local_to_op(mir::RETURN_PLACE, None)
|
||||
.expect("return place should always be live");
|
||||
let dest = self.frame().return_place.clone();
|
||||
let res = if self.stack().len() == 1 {
|
||||
// The initializer of constants and statics will get validated separately
|
||||
// after the constant has been fully evaluated. While we could fall back to the default
|
||||
// code path, that will cause -Zenforce-validity to cycle on static initializers.
|
||||
// Reading from a static's memory is not allowed during its evaluation, and will always
|
||||
// trigger a cycle error. Validation must read from the memory of the current item.
|
||||
// For Miri this means we do not validate the root frame return value,
|
||||
// but Miri anyway calls `read_target_isize` on that so separate validation
|
||||
// is not needed.
|
||||
self.copy_op_no_dest_validation(&op, &dest)
|
||||
} else {
|
||||
self.copy_op_allow_transmute(&op, &dest)
|
||||
};
|
||||
trace!("return value: {:?}", self.dump_place(&dest.into()));
|
||||
// We delay actually short-circuiting on this error until *after* the stack frame is
|
||||
// popped, since we want this error to be attributed to the caller, whose type defines
|
||||
// this transmute.
|
||||
res
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// All right, now it is time to actually pop the frame.
|
||||
let stack_pop_info = self.pop_stack_frame_raw(unwinding)?;
|
||||
|
||||
// Report error from return value copy, if any.
|
||||
copy_ret_result?;
|
||||
|
||||
match stack_pop_info.return_action {
|
||||
ReturnAction::Normal => {}
|
||||
ReturnAction::NoJump => {
|
||||
// The hook already did everything.
|
||||
return Ok(());
|
||||
}
|
||||
ReturnAction::NoCleanup => {
|
||||
// If we are not doing cleanup, also skip everything else.
|
||||
assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked");
|
||||
assert!(!unwinding, "tried to skip cleanup during unwinding");
|
||||
// Skip machine hook.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Normal return, figure out where to jump.
|
||||
if unwinding {
|
||||
// Follow the unwind edge.
|
||||
match stack_pop_info.return_to_block {
|
||||
StackPopCleanup::Goto { unwind, .. } => {
|
||||
// This must be the very last thing that happens, since it can in fact push a new stack frame.
|
||||
self.unwind_to_block(unwind)
|
||||
}
|
||||
StackPopCleanup::Root { .. } => {
|
||||
panic!("encountered StackPopCleanup::Root when unwinding!")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Follow the normal return edge.
|
||||
match stack_pop_info.return_to_block {
|
||||
StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret),
|
||||
StackPopCleanup::Root { .. } => {
|
||||
assert!(
|
||||
self.stack().is_empty(),
|
||||
"only the bottommost frame can have StackPopCleanup::Root"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +1,29 @@
|
|||
use std::cell::Cell;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use either::{Either, Left, Right};
|
||||
use either::{Left, Right};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::{self as hir};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::at::ToTrace;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::mir::interpret::{
|
||||
CtfeProvenance, ErrorHandled, InvalidMetaKind, ReportedErrorInfo,
|
||||
};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{
|
||||
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
|
||||
TyAndLayout,
|
||||
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
|
||||
};
|
||||
use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, Variance};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||
use rustc_middle::{mir, span_bug};
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use tracing::{debug, info, info_span, instrument, trace};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use super::{
|
||||
err_inval, throw_inval, throw_ub, throw_ub_custom, throw_unsup, GlobalId, Immediate,
|
||||
InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind,
|
||||
OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance,
|
||||
ReturnAction, Scalar,
|
||||
err_inval, throw_inval, throw_ub, throw_ub_custom, Frame, FrameInfo, GlobalId, InterpErrorInfo,
|
||||
InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic,
|
||||
Projectable, Provenance,
|
||||
};
|
||||
use crate::{errors, fluent_generated as fluent, util, ReportErrorExt};
|
||||
use crate::{fluent_generated as fluent, util, ReportErrorExt};
|
||||
|
||||
pub struct InterpCx<'tcx, M: Machine<'tcx>> {
|
||||
/// Stores the `Machine` instance.
|
||||
|
|
@ -57,314 +46,6 @@ pub struct InterpCx<'tcx, M: Machine<'tcx>> {
|
|||
pub recursion_limit: Limit,
|
||||
}
|
||||
|
||||
// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread
|
||||
// boundary and dropped in the other thread, it would exit the span in the other thread.
|
||||
struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>);
|
||||
|
||||
impl SpanGuard {
|
||||
/// By default a `SpanGuard` does nothing.
|
||||
fn new() -> Self {
|
||||
Self(tracing::Span::none(), std::marker::PhantomData)
|
||||
}
|
||||
|
||||
/// If a span is entered, we exit the previous span (if any, normally none) and enter the
|
||||
/// new span. This is mainly so we don't have to use `Option` for the `tracing_span` field of
|
||||
/// `Frame` by creating a dummy span to being with and then entering it once the frame has
|
||||
/// been pushed.
|
||||
fn enter(&mut self, span: tracing::Span) {
|
||||
// This executes the destructor on the previous instance of `SpanGuard`, ensuring that
|
||||
// we never enter or exit more spans than vice versa. Unless you `mem::leak`, then we
|
||||
// can't protect the tracing stack, but that'll just lead to weird logging, no actual
|
||||
// problems.
|
||||
*self = Self(span, std::marker::PhantomData);
|
||||
self.0.with_subscriber(|(id, dispatch)| {
|
||||
dispatch.enter(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SpanGuard {
|
||||
fn drop(&mut self) {
|
||||
self.0.with_subscriber(|(id, dispatch)| {
|
||||
dispatch.exit(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// A stack frame.
|
||||
pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Function and callsite information
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// The MIR for the function called on this frame.
|
||||
pub body: &'tcx mir::Body<'tcx>,
|
||||
|
||||
/// The def_id and args of the current function.
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
|
||||
/// Extra data for the machine.
|
||||
pub extra: Extra,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Return place and locals
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Work to perform when returning from this function.
|
||||
pub return_to_block: StackPopCleanup,
|
||||
|
||||
/// The location where the result of the current stack frame should be written to,
|
||||
/// and its layout in the caller.
|
||||
pub return_place: MPlaceTy<'tcx, Prov>,
|
||||
|
||||
/// The list of locals for this stack frame, stored in order as
|
||||
/// `[return_ptr, arguments..., variables..., temporaries...]`.
|
||||
/// The locals are stored as `Option<Value>`s.
|
||||
/// `None` represents a local that is currently dead, while a live local
|
||||
/// can either directly contain `Scalar` or refer to some part of an `Allocation`.
|
||||
///
|
||||
/// Do *not* access this directly; always go through the machine hook!
|
||||
pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
|
||||
|
||||
/// The span of the `tracing` crate is stored here.
|
||||
/// When the guard is dropped, the span is exited. This gives us
|
||||
/// a full stack trace on all tracing statements.
|
||||
tracing_span: SpanGuard,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Current position within the function
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// If this is `Right`, we are not currently executing any particular statement in
|
||||
/// this frame (can happen e.g. during frame initialization, and during unwinding on
|
||||
/// frames without cleanup code).
|
||||
///
|
||||
/// Needs to be public because ConstProp does unspeakable things to it.
|
||||
pub loc: Either<mir::Location, Span>,
|
||||
}
|
||||
|
||||
/// What we store about a frame in an interpreter backtrace.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FrameInfo<'tcx> {
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these
|
||||
pub enum StackPopCleanup {
|
||||
/// Jump to the next block in the caller, or cause UB if None (that's a function
|
||||
/// that may never return). Also store layout of return place so
|
||||
/// we can validate it at that layout.
|
||||
/// `ret` stores the block we jump to on a normal return, while `unwind`
|
||||
/// stores the block used for cleanup during unwinding.
|
||||
Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction },
|
||||
/// The root frame of the stack: nowhere else to jump to.
|
||||
/// `cleanup` says whether locals are deallocated. Static computation
|
||||
/// wants them leaked to intern what they need (and just throw away
|
||||
/// the entire `ecx` when it is done).
|
||||
Root { cleanup: bool },
|
||||
}
|
||||
|
||||
/// Return type of [`InterpCx::pop_stack_frame`].
|
||||
pub struct StackPopInfo<'tcx, Prov: Provenance> {
|
||||
/// Additional information about the action to be performed when returning from the popped
|
||||
/// stack frame.
|
||||
pub return_action: ReturnAction,
|
||||
|
||||
/// [`return_to_block`](Frame::return_to_block) of the popped stack frame.
|
||||
pub return_to_block: StackPopCleanup,
|
||||
|
||||
/// [`return_place`](Frame::return_place) of the popped stack frame.
|
||||
pub return_place: MPlaceTy<'tcx, Prov>,
|
||||
}
|
||||
|
||||
/// State of a local variable including a memoized layout
|
||||
#[derive(Clone)]
|
||||
pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
value: LocalValue<Prov>,
|
||||
/// Don't modify if `Some`, this is only used to prevent computing the layout twice.
|
||||
/// Avoids computing the layout of locals that are never actually initialized.
|
||||
layout: Cell<Option<TyAndLayout<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("LocalState")
|
||||
.field("value", &self.value)
|
||||
.field("ty", &self.layout.get().map(|l| l.ty))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Current value of a local variable
|
||||
///
|
||||
/// This does not store the type of the local; the type is given by `body.local_decls` and can never
|
||||
/// change, so by not storing here we avoid having to maintain that as an invariant.
|
||||
#[derive(Copy, Clone, Debug)] // Miri debug-prints these
|
||||
pub(super) enum LocalValue<Prov: Provenance = CtfeProvenance> {
|
||||
/// This local is not currently alive, and cannot be used at all.
|
||||
Dead,
|
||||
/// A normal, live local.
|
||||
/// Mostly for convenience, we re-use the `Operand` type here.
|
||||
/// This is an optimization over just always having a pointer here;
|
||||
/// we can thus avoid doing an allocation when the local just stores
|
||||
/// immediate values *and* never has its address taken.
|
||||
Live(Operand<Prov>),
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
|
||||
pub fn make_live_uninit(&mut self) {
|
||||
self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit));
|
||||
}
|
||||
|
||||
/// This is a hack because Miri needs a way to visit all the provenance in a `LocalState`
|
||||
/// without having a layout or `TyCtxt` available, and we want to keep the `Operand` type
|
||||
/// private.
|
||||
pub fn as_mplace_or_imm(
|
||||
&self,
|
||||
) -> Option<Either<(Pointer<Option<Prov>>, MemPlaceMeta<Prov>), Immediate<Prov>>> {
|
||||
match self.value {
|
||||
LocalValue::Dead => None,
|
||||
LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))),
|
||||
LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the local's value or error if the local is not yet live or not live anymore.
|
||||
#[inline(always)]
|
||||
pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
|
||||
match &self.value {
|
||||
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
|
||||
LocalValue::Live(val) => Ok(val),
|
||||
}
|
||||
}
|
||||
|
||||
/// Overwrite the local. If the local can be overwritten in place, return a reference
|
||||
/// to do so; otherwise return the `MemPlace` to consult instead.
|
||||
#[inline(always)]
|
||||
pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
|
||||
match &mut self.value {
|
||||
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
|
||||
LocalValue::Live(val) => Ok(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
|
||||
pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'tcx, Prov, Extra> {
|
||||
Frame {
|
||||
body: self.body,
|
||||
instance: self.instance,
|
||||
return_to_block: self.return_to_block,
|
||||
return_place: self.return_place,
|
||||
locals: self.locals,
|
||||
loc: self.loc,
|
||||
extra,
|
||||
tracing_span: self.tracing_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> {
|
||||
/// Get the current location within the Frame.
|
||||
///
|
||||
/// If this is `Right`, we are not currently executing any particular statement in
|
||||
/// this frame (can happen e.g. during frame initialization, and during unwinding on
|
||||
/// frames without cleanup code).
|
||||
///
|
||||
/// Used by priroda.
|
||||
pub fn current_loc(&self) -> Either<mir::Location, Span> {
|
||||
self.loc
|
||||
}
|
||||
|
||||
/// Return the `SourceInfo` of the current instruction.
|
||||
pub fn current_source_info(&self) -> Option<&mir::SourceInfo> {
|
||||
self.loc.left().map(|loc| self.body.source_info(loc))
|
||||
}
|
||||
|
||||
pub fn current_span(&self) -> Span {
|
||||
match self.loc {
|
||||
Left(loc) => self.body.source_info(loc).span,
|
||||
Right(span) => span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> {
|
||||
// We first try to get a HirId via the current source scope,
|
||||
// and fall back to `body.source`.
|
||||
self.current_source_info()
|
||||
.and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data {
|
||||
mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
|
||||
mir::ClearCrossCrate::Clear => None,
|
||||
})
|
||||
.or_else(|| {
|
||||
let def_id = self.body.source.def_id().as_local();
|
||||
def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the address of the buffer where the locals are stored. This is used by `Place` as a
|
||||
/// sanity check to detect bugs where we mix up which stack frame a place refers to.
|
||||
#[inline(always)]
|
||||
pub(super) fn locals_addr(&self) -> usize {
|
||||
self.locals.raw.as_ptr().addr()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec<FrameInfo<'tcx>> {
|
||||
let mut frames = Vec::new();
|
||||
// This deliberately does *not* honor `requires_caller_location` since it is used for much
|
||||
// more than just panics.
|
||||
for frame in stack.iter().rev() {
|
||||
let span = match frame.loc {
|
||||
Left(loc) => {
|
||||
// If the stacktrace passes through MIR-inlined source scopes, add them.
|
||||
let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc);
|
||||
let mut scope_data = &frame.body.source_scopes[scope];
|
||||
while let Some((instance, call_span)) = scope_data.inlined {
|
||||
frames.push(FrameInfo { span, instance });
|
||||
span = call_span;
|
||||
scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()];
|
||||
}
|
||||
span
|
||||
}
|
||||
Right(span) => span,
|
||||
};
|
||||
frames.push(FrameInfo { span, instance: frame.instance });
|
||||
}
|
||||
trace!("generate stacktrace: {:#?}", frames);
|
||||
frames
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: only used by miri, should be removed once translatable.
|
||||
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
|
||||
write!(f, "inside closure")
|
||||
} else {
|
||||
// Note: this triggers a `must_produce_diag` state, which means that if we ever
|
||||
// get here we must emit a diagnostic. We should never display a `FrameInfo` unless
|
||||
// we actually want to emit a warning or error to the user.
|
||||
write!(f, "inside `{}`", self.instance)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> FrameInfo<'tcx> {
|
||||
pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote {
|
||||
let span = self.span;
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
|
||||
errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
|
||||
} else {
|
||||
let instance = format!("{}", self.instance);
|
||||
// Note: this triggers a `must_produce_diag` state, which means that if we ever get
|
||||
// here we must emit a diagnostic. We should never display a `FrameInfo` unless we
|
||||
// actually want to emit a warning or error to the user.
|
||||
errors::FrameNote { where_: "instance", span, instance, times: 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> HasDataLayout for InterpCx<'tcx, M> {
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &TargetDataLayout {
|
||||
|
|
@ -703,30 +384,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn layout_of_local(
|
||||
&self,
|
||||
frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
|
||||
local: mir::Local,
|
||||
layout: Option<TyAndLayout<'tcx>>,
|
||||
) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
|
||||
let state = &frame.locals[local];
|
||||
if let Some(layout) = state.layout.get() {
|
||||
return Ok(layout);
|
||||
}
|
||||
|
||||
let layout = from_known_layout(self.tcx, self.param_env, layout, || {
|
||||
let local_ty = frame.body.local_decls[local].ty;
|
||||
let local_ty =
|
||||
self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
|
||||
self.layout_of(local_ty)
|
||||
})?;
|
||||
|
||||
// Layouts of locals are requested a lot, so we cache them.
|
||||
state.layout.set(Some(layout));
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
/// Returns the actual dynamic size and alignment of the place at the given type.
|
||||
/// Only the "meta" (metadata) part of the place matters.
|
||||
/// This can fail to provide an answer for extern types.
|
||||
|
|
@ -825,132 +482,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.size_and_align_of(&mplace.meta(), &mplace.layout)
|
||||
}
|
||||
|
||||
#[instrument(skip(self, body, return_place, return_to_block), level = "debug")]
|
||||
pub fn push_stack_frame(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
return_place: &MPlaceTy<'tcx, M::Provenance>,
|
||||
return_to_block: StackPopCleanup,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("body: {:#?}", body);
|
||||
|
||||
// First push a stack frame so we have access to the local args
|
||||
self.push_new_stack_frame(instance, body, return_to_block, return_place.clone())?;
|
||||
|
||||
self.after_stack_frame_push(instance, body)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a new stack frame, initializes it and pushes it onto the stack.
|
||||
/// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame).
|
||||
fn push_new_stack_frame(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
return_to_block: StackPopCleanup,
|
||||
return_place: MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) };
|
||||
let locals = IndexVec::from_elem(dead_local, &body.local_decls);
|
||||
let pre_frame = Frame {
|
||||
body,
|
||||
loc: Right(body.span), // Span used for errors caused during preamble.
|
||||
return_to_block,
|
||||
return_place,
|
||||
locals,
|
||||
instance,
|
||||
tracing_span: SpanGuard::new(),
|
||||
extra: (),
|
||||
};
|
||||
let frame = M::init_frame(self, pre_frame)?;
|
||||
self.stack_mut().push(frame);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame).
|
||||
fn after_stack_frame_push(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
||||
for &const_ in body.required_consts() {
|
||||
let c =
|
||||
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
|
||||
c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {
|
||||
err.emit_note(*self.tcx);
|
||||
err
|
||||
})?;
|
||||
}
|
||||
|
||||
// done
|
||||
M::after_stack_push(self)?;
|
||||
self.frame_mut().loc = Left(mir::Location::START);
|
||||
|
||||
let span = info_span!("frame", "{}", instance);
|
||||
self.frame_mut().tracing_span.enter(span);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pops a stack frame from the stack and returns some information about it.
|
||||
///
|
||||
/// This also deallocates locals, if necessary.
|
||||
///
|
||||
/// [`M::before_stack_pop`] should be called before calling this function.
|
||||
/// [`M::after_stack_pop`] is called by this function automatically.
|
||||
///
|
||||
/// [`M::before_stack_pop`]: Machine::before_stack_pop
|
||||
/// [`M::after_stack_pop`]: Machine::after_stack_pop
|
||||
pub fn pop_stack_frame(
|
||||
&mut self,
|
||||
unwinding: bool,
|
||||
) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> {
|
||||
let cleanup = self.cleanup_current_frame_locals()?;
|
||||
|
||||
let frame =
|
||||
self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
|
||||
|
||||
let return_to_block = frame.return_to_block;
|
||||
let return_place = frame.return_place.clone();
|
||||
|
||||
let return_action;
|
||||
if cleanup {
|
||||
return_action = M::after_stack_pop(self, frame, unwinding)?;
|
||||
assert_ne!(return_action, ReturnAction::NoCleanup);
|
||||
} else {
|
||||
return_action = ReturnAction::NoCleanup;
|
||||
};
|
||||
|
||||
Ok(StackPopInfo { return_action, return_to_block, return_place })
|
||||
}
|
||||
|
||||
/// A private helper for [`pop_stack_frame`](InterpCx::pop_stack_frame).
|
||||
/// Returns `true` if cleanup has been done, `false` otherwise.
|
||||
fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> {
|
||||
// Cleanup: deallocate locals.
|
||||
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
|
||||
// We do this while the frame is still on the stack, so errors point to the callee.
|
||||
let return_to_block = self.frame().return_to_block;
|
||||
let cleanup = match return_to_block {
|
||||
StackPopCleanup::Goto { .. } => true,
|
||||
StackPopCleanup::Root { cleanup, .. } => cleanup,
|
||||
};
|
||||
|
||||
if cleanup {
|
||||
// We need to take the locals out, since we need to mutate while iterating.
|
||||
let locals = mem::take(&mut self.frame_mut().locals);
|
||||
for local in &locals {
|
||||
self.deallocate_local(local.value)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cleanup)
|
||||
}
|
||||
|
||||
/// Jump to the given block.
|
||||
#[inline]
|
||||
pub fn go_to_block(&mut self, target: mir::BasicBlock) {
|
||||
|
|
@ -997,248 +528,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Pops the current frame from the stack, deallocating the
|
||||
/// memory for allocated locals, and jumps to an appropriate place.
|
||||
///
|
||||
/// If `unwinding` is `false`, then we are performing a normal return
|
||||
/// from a function. In this case, we jump back into the frame of the caller,
|
||||
/// and continue execution as normal.
|
||||
///
|
||||
/// If `unwinding` is `true`, then we are in the middle of a panic,
|
||||
/// and need to unwind this frame. In this case, we jump to the
|
||||
/// `cleanup` block for the function, which is responsible for running
|
||||
/// `Drop` impls for any locals that have been initialized at this point.
|
||||
/// The cleanup block ends with a special `Resume` terminator, which will
|
||||
/// cause us to continue unwinding.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(super) fn return_from_current_stack_frame(
|
||||
&mut self,
|
||||
unwinding: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
info!(
|
||||
"popping stack frame ({})",
|
||||
if unwinding { "during unwinding" } else { "returning from function" }
|
||||
);
|
||||
|
||||
// Check `unwinding`.
|
||||
assert_eq!(
|
||||
unwinding,
|
||||
match self.frame().loc {
|
||||
Left(loc) => self.body().basic_blocks[loc.block].is_cleanup,
|
||||
Right(_) => true,
|
||||
}
|
||||
);
|
||||
if unwinding && self.frame_idx() == 0 {
|
||||
throw_ub_custom!(fluent::const_eval_unwind_past_top);
|
||||
}
|
||||
|
||||
M::before_stack_pop(self, self.frame())?;
|
||||
|
||||
// Copy return value. Must of course happen *before* we deallocate the locals.
|
||||
let copy_ret_result = if !unwinding {
|
||||
let op = self
|
||||
.local_to_op(mir::RETURN_PLACE, None)
|
||||
.expect("return place should always be live");
|
||||
let dest = self.frame().return_place.clone();
|
||||
let err = if self.stack().len() == 1 {
|
||||
// The initializer of constants and statics will get validated separately
|
||||
// after the constant has been fully evaluated. While we could fall back to the default
|
||||
// code path, that will cause -Zenforce-validity to cycle on static initializers.
|
||||
// Reading from a static's memory is not allowed during its evaluation, and will always
|
||||
// trigger a cycle error. Validation must read from the memory of the current item.
|
||||
// For Miri this means we do not validate the root frame return value,
|
||||
// but Miri anyway calls `read_target_isize` on that so separate validation
|
||||
// is not needed.
|
||||
self.copy_op_no_dest_validation(&op, &dest)
|
||||
} else {
|
||||
self.copy_op_allow_transmute(&op, &dest)
|
||||
};
|
||||
trace!("return value: {:?}", self.dump_place(&dest.into()));
|
||||
// We delay actually short-circuiting on this error until *after* the stack frame is
|
||||
// popped, since we want this error to be attributed to the caller, whose type defines
|
||||
// this transmute.
|
||||
err
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// All right, now it is time to actually pop the frame.
|
||||
let stack_pop_info = self.pop_stack_frame(unwinding)?;
|
||||
|
||||
// Report error from return value copy, if any.
|
||||
copy_ret_result?;
|
||||
|
||||
match stack_pop_info.return_action {
|
||||
ReturnAction::Normal => {}
|
||||
ReturnAction::NoJump => {
|
||||
// The hook already did everything.
|
||||
return Ok(());
|
||||
}
|
||||
ReturnAction::NoCleanup => {
|
||||
// If we are not doing cleanup, also skip everything else.
|
||||
assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked");
|
||||
assert!(!unwinding, "tried to skip cleanup during unwinding");
|
||||
// Skip machine hook.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Normal return, figure out where to jump.
|
||||
if unwinding {
|
||||
// Follow the unwind edge.
|
||||
let unwind = match stack_pop_info.return_to_block {
|
||||
StackPopCleanup::Goto { unwind, .. } => unwind,
|
||||
StackPopCleanup::Root { .. } => {
|
||||
panic!("encountered StackPopCleanup::Root when unwinding!")
|
||||
}
|
||||
};
|
||||
// This must be the very last thing that happens, since it can in fact push a new stack frame.
|
||||
self.unwind_to_block(unwind)
|
||||
} else {
|
||||
// Follow the normal return edge.
|
||||
match stack_pop_info.return_to_block {
|
||||
StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret),
|
||||
StackPopCleanup::Root { .. } => {
|
||||
assert!(
|
||||
self.stack().is_empty(),
|
||||
"only the topmost frame can have StackPopCleanup::Root"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// In the current stack frame, mark all locals as live that are not arguments and don't have
|
||||
/// `Storage*` annotations (this includes the return place).
|
||||
pub fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> {
|
||||
self.storage_live(mir::RETURN_PLACE)?;
|
||||
|
||||
let body = self.body();
|
||||
let always_live = always_storage_live_locals(body);
|
||||
for local in body.vars_and_temps_iter() {
|
||||
if always_live.contains(local) {
|
||||
self.storage_live(local)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn storage_live_dyn(
|
||||
&mut self,
|
||||
local: mir::Local,
|
||||
meta: MemPlaceMeta<M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("{:?} is now live", local);
|
||||
|
||||
// We avoid `ty.is_trivially_sized` since that does something expensive for ADTs.
|
||||
fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Uint(_)
|
||||
| ty::Int(_)
|
||||
| ty::Bool
|
||||
| ty::Float(_)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Char
|
||||
| ty::Ref(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Never
|
||||
| ty::Error(_)
|
||||
| ty::Dynamic(_, _, ty::DynStar) => true,
|
||||
|
||||
ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
|
||||
|
||||
ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)),
|
||||
|
||||
ty::Pat(ty, ..) => is_very_trivially_sized(*ty),
|
||||
|
||||
// We don't want to do any queries, so there is not much we can do with ADTs.
|
||||
ty::Adt(..) => false,
|
||||
|
||||
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false,
|
||||
|
||||
ty::Infer(ty::TyVar(_)) => false,
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a hot function, we avoid computing the layout when possible.
|
||||
// `unsized_` will be `None` for sized types and `Some(layout)` for unsized types.
|
||||
let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) {
|
||||
None
|
||||
} else {
|
||||
// We need the layout.
|
||||
let layout = self.layout_of_local(self.frame(), local, None)?;
|
||||
if layout.is_sized() { None } else { Some(layout) }
|
||||
};
|
||||
|
||||
let local_val = LocalValue::Live(if let Some(layout) = unsized_ {
|
||||
if !meta.has_meta() {
|
||||
throw_unsup!(UnsizedLocal);
|
||||
}
|
||||
// Need to allocate some memory, since `Immediate::Uninit` cannot be unsized.
|
||||
let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?;
|
||||
Operand::Indirect(*dest_place.mplace())
|
||||
} else {
|
||||
assert!(!meta.has_meta()); // we're dropping the metadata
|
||||
// Just make this an efficient immediate.
|
||||
// Note that not calling `layout_of` here does have one real consequence:
|
||||
// if the type is too big, we'll only notice this when the local is actually initialized,
|
||||
// which is a bit too late -- we should ideally notice this already here, when the memory
|
||||
// is conceptually allocated. But given how rare that error is and that this is a hot function,
|
||||
// we accept this downside for now.
|
||||
Operand::Immediate(Immediate::Uninit)
|
||||
});
|
||||
|
||||
// If the local is already live, deallocate its old memory.
|
||||
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
|
||||
self.deallocate_local(old)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mark a storage as live, killing the previous content.
|
||||
#[inline(always)]
|
||||
pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||
self.storage_live_dyn(local, MemPlaceMeta::None)
|
||||
}
|
||||
|
||||
pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
|
||||
trace!("{:?} is now dead", local);
|
||||
|
||||
// If the local is already dead, this is a NOP.
|
||||
let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
|
||||
self.deallocate_local(old)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> {
|
||||
if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
|
||||
// All locals have a backing allocation, even if the allocation is empty
|
||||
// due to the local having ZST type. Hence we can `unwrap`.
|
||||
trace!(
|
||||
"deallocating local {:?}: {:?}",
|
||||
local,
|
||||
// Locals always have a `alloc_id` (they are never the result of a int2ptr).
|
||||
self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
|
||||
);
|
||||
self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Call a query that can return `ErrorHandled`. Should be used for statics and other globals.
|
||||
/// (`mir::Const`/`ty::Const` have `eval` methods that can be used directly instead.)
|
||||
pub fn ctfe_query<T>(
|
||||
|
|
@ -1328,39 +617,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for PlacePrinter<'a, 'tcx, M> {
|
|||
}
|
||||
write!(fmt, ":")?;
|
||||
|
||||
match self.ecx.frame().locals[local].value {
|
||||
LocalValue::Dead => write!(fmt, " is dead")?,
|
||||
LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => {
|
||||
write!(fmt, " is uninitialized")?
|
||||
}
|
||||
LocalValue::Live(Operand::Indirect(mplace)) => {
|
||||
write!(
|
||||
fmt,
|
||||
" by {} ref {:?}:",
|
||||
match mplace.meta {
|
||||
MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"),
|
||||
MemPlaceMeta::None => String::new(),
|
||||
},
|
||||
mplace.ptr,
|
||||
)?;
|
||||
allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id));
|
||||
}
|
||||
LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
|
||||
write!(fmt, " {val:?}")?;
|
||||
if let Scalar::Ptr(ptr, _size) = val {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
}
|
||||
LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
|
||||
write!(fmt, " ({val1:?}, {val2:?})")?;
|
||||
if let Scalar::Ptr(ptr, _size) = val1 {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
if let Scalar::Ptr(ptr, _size) = val2 {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.ecx.frame().locals[local].print(&mut allocs, fmt)?;
|
||||
|
||||
write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs.into_iter().flatten().collect()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ pub enum ReturnAction {
|
|||
/// took care of everything.
|
||||
NoJump,
|
||||
|
||||
/// Returned by [`InterpCx::pop_stack_frame`] when no cleanup should be done.
|
||||
/// Returned by [`InterpCx::pop_stack_frame_raw`] when no cleanup should be done.
|
||||
NoCleanup,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! An interpreter for MIR used in CTFE and by miri
|
||||
|
||||
mod call;
|
||||
mod cast;
|
||||
mod discriminant;
|
||||
mod eval_context;
|
||||
|
|
@ -11,8 +12,8 @@ mod operand;
|
|||
mod operator;
|
||||
mod place;
|
||||
mod projection;
|
||||
mod stack;
|
||||
mod step;
|
||||
mod terminator;
|
||||
mod traits;
|
||||
mod util;
|
||||
mod validity;
|
||||
|
|
@ -22,7 +23,8 @@ use eval_context::{from_known_layout, mir_assign_valid_types};
|
|||
#[doc(no_inline)]
|
||||
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
|
||||
|
||||
pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup};
|
||||
pub use self::call::FnArg;
|
||||
pub use self::eval_context::{format_interp_error, InterpCx};
|
||||
pub use self::intern::{
|
||||
intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind,
|
||||
InternResult,
|
||||
|
|
@ -35,7 +37,7 @@ pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
|
|||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||
use self::place::{MemPlace, Place};
|
||||
pub use self::projection::{OffsetMode, Projectable};
|
||||
pub use self::terminator::FnArg;
|
||||
pub use self::stack::{Frame, FrameInfo, LocalState, StackPopCleanup, StackPopInfo};
|
||||
pub(crate) use self::util::create_static_alloc;
|
||||
pub use self::validity::{CtfeValidationMode, RefTracking};
|
||||
pub use self::visitor::ValueVisitor;
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||
#[inline]
|
||||
pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
|
||||
debug_assert!(layout.abi.is_scalar(), "`ImmTy::from_scalar` on non-scalar layout");
|
||||
debug_assert_eq!(val.size(), layout.size);
|
||||
ImmTy { imm: val.into(), layout }
|
||||
}
|
||||
|
||||
|
|
|
|||
651
compiler/rustc_const_eval/src/interpret/stack.rs
Normal file
651
compiler/rustc_const_eval/src/interpret/stack.rs
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
//! Manages the low-level pushing and popping of stack frames and the (de)allocation of local variables.
|
||||
//! For hadling of argument passing and return values, see the `call` module.
|
||||
use std::cell::Cell;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use either::{Either, Left, Right};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||
use rustc_span::Span;
|
||||
use tracing::{info_span, instrument, trace};
|
||||
|
||||
use super::{
|
||||
from_known_layout, throw_ub, throw_unsup, AllocId, CtfeProvenance, Immediate, InterpCx,
|
||||
InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, MemoryKind, Operand, Pointer,
|
||||
Provenance, ReturnAction, Scalar,
|
||||
};
|
||||
use crate::errors;
|
||||
|
||||
// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread
|
||||
// boundary and dropped in the other thread, it would exit the span in the other thread.
|
||||
struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>);
|
||||
|
||||
impl SpanGuard {
|
||||
/// By default a `SpanGuard` does nothing.
|
||||
fn new() -> Self {
|
||||
Self(tracing::Span::none(), std::marker::PhantomData)
|
||||
}
|
||||
|
||||
/// If a span is entered, we exit the previous span (if any, normally none) and enter the
|
||||
/// new span. This is mainly so we don't have to use `Option` for the `tracing_span` field of
|
||||
/// `Frame` by creating a dummy span to being with and then entering it once the frame has
|
||||
/// been pushed.
|
||||
fn enter(&mut self, span: tracing::Span) {
|
||||
// This executes the destructor on the previous instance of `SpanGuard`, ensuring that
|
||||
// we never enter or exit more spans than vice versa. Unless you `mem::leak`, then we
|
||||
// can't protect the tracing stack, but that'll just lead to weird logging, no actual
|
||||
// problems.
|
||||
*self = Self(span, std::marker::PhantomData);
|
||||
self.0.with_subscriber(|(id, dispatch)| {
|
||||
dispatch.enter(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SpanGuard {
|
||||
fn drop(&mut self) {
|
||||
self.0.with_subscriber(|(id, dispatch)| {
|
||||
dispatch.exit(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// A stack frame.
|
||||
pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Function and callsite information
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// The MIR for the function called on this frame.
|
||||
pub(super) body: &'tcx mir::Body<'tcx>,
|
||||
|
||||
/// The def_id and args of the current function.
|
||||
pub(super) instance: ty::Instance<'tcx>,
|
||||
|
||||
/// Extra data for the machine.
|
||||
pub extra: Extra,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Return place and locals
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Work to perform when returning from this function.
|
||||
return_to_block: StackPopCleanup,
|
||||
|
||||
/// The location where the result of the current stack frame should be written to,
|
||||
/// and its layout in the caller.
|
||||
pub return_place: MPlaceTy<'tcx, Prov>,
|
||||
|
||||
/// The list of locals for this stack frame, stored in order as
|
||||
/// `[return_ptr, arguments..., variables..., temporaries...]`.
|
||||
/// The locals are stored as `Option<Value>`s.
|
||||
/// `None` represents a local that is currently dead, while a live local
|
||||
/// can either directly contain `Scalar` or refer to some part of an `Allocation`.
|
||||
///
|
||||
/// Do *not* access this directly; always go through the machine hook!
|
||||
pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
|
||||
|
||||
/// The span of the `tracing` crate is stored here.
|
||||
/// When the guard is dropped, the span is exited. This gives us
|
||||
/// a full stack trace on all tracing statements.
|
||||
tracing_span: SpanGuard,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Current position within the function
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// If this is `Right`, we are not currently executing any particular statement in
|
||||
/// this frame (can happen e.g. during frame initialization, and during unwinding on
|
||||
/// frames without cleanup code).
|
||||
///
|
||||
/// Needs to be public because ConstProp does unspeakable things to it.
|
||||
pub(super) loc: Either<mir::Location, Span>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these
|
||||
pub enum StackPopCleanup {
|
||||
/// Jump to the next block in the caller, or cause UB if None (that's a function
|
||||
/// that may never return). Also store layout of return place so
|
||||
/// we can validate it at that layout.
|
||||
/// `ret` stores the block we jump to on a normal return, while `unwind`
|
||||
/// stores the block used for cleanup during unwinding.
|
||||
Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction },
|
||||
/// The root frame of the stack: nowhere else to jump to.
|
||||
/// `cleanup` says whether locals are deallocated. Static computation
|
||||
/// wants them leaked to intern what they need (and just throw away
|
||||
/// the entire `ecx` when it is done).
|
||||
Root { cleanup: bool },
|
||||
}
|
||||
|
||||
/// Return type of [`InterpCx::pop_stack_frame_raw`].
|
||||
pub struct StackPopInfo<'tcx, Prov: Provenance> {
|
||||
/// Additional information about the action to be performed when returning from the popped
|
||||
/// stack frame.
|
||||
pub return_action: ReturnAction,
|
||||
|
||||
/// [`return_to_block`](Frame::return_to_block) of the popped stack frame.
|
||||
pub return_to_block: StackPopCleanup,
|
||||
|
||||
/// [`return_place`](Frame::return_place) of the popped stack frame.
|
||||
pub return_place: MPlaceTy<'tcx, Prov>,
|
||||
}
|
||||
|
||||
/// State of a local variable including a memoized layout
|
||||
#[derive(Clone)]
|
||||
pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
value: LocalValue<Prov>,
|
||||
/// Don't modify if `Some`, this is only used to prevent computing the layout twice.
|
||||
/// Avoids computing the layout of locals that are never actually initialized.
|
||||
layout: Cell<Option<TyAndLayout<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("LocalState")
|
||||
.field("value", &self.value)
|
||||
.field("ty", &self.layout.get().map(|l| l.ty))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Current value of a local variable
|
||||
///
|
||||
/// This does not store the type of the local; the type is given by `body.local_decls` and can never
|
||||
/// change, so by not storing here we avoid having to maintain that as an invariant.
|
||||
#[derive(Copy, Clone, Debug)] // Miri debug-prints these
|
||||
pub(super) enum LocalValue<Prov: Provenance = CtfeProvenance> {
|
||||
/// This local is not currently alive, and cannot be used at all.
|
||||
Dead,
|
||||
/// A normal, live local.
|
||||
/// Mostly for convenience, we re-use the `Operand` type here.
|
||||
/// This is an optimization over just always having a pointer here;
|
||||
/// we can thus avoid doing an allocation when the local just stores
|
||||
/// immediate values *and* never has its address taken.
|
||||
Live(Operand<Prov>),
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
|
||||
pub fn make_live_uninit(&mut self) {
|
||||
self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit));
|
||||
}
|
||||
|
||||
/// This is a hack because Miri needs a way to visit all the provenance in a `LocalState`
|
||||
/// without having a layout or `TyCtxt` available, and we want to keep the `Operand` type
|
||||
/// private.
|
||||
pub fn as_mplace_or_imm(
|
||||
&self,
|
||||
) -> Option<Either<(Pointer<Option<Prov>>, MemPlaceMeta<Prov>), Immediate<Prov>>> {
|
||||
match self.value {
|
||||
LocalValue::Dead => None,
|
||||
LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))),
|
||||
LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the local's value or error if the local is not yet live or not live anymore.
|
||||
#[inline(always)]
|
||||
pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
|
||||
match &self.value {
|
||||
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
|
||||
LocalValue::Live(val) => Ok(val),
|
||||
}
|
||||
}
|
||||
|
||||
/// Overwrite the local. If the local can be overwritten in place, return a reference
|
||||
/// to do so; otherwise return the `MemPlace` to consult instead.
|
||||
#[inline(always)]
|
||||
pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
|
||||
match &mut self.value {
|
||||
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
|
||||
LocalValue::Live(val) => Ok(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// What we store about a frame in an interpreter backtrace.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FrameInfo<'tcx> {
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// FIXME: only used by miri, should be removed once translatable.
|
||||
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
|
||||
write!(f, "inside closure")
|
||||
} else {
|
||||
// Note: this triggers a `must_produce_diag` state, which means that if we ever
|
||||
// get here we must emit a diagnostic. We should never display a `FrameInfo` unless
|
||||
// we actually want to emit a warning or error to the user.
|
||||
write!(f, "inside `{}`", self.instance)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> FrameInfo<'tcx> {
|
||||
pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote {
|
||||
let span = self.span;
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
|
||||
errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
|
||||
} else {
|
||||
let instance = format!("{}", self.instance);
|
||||
// Note: this triggers a `must_produce_diag` state, which means that if we ever get
|
||||
// here we must emit a diagnostic. We should never display a `FrameInfo` unless we
|
||||
// actually want to emit a warning or error to the user.
|
||||
errors::FrameNote { where_: "instance", span, instance, times: 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
|
||||
pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'tcx, Prov, Extra> {
|
||||
Frame {
|
||||
body: self.body,
|
||||
instance: self.instance,
|
||||
return_to_block: self.return_to_block,
|
||||
return_place: self.return_place,
|
||||
locals: self.locals,
|
||||
loc: self.loc,
|
||||
extra,
|
||||
tracing_span: self.tracing_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> {
|
||||
/// Get the current location within the Frame.
|
||||
///
|
||||
/// If this is `Right`, we are not currently executing any particular statement in
|
||||
/// this frame (can happen e.g. during frame initialization, and during unwinding on
|
||||
/// frames without cleanup code).
|
||||
///
|
||||
/// Used by [priroda](https://github.com/oli-obk/priroda).
|
||||
pub fn current_loc(&self) -> Either<mir::Location, Span> {
|
||||
self.loc
|
||||
}
|
||||
|
||||
pub fn body(&self) -> &'tcx mir::Body<'tcx> {
|
||||
self.body
|
||||
}
|
||||
|
||||
pub fn instance(&self) -> ty::Instance<'tcx> {
|
||||
self.instance
|
||||
}
|
||||
|
||||
/// Return the `SourceInfo` of the current instruction.
|
||||
pub fn current_source_info(&self) -> Option<&mir::SourceInfo> {
|
||||
self.loc.left().map(|loc| self.body.source_info(loc))
|
||||
}
|
||||
|
||||
pub fn current_span(&self) -> Span {
|
||||
match self.loc {
|
||||
Left(loc) => self.body.source_info(loc).span,
|
||||
Right(span) => span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> {
|
||||
// We first try to get a HirId via the current source scope,
|
||||
// and fall back to `body.source`.
|
||||
self.current_source_info()
|
||||
.and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data {
|
||||
mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
|
||||
mir::ClearCrossCrate::Clear => None,
|
||||
})
|
||||
.or_else(|| {
|
||||
let def_id = self.body.source.def_id().as_local();
|
||||
def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the address of the buffer where the locals are stored. This is used by `Place` as a
|
||||
/// sanity check to detect bugs where we mix up which stack frame a place refers to.
|
||||
#[inline(always)]
|
||||
pub(super) fn locals_addr(&self) -> usize {
|
||||
self.locals.raw.as_ptr().addr()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec<FrameInfo<'tcx>> {
|
||||
let mut frames = Vec::new();
|
||||
// This deliberately does *not* honor `requires_caller_location` since it is used for much
|
||||
// more than just panics.
|
||||
for frame in stack.iter().rev() {
|
||||
let span = match frame.loc {
|
||||
Left(loc) => {
|
||||
// If the stacktrace passes through MIR-inlined source scopes, add them.
|
||||
let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc);
|
||||
let mut scope_data = &frame.body.source_scopes[scope];
|
||||
while let Some((instance, call_span)) = scope_data.inlined {
|
||||
frames.push(FrameInfo { span, instance });
|
||||
span = call_span;
|
||||
scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()];
|
||||
}
|
||||
span
|
||||
}
|
||||
Right(span) => span,
|
||||
};
|
||||
frames.push(FrameInfo { span, instance: frame.instance });
|
||||
}
|
||||
trace!("generate stacktrace: {:#?}", frames);
|
||||
frames
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Very low-level helper that pushes a stack frame without initializing
|
||||
/// the arguments or local variables.
|
||||
///
|
||||
/// The high-level version of this is `init_stack_frame`.
|
||||
#[instrument(skip(self, body, return_place, return_to_block), level = "debug")]
|
||||
pub(crate) fn push_stack_frame_raw(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
return_place: &MPlaceTy<'tcx, M::Provenance>,
|
||||
return_to_block: StackPopCleanup,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("body: {:#?}", body);
|
||||
|
||||
// We can push a `Root` frame if and only if the stack is empty.
|
||||
debug_assert_eq!(
|
||||
self.stack().is_empty(),
|
||||
matches!(return_to_block, StackPopCleanup::Root { .. })
|
||||
);
|
||||
|
||||
// First push a stack frame so we have access to `instantiate_from_current_frame` and other
|
||||
// `self.frame()`-based functions.
|
||||
let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) };
|
||||
let locals = IndexVec::from_elem(dead_local, &body.local_decls);
|
||||
let pre_frame = Frame {
|
||||
body,
|
||||
loc: Right(body.span), // Span used for errors caused during preamble.
|
||||
return_to_block,
|
||||
return_place: return_place.clone(),
|
||||
locals,
|
||||
instance,
|
||||
tracing_span: SpanGuard::new(),
|
||||
extra: (),
|
||||
};
|
||||
let frame = M::init_frame(self, pre_frame)?;
|
||||
self.stack_mut().push(frame);
|
||||
|
||||
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
||||
for &const_ in body.required_consts() {
|
||||
let c =
|
||||
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
|
||||
c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {
|
||||
err.emit_note(*self.tcx);
|
||||
err
|
||||
})?;
|
||||
}
|
||||
|
||||
// Finish things up.
|
||||
M::after_stack_push(self)?;
|
||||
self.frame_mut().loc = Left(mir::Location::START);
|
||||
let span = info_span!("frame", "{}", instance);
|
||||
self.frame_mut().tracing_span.enter(span);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Low-level helper that pops a stack frame from the stack and returns some information about
|
||||
/// it.
|
||||
///
|
||||
/// This also deallocates locals, if necessary.
|
||||
///
|
||||
/// [`M::before_stack_pop`] should be called before calling this function.
|
||||
/// [`M::after_stack_pop`] is called by this function automatically.
|
||||
///
|
||||
/// The high-level version of this is `return_from_current_stack_frame`.
|
||||
///
|
||||
/// [`M::before_stack_pop`]: Machine::before_stack_pop
|
||||
/// [`M::after_stack_pop`]: Machine::after_stack_pop
|
||||
pub(super) fn pop_stack_frame_raw(
|
||||
&mut self,
|
||||
unwinding: bool,
|
||||
) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> {
|
||||
let cleanup = self.cleanup_current_frame_locals()?;
|
||||
|
||||
let frame =
|
||||
self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
|
||||
|
||||
let return_to_block = frame.return_to_block;
|
||||
let return_place = frame.return_place.clone();
|
||||
|
||||
let return_action;
|
||||
if cleanup {
|
||||
return_action = M::after_stack_pop(self, frame, unwinding)?;
|
||||
assert_ne!(return_action, ReturnAction::NoCleanup);
|
||||
} else {
|
||||
return_action = ReturnAction::NoCleanup;
|
||||
};
|
||||
|
||||
Ok(StackPopInfo { return_action, return_to_block, return_place })
|
||||
}
|
||||
|
||||
/// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw).
|
||||
/// Returns `true` if cleanup has been done, `false` otherwise.
|
||||
fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> {
|
||||
// Cleanup: deallocate locals.
|
||||
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
|
||||
// We do this while the frame is still on the stack, so errors point to the callee.
|
||||
let return_to_block = self.frame().return_to_block;
|
||||
let cleanup = match return_to_block {
|
||||
StackPopCleanup::Goto { .. } => true,
|
||||
StackPopCleanup::Root { cleanup, .. } => cleanup,
|
||||
};
|
||||
|
||||
if cleanup {
|
||||
// We need to take the locals out, since we need to mutate while iterating.
|
||||
let locals = mem::take(&mut self.frame_mut().locals);
|
||||
for local in &locals {
|
||||
self.deallocate_local(local.value)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cleanup)
|
||||
}
|
||||
|
||||
/// In the current stack frame, mark all locals as live that are not arguments and don't have
|
||||
/// `Storage*` annotations (this includes the return place).
|
||||
pub(crate) fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> {
|
||||
self.storage_live(mir::RETURN_PLACE)?;
|
||||
|
||||
let body = self.body();
|
||||
let always_live = always_storage_live_locals(body);
|
||||
for local in body.vars_and_temps_iter() {
|
||||
if always_live.contains(local) {
|
||||
self.storage_live(local)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn storage_live_dyn(
|
||||
&mut self,
|
||||
local: mir::Local,
|
||||
meta: MemPlaceMeta<M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("{:?} is now live", local);
|
||||
|
||||
// We avoid `ty.is_trivially_sized` since that does something expensive for ADTs.
|
||||
fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Uint(_)
|
||||
| ty::Int(_)
|
||||
| ty::Bool
|
||||
| ty::Float(_)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Char
|
||||
| ty::Ref(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Never
|
||||
| ty::Error(_)
|
||||
| ty::Dynamic(_, _, ty::DynStar) => true,
|
||||
|
||||
ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
|
||||
|
||||
ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)),
|
||||
|
||||
ty::Pat(ty, ..) => is_very_trivially_sized(*ty),
|
||||
|
||||
// We don't want to do any queries, so there is not much we can do with ADTs.
|
||||
ty::Adt(..) => false,
|
||||
|
||||
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false,
|
||||
|
||||
ty::Infer(ty::TyVar(_)) => false,
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a hot function, we avoid computing the layout when possible.
|
||||
// `unsized_` will be `None` for sized types and `Some(layout)` for unsized types.
|
||||
let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) {
|
||||
None
|
||||
} else {
|
||||
// We need the layout.
|
||||
let layout = self.layout_of_local(self.frame(), local, None)?;
|
||||
if layout.is_sized() { None } else { Some(layout) }
|
||||
};
|
||||
|
||||
let local_val = LocalValue::Live(if let Some(layout) = unsized_ {
|
||||
if !meta.has_meta() {
|
||||
throw_unsup!(UnsizedLocal);
|
||||
}
|
||||
// Need to allocate some memory, since `Immediate::Uninit` cannot be unsized.
|
||||
let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?;
|
||||
Operand::Indirect(*dest_place.mplace())
|
||||
} else {
|
||||
assert!(!meta.has_meta()); // we're dropping the metadata
|
||||
// Just make this an efficient immediate.
|
||||
// Note that not calling `layout_of` here does have one real consequence:
|
||||
// if the type is too big, we'll only notice this when the local is actually initialized,
|
||||
// which is a bit too late -- we should ideally notice this already here, when the memory
|
||||
// is conceptually allocated. But given how rare that error is and that this is a hot function,
|
||||
// we accept this downside for now.
|
||||
Operand::Immediate(Immediate::Uninit)
|
||||
});
|
||||
|
||||
// If the local is already live, deallocate its old memory.
|
||||
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
|
||||
self.deallocate_local(old)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mark a storage as live, killing the previous content.
|
||||
#[inline(always)]
|
||||
pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||
self.storage_live_dyn(local, MemPlaceMeta::None)
|
||||
}
|
||||
|
||||
pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
|
||||
trace!("{:?} is now dead", local);
|
||||
|
||||
// If the local is already dead, this is a NOP.
|
||||
let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
|
||||
self.deallocate_local(old)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> {
|
||||
if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
|
||||
// All locals have a backing allocation, even if the allocation is empty
|
||||
// due to the local having ZST type. Hence we can `unwrap`.
|
||||
trace!(
|
||||
"deallocating local {:?}: {:?}",
|
||||
local,
|
||||
// Locals always have a `alloc_id` (they are never the result of a int2ptr).
|
||||
self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
|
||||
);
|
||||
self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn layout_of_local(
|
||||
&self,
|
||||
frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
|
||||
local: mir::Local,
|
||||
layout: Option<TyAndLayout<'tcx>>,
|
||||
) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
|
||||
let state = &frame.locals[local];
|
||||
if let Some(layout) = state.layout.get() {
|
||||
return Ok(layout);
|
||||
}
|
||||
|
||||
let layout = from_known_layout(self.tcx, self.param_env, layout, || {
|
||||
let local_ty = frame.body.local_decls[local].ty;
|
||||
let local_ty =
|
||||
self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
|
||||
self.layout_of(local_ty)
|
||||
})?;
|
||||
|
||||
// Layouts of locals are requested a lot, so we cache them.
|
||||
state.layout.set(Some(layout));
|
||||
Ok(layout)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
|
||||
pub(super) fn print(
|
||||
&self,
|
||||
allocs: &mut Vec<Option<AllocId>>,
|
||||
fmt: &mut std::fmt::Formatter<'_>,
|
||||
) -> std::fmt::Result {
|
||||
match self.value {
|
||||
LocalValue::Dead => write!(fmt, " is dead")?,
|
||||
LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => {
|
||||
write!(fmt, " is uninitialized")?
|
||||
}
|
||||
LocalValue::Live(Operand::Indirect(mplace)) => {
|
||||
write!(
|
||||
fmt,
|
||||
" by {} ref {:?}:",
|
||||
match mplace.meta {
|
||||
MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"),
|
||||
MemPlaceMeta::None => String::new(),
|
||||
},
|
||||
mplace.ptr,
|
||||
)?;
|
||||
allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id));
|
||||
}
|
||||
LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
|
||||
write!(fmt, " {val:?}")?;
|
||||
if let Scalar::Ptr(ptr, _size) = val {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
}
|
||||
LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
|
||||
write!(fmt, " ({val1:?}, {val2:?})")?;
|
||||
if let Scalar::Ptr(ptr, _size) = val1 {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
if let Scalar::Ptr(ptr, _size) = val2 {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -4,15 +4,29 @@
|
|||
|
||||
use either::Either;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_middle::ty::layout::FnAbiOf;
|
||||
use rustc_middle::ty::{self, Instance, Ty};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
|
||||
use tracing::{info, instrument, trace};
|
||||
|
||||
use super::{
|
||||
ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar,
|
||||
throw_ub, FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta,
|
||||
PlaceTy, Projectable, Scalar,
|
||||
};
|
||||
use crate::util;
|
||||
|
||||
struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> {
|
||||
callee: FnVal<'tcx, M::ExtraFnVal>,
|
||||
args: Vec<FnArg<'tcx, M::Provenance>>,
|
||||
fn_sig: ty::FnSig<'tcx>,
|
||||
fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>,
|
||||
/// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`])
|
||||
with_caller_location: bool,
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Returns `true` as long as there are more things to do.
|
||||
///
|
||||
|
|
@ -36,7 +50,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
if let Some(stmt) = basic_block.statements.get(loc.statement_index) {
|
||||
let old_frames = self.frame_idx();
|
||||
self.statement(stmt)?;
|
||||
self.eval_statement(stmt)?;
|
||||
// Make sure we are not updating `statement_index` of the wrong frame.
|
||||
assert_eq!(old_frames, self.frame_idx());
|
||||
// Advance the program counter.
|
||||
|
|
@ -47,7 +61,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
M::before_terminator(self)?;
|
||||
|
||||
let terminator = basic_block.terminator();
|
||||
self.terminator(terminator)?;
|
||||
self.eval_terminator(terminator)?;
|
||||
if !self.stack().is_empty() {
|
||||
if let Either::Left(loc) = self.frame().loc {
|
||||
info!("// executing {:?}", loc.block);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +74,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// statement counter.
|
||||
///
|
||||
/// This does NOT move the statement counter forward, the caller has to do that!
|
||||
pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
|
||||
pub fn eval_statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
|
||||
info!("{:?}", stmt);
|
||||
|
||||
use rustc_middle::mir::StatementKind::*;
|
||||
|
|
@ -349,16 +368,222 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Evaluate the given terminator. Will also adjust the stack frame and statement position accordingly.
|
||||
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
|
||||
/// Evaluate the arguments of a function call
|
||||
fn eval_fn_call_argument(
|
||||
&self,
|
||||
op: &mir::Operand<'tcx>,
|
||||
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
|
||||
Ok(match op {
|
||||
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
|
||||
// Make a regular copy.
|
||||
let op = self.eval_operand(op, None)?;
|
||||
FnArg::Copy(op)
|
||||
}
|
||||
mir::Operand::Move(place) => {
|
||||
// If this place lives in memory, preserve its location.
|
||||
// We call `place_to_op` which will be an `MPlaceTy` whenever there exists
|
||||
// an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
|
||||
// which can return a local even if that has an mplace.)
|
||||
let place = self.eval_place(*place)?;
|
||||
let op = self.place_to_op(&place)?;
|
||||
|
||||
match op.as_mplace_or_imm() {
|
||||
Either::Left(mplace) => FnArg::InPlace(mplace),
|
||||
Either::Right(_imm) => {
|
||||
// This argument doesn't live in memory, so there's no place
|
||||
// to make inaccessible during the call.
|
||||
// We rely on there not being any stray `PlaceTy` that would let the
|
||||
// caller directly access this local!
|
||||
// This is also crucial for tail calls, where we want the `FnArg` to
|
||||
// stay valid when the old stack frame gets popped.
|
||||
FnArg::Copy(op)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
|
||||
/// necessary information about callee and arguments to make a call.
|
||||
fn eval_callee_and_args(
|
||||
&self,
|
||||
terminator: &mir::Terminator<'tcx>,
|
||||
func: &mir::Operand<'tcx>,
|
||||
args: &[Spanned<mir::Operand<'tcx>>],
|
||||
) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
|
||||
let func = self.eval_operand(func, None)?;
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| self.eval_fn_call_argument(&arg.node))
|
||||
.collect::<InterpResult<'tcx, Vec<_>>>()?;
|
||||
|
||||
let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
|
||||
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder);
|
||||
let extra_args = &args[fn_sig.inputs().len()..];
|
||||
let extra_args =
|
||||
self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty));
|
||||
|
||||
let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() {
|
||||
ty::FnPtr(_sig) => {
|
||||
let fn_ptr = self.read_pointer(&func)?;
|
||||
let fn_val = self.get_ptr_fn(fn_ptr)?;
|
||||
(fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false)
|
||||
}
|
||||
ty::FnDef(def_id, args) => {
|
||||
let instance = self.resolve(def_id, args)?;
|
||||
(
|
||||
FnVal::Instance(instance),
|
||||
self.fn_abi_of_instance(instance, extra_args)?,
|
||||
instance.def.requires_caller_location(*self.tcx),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location })
|
||||
}
|
||||
|
||||
fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
|
||||
info!("{:?}", terminator.kind);
|
||||
|
||||
self.eval_terminator(terminator)?;
|
||||
if !self.stack().is_empty() {
|
||||
if let Either::Left(loc) = self.frame().loc {
|
||||
info!("// executing {:?}", loc.block);
|
||||
use rustc_middle::mir::TerminatorKind::*;
|
||||
match terminator.kind {
|
||||
Return => {
|
||||
self.return_from_current_stack_frame(/* unwinding */ false)?
|
||||
}
|
||||
|
||||
Goto { target } => self.go_to_block(target),
|
||||
|
||||
SwitchInt { ref discr, ref targets } => {
|
||||
let discr = self.read_immediate(&self.eval_operand(discr, None)?)?;
|
||||
trace!("SwitchInt({:?})", *discr);
|
||||
|
||||
// Branch to the `otherwise` case by default, if no match is found.
|
||||
let mut target_block = targets.otherwise();
|
||||
|
||||
for (const_int, target) in targets.iter() {
|
||||
// Compare using MIR BinOp::Eq, to also support pointer values.
|
||||
// (Avoiding `self.binary_op` as that does some redundant layout computation.)
|
||||
let res = self.binary_op(
|
||||
mir::BinOp::Eq,
|
||||
&discr,
|
||||
&ImmTy::from_uint(const_int, discr.layout),
|
||||
)?;
|
||||
if res.to_scalar().to_bool()? {
|
||||
target_block = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.go_to_block(target_block);
|
||||
}
|
||||
|
||||
Call {
|
||||
ref func,
|
||||
ref args,
|
||||
destination,
|
||||
target,
|
||||
unwind,
|
||||
call_source: _,
|
||||
fn_span: _,
|
||||
} => {
|
||||
let old_stack = self.frame_idx();
|
||||
let old_loc = self.frame().loc;
|
||||
|
||||
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
|
||||
self.eval_callee_and_args(terminator, func, args)?;
|
||||
|
||||
let destination = self.force_allocation(&self.eval_place(destination)?)?;
|
||||
self.init_fn_call(
|
||||
callee,
|
||||
(fn_sig.abi, fn_abi),
|
||||
&args,
|
||||
with_caller_location,
|
||||
&destination,
|
||||
target,
|
||||
if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable },
|
||||
)?;
|
||||
// Sanity-check that `eval_fn_call` either pushed a new frame or
|
||||
// did a jump to another block.
|
||||
if self.frame_idx() == old_stack && self.frame().loc == old_loc {
|
||||
span_bug!(terminator.source_info.span, "evaluating this call made no progress");
|
||||
}
|
||||
}
|
||||
|
||||
TailCall { ref func, ref args, fn_span: _ } => {
|
||||
let old_frame_idx = self.frame_idx();
|
||||
|
||||
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
|
||||
self.eval_callee_and_args(terminator, func, args)?;
|
||||
|
||||
self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?;
|
||||
|
||||
if self.frame_idx() != old_frame_idx {
|
||||
span_bug!(
|
||||
terminator.source_info.span,
|
||||
"evaluating this tail call pushed a new stack frame"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Drop { place, target, unwind, replace: _ } => {
|
||||
let place = self.eval_place(place)?;
|
||||
let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||
if let ty::InstanceKind::DropGlue(_, None) = instance.def {
|
||||
// This is the branch we enter if and only if the dropped type has no drop glue
|
||||
// whatsoever. This can happen as a result of monomorphizing a drop of a
|
||||
// generic. In order to make sure that generic and non-generic code behaves
|
||||
// roughly the same (and in keeping with Mir semantics) we do nothing here.
|
||||
self.go_to_block(target);
|
||||
return Ok(());
|
||||
}
|
||||
trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty);
|
||||
self.init_drop_in_place_call(&place, instance, target, unwind)?;
|
||||
}
|
||||
|
||||
Assert { ref cond, expected, ref msg, target, unwind } => {
|
||||
let ignored =
|
||||
M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check();
|
||||
let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?;
|
||||
if ignored || expected == cond_val {
|
||||
self.go_to_block(target);
|
||||
} else {
|
||||
M::assert_panic(self, msg, unwind)?;
|
||||
}
|
||||
}
|
||||
|
||||
UnwindTerminate(reason) => {
|
||||
M::unwind_terminate(self, reason)?;
|
||||
}
|
||||
|
||||
// When we encounter Resume, we've finished unwinding
|
||||
// cleanup for the current stack frame. We pop it in order
|
||||
// to continue unwinding the next frame
|
||||
UnwindResume => {
|
||||
trace!("unwinding: resuming from cleanup");
|
||||
// By definition, a Resume terminator means
|
||||
// that we're unwinding
|
||||
self.return_from_current_stack_frame(/* unwinding */ true)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// It is UB to ever encounter this.
|
||||
Unreachable => throw_ub!(Unreachable),
|
||||
|
||||
// These should never occur for MIR we actually run.
|
||||
FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | CoroutineDrop => span_bug!(
|
||||
terminator.source_info.span,
|
||||
"{:#?} should have been eliminated by MIR pass",
|
||||
terminator.kind
|
||||
),
|
||||
|
||||
InlineAsm { template, ref operands, options, ref targets, .. } => {
|
||||
M::eval_inline_asm(self, template, operands, options, targets)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement};
|
||||
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
|
||||
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
|
||||
|
||||
use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine};
|
||||
|
|
@ -30,10 +30,10 @@ pub fn check_validity_requirement<'tcx>(
|
|||
return Ok(!layout.abi.is_uninhabited());
|
||||
}
|
||||
|
||||
let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env };
|
||||
if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks {
|
||||
might_permit_raw_init_strict(layout, tcx, kind)
|
||||
might_permit_raw_init_strict(layout, &layout_cx, kind)
|
||||
} else {
|
||||
let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env };
|
||||
might_permit_raw_init_lax(layout, &layout_cx, kind)
|
||||
}
|
||||
}
|
||||
|
|
@ -42,12 +42,12 @@ pub fn check_validity_requirement<'tcx>(
|
|||
/// details.
|
||||
fn might_permit_raw_init_strict<'tcx>(
|
||||
ty: TyAndLayout<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
kind: ValidityRequirement,
|
||||
) -> Result<bool, &'tcx LayoutError<'tcx>> {
|
||||
let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
|
||||
|
||||
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
|
||||
let mut cx = InterpCx::new(cx.tcx, rustc_span::DUMMY_SP, cx.param_env, machine);
|
||||
|
||||
let allocated = cx
|
||||
.allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
|
||||
|
|
|
|||
|
|
@ -741,6 +741,16 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
|
|||
self
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn highlighted_span_note(
|
||||
&mut self,
|
||||
span: impl Into<MultiSpan>,
|
||||
msg: Vec<StringPart>,
|
||||
) -> &mut Self {
|
||||
self.sub_with_highlights(Level::Note, msg, span.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// This is like [`Diag::note()`], but it's only printed once.
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn note_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
|
||||
|
|
@ -815,6 +825,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Add a help message attached to this diagnostic with a customizable highlighted message.
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn highlighted_span_help(
|
||||
&mut self,
|
||||
span: impl Into<MultiSpan>,
|
||||
msg: Vec<StringPart>,
|
||||
) -> &mut Self {
|
||||
self.sub_with_highlights(Level::Help, msg, span.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Prints the span with some help above it.
|
||||
/// This is like [`Diag::help()`], but it gets its own span.
|
||||
#[rustc_lint_diagnostics]
|
||||
|
|
|
|||
|
|
@ -1346,10 +1346,11 @@ impl HumanEmitter {
|
|||
buffer.append(0, ": ", header_style);
|
||||
label_width += 2;
|
||||
}
|
||||
for (text, _) in msgs.iter() {
|
||||
let mut line = 0;
|
||||
for (text, style) in msgs.iter() {
|
||||
let text = self.translate_message(text, args).map_err(Report::new).unwrap();
|
||||
// Account for newlines to align output to its label.
|
||||
for (line, text) in normalize_whitespace(&text).lines().enumerate() {
|
||||
for text in normalize_whitespace(&text).lines() {
|
||||
buffer.append(
|
||||
line,
|
||||
&format!(
|
||||
|
|
@ -1357,8 +1358,38 @@ impl HumanEmitter {
|
|||
if line == 0 { String::new() } else { " ".repeat(label_width) },
|
||||
text
|
||||
),
|
||||
header_style,
|
||||
match style {
|
||||
Style::Highlight => *style,
|
||||
_ => header_style,
|
||||
},
|
||||
);
|
||||
line += 1;
|
||||
}
|
||||
// We add lines above, but if the last line has no explicit newline (which would
|
||||
// yield an empty line), then we revert one line up to continue with the next
|
||||
// styled text chunk on the same line as the last one from the prior one. Otherwise
|
||||
// every `text` would appear on their own line (because even though they didn't end
|
||||
// in '\n', they advanced `line` by one).
|
||||
if line > 0 {
|
||||
line -= 1;
|
||||
}
|
||||
}
|
||||
if self.short_message {
|
||||
let labels = msp
|
||||
.span_labels()
|
||||
.into_iter()
|
||||
.filter_map(|label| match label.label {
|
||||
Some(msg) if label.is_primary => {
|
||||
let text = self.translate_message(&msg, args).ok()?;
|
||||
if !text.trim().is_empty() { Some(text.to_string()) } else { None }
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
if !labels.is_empty() {
|
||||
buffer.append(line, ": ", Style::NoStyle);
|
||||
buffer.append(line, &labels, Style::NoStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2564,22 +2595,13 @@ fn num_decimal_digits(num: usize) -> usize {
|
|||
|
||||
// We replace some characters so the CLI output is always consistent and underlines aligned.
|
||||
// Keep the following list in sync with `rustc_span::char_width`.
|
||||
// ATTENTION: keep lexicografically sorted so that the binary search will work
|
||||
const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
|
||||
('\t', " "), // We do our own tab replacement
|
||||
('\u{200D}', ""), // Replace ZWJ with nothing for consistent terminal output of grapheme clusters.
|
||||
('\u{202A}', "<EFBFBD>"), // The following unicode text flow control characters are inconsistently
|
||||
('\u{202B}', "<EFBFBD>"), // supported across CLIs and can cause confusion due to the bytes on disk
|
||||
('\u{202D}', "<EFBFBD>"), // not corresponding to the visible source code, so we replace them always.
|
||||
('\u{202E}', "<EFBFBD>"),
|
||||
('\u{2066}', "<EFBFBD>"),
|
||||
('\u{2067}', "<EFBFBD>"),
|
||||
('\u{2068}', "<EFBFBD>"),
|
||||
('\u{202C}', "<EFBFBD>"),
|
||||
('\u{2069}', "<EFBFBD>"),
|
||||
// tidy-alphabetical-start
|
||||
// In terminals without Unicode support the following will be garbled, but in *all* terminals
|
||||
// the underlying codepoint will be as well. We could gate this replacement behind a "unicode
|
||||
// support" gate.
|
||||
('\u{0000}', "␀"),
|
||||
('\0', "␀"),
|
||||
('\u{0001}', "␁"),
|
||||
('\u{0002}', "␂"),
|
||||
('\u{0003}', "␃"),
|
||||
|
|
@ -2588,11 +2610,12 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
|
|||
('\u{0006}', "␆"),
|
||||
('\u{0007}', "␇"),
|
||||
('\u{0008}', "␈"),
|
||||
('\u{000B}', "␋"),
|
||||
('\u{000C}', "␌"),
|
||||
('\u{000D}', "␍"),
|
||||
('\u{000E}', "␎"),
|
||||
('\u{000F}', "␏"),
|
||||
('\u{0009}', " "), // We do our own tab replacement
|
||||
('\u{000b}', "␋"),
|
||||
('\u{000c}', "␌"),
|
||||
('\u{000d}', "␍"),
|
||||
('\u{000e}', "␎"),
|
||||
('\u{000f}', "␏"),
|
||||
('\u{0010}', "␐"),
|
||||
('\u{0011}', "␑"),
|
||||
('\u{0012}', "␒"),
|
||||
|
|
@ -2603,21 +2626,37 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
|
|||
('\u{0017}', "␗"),
|
||||
('\u{0018}', "␘"),
|
||||
('\u{0019}', "␙"),
|
||||
('\u{001A}', "␚"),
|
||||
('\u{001B}', "␛"),
|
||||
('\u{001C}', "␜"),
|
||||
('\u{001D}', "␝"),
|
||||
('\u{001E}', "␞"),
|
||||
('\u{001F}', "␟"),
|
||||
('\u{007F}', "␡"),
|
||||
('\u{001a}', "␚"),
|
||||
('\u{001b}', "␛"),
|
||||
('\u{001c}', "␜"),
|
||||
('\u{001d}', "␝"),
|
||||
('\u{001e}', "␞"),
|
||||
('\u{001f}', "␟"),
|
||||
('\u{007f}', "␡"),
|
||||
('\u{200d}', ""), // Replace ZWJ for consistent terminal output of grapheme clusters.
|
||||
('\u{202a}', "<EFBFBD>"), // The following unicode text flow control characters are inconsistently
|
||||
('\u{202b}', "<EFBFBD>"), // supported across CLIs and can cause confusion due to the bytes on disk
|
||||
('\u{202c}', "<EFBFBD>"), // not corresponding to the visible source code, so we replace them always.
|
||||
('\u{202d}', "<EFBFBD>"),
|
||||
('\u{202e}', "<EFBFBD>"),
|
||||
('\u{2066}', "<EFBFBD>"),
|
||||
('\u{2067}', "<EFBFBD>"),
|
||||
('\u{2068}', "<EFBFBD>"),
|
||||
('\u{2069}', "<EFBFBD>"),
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
|
||||
fn normalize_whitespace(str: &str) -> String {
|
||||
let mut s = str.to_string();
|
||||
for (c, replacement) in OUTPUT_REPLACEMENTS {
|
||||
s = s.replace(*c, replacement);
|
||||
}
|
||||
s
|
||||
fn normalize_whitespace(s: &str) -> String {
|
||||
// Scan the input string for a character in the ordered table above. If it's present, replace
|
||||
// it with it's alternative string (it can be more than 1 char!). Otherwise, retain the input
|
||||
// char. At the end, allocate all chars into a string in one operation.
|
||||
s.chars().fold(String::with_capacity(s.len()), |mut s, c| {
|
||||
match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) {
|
||||
Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1),
|
||||
_ => s.push(c),
|
||||
}
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
|
||||
|
|
|
|||
|
|
@ -3700,6 +3700,11 @@ impl<'hir> OwnerNode<'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if node is an impl block.
|
||||
pub fn is_impl_block(&self) -> bool {
|
||||
matches!(self, OwnerNode::Item(Item { kind: ItemKind::Impl(_), .. }))
|
||||
}
|
||||
|
||||
expect_methods_self! {
|
||||
expect_item, &'hir Item<'hir>, OwnerNode::Item(n), n;
|
||||
expect_foreign_item, &'hir ForeignItem<'hir>, OwnerNode::ForeignItem(n), n;
|
||||
|
|
|
|||
|
|
@ -552,21 +552,34 @@ pub(crate) fn check_generic_arg_count(
|
|||
synth_provided,
|
||||
}
|
||||
} else {
|
||||
let num_missing_args = expected_max - provided;
|
||||
// Check if associated type bounds are incorrectly written in impl block header like:
|
||||
// ```
|
||||
// trait Foo<T> {}
|
||||
// impl Foo<T: Default> for u8 {}
|
||||
// ```
|
||||
let parent_is_impl_block = cx
|
||||
.tcx()
|
||||
.hir()
|
||||
.parent_owner_iter(seg.hir_id)
|
||||
.next()
|
||||
.is_some_and(|(_, owner_node)| owner_node.is_impl_block());
|
||||
if parent_is_impl_block {
|
||||
let constraint_names: Vec<_> =
|
||||
gen_args.constraints.iter().map(|b| b.ident.name).collect();
|
||||
let param_names: Vec<_> = gen_params
|
||||
.own_params
|
||||
.iter()
|
||||
.filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter
|
||||
.map(|param| param.name)
|
||||
.collect();
|
||||
if constraint_names == param_names {
|
||||
// We set this to true and delay emitting `WrongNumberOfGenericArgs`
|
||||
// to provide a succinct error for cases like issue #113073
|
||||
all_params_are_binded = true;
|
||||
};
|
||||
}
|
||||
|
||||
let constraint_names: Vec<_> =
|
||||
gen_args.constraints.iter().map(|b| b.ident.name).collect();
|
||||
let param_names: Vec<_> = gen_params
|
||||
.own_params
|
||||
.iter()
|
||||
.filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter
|
||||
.map(|param| param.name)
|
||||
.collect();
|
||||
if constraint_names == param_names {
|
||||
// We set this to true and delay emitting `WrongNumberOfGenericArgs`
|
||||
// to provide a succinct error for cases like issue #113073
|
||||
all_params_are_binded = true;
|
||||
};
|
||||
let num_missing_args = expected_max - provided;
|
||||
|
||||
GenericArgsInfo::MissingTypesOrConsts {
|
||||
num_missing_args,
|
||||
|
|
|
|||
|
|
@ -518,8 +518,8 @@ lint_non_binding_let_multi_suggestion =
|
|||
lint_non_binding_let_on_drop_type =
|
||||
non-binding let on a type that implements `Drop`
|
||||
|
||||
lint_non_binding_let_on_sync_lock =
|
||||
non-binding let on a synchronization lock
|
||||
lint_non_binding_let_on_sync_lock = non-binding let on a synchronization lock
|
||||
.label = this lock is not assigned to a binding and is immediately dropped
|
||||
|
||||
lint_non_binding_let_suggestion =
|
||||
consider binding to an unused variable to avoid immediately dropping the value
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
|
|||
];
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) {
|
||||
if matches!(local.source, rustc_hir::LocalSource::AsyncFn) {
|
||||
return;
|
||||
|
|
@ -156,12 +155,12 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
|||
is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)),
|
||||
};
|
||||
if is_sync_lock {
|
||||
let mut span = MultiSpan::from_span(pat.span);
|
||||
span.push_span_label(
|
||||
pat.span,
|
||||
"this lock is not assigned to a binding and is immediately dropped".to_string(),
|
||||
let span = MultiSpan::from_span(pat.span);
|
||||
cx.emit_span_lint(
|
||||
LET_UNDERSCORE_LOCK,
|
||||
span,
|
||||
NonBindingLet::SyncLock { sub, pat: pat.span },
|
||||
);
|
||||
cx.emit_span_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
|
||||
// Only emit let_underscore_drop for top-level `_` patterns.
|
||||
} else if can_use_init.is_some() {
|
||||
cx.emit_span_lint(LET_UNDERSCORE_DROP, local.span, NonBindingLet::DropType { sub });
|
||||
|
|
|
|||
|
|
@ -957,6 +957,8 @@ pub struct BadOptAccessDiag<'a> {
|
|||
pub enum NonBindingLet {
|
||||
#[diag(lint_non_binding_let_on_sync_lock)]
|
||||
SyncLock {
|
||||
#[label]
|
||||
pat: Span,
|
||||
#[subdiagnostic]
|
||||
sub: NonBindingLetSub,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -458,8 +458,11 @@ fn lint_int_literal<'tcx>(
|
|||
}
|
||||
|
||||
let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span };
|
||||
let lit =
|
||||
cx.sess().source_map().span_to_snippet(span).expect("must get snippet from literal");
|
||||
let lit = cx
|
||||
.sess()
|
||||
.source_map()
|
||||
.span_to_snippet(span)
|
||||
.unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() });
|
||||
let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
|
||||
.map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
|
||||
|
||||
|
|
@ -485,6 +488,7 @@ fn lint_uint_literal<'tcx>(
|
|||
ast::LitKind::Int(v, _) => v.get(),
|
||||
_ => bug!(),
|
||||
};
|
||||
|
||||
if lit_val < min || lit_val > max {
|
||||
if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) {
|
||||
match par_e.kind {
|
||||
|
|
@ -526,7 +530,7 @@ fn lint_uint_literal<'tcx>(
|
|||
.sess()
|
||||
.source_map()
|
||||
.span_to_snippet(lit.span)
|
||||
.expect("must get snippet from literal"),
|
||||
.unwrap_or_else(|_| lit_val.to_string()),
|
||||
min,
|
||||
max,
|
||||
},
|
||||
|
|
@ -551,14 +555,14 @@ fn lint_literal<'tcx>(
|
|||
}
|
||||
ty::Uint(t) => lint_uint_literal(cx, e, lit, t),
|
||||
ty::Float(t) => {
|
||||
let is_infinite = match lit.node {
|
||||
let (is_infinite, sym) = match lit.node {
|
||||
ast::LitKind::Float(v, _) => match t {
|
||||
// FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI
|
||||
// issues resolved).
|
||||
ty::FloatTy::F16 => Ok(false),
|
||||
ty::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
|
||||
ty::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
|
||||
ty::FloatTy::F128 => Ok(false),
|
||||
ty::FloatTy::F16 => (Ok(false), v),
|
||||
ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v),
|
||||
ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v),
|
||||
ty::FloatTy::F128 => (Ok(false), v),
|
||||
},
|
||||
_ => bug!(),
|
||||
};
|
||||
|
|
@ -572,7 +576,7 @@ fn lint_literal<'tcx>(
|
|||
.sess()
|
||||
.source_map()
|
||||
.span_to_snippet(lit.span)
|
||||
.expect("must get snippet from literal"),
|
||||
.unwrap_or_else(|_| sym.to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2183,6 +2183,12 @@ rustc_queries! {
|
|||
desc { "looking up supported target features" }
|
||||
}
|
||||
|
||||
query implied_target_features(feature: Symbol) -> &'tcx UnordSet<Symbol> {
|
||||
arena_cache
|
||||
eval_always
|
||||
desc { "looking up implied target features" }
|
||||
}
|
||||
|
||||
query features_query(_: ()) -> &'tcx rustc_feature::Features {
|
||||
feedable
|
||||
desc { "looking up enabled feature gates" }
|
||||
|
|
|
|||
|
|
@ -250,6 +250,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| sym::deny
|
||||
| sym::forbid
|
||||
| sym::cfg
|
||||
| sym::cfg_attr
|
||||
// need to be fixed
|
||||
| sym::cfi_encoding // FIXME(cfi_encoding)
|
||||
| sym::may_dangle // FIXME(dropck_eyepatch)
|
||||
|
|
|
|||
|
|
@ -838,10 +838,14 @@ pub enum Input {
|
|||
|
||||
impl Input {
|
||||
pub fn filestem(&self) -> &str {
|
||||
match *self {
|
||||
Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(),
|
||||
Input::Str { .. } => "rust_out",
|
||||
if let Input::File(ifile) = self {
|
||||
// If for some reason getting the file stem as a UTF-8 string fails,
|
||||
// then fallback to a fixed name.
|
||||
if let Some(name) = ifile.file_stem().and_then(OsStr::to_str) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
"rust_out"
|
||||
}
|
||||
|
||||
pub fn source_name(&self) -> FileName {
|
||||
|
|
|
|||
|
|
@ -276,6 +276,7 @@ symbols! {
|
|||
Path,
|
||||
PathBuf,
|
||||
Pending,
|
||||
PinCoerceUnsized,
|
||||
Pointer,
|
||||
Poll,
|
||||
ProcMacro,
|
||||
|
|
|
|||
|
|
@ -339,8 +339,6 @@ const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[
|
|||
// tidy-alphabetical-end
|
||||
];
|
||||
|
||||
const WASM_IMPLICIT_FEATURES: &[(&str, &str)] = &[("relaxed-simd", "simd128")];
|
||||
|
||||
const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))];
|
||||
|
||||
const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[
|
||||
|
|
@ -411,6 +409,79 @@ const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[
|
|||
// tidy-alphabetical-end
|
||||
];
|
||||
|
||||
const X86_IMPLIED_FEATURES: &[(&str, &[&str])] = &[
|
||||
// tidy-alphabetical-start
|
||||
("aes", &["sse2"]),
|
||||
("avx", &["sse4.2"]),
|
||||
("avx2", &["avx"]),
|
||||
("avx512bf16", &["avx512bw"]),
|
||||
("avx512bitalg", &["avx512bw"]),
|
||||
("avx512bw", &["avx512f"]),
|
||||
("avx512cd", &["avx512f"]),
|
||||
("avx512dq", &["avx512f"]),
|
||||
("avx512f", &["avx2"]),
|
||||
("avx512fp16", &["avx512bw", "avx512vl", "avx512dq"]),
|
||||
("avx512vbmi", &["avx512bw"]),
|
||||
("avx512vbmi2", &["avx512bw"]),
|
||||
("avx512vl", &["avx512f"]),
|
||||
("avx512vnni", &["avx512f"]),
|
||||
("avx512vp2intersect", &["avx512f"]),
|
||||
("avx512vpopcntdq", &["avx512f"]),
|
||||
("f16c", &["avx"]),
|
||||
("fma", &["avx"]),
|
||||
("gfni", &["sse2"]),
|
||||
("pclmulqdq", &["sse2"]),
|
||||
("sha", &["sse2"]),
|
||||
("sse2", &["sse"]),
|
||||
("sse3", &["sse2"]),
|
||||
("sse4.1", &["ssse3"]),
|
||||
("sse4.2", &["sse4.1"]),
|
||||
("ssse3", &["sse3"]),
|
||||
("vaes", &["avx", "aes"]),
|
||||
("vpclmulqdq", &["avx", "pclmulqdq"]),
|
||||
("xsavec", &["xsave"]),
|
||||
("xsaveopt", &["xsave"]),
|
||||
("xsaves", &["xsave"]),
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
|
||||
const AARCH64_IMPLIED_FEATURES: &[(&str, &[&str])] = &[
|
||||
// tidy-alphabetical-start
|
||||
("aes", &["neon"]),
|
||||
("f32mm", &["sve"]),
|
||||
("f64mm", &["sve"]),
|
||||
("fcma", &["neon"]),
|
||||
("fhm", &["fp16"]),
|
||||
("fp16", &["neon"]),
|
||||
("jsconv", &["neon"]),
|
||||
("rcpc2", &["rcpc"]),
|
||||
("sha2", &["neon"]),
|
||||
("sha3", &["sha2"]),
|
||||
("sm4", &["neon"]),
|
||||
("sve", &["fp16"]),
|
||||
("sve2", &["sve"]),
|
||||
("sve2-aes", &["sve2", "aes"]),
|
||||
("sve2-bitperm", &["sve2"]),
|
||||
("sve2-sha3", &["sve2", "sha3"]),
|
||||
("sve2-sm4", &["sve2", "sm4"]),
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
|
||||
const RISCV_IMPLIED_FEATURES: &[(&str, &[&str])] = &[
|
||||
// tidy-alphabetical-start
|
||||
("zb", &["zba", "zbc", "zbs"]),
|
||||
("zk", &["zkn", "zkr", "zks", "zkt", "zbkb", "zbkc", "zkbx"]),
|
||||
("zkn", &["zknd", "zkne", "zknh", "zbkb", "zbkc", "zkbx"]),
|
||||
("zks", &["zksed", "zksh", "zbkb", "zbkc", "zkbx"]),
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
|
||||
const WASM_IMPLIED_FEATURES: &[(&str, &[&str])] = &[
|
||||
// tidy-alphabetical-start
|
||||
("relaxed-simd", &["simd128"]),
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
|
||||
/// When rustdoc is running, provide a list of all known features so that all their respective
|
||||
/// primitives may be documented.
|
||||
///
|
||||
|
|
@ -458,11 +529,12 @@ impl super::spec::Target {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a list of target features. Each items first target feature
|
||||
/// implicitly enables the second one.
|
||||
pub fn implicit_target_features(&self) -> &'static [(&'static str, &'static str)] {
|
||||
pub fn implied_target_features(&self) -> &'static [(&'static str, &'static [&'static str])] {
|
||||
match &*self.arch {
|
||||
"wasm32" | "wasm64" => WASM_IMPLICIT_FEATURES,
|
||||
"aarch4" => AARCH64_IMPLIED_FEATURES,
|
||||
"riscv32" | "riscv64" => RISCV_IMPLIED_FEATURES,
|
||||
"x86" | "x86_64" => X86_IMPLIED_FEATURES,
|
||||
"wasm32" | "wasm64" => WASM_IMPLIED_FEATURES,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, StringPart,
|
||||
pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey,
|
||||
StringPart,
|
||||
};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{self as hir, LangItem, Node};
|
||||
use rustc_infer::infer::{InferOk, TypeTrace};
|
||||
|
|
@ -1624,9 +1625,131 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
other: bool,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> bool {
|
||||
// If we have a single implementation, try to unify it with the trait ref
|
||||
// that failed. This should uncover a better hint for what *is* implemented.
|
||||
let alternative_candidates = |def_id: DefId| {
|
||||
let mut impl_candidates: Vec<_> = self
|
||||
.tcx
|
||||
.all_impls(def_id)
|
||||
// ignore `do_not_recommend` items
|
||||
.filter(|def_id| {
|
||||
!self
|
||||
.tcx
|
||||
.has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend])
|
||||
})
|
||||
// Ignore automatically derived impls and `!Trait` impls.
|
||||
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
|
||||
.filter_map(|header| {
|
||||
(header.polarity != ty::ImplPolarity::Negative
|
||||
|| self.tcx.is_automatically_derived(def_id))
|
||||
.then(|| header.trait_ref.instantiate_identity())
|
||||
})
|
||||
.filter(|trait_ref| {
|
||||
let self_ty = trait_ref.self_ty();
|
||||
// Avoid mentioning type parameters.
|
||||
if let ty::Param(_) = self_ty.kind() {
|
||||
false
|
||||
}
|
||||
// Avoid mentioning types that are private to another crate
|
||||
else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
|
||||
// FIXME(compiler-errors): This could be generalized, both to
|
||||
// be more granular, and probably look past other `#[fundamental]`
|
||||
// types, too.
|
||||
self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
impl_candidates.sort_by_key(|tr| tr.to_string());
|
||||
impl_candidates.dedup();
|
||||
impl_candidates
|
||||
};
|
||||
|
||||
// We'll check for the case where the reason for the mismatch is that the trait comes from
|
||||
// one crate version and the type comes from another crate version, even though they both
|
||||
// are from the same crate.
|
||||
let trait_def_id = trait_ref.def_id();
|
||||
if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind()
|
||||
&& let found_type = def.did()
|
||||
&& trait_def_id.krate != found_type.krate
|
||||
&& self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate)
|
||||
{
|
||||
let name = self.tcx.crate_name(trait_def_id.krate);
|
||||
let spans: Vec<_> = [trait_def_id, found_type]
|
||||
.into_iter()
|
||||
.filter_map(|def_id| self.tcx.extern_crate(def_id))
|
||||
.map(|data| {
|
||||
let dependency = if data.dependency_of == LOCAL_CRATE {
|
||||
"direct dependency of the current crate".to_string()
|
||||
} else {
|
||||
let dep = self.tcx.crate_name(data.dependency_of);
|
||||
format!("dependency of crate `{dep}`")
|
||||
};
|
||||
(
|
||||
data.span,
|
||||
format!("one version of crate `{name}` is used here, as a {dependency}"),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
|
||||
for (sp, label) in spans.into_iter() {
|
||||
span.push_span_label(sp, label);
|
||||
}
|
||||
err.highlighted_span_help(
|
||||
span,
|
||||
vec![
|
||||
StringPart::normal("you have ".to_string()),
|
||||
StringPart::highlighted("multiple different versions".to_string()),
|
||||
StringPart::normal(" of crate `".to_string()),
|
||||
StringPart::highlighted(format!("{name}")),
|
||||
StringPart::normal("` in your dependency graph".to_string()),
|
||||
],
|
||||
);
|
||||
let candidates = if impl_candidates.is_empty() {
|
||||
alternative_candidates(trait_def_id)
|
||||
} else {
|
||||
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
|
||||
};
|
||||
if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| {
|
||||
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
|
||||
&& let candidate_def_id = def.did()
|
||||
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
|
||||
&& let Some(found) = self.tcx.opt_item_name(found_type)
|
||||
&& name == found
|
||||
&& candidate_def_id.krate != found_type.krate
|
||||
&& self.tcx.crate_name(candidate_def_id.krate)
|
||||
== self.tcx.crate_name(found_type.krate)
|
||||
{
|
||||
// A candidate was found of an item with the same name, from two separate
|
||||
// versions of the same crate, let's clarify.
|
||||
Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
let mut span: MultiSpan = vec![sp_candidate, sp_found].into();
|
||||
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
|
||||
span.push_span_label(sp_candidate, "this type implements the required trait");
|
||||
span.push_span_label(sp_found, "this type doesn't implement the required trait");
|
||||
err.highlighted_span_note(
|
||||
span,
|
||||
vec![
|
||||
StringPart::normal(
|
||||
"two types coming from two different versions of the same crate are \
|
||||
different types "
|
||||
.to_string(),
|
||||
),
|
||||
StringPart::highlighted("even if they look the same".to_string()),
|
||||
],
|
||||
);
|
||||
}
|
||||
err.help("you can use `cargo tree` to explore your dependency tree");
|
||||
return true;
|
||||
}
|
||||
|
||||
if let [single] = &impl_candidates {
|
||||
// If we have a single implementation, try to unify it with the trait ref
|
||||
// that failed. This should uncover a better hint for what *is* implemented.
|
||||
if self.probe(|_| {
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
|
||||
|
|
@ -1798,43 +1921,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
|
||||
return false;
|
||||
}
|
||||
let mut impl_candidates: Vec<_> = self
|
||||
.tcx
|
||||
.all_impls(def_id)
|
||||
// ignore `do_not_recommend` items
|
||||
.filter(|def_id| {
|
||||
!self
|
||||
.tcx
|
||||
.has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend])
|
||||
})
|
||||
// Ignore automatically derived impls and `!Trait` impls.
|
||||
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
|
||||
.filter_map(|header| {
|
||||
(header.polarity != ty::ImplPolarity::Negative
|
||||
|| self.tcx.is_automatically_derived(def_id))
|
||||
.then(|| header.trait_ref.instantiate_identity())
|
||||
})
|
||||
.filter(|trait_ref| {
|
||||
let self_ty = trait_ref.self_ty();
|
||||
// Avoid mentioning type parameters.
|
||||
if let ty::Param(_) = self_ty.kind() {
|
||||
false
|
||||
}
|
||||
// Avoid mentioning types that are private to another crate
|
||||
else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
|
||||
// FIXME(compiler-errors): This could be generalized, both to
|
||||
// be more granular, and probably look past other `#[fundamental]`
|
||||
// types, too.
|
||||
self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
impl_candidates.sort_by_key(|tr| tr.to_string());
|
||||
impl_candidates.dedup();
|
||||
return report(impl_candidates, err);
|
||||
return report(alternative_candidates(def_id), err);
|
||||
}
|
||||
|
||||
// Sort impl candidates so that ordering is consistent for UI tests.
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ use core::ops::{
|
|||
AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut,
|
||||
DerefPure, DispatchFromDyn, Receiver,
|
||||
};
|
||||
use core::pin::Pin;
|
||||
use core::pin::{Pin, PinCoerceUnsized};
|
||||
use core::ptr::{self, addr_of_mut, NonNull, Unique};
|
||||
use core::task::{Context, Poll};
|
||||
use core::{borrow, fmt, slice};
|
||||
|
|
@ -2726,3 +2726,6 @@ impl<T: core::error::Error> core::error::Error for Box<T> {
|
|||
core::error::Error::provide(&**self, request);
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Box<T, A> {}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@
|
|||
#![feature(maybe_uninit_uninit_array_transpose)]
|
||||
#![feature(panic_internals)]
|
||||
#![feature(pattern)]
|
||||
#![feature(pin_coerce_unsized_trait)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(ptr_metadata)]
|
||||
#![feature(ptr_sub_ptr)]
|
||||
|
|
|
|||
|
|
@ -256,6 +256,7 @@ use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Rece
|
|||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::pin::Pin;
|
||||
use core::pin::PinCoerceUnsized;
|
||||
use core::ptr::{self, drop_in_place, NonNull};
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::slice::from_raw_parts_mut;
|
||||
|
|
@ -2177,6 +2178,12 @@ impl<T: ?Sized, A: Allocator> Deref for Rc<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Rc<T, A> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Weak<T, A> {}
|
||||
|
||||
#[unstable(feature = "deref_pure_trait", issue = "87121")]
|
||||
unsafe impl<T: ?Sized, A: Allocator> DerefPure for Rc<T, A> {}
|
||||
|
||||
|
|
@ -3691,6 +3698,9 @@ impl<T: ?Sized, A: Allocator> Deref for UniqueRc<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for UniqueRc<T> {}
|
||||
|
||||
#[unstable(feature = "unique_rc_arc", issue = "112566")]
|
||||
impl<T: ?Sized, A: Allocator> DerefMut for UniqueRc<T, A> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use core::marker::{PhantomData, Unsize};
|
|||
use core::mem::{self, align_of_val_raw, ManuallyDrop};
|
||||
use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver};
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use core::pin::Pin;
|
||||
use core::pin::{Pin, PinCoerceUnsized};
|
||||
use core::ptr::{self, NonNull};
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::slice::from_raw_parts_mut;
|
||||
|
|
@ -2142,6 +2142,12 @@ impl<T: ?Sized, A: Allocator> Deref for Arc<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Arc<T, A> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Weak<T, A> {}
|
||||
|
||||
#[unstable(feature = "deref_pure_trait", issue = "87121")]
|
||||
unsafe impl<T: ?Sized, A: Allocator> DerefPure for Arc<T, A> {}
|
||||
|
||||
|
|
|
|||
|
|
@ -227,3 +227,17 @@ fn make_mut_unsized() {
|
|||
assert_eq!(*data, [11, 21, 31]);
|
||||
assert_eq!(*other_data, [110, 20, 30]);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
mod pin_coerce_unsized {
|
||||
use alloc::sync::Arc;
|
||||
use core::pin::Pin;
|
||||
|
||||
pub trait MyTrait {}
|
||||
impl MyTrait for String {}
|
||||
|
||||
// Pin coercion should work for Arc
|
||||
pub fn pin_arc(arg: Pin<Arc<String>>) -> Pin<Arc<dyn MyTrait>> {
|
||||
arg
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,3 +179,40 @@ unsafe impl Allocator for ConstAllocator {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
mod pin_coerce_unsized {
|
||||
use alloc::boxed::Box;
|
||||
use core::pin::Pin;
|
||||
|
||||
trait MyTrait {
|
||||
fn action(&self) -> &str;
|
||||
}
|
||||
impl MyTrait for String {
|
||||
fn action(&self) -> &str {
|
||||
&*self
|
||||
}
|
||||
}
|
||||
struct MyStruct;
|
||||
impl MyTrait for MyStruct {
|
||||
fn action(&self) -> &str {
|
||||
"MyStruct"
|
||||
}
|
||||
}
|
||||
|
||||
// Pin coercion should work for Box
|
||||
fn pin_box<T: MyTrait + 'static>(arg: Pin<Box<T>>) -> Pin<Box<dyn MyTrait>> {
|
||||
arg
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pin_coerce_unsized_box() {
|
||||
let my_string = "my string";
|
||||
let a_string = Box::pin(String::from(my_string));
|
||||
let pin_box_str = pin_box(a_string);
|
||||
assert_eq!(pin_box_str.as_ref().action(), my_string);
|
||||
let a_struct = Box::pin(MyStruct);
|
||||
let pin_box_struct = pin_box(a_struct);
|
||||
assert_eq!(pin_box_struct.as_ref().action(), "MyStruct");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#![feature(drain_keep_rest)]
|
||||
#![feature(local_waker)]
|
||||
#![feature(vec_pop_if)]
|
||||
#![feature(unique_rc_arc)]
|
||||
#![allow(internal_features)]
|
||||
#![deny(fuzzy_provenance_casts)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
|
|
|||
|
|
@ -205,3 +205,20 @@ fn weak_may_dangle() {
|
|||
// `val` dropped here while still borrowed
|
||||
// borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak`
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
mod pin_coerce_unsized {
|
||||
use alloc::rc::{Rc, UniqueRc};
|
||||
use core::pin::Pin;
|
||||
|
||||
pub trait MyTrait {}
|
||||
impl MyTrait for String {}
|
||||
|
||||
// Pin coercion should work for Rc
|
||||
pub fn pin_rc(arg: Pin<Rc<String>>) -> Pin<Rc<dyn MyTrait>> {
|
||||
arg
|
||||
}
|
||||
pub fn pin_unique_rc(arg: Pin<UniqueRc<String>>) -> Pin<UniqueRc<dyn MyTrait>> {
|
||||
arg
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ use crate::fmt::{self, Debug, Display};
|
|||
use crate::marker::{PhantomData, Unsize};
|
||||
use crate::mem;
|
||||
use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn};
|
||||
use crate::pin::PinCoerceUnsized;
|
||||
use crate::ptr::{self, NonNull};
|
||||
|
||||
mod lazy;
|
||||
|
|
@ -2396,3 +2397,21 @@ fn assert_coerce_unsized(
|
|||
let _: Cell<&dyn Send> = c;
|
||||
let _: RefCell<&dyn Send> = d;
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for UnsafeCell<T> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for SyncUnsafeCell<T> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for Cell<T> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for RefCell<T> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<'b, T: ?Sized> PinCoerceUnsized for Ref<'b, T> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<'b, T: ?Sized> PinCoerceUnsized for RefMut<'b, T> {}
|
||||
|
|
|
|||
|
|
@ -1715,10 +1715,56 @@ impl<Ptr: fmt::Pointer> fmt::Pointer for Pin<Ptr> {
|
|||
// for other reasons, though, so we just need to take care not to allow such
|
||||
// impls to land in std.
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
impl<Ptr, U> CoerceUnsized<Pin<U>> for Pin<Ptr> where Ptr: CoerceUnsized<U> {}
|
||||
impl<Ptr, U> CoerceUnsized<Pin<U>> for Pin<Ptr>
|
||||
where
|
||||
Ptr: CoerceUnsized<U> + PinCoerceUnsized,
|
||||
U: PinCoerceUnsized,
|
||||
{
|
||||
}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
impl<Ptr, U> DispatchFromDyn<Pin<U>> for Pin<Ptr> where Ptr: DispatchFromDyn<U> {}
|
||||
impl<Ptr, U> DispatchFromDyn<Pin<U>> for Pin<Ptr>
|
||||
where
|
||||
Ptr: DispatchFromDyn<U> + PinCoerceUnsized,
|
||||
U: PinCoerceUnsized,
|
||||
{
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
/// Trait that indicates that this is a pointer or a wrapper for one, where
|
||||
/// unsizing can be performed on the pointee when it is pinned.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// If this type implements `Deref`, then the concrete type returned by `deref`
|
||||
/// and `deref_mut` must not change without a modification. The following
|
||||
/// operations are not considered modifications:
|
||||
///
|
||||
/// * Moving the pointer.
|
||||
/// * Performing unsizing coercions on the pointer.
|
||||
/// * Performing dynamic dispatch with the pointer.
|
||||
/// * Calling `deref` or `deref_mut` on the pointer.
|
||||
///
|
||||
/// The concrete type of a trait object is the type that the vtable corresponds
|
||||
/// to. The concrete type of a slice is an array of the same element type and
|
||||
/// the length specified in the metadata. The concrete type of a sized type
|
||||
/// is the type itself.
|
||||
pub unsafe trait PinCoerceUnsized {}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a T {}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a mut T {}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
unsafe impl<T: PinCoerceUnsized> PinCoerceUnsized for Pin<T> {}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for *const T {}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for *mut T {}
|
||||
|
||||
/// Constructs a <code>[Pin]<[&mut] T></code>, by pinning a `value: T` locally.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use crate::marker::Unsize;
|
|||
use crate::mem::{MaybeUninit, SizedTypeProperties};
|
||||
use crate::num::NonZero;
|
||||
use crate::ops::{CoerceUnsized, DispatchFromDyn};
|
||||
use crate::pin::PinCoerceUnsized;
|
||||
use crate::ptr::Unique;
|
||||
use crate::slice::{self, SliceIndex};
|
||||
use crate::ub_checks::assert_unsafe_precondition;
|
||||
|
|
@ -1724,6 +1725,9 @@ impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Uns
|
|||
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
|
||||
impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for NonNull<T> {}
|
||||
|
||||
#[stable(feature = "nonnull", since = "1.25.0")]
|
||||
impl<T: ?Sized> fmt::Debug for NonNull<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::fmt;
|
||||
use crate::marker::{PhantomData, Unsize};
|
||||
use crate::ops::{CoerceUnsized, DispatchFromDyn};
|
||||
use crate::pin::PinCoerceUnsized;
|
||||
use crate::ptr::NonNull;
|
||||
|
||||
/// A wrapper around a raw non-null `*mut T` that indicates that the possessor
|
||||
|
|
@ -166,6 +167,9 @@ impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsiz
|
|||
#[unstable(feature = "ptr_internals", issue = "none")]
|
||||
impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for Unique<T> {}
|
||||
|
||||
#[unstable(feature = "ptr_internals", issue = "none")]
|
||||
impl<T: ?Sized> fmt::Debug for Unique<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
|
|||
|
|
@ -29,3 +29,49 @@ fn pin_const() {
|
|||
|
||||
pin_mut_const();
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
mod pin_coerce_unsized {
|
||||
use core::cell::{Cell, RefCell, UnsafeCell};
|
||||
use core::pin::Pin;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
pub trait MyTrait {}
|
||||
impl MyTrait for String {}
|
||||
|
||||
// These Pins should continue to compile.
|
||||
// Do note that these instances of Pin types cannot be used
|
||||
// meaningfully because all methods require a Deref/DerefMut
|
||||
// bounds on the pointer type and Cell, RefCell and UnsafeCell
|
||||
// do not implement Deref/DerefMut.
|
||||
|
||||
pub fn cell(arg: Pin<Cell<Box<String>>>) -> Pin<Cell<Box<dyn MyTrait>>> {
|
||||
arg
|
||||
}
|
||||
pub fn ref_cell(arg: Pin<RefCell<Box<String>>>) -> Pin<RefCell<Box<dyn MyTrait>>> {
|
||||
arg
|
||||
}
|
||||
pub fn unsafe_cell(arg: Pin<UnsafeCell<Box<String>>>) -> Pin<UnsafeCell<Box<dyn MyTrait>>> {
|
||||
arg
|
||||
}
|
||||
|
||||
// These sensible Pin coercions are possible.
|
||||
pub fn pin_mut_ref(arg: Pin<&mut String>) -> Pin<&mut dyn MyTrait> {
|
||||
arg
|
||||
}
|
||||
pub fn pin_ref(arg: Pin<&String>) -> Pin<&dyn MyTrait> {
|
||||
arg
|
||||
}
|
||||
pub fn pin_ptr(arg: Pin<*const String>) -> Pin<*const dyn MyTrait> {
|
||||
arg
|
||||
}
|
||||
pub fn pin_ptr_mut(arg: Pin<*mut String>) -> Pin<*mut dyn MyTrait> {
|
||||
arg
|
||||
}
|
||||
pub fn pin_non_null(arg: Pin<NonNull<String>>) -> Pin<NonNull<dyn MyTrait>> {
|
||||
arg
|
||||
}
|
||||
pub fn nesting_pins(arg: Pin<Pin<&String>>) -> Pin<Pin<&dyn MyTrait>> {
|
||||
arg
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,40 @@ impl<R: Read> BufReader<R> {
|
|||
pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
|
||||
BufReader { inner, buf: Buffer::with_capacity(capacity) }
|
||||
}
|
||||
|
||||
/// Attempt to look ahead `n` bytes.
|
||||
///
|
||||
/// `n` must be less than `capacity`.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(bufreader_peek)]
|
||||
/// use std::io::{Read, BufReader};
|
||||
///
|
||||
/// let mut bytes = &b"oh, hello"[..];
|
||||
/// let mut rdr = BufReader::with_capacity(6, &mut bytes);
|
||||
/// assert_eq!(rdr.peek(2).unwrap(), b"oh");
|
||||
/// let mut buf = [0; 4];
|
||||
/// rdr.read(&mut buf[..]).unwrap();
|
||||
/// assert_eq!(&buf, b"oh, ");
|
||||
/// assert_eq!(rdr.peek(2).unwrap(), b"he");
|
||||
/// let mut s = String::new();
|
||||
/// rdr.read_to_string(&mut s).unwrap();
|
||||
/// assert_eq!(&s, "hello");
|
||||
/// ```
|
||||
#[unstable(feature = "bufreader_peek", issue = "128405")]
|
||||
pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> {
|
||||
assert!(n <= self.capacity());
|
||||
while n > self.buf.buffer().len() {
|
||||
if self.buf.pos() > 0 {
|
||||
self.buf.backshift();
|
||||
}
|
||||
self.buf.read_more(&mut self.inner)?;
|
||||
debug_assert_eq!(self.buf.pos(), 0);
|
||||
}
|
||||
Ok(&self.buf.buffer()[..n])
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ?Sized> BufReader<R> {
|
||||
|
|
|
|||
|
|
@ -97,6 +97,27 @@ impl Buffer {
|
|||
self.pos = self.pos.saturating_sub(amt);
|
||||
}
|
||||
|
||||
/// Read more bytes into the buffer without discarding any of its contents
|
||||
pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<()> {
|
||||
let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]);
|
||||
let old_init = self.initialized - self.pos;
|
||||
unsafe {
|
||||
buf.set_init(old_init);
|
||||
}
|
||||
reader.read_buf(buf.unfilled())?;
|
||||
self.filled += buf.len();
|
||||
self.initialized += buf.init_len() - old_init;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove bytes that have already been read from the buffer.
|
||||
pub fn backshift(&mut self) {
|
||||
self.buf.copy_within(self.pos.., 0);
|
||||
self.initialized -= self.pos;
|
||||
self.filled -= self.pos;
|
||||
self.pos = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
|
|
|
|||
|
|
@ -339,6 +339,7 @@
|
|||
#![feature(maybe_uninit_write_slice)]
|
||||
#![feature(panic_can_unwind)]
|
||||
#![feature(panic_internals)]
|
||||
#![feature(pin_coerce_unsized_trait)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(prelude_2024)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! VxWorks-specific definitions
|
||||
|
||||
#![stable(feature = "raw_ext", since = "1.1.0")]
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod fs;
|
||||
pub mod raw;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use crate::cell::UnsafeCell;
|
|||
use crate::convert::TryInto;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut};
|
||||
use crate::pin::PinCoerceUnsized;
|
||||
use crate::ptr::{self, NonNull};
|
||||
use crate::slice::SliceIndex;
|
||||
use crate::{cmp, intrinsics, slice};
|
||||
|
|
@ -751,6 +752,9 @@ where
|
|||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
impl<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {}
|
||||
|
||||
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
|
||||
unsafe impl<T: ?Sized> PinCoerceUnsized for UserRef<T> {}
|
||||
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
impl<T, I> Index<I> for UserRef<[T]>
|
||||
where
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
use libc::{self, c_char, c_int, RTP_ID};
|
||||
|
||||
use crate::io::{self, ErrorKind};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ use crate::mem::{self, ManuallyDrop};
|
|||
use crate::num::NonZero;
|
||||
#[cfg(all(target_os = "linux", target_env = "gnu"))]
|
||||
use crate::sys::weak::dlsym;
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))]
|
||||
#[cfg(any(
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "nto",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
use crate::sys::weak::weak;
|
||||
use crate::sys::{os, stack_overflow};
|
||||
use crate::time::Duration;
|
||||
|
|
@ -212,17 +217,38 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn set_name(name: &CStr) {
|
||||
// FIXME(libc): adding real STATUS, ERROR type eventually.
|
||||
weak! {
|
||||
fn taskNameSet(
|
||||
libc::TASK_ID, *mut libc::c_char
|
||||
) -> libc::c_int
|
||||
}
|
||||
|
||||
// We can't assume taskNameSet is necessarily available.
|
||||
// VX_TASK_NAME_LEN can be found set to 31,
|
||||
// however older versions can be set to only 10.
|
||||
// FIXME(vxworks): if the minimum supported VxWorks is >= 7, the maximum length can be changed to 31.
|
||||
if let Some(f) = taskNameSet.get() {
|
||||
const VX_TASK_NAME_LEN: usize = 10;
|
||||
|
||||
let name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name);
|
||||
let status = unsafe { f(libc::taskIdSelf(), name.as_mut_ptr()) };
|
||||
debug_assert_eq!(res, libc::OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_env = "newlib",
|
||||
target_os = "l4re",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox",
|
||||
target_os = "vxworks",
|
||||
target_os = "hurd",
|
||||
target_os = "aix",
|
||||
))]
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// Newlib, Emscripten, and VxWorks have no way to set a thread name.
|
||||
// Newlib and Emscripten have no way to set a thread name.
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "espidf"))]
|
||||
|
|
@ -291,6 +317,7 @@ impl Drop for Thread {
|
|||
target_os = "nto",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "vxworks",
|
||||
target_vendor = "apple",
|
||||
))]
|
||||
fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
|
||||
|
|
|
|||
|
|
@ -918,7 +918,6 @@ impl Step for Src {
|
|||
// translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs`
|
||||
let dst_src = tarball.image_dir().join("lib/rustlib/src/rust");
|
||||
|
||||
let src_files = ["Cargo.lock"];
|
||||
// This is the reduced set of paths which will become the rust-src component
|
||||
// (essentially libstd and all of its path dependencies).
|
||||
copy_src_dirs(
|
||||
|
|
@ -937,9 +936,6 @@ impl Step for Src {
|
|||
],
|
||||
&dst_src,
|
||||
);
|
||||
for file in src_files.iter() {
|
||||
builder.copy_link(&builder.src.join(file), &dst_src.join(file));
|
||||
}
|
||||
|
||||
tarball.generate()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ pub(crate) fn create_config(
|
|||
lint_cap,
|
||||
scrape_examples_options,
|
||||
expanded_args,
|
||||
remap_path_prefix,
|
||||
..
|
||||
}: RustdocOptions,
|
||||
RenderOptions { document_private, .. }: &RenderOptions,
|
||||
|
|
@ -247,6 +248,7 @@ pub(crate) fn create_config(
|
|||
describe_lints,
|
||||
crate_name,
|
||||
test,
|
||||
remap_path_prefix,
|
||||
..Options::default()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -390,13 +390,18 @@ function preLoadCss(cssUrl) {
|
|||
if (splitAt !== -1) {
|
||||
const implId = savedHash.slice(0, splitAt);
|
||||
const assocId = savedHash.slice(splitAt + 1);
|
||||
const implElem = document.getElementById(implId);
|
||||
if (implElem && implElem.parentElement.tagName === "SUMMARY" &&
|
||||
implElem.parentElement.parentElement.tagName === "DETAILS") {
|
||||
onEachLazy(implElem.parentElement.parentElement.querySelectorAll(
|
||||
const implElems = document.querySelectorAll(
|
||||
`details > summary > section[id^="${implId}"]`,
|
||||
);
|
||||
onEachLazy(implElems, implElem => {
|
||||
const numbered = /^(.+?)-([0-9]+)$/.exec(implElem.id);
|
||||
if (implElem.id !== implId && (!numbered || numbered[1] !== implId)) {
|
||||
return false;
|
||||
}
|
||||
return onEachLazy(implElem.parentElement.parentElement.querySelectorAll(
|
||||
`[id^="${assocId}"]`),
|
||||
item => {
|
||||
const numbered = /([^-]+)-([0-9]+)/.exec(item.id);
|
||||
const numbered = /^(.+?)-([0-9]+)$/.exec(item.id);
|
||||
if (item.id === assocId || (numbered && numbered[1] === assocId)) {
|
||||
openParentDetails(item);
|
||||
item.scrollIntoView();
|
||||
|
|
@ -404,10 +409,11 @@ function preLoadCss(cssUrl) {
|
|||
setTimeout(() => {
|
||||
window.location.replace("#" + item.id);
|
||||
}, 0);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#![warn(clippy::uninit_vec)]
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
#[derive(Default)]
|
||||
struct MyVec {
|
||||
|
|
@ -12,6 +13,12 @@ union MyOwnMaybeUninit {
|
|||
uninit: (),
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/119620
|
||||
unsafe fn requires_paramenv<S>() {
|
||||
let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1);
|
||||
vec.set_len(1);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// with_capacity() -> set_len() should be detected
|
||||
let mut vec: Vec<u8> = Vec::with_capacity(1000);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:17:5
|
||||
--> tests/ui/uninit_vec.rs:18:5
|
||||
|
|
||||
LL | let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | vec.set_len(1);
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
= note: `-D clippy::uninit-vec` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]`
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:24:5
|
||||
|
|
||||
LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -8,11 +20,9 @@ LL | vec.set_len(200);
|
|||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
= note: `-D clippy::uninit-vec` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]`
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:24:5
|
||||
--> tests/ui/uninit_vec.rs:31:5
|
||||
|
|
||||
LL | vec.reserve(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -23,7 +33,7 @@ LL | vec.set_len(200);
|
|||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
|
||||
error: calling `set_len()` on empty `Vec` creates out-of-bound values
|
||||
--> tests/ui/uninit_vec.rs:31:5
|
||||
--> tests/ui/uninit_vec.rs:38:5
|
||||
|
|
||||
LL | let mut vec: Vec<u8> = Vec::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -32,7 +42,7 @@ LL | vec.set_len(200);
|
|||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: calling `set_len()` on empty `Vec` creates out-of-bound values
|
||||
--> tests/ui/uninit_vec.rs:38:5
|
||||
--> tests/ui/uninit_vec.rs:45:5
|
||||
|
|
||||
LL | let mut vec: Vec<u8> = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -41,7 +51,7 @@ LL | vec.set_len(200);
|
|||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: calling `set_len()` on empty `Vec` creates out-of-bound values
|
||||
--> tests/ui/uninit_vec.rs:44:5
|
||||
--> tests/ui/uninit_vec.rs:51:5
|
||||
|
|
||||
LL | let mut vec: Vec<u8> = Vec::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -50,7 +60,7 @@ LL | vec.set_len(200);
|
|||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:61:5
|
||||
--> tests/ui/uninit_vec.rs:68:5
|
||||
|
|
||||
LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -61,7 +71,7 @@ LL | vec.set_len(200);
|
|||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:71:5
|
||||
--> tests/ui/uninit_vec.rs:78:5
|
||||
|
|
||||
LL | my_vec.vec.reserve(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -72,7 +82,7 @@ LL | my_vec.vec.set_len(200);
|
|||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:77:5
|
||||
--> tests/ui/uninit_vec.rs:84:5
|
||||
|
|
||||
LL | my_vec.vec = Vec::with_capacity(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -83,7 +93,7 @@ LL | my_vec.vec.set_len(200);
|
|||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:52:9
|
||||
--> tests/ui/uninit_vec.rs:59:9
|
||||
|
|
||||
LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -94,7 +104,7 @@ LL | vec.set_len(200);
|
|||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:56:9
|
||||
--> tests/ui/uninit_vec.rs:63:9
|
||||
|
|
||||
LL | vec.reserve(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -105,7 +115,7 @@ LL | vec.set_len(200);
|
|||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
|
||||
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
|
||||
--> tests/ui/uninit_vec.rs:132:9
|
||||
--> tests/ui/uninit_vec.rs:139:9
|
||||
|
|
||||
LL | let mut vec: Vec<T> = Vec::with_capacity(1000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -115,5 +125,5 @@ LL | vec.set_len(10);
|
|||
|
|
||||
= help: initialize the buffer or wrap the content in `MaybeUninit`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
c9687a95a602091777e28703aa5abf20f1ce1797
|
||||
6696447f784a888446d13bb400a8d507a68331c9
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ pub struct Thread<'tcx> {
|
|||
/// which then forwards it to 'Resume'. However this argument is implicit in MIR,
|
||||
/// so we have to store it out-of-band. When there are multiple active unwinds,
|
||||
/// the innermost one is always caught first, so we can store them as a stack.
|
||||
pub(crate) panic_payloads: Vec<Scalar>,
|
||||
pub(crate) panic_payloads: Vec<ImmTy<'tcx>>,
|
||||
|
||||
/// Last OS error location in memory. It is a 32-bit integer.
|
||||
pub(crate) last_error: Option<MPlaceTy<'tcx>>,
|
||||
|
|
@ -377,10 +377,6 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> {
|
|||
return_place,
|
||||
locals,
|
||||
extra,
|
||||
body: _,
|
||||
instance: _,
|
||||
return_to_block: _,
|
||||
loc: _,
|
||||
// There are some private fields we cannot access; they contain no tags.
|
||||
..
|
||||
} = self;
|
||||
|
|
@ -952,7 +948,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
instance,
|
||||
start_abi,
|
||||
&[*func_arg],
|
||||
&[func_arg],
|
||||
Some(&ret_place),
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -307,7 +307,8 @@ pub fn create_ecx<'tcx>(
|
|||
// First argument is constructed later, because it's skipped if the entry function uses #[start].
|
||||
|
||||
// Second argument (argc): length of `config.args`.
|
||||
let argc = Scalar::from_target_usize(u64::try_from(config.args.len()).unwrap(), &ecx);
|
||||
let argc =
|
||||
ImmTy::from_int(i64::try_from(config.args.len()).unwrap(), ecx.machine.layouts.isize);
|
||||
// Third argument (`argv`): created from `config.args`.
|
||||
let argv = {
|
||||
// Put each argument in memory, collect pointers.
|
||||
|
|
@ -334,13 +335,11 @@ pub fn create_ecx<'tcx>(
|
|||
ecx.write_immediate(arg, &place)?;
|
||||
}
|
||||
ecx.mark_immutable(&argvs_place);
|
||||
// A pointer to that place is the 3rd argument for main.
|
||||
let argv = argvs_place.to_ref(&ecx);
|
||||
// Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
|
||||
{
|
||||
let argc_place =
|
||||
ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
|
||||
ecx.write_scalar(argc, &argc_place)?;
|
||||
ecx.write_immediate(*argc, &argc_place)?;
|
||||
ecx.mark_immutable(&argc_place);
|
||||
ecx.machine.argc = Some(argc_place.ptr());
|
||||
|
||||
|
|
@ -348,7 +347,7 @@ pub fn create_ecx<'tcx>(
|
|||
ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?,
|
||||
MiriMemoryKind::Machine.into(),
|
||||
)?;
|
||||
ecx.write_immediate(argv, &argv_place)?;
|
||||
ecx.write_pointer(argvs_place.ptr(), &argv_place)?;
|
||||
ecx.mark_immutable(&argv_place);
|
||||
ecx.machine.argv = Some(argv_place.ptr());
|
||||
}
|
||||
|
|
@ -369,7 +368,7 @@ pub fn create_ecx<'tcx>(
|
|||
}
|
||||
ecx.mark_immutable(&cmd_place);
|
||||
}
|
||||
argv
|
||||
ecx.mplace_to_ref(&argvs_place)?
|
||||
};
|
||||
|
||||
// Return place (in static memory so that it does not count as leak).
|
||||
|
|
@ -405,10 +404,14 @@ pub fn create_ecx<'tcx>(
|
|||
start_instance,
|
||||
Abi::Rust,
|
||||
&[
|
||||
Scalar::from_pointer(main_ptr, &ecx).into(),
|
||||
argc.into(),
|
||||
ImmTy::from_scalar(
|
||||
Scalar::from_pointer(main_ptr, &ecx),
|
||||
// FIXME use a proper fn ptr type
|
||||
ecx.machine.layouts.const_raw_ptr,
|
||||
),
|
||||
argc,
|
||||
argv,
|
||||
Scalar::from_u8(sigpipe).into(),
|
||||
ImmTy::from_uint(sigpipe, ecx.machine.layouts.u8),
|
||||
],
|
||||
Some(&ret_place),
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
|
|
@ -418,7 +421,7 @@ pub fn create_ecx<'tcx>(
|
|||
ecx.call_function(
|
||||
entry_instance,
|
||||
Abi::Rust,
|
||||
&[argc.into(), argv],
|
||||
&[argc, argv],
|
||||
Some(&ret_place),
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@ use rustc_apfloat::Float;
|
|||
use rustc_hir::{
|
||||
def::{DefKind, Namespace},
|
||||
def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE},
|
||||
Safety,
|
||||
};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
use rustc_middle::middle::exported_symbols::ExportedSymbol;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::layout::MaybeResult;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, MaybeResult};
|
||||
use rustc_middle::ty::{
|
||||
self,
|
||||
layout::{LayoutOf, TyAndLayout},
|
||||
|
|
@ -492,48 +493,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&mut self,
|
||||
f: ty::Instance<'tcx>,
|
||||
caller_abi: Abi,
|
||||
args: &[Immediate<Provenance>],
|
||||
args: &[ImmTy<'tcx>],
|
||||
dest: Option<&MPlaceTy<'tcx>>,
|
||||
stack_pop: StackPopCleanup,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private.
|
||||
let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi();
|
||||
if callee_abi != caller_abi {
|
||||
throw_ub_format!(
|
||||
"calling a function with ABI {} using caller ABI {}",
|
||||
callee_abi.name(),
|
||||
caller_abi.name()
|
||||
)
|
||||
}
|
||||
|
||||
// Push frame.
|
||||
// Get MIR.
|
||||
let mir = this.load_mir(f.def, None)?;
|
||||
let dest = match dest {
|
||||
Some(dest) => dest.clone(),
|
||||
None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?),
|
||||
};
|
||||
this.push_stack_frame(f, mir, &dest, stack_pop)?;
|
||||
|
||||
// Initialize arguments.
|
||||
let mut callee_args = this.frame().body.args_iter();
|
||||
for arg in args {
|
||||
let local = callee_args
|
||||
.next()
|
||||
.ok_or_else(|| err_ub_format!("callee has fewer arguments than expected"))?;
|
||||
// Make the local live, and insert the initial value.
|
||||
this.storage_live(local)?;
|
||||
let callee_arg = this.local_to_place(local)?;
|
||||
this.write_immediate(*arg, &callee_arg)?;
|
||||
}
|
||||
if callee_args.next().is_some() {
|
||||
throw_ub_format!("callee has more arguments than expected");
|
||||
}
|
||||
// Construct a function pointer type representing the caller perspective.
|
||||
let sig = this.tcx.mk_fn_sig(
|
||||
args.iter().map(|a| a.layout.ty),
|
||||
dest.layout.ty,
|
||||
/*c_variadic*/ false,
|
||||
Safety::Safe,
|
||||
caller_abi,
|
||||
);
|
||||
let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
|
||||
|
||||
// Initialize remaining locals.
|
||||
this.storage_live_for_always_live_locals()?;
|
||||
|
||||
Ok(())
|
||||
this.init_stack_frame(
|
||||
f,
|
||||
mir,
|
||||
caller_fn_abi,
|
||||
&args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
|
||||
/*with_caller_location*/ false,
|
||||
&dest,
|
||||
stack_pop,
|
||||
)
|
||||
}
|
||||
|
||||
/// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter
|
||||
|
|
@ -1114,12 +1105,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// Make an attempt to get at the instance of the function this is inlined from.
|
||||
let instance: Option<_> = try {
|
||||
let scope = frame.current_source_info()?.scope;
|
||||
let inlined_parent = frame.body.source_scopes[scope].inlined_parent_scope?;
|
||||
let source = &frame.body.source_scopes[inlined_parent];
|
||||
let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
|
||||
let source = &frame.body().source_scopes[inlined_parent];
|
||||
source.inlined.expect("inlined_parent_scope points to scope without inline info").0
|
||||
};
|
||||
// Fall back to the instance of the function itself.
|
||||
let instance = instance.unwrap_or(frame.instance);
|
||||
let instance = instance.unwrap_or(frame.instance());
|
||||
// Now check the crate it is in. We could try to be clever here and e.g. check if this is
|
||||
// the same crate as `start_fn`, but that would not work for running std tests in Miri, so
|
||||
// we'd need some more hacks anyway. So we just check the name of the crate. If someone
|
||||
|
|
@ -1359,9 +1350,9 @@ impl<'tcx> MiriMachine<'tcx> {
|
|||
|
||||
/// This is the source of truth for the `is_user_relevant` flag in our `FrameExtra`.
|
||||
pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool {
|
||||
let def_id = frame.instance.def_id();
|
||||
let def_id = frame.instance().def_id();
|
||||
(def_id.is_local() || self.local_crates.contains(&def_id.krate))
|
||||
&& !frame.instance.def.requires_caller_location(self.tcx)
|
||||
&& !frame.instance().def.requires_caller_location(self.tcx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1352,7 +1352,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
|
||||
// Start recording our event before doing anything else
|
||||
let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
|
||||
let fn_name = frame.instance.to_string();
|
||||
let fn_name = frame.instance().to_string();
|
||||
let entry = ecx.machine.string_cache.entry(fn_name.clone());
|
||||
let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
|
||||
|
||||
|
|
@ -1443,7 +1443,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
// tracing-tree can autoamtically annotate scope changes, but it gets very confused by our
|
||||
// concurrency and what it prints is just plain wrong. So we print our own information
|
||||
// instead. (Cc https://github.com/rust-lang/miri/issues/2266)
|
||||
info!("Leaving {}", ecx.frame().instance);
|
||||
info!("Leaving {}", ecx.frame().instance());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1473,7 +1473,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
// Needs to be done after dropping frame to show up on the right nesting level.
|
||||
// (Cc https://github.com/rust-lang/miri/issues/2266)
|
||||
if !ecx.active_thread_stack().is_empty() {
|
||||
info!("Continuing in {}", ecx.frame().instance);
|
||||
info!("Continuing in {}", ecx.frame().instance());
|
||||
}
|
||||
res
|
||||
}
|
||||
|
|
@ -1486,7 +1486,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
|
||||
panic!("after_local_allocated should only be called on fresh allocations");
|
||||
};
|
||||
let local_decl = &ecx.frame().body.local_decls[local];
|
||||
let local_decl = &ecx.frame().body().local_decls[local];
|
||||
let span = local_decl.source_info.span;
|
||||
ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let mut data = Vec::new();
|
||||
for frame in this.active_thread_stack().iter().rev() {
|
||||
// Match behavior of debuginfo (`FunctionCx::adjusted_span_and_dbg_scope`).
|
||||
let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body.span);
|
||||
data.push((frame.instance, span.lo()));
|
||||
let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body().span);
|
||||
data.push((frame.instance(), span.lo()));
|
||||
}
|
||||
|
||||
let ptrs: Vec<_> = data
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ pub struct CatchUnwindData<'tcx> {
|
|||
/// The `catch_fn` callback to call in case of a panic.
|
||||
catch_fn: Pointer,
|
||||
/// The `data` argument for that callback.
|
||||
data: Scalar,
|
||||
data: ImmTy<'tcx>,
|
||||
/// The return place from the original call to `try`.
|
||||
dest: MPlaceTy<'tcx>,
|
||||
/// The return block from the original call to `try`.
|
||||
|
|
@ -48,9 +48,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
trace!("miri_start_unwind: {:?}", this.frame().instance);
|
||||
trace!("miri_start_unwind: {:?}", this.frame().instance());
|
||||
|
||||
let payload = this.read_scalar(payload)?;
|
||||
let payload = this.read_immediate(payload)?;
|
||||
let thread = this.active_thread_mut();
|
||||
thread.panic_payloads.push(payload);
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// Get all the arguments.
|
||||
let [try_fn, data, catch_fn] = check_arg_count(args)?;
|
||||
let try_fn = this.read_pointer(try_fn)?;
|
||||
let data = this.read_scalar(data)?;
|
||||
let data = this.read_immediate(data)?;
|
||||
let catch_fn = this.read_pointer(catch_fn)?;
|
||||
|
||||
// Now we make a function call, and pass `data` as first and only argument.
|
||||
|
|
@ -89,7 +89,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
f_instance,
|
||||
Abi::Rust,
|
||||
&[data.into()],
|
||||
&[data.clone()],
|
||||
None,
|
||||
// Directly return to caller.
|
||||
StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Continue },
|
||||
|
|
@ -124,7 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// and we are unwinding, so we should catch that.
|
||||
trace!(
|
||||
"unwinding: found catch_panic frame during unwinding: {:?}",
|
||||
this.frame().instance
|
||||
this.frame().instance()
|
||||
);
|
||||
|
||||
// We set the return value of `try` to 1, since there was a panic.
|
||||
|
|
@ -140,7 +140,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
f_instance,
|
||||
Abi::Rust,
|
||||
&[catch_unwind.data.into(), payload.into()],
|
||||
&[catch_unwind.data, payload],
|
||||
None,
|
||||
// Directly return to caller of `try`.
|
||||
StackPopCleanup::Goto {
|
||||
|
|
@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
panic,
|
||||
Abi::Rust,
|
||||
&[msg.to_ref(this)],
|
||||
&[this.mplace_to_ref(&msg)?],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind },
|
||||
)
|
||||
|
|
@ -188,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
panic,
|
||||
Abi::Rust,
|
||||
&[msg.to_ref(this)],
|
||||
&[this.mplace_to_ref(&msg)?],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
|
||||
)
|
||||
|
|
@ -207,9 +207,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// Forward to `panic_bounds_check` lang item.
|
||||
|
||||
// First arg: index.
|
||||
let index = this.read_scalar(&this.eval_operand(index, None)?)?;
|
||||
let index = this.read_immediate(&this.eval_operand(index, None)?)?;
|
||||
// Second arg: len.
|
||||
let len = this.read_scalar(&this.eval_operand(len, None)?)?;
|
||||
let len = this.read_immediate(&this.eval_operand(len, None)?)?;
|
||||
|
||||
// Call the lang item.
|
||||
let panic_bounds_check = this.tcx.lang_items().panic_bounds_check_fn().unwrap();
|
||||
|
|
@ -217,7 +217,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
panic_bounds_check,
|
||||
Abi::Rust,
|
||||
&[index.into(), len.into()],
|
||||
&[index, len],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind },
|
||||
)?;
|
||||
|
|
@ -226,9 +226,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// Forward to `panic_misaligned_pointer_dereference` lang item.
|
||||
|
||||
// First arg: required.
|
||||
let required = this.read_scalar(&this.eval_operand(required, None)?)?;
|
||||
let required = this.read_immediate(&this.eval_operand(required, None)?)?;
|
||||
// Second arg: found.
|
||||
let found = this.read_scalar(&this.eval_operand(found, None)?)?;
|
||||
let found = this.read_immediate(&this.eval_operand(found, None)?)?;
|
||||
|
||||
// Call the lang item.
|
||||
let panic_misaligned_pointer_dereference =
|
||||
|
|
@ -238,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
panic_misaligned_pointer_dereference,
|
||||
Abi::Rust,
|
||||
&[required.into(), found.into()],
|
||||
&[required, found],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind },
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -315,6 +315,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// FIXME: Technically, the reason should be `DLL_PROCESS_DETACH` when the main thread exits
|
||||
// but std treats both the same.
|
||||
let reason = this.eval_windows("c", "DLL_THREAD_DETACH");
|
||||
let null_ptr =
|
||||
ImmTy::from_scalar(Scalar::null_ptr(this), this.machine.layouts.const_raw_ptr);
|
||||
|
||||
// The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
|
||||
// FIXME: `h` should be a handle to the current module and what `pv` should be is unknown
|
||||
|
|
@ -322,7 +324,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
thread_callback,
|
||||
Abi::System { unwind: false },
|
||||
&[Scalar::null_ptr(this).into(), reason.into(), Scalar::null_ptr(this).into()],
|
||||
&[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],
|
||||
None,
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
)?;
|
||||
|
|
@ -343,7 +345,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
instance,
|
||||
Abi::C { unwind: false },
|
||||
&[data.into()],
|
||||
&[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],
|
||||
None,
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
)?;
|
||||
|
|
@ -380,7 +382,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.call_function(
|
||||
instance,
|
||||
Abi::C { unwind: false },
|
||||
&[ptr.into()],
|
||||
&[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
|
||||
None,
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::*;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
|
|
@ -24,7 +23,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
start_routine,
|
||||
Abi::C { unwind: false },
|
||||
func_arg,
|
||||
this.layout_of(this.tcx.types.usize)?,
|
||||
this.machine.layouts.mut_raw_ptr,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
//@ignore-target-windows: No pthreads on Windows
|
||||
//~^ERROR: calling a function with more arguments than it expected
|
||||
|
||||
//! The thread function must have exactly one argument.
|
||||
|
||||
use std::{mem, ptr};
|
||||
|
||||
extern "C" fn thread_start() -> *mut libc::c_void {
|
||||
panic!() //~ ERROR: callee has fewer arguments than expected
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
error: Undefined Behavior: callee has fewer arguments than expected
|
||||
--> $DIR/libc_pthread_create_too_few_args.rs:LL:CC
|
||||
|
|
||||
LL | panic!()
|
||||
| ^^^^^^^^ callee has fewer arguments than expected
|
||||
error: Undefined Behavior: calling a function with more arguments than it expected
|
||||
|
|
||||
= note: calling a function with more arguments than it expected
|
||||
= note: (no span available)
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC
|
||||
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
//@ignore-target-windows: No pthreads on Windows
|
||||
//~^ERROR: calling a function with fewer arguments than it requires
|
||||
|
||||
//! The thread function must have exactly one argument.
|
||||
|
||||
use std::{mem, ptr};
|
||||
|
||||
extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_void {
|
||||
panic!() //~ ERROR: callee has more arguments than expected
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
error: Undefined Behavior: callee has more arguments than expected
|
||||
--> $DIR/libc_pthread_create_too_many_args.rs:LL:CC
|
||||
|
|
||||
LL | panic!()
|
||||
| ^^^^^^^^ callee has more arguments than expected
|
||||
error: Undefined Behavior: calling a function with fewer arguments than it requires
|
||||
|
|
||||
= note: calling a function with fewer arguments than it requires
|
||||
= note: (no span available)
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC
|
||||
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ fn main() {
|
|||
// Make sure we check the ABI when Miri itself invokes a function
|
||||
// as part of a shim implementation.
|
||||
std::intrinsics::catch_unwind(
|
||||
//~^ ERROR: calling a function with ABI C using caller ABI Rust
|
||||
//~^ ERROR: calling a function with calling convention C using calling convention Rust
|
||||
std::mem::transmute::<extern "C" fn(*mut u8), _>(try_fn),
|
||||
std::ptr::null_mut(),
|
||||
|_, _| unreachable!(),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
error: Undefined Behavior: calling a function with ABI C using caller ABI Rust
|
||||
error: Undefined Behavior: calling a function with calling convention C using calling convention Rust
|
||||
--> $DIR/check_callback_abi.rs:LL:CC
|
||||
|
|
||||
LL | / std::intrinsics::catch_unwind(
|
||||
|
|
@ -7,7 +7,7 @@ LL | | std::mem::transmute::<extern "C" fn(*mut u8), _>(try_fn),
|
|||
LL | | std::ptr::null_mut(),
|
||||
LL | | |_, _| unreachable!(),
|
||||
LL | | );
|
||||
| |_________^ calling a function with ABI C using caller ABI Rust
|
||||
| |_________^ calling a function with calling convention C using calling convention Rust
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(start)]
|
||||
#![no_std]
|
||||
//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort
|
||||
//@normalize-stderr-test: "id 20" -> "id $$ALLOC"
|
||||
//@compile-flags: -Zmiri-track-alloc-id=21 -Zmiri-track-alloc-accesses -Cpanic=abort
|
||||
//@normalize-stderr-test: "id 21" -> "id $$ALLOC"
|
||||
//@only-target-linux: alloc IDs differ between OSes (due to extern static allocations)
|
||||
|
||||
extern "Rust" {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,17 @@ pub fn dynamic_lib_name(name: &str) -> String {
|
|||
format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION)
|
||||
}
|
||||
|
||||
/// Construct the name of the import library for the dynamic library, exclusive to MSVC and
|
||||
/// accepted by link.exe.
|
||||
#[track_caller]
|
||||
#[must_use]
|
||||
pub fn msvc_import_dynamic_lib_name(name: &str) -> String {
|
||||
assert!(is_msvc(), "this function is exclusive to MSVC");
|
||||
assert!(!name.contains(char::is_whitespace), "import library name cannot contain whitespace");
|
||||
|
||||
format!("{name}.dll.lib")
|
||||
}
|
||||
|
||||
/// Construct the dynamic library extension based on the target.
|
||||
#[must_use]
|
||||
pub fn dynamic_lib_extension() -> &'static str {
|
||||
|
|
|
|||
|
|
@ -72,13 +72,14 @@ pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, targe
|
|||
|
||||
/// Helpers for building names of output artifacts that are potentially target-specific.
|
||||
pub use artifact_names::{
|
||||
bin_name, dynamic_lib_extension, dynamic_lib_name, rust_lib_name, static_lib_name,
|
||||
bin_name, dynamic_lib_extension, dynamic_lib_name, msvc_import_dynamic_lib_name, rust_lib_name,
|
||||
static_lib_name,
|
||||
};
|
||||
|
||||
/// Path-related helpers.
|
||||
pub use path_helpers::{
|
||||
cwd, filename_not_in_denylist, has_extension, has_prefix, has_suffix, not_contains, path,
|
||||
shallow_find_files, source_root,
|
||||
cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, has_suffix,
|
||||
not_contains, path, shallow_find_files, source_root,
|
||||
};
|
||||
|
||||
/// Helpers for scoped test execution where certain properties are attempted to be maintained.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::env::env_var;
|
||||
use crate::rfs;
|
||||
|
||||
/// Return the current working directory.
|
||||
///
|
||||
|
|
@ -40,7 +41,7 @@ pub fn shallow_find_files<P: AsRef<Path>, F: Fn(&PathBuf) -> bool>(
|
|||
filter: F,
|
||||
) -> Vec<PathBuf> {
|
||||
let mut matching_files = Vec::new();
|
||||
for entry in std::fs::read_dir(path).unwrap() {
|
||||
for entry in rfs::read_dir(path) {
|
||||
let entry = entry.expect("failed to read directory entry.");
|
||||
let path = entry.path();
|
||||
|
||||
|
|
@ -78,3 +79,8 @@ pub fn has_extension<P: AsRef<Path>>(path: P, extension: &str) -> bool {
|
|||
pub fn has_suffix<P: AsRef<Path>>(path: P, suffix: &str) -> bool {
|
||||
path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().ends_with(suffix))
|
||||
}
|
||||
|
||||
/// Returns true if the filename at `path` contains `needle`.
|
||||
pub fn filename_contains<P: AsRef<Path>>(path: P, needle: &str) -> bool {
|
||||
path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(needle))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
run-make/branch-protection-check-IBT/Makefile
|
||||
run-make/cat-and-grep-sanity-check/Makefile
|
||||
run-make/cdylib-dylib-linkage/Makefile
|
||||
run-make/cross-lang-lto-upstream-rlibs/Makefile
|
||||
run-make/dep-info-doesnt-run-much/Makefile
|
||||
run-make/dep-info-spaces/Makefile
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ pub unsafe fn crc32sse(v: u8) -> u32 {
|
|||
_mm_crc32_u8(out, v)
|
||||
}
|
||||
|
||||
// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32"}}
|
||||
// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32.*"}}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//@ revisions: COMPAT INCOMPAT
|
||||
//@ needs-llvm-components: x86
|
||||
//@ compile-flags: --target=x86_64-unknown-linux-gnu -Copt-level=3
|
||||
//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx
|
||||
//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx,+sse4.2,+sse4.1,+ssse3,+sse3
|
||||
//@ [INCOMPAT] compile-flags: -Ctarget-feature=-avx2,-avx
|
||||
|
||||
// See also tests/assembly/target-feature-multiple.rs
|
||||
|
|
@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 {
|
|||
}
|
||||
|
||||
// CHECK: attributes [[APPLEATTRS]]
|
||||
// COMPAT-SAME: "target-features"="+avx2,+avx,+avx"
|
||||
// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx"
|
||||
// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}"
|
||||
// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}"
|
||||
// CHECK: attributes [[BANANAATTRS]]
|
||||
// COMPAT-SAME: "target-features"="+avx2,+avx"
|
||||
// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}"
|
||||
// INCOMPAT-SAME: "target-features"="-avx2,-avx"
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
# This test checks that cdylibs can link against dylibs as dependencies, after this restriction was disabled.
|
||||
# See https://github.com/rust-lang/rust/commit/72aaa3a414d17aa0c4f19feafa5bab5f84b60e63
|
||||
|
||||
# ignore-cross-compile
|
||||
include ../tools.mk
|
||||
|
||||
TARGET_SYSROOT := $(shell $(RUSTC) --print sysroot)/lib/rustlib/$(TARGET)/lib
|
||||
|
||||
ifdef IS_MSVC
|
||||
LIBSTD := $(wildcard $(TARGET_SYSROOT)/libstd-*.dll.lib)
|
||||
else
|
||||
LIBSTD := $(wildcard $(TARGET_SYSROOT)/$(call DYLIB_GLOB,std))
|
||||
STD := $(basename $(patsubst lib%,%, $(notdir $(LIBSTD))))
|
||||
endif
|
||||
|
||||
all: $(call RUN_BINFILE,foo)
|
||||
$(call RUN,foo)
|
||||
|
||||
ifdef IS_MSVC
|
||||
CLIBS := $(TMPDIR)/foo.dll.lib $(TMPDIR)/bar.dll.lib $(LIBSTD)
|
||||
$(call RUN_BINFILE,foo): $(call DYLIB,foo)
|
||||
$(CC) $(CFLAGS) foo.c $(CLIBS) $(call OUT_EXE,foo)
|
||||
else
|
||||
CLIBS := -lfoo -lbar -l$(STD) -L $(TMPDIR) -L $(TARGET_SYSROOT)
|
||||
$(call RUN_BINFILE,foo): $(call DYLIB,foo)
|
||||
$(CC) $(CFLAGS) foo.c $(CLIBS) -o $(call RUN_BINFILE,foo)
|
||||
endif
|
||||
|
||||
$(call DYLIB,foo):
|
||||
$(RUSTC) -C prefer-dynamic bar.rs
|
||||
$(RUSTC) foo.rs
|
||||
41
tests/run-make/cdylib-dylib-linkage/rmake.rs
Normal file
41
tests/run-make/cdylib-dylib-linkage/rmake.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Previously, rustc mandated that cdylibs could only link against rlibs as dependencies,
|
||||
// making linkage between cdylibs and dylibs impossible. After this was changed in #68448,
|
||||
// this test attempts to link both `foo` (a cdylib) and `bar` (a dylib) and checks that
|
||||
// both compilation and execution are successful.
|
||||
// See https://github.com/rust-lang/rust/pull/68448
|
||||
|
||||
//@ ignore-cross-compile
|
||||
// Reason: the compiled binary is executed
|
||||
|
||||
use run_make_support::{
|
||||
bin_name, cc, dynamic_lib_extension, dynamic_lib_name, filename_contains, has_extension,
|
||||
has_prefix, has_suffix, is_msvc, msvc_import_dynamic_lib_name, path, run, rustc,
|
||||
shallow_find_files, target,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
rustc().arg("-Cprefer-dynamic").input("bar.rs").run();
|
||||
rustc().input("foo.rs").run();
|
||||
let sysroot = rustc().print("sysroot").run().stdout_utf8();
|
||||
let sysroot = sysroot.trim();
|
||||
let target_sysroot = path(sysroot).join("lib/rustlib").join(target()).join("lib");
|
||||
if is_msvc() {
|
||||
let mut libs = shallow_find_files(&target_sysroot, |path| {
|
||||
has_prefix(path, "libstd-") && has_suffix(path, ".dll.lib")
|
||||
});
|
||||
libs.push(path(msvc_import_dynamic_lib_name("foo")));
|
||||
libs.push(path(msvc_import_dynamic_lib_name("bar")));
|
||||
cc().input("foo.c").args(&libs).out_exe("foo").run();
|
||||
} else {
|
||||
let stdlibs = shallow_find_files(&target_sysroot, |path| {
|
||||
has_extension(path, dynamic_lib_extension()) && filename_contains(path, "std")
|
||||
});
|
||||
cc().input("foo.c")
|
||||
.args(&[dynamic_lib_name("foo"), dynamic_lib_name("bar")])
|
||||
.arg(stdlibs.get(0).unwrap())
|
||||
.library_search_path(&target_sysroot)
|
||||
.output(bin_name("foo"))
|
||||
.run();
|
||||
}
|
||||
run("foo");
|
||||
}
|
||||
6
tests/run-make/crate-loading/multiple-dep-versions-1.rs
Normal file
6
tests/run-make/crate-loading/multiple-dep-versions-1.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#![crate_name = "dependency"]
|
||||
#![crate_type = "rlib"]
|
||||
pub struct Type;
|
||||
pub trait Trait {}
|
||||
impl Trait for Type {}
|
||||
pub fn do_something<X: Trait>(_: X) {}
|
||||
6
tests/run-make/crate-loading/multiple-dep-versions-2.rs
Normal file
6
tests/run-make/crate-loading/multiple-dep-versions-2.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#![crate_name = "dependency"]
|
||||
#![crate_type = "rlib"]
|
||||
pub struct Type(pub i32);
|
||||
pub trait Trait {}
|
||||
impl Trait for Type {}
|
||||
pub fn do_something<X: Trait>(_: X) {}
|
||||
8
tests/run-make/crate-loading/multiple-dep-versions.rs
Normal file
8
tests/run-make/crate-loading/multiple-dep-versions.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
extern crate dep_2_reexport;
|
||||
extern crate dependency;
|
||||
use dep_2_reexport::do_something;
|
||||
use dependency::Type;
|
||||
|
||||
fn main() {
|
||||
do_something(Type);
|
||||
}
|
||||
27
tests/run-make/crate-loading/rmake.rs
Normal file
27
tests/run-make/crate-loading/rmake.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//@ only-linux
|
||||
//@ ignore-wasm32
|
||||
//@ ignore-wasm64
|
||||
|
||||
use run_make_support::rfs::copy;
|
||||
use run_make_support::{assert_contains, rust_lib_name, rustc};
|
||||
|
||||
fn main() {
|
||||
rustc().input("multiple-dep-versions-1.rs").run();
|
||||
rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run();
|
||||
|
||||
let out = rustc()
|
||||
.input("multiple-dep-versions.rs")
|
||||
.extern_("dependency", rust_lib_name("dependency"))
|
||||
.extern_("dep_2_reexport", rust_lib_name("dependency2"))
|
||||
.run_fail()
|
||||
.assert_stderr_contains(
|
||||
"you have multiple different versions of crate `dependency` in your dependency graph",
|
||||
)
|
||||
.assert_stderr_contains(
|
||||
"two types coming from two different versions of the same crate are different types \
|
||||
even if they look the same",
|
||||
)
|
||||
.assert_stderr_contains("this type doesn't implement the required trait")
|
||||
.assert_stderr_contains("this type implements the required trait")
|
||||
.assert_stderr_contains("this is the required trait");
|
||||
}
|
||||
13
tests/run-make/dos-device-input/rmake.rs
Normal file
13
tests/run-make/dos-device-input/rmake.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//@ only-windows
|
||||
// Reason: dos devices are a Windows thing
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use run_make_support::{rustc, static_lib_name};
|
||||
|
||||
fn main() {
|
||||
rustc().input(r"\\.\NUL").crate_type("staticlib").run();
|
||||
rustc().input(r"\\?\NUL").crate_type("staticlib").run();
|
||||
|
||||
assert!(Path::new(&static_lib_name("rust_out")).exists());
|
||||
}
|
||||
|
|
@ -41,3 +41,24 @@ assert-document-property: ({
|
|||
"URL": "struct.ZyxwvutMethodDisambiguation.html#method.method_impl_disambiguation-1"
|
||||
}, ENDS_WITH)
|
||||
assert: "section:target"
|
||||
|
||||
// Checks that, if a type has two methods with the same name,
|
||||
// and if it has multiple inherent impl blocks, that the numeric
|
||||
// impl block's disambiguator is also acted upon.
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->bool"
|
||||
wait-for: "#search-tabs"
|
||||
assert-count: ("a.result-method", 1)
|
||||
assert-attribute: ("a.result-method", {
|
||||
"href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockStruct/method.second_fn"
|
||||
})
|
||||
click: "a.result-method"
|
||||
wait-for: "details:has(summary > #impl-MultiImplBlockStruct-1) > div section[id='method.second_fn']:target"
|
||||
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->u32"
|
||||
wait-for: "#search-tabs"
|
||||
assert-count: ("a.result-method", 1)
|
||||
assert-attribute: ("a.result-method", {
|
||||
"href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockTrait-for-MultiImplBlockStruct/method.second_fn"
|
||||
})
|
||||
click: "a.result-method"
|
||||
wait-for: "details:has(summary > #impl-MultiImplBlockTrait-for-MultiImplBlockStruct) > div section[id='method.second_fn-1']:target"
|
||||
|
|
|
|||
|
|
@ -1 +1,19 @@
|
|||
pub fn tadam() {}
|
||||
pub struct MultiImplBlockStruct;
|
||||
|
||||
impl MultiImplBlockStruct {
|
||||
pub fn first_fn() {}
|
||||
}
|
||||
|
||||
impl MultiImplBlockStruct {
|
||||
pub fn second_fn(self) -> bool { true }
|
||||
}
|
||||
|
||||
pub trait MultiImplBlockTrait {
|
||||
fn first_fn();
|
||||
fn second_fn(self) -> u32;
|
||||
}
|
||||
|
||||
impl MultiImplBlockTrait for MultiImplBlockStruct {
|
||||
fn first_fn() {}
|
||||
fn second_fn(self) -> u32 { 1 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//@ compile-flags:--test --error-format=short
|
||||
//@ check-stdout
|
||||
//@ error-pattern:cannot find function `foo` in this scope
|
||||
//@ error-pattern:cannot find function `foo`
|
||||
//@ normalize-stdout-test: "tests/rustdoc-ui/issues" -> "$$DIR"
|
||||
//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
//@ failure-status: 101
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ test $DIR/issue-81662-shortness.rs - foo (line 8) ... FAILED
|
|||
failures:
|
||||
|
||||
---- $DIR/issue-81662-shortness.rs - foo (line 8) stdout ----
|
||||
$DIR/issue-81662-shortness.rs:9:1: error[E0425]: cannot find function `foo` in this scope
|
||||
$DIR/issue-81662-shortness.rs:9:1: error[E0425]: cannot find function `foo` in this scope: not found in this scope
|
||||
error: aborting due to 1 previous error
|
||||
Couldn't compile the test.
|
||||
|
||||
|
|
|
|||
10
tests/rustdoc-ui/remap-path-prefix-lint.rs
Normal file
10
tests/rustdoc-ui/remap-path-prefix-lint.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Regression test for remapped paths in rustdoc errors
|
||||
// <https://github.com/rust-lang/rust/issues/69264>.
|
||||
|
||||
//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path
|
||||
//@ rustc-env:RUST_BACKTRACE=0
|
||||
|
||||
#![deny(rustdoc::invalid_html_tags)]
|
||||
|
||||
/// </script>
|
||||
pub struct Bar;
|
||||
14
tests/rustdoc-ui/remap-path-prefix-lint.stderr
Normal file
14
tests/rustdoc-ui/remap-path-prefix-lint.stderr
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
error: unopened HTML tag `script`
|
||||
--> remapped_path/remap-path-prefix-lint.rs:9:5
|
||||
|
|
||||
LL | /// </script>
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> remapped_path/remap-path-prefix-lint.rs:7:9
|
||||
|
|
||||
LL | #![deny(rustdoc::invalid_html_tags)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
trait Trait<Type> {
|
||||
type Type;
|
||||
|
||||
fn one(&self, val: impl Trait<Type: Default>);
|
||||
//~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied
|
||||
|
||||
fn two<T: Trait<Type: Default>>(&self) -> T;
|
||||
//~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied
|
||||
|
||||
fn three<T>(&self) -> T where
|
||||
T: Trait<Type: Default>,;
|
||||
//~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied
|
||||
--> $DIR/name-same-as-generic-type-issue-128249.rs:4:30
|
||||
|
|
||||
LL | fn one(&self, val: impl Trait<Type: Default>);
|
||||
| ^^^^^ expected 1 generic argument
|
||||
|
|
||||
note: trait defined here, with 1 generic parameter: `Type`
|
||||
--> $DIR/name-same-as-generic-type-issue-128249.rs:1:7
|
||||
|
|
||||
LL | trait Trait<Type> {
|
||||
| ^^^^^ ----
|
||||
help: add missing generic argument
|
||||
|
|
||||
LL | fn one(&self, val: impl Trait<Type, Type: Default>);
|
||||
| +++++
|
||||
|
||||
error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied
|
||||
--> $DIR/name-same-as-generic-type-issue-128249.rs:7:15
|
||||
|
|
||||
LL | fn two<T: Trait<Type: Default>>(&self) -> T;
|
||||
| ^^^^^ expected 1 generic argument
|
||||
|
|
||||
note: trait defined here, with 1 generic parameter: `Type`
|
||||
--> $DIR/name-same-as-generic-type-issue-128249.rs:1:7
|
||||
|
|
||||
LL | trait Trait<Type> {
|
||||
| ^^^^^ ----
|
||||
help: add missing generic argument
|
||||
|
|
||||
LL | fn two<T: Trait<Type, Type: Default>>(&self) -> T;
|
||||
| +++++
|
||||
|
||||
error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied
|
||||
--> $DIR/name-same-as-generic-type-issue-128249.rs:11:12
|
||||
|
|
||||
LL | T: Trait<Type: Default>,;
|
||||
| ^^^^^ expected 1 generic argument
|
||||
|
|
||||
note: trait defined here, with 1 generic parameter: `Type`
|
||||
--> $DIR/name-same-as-generic-type-issue-128249.rs:1:7
|
||||
|
|
||||
LL | trait Trait<Type> {
|
||||
| ^^^^^ ----
|
||||
help: add missing generic argument
|
||||
|
|
||||
LL | T: Trait<Type, Type: Default>,;
|
||||
| +++++
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0107`.
|
||||
68
tests/ui/attributes/check-cfg_attr-ice.rs
Normal file
68
tests/ui/attributes/check-cfg_attr-ice.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//! I missed a `cfg_attr` match in #128581, it should have had the same treatment as `cfg`. If
|
||||
//! an invalid attribute starting with `cfg_attr` is passed, then it would trigger an ICE because
|
||||
//! it was not considered "checked" (e.g. `#[cfg_attr::skip]` or `#[cfg_attr::no_such_thing]`).
|
||||
//!
|
||||
//! This test is not exhaustive, there's too many possible positions to check, instead it just does
|
||||
//! a basic smoke test in a few select positions to make sure we don't ICE for e.g.
|
||||
//! `#[cfg_attr::no_such_thing]`.
|
||||
//!
|
||||
//! issue: rust-lang/rust#128716
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
mod we_are_no_strangers_to_love {}
|
||||
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
struct YouKnowTheRules {
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
and_so_do_i: u8,
|
||||
}
|
||||
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
fn a_full_commitment() {
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
let is_what_i_am_thinking_of = ();
|
||||
}
|
||||
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
union AnyOtherGuy {
|
||||
owo: ()
|
||||
}
|
||||
struct This;
|
||||
|
||||
#[cfg_attr(FALSE, doc = "you wouldn't get this")]
|
||||
impl From<AnyOtherGuy> for This {
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
fn from(#[cfg_attr::no_such_thing] any_other_guy: AnyOtherGuy) -> This {
|
||||
//~^ ERROR failed to resolve
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR attributes on expressions are experimental
|
||||
//~| ERROR failed to resolve
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
enum NeverGonna {
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
GiveYouUp(#[cfg_attr::no_such_thing] u8),
|
||||
//~^ ERROR failed to resolve
|
||||
LetYouDown {
|
||||
#![cfg_attr::no_such_thing]
|
||||
//~^ ERROR an inner attribute is not permitted in this context
|
||||
never_gonna: (),
|
||||
round_around: (),
|
||||
#[cfg_attr::no_such_thing]
|
||||
//~^ ERROR failed to resolve
|
||||
and_desert_you: (),
|
||||
},
|
||||
}
|
||||
101
tests/ui/attributes/check-cfg_attr-ice.stderr
Normal file
101
tests/ui/attributes/check-cfg_attr-ice.stderr
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
error: an inner attribute is not permitted in this context
|
||||
--> $DIR/check-cfg_attr-ice.rs:60:9
|
||||
|
|
||||
LL | #![cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
|
||||
= note: outer attributes, like `#[test]`, annotate the item following them
|
||||
|
||||
error[E0658]: attributes on expressions are experimental
|
||||
--> $DIR/check-cfg_attr-ice.rs:45:9
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
|
||||
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:52:3
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:55:7
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:57:17
|
||||
|
|
||||
LL | GiveYouUp(#[cfg_attr::no_such_thing] u8),
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:64:11
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:41:7
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:43:15
|
||||
|
|
||||
LL | fn from(#[cfg_attr::no_such_thing] any_other_guy: AnyOtherGuy) -> This {
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:45:11
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:32:3
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:24:3
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:27:7
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:16:3
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:19:7
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
|
||||
--> $DIR/check-cfg_attr-ice.rs:12:3
|
||||
|
|
||||
LL | #[cfg_attr::no_such_thing]
|
||||
| ^^^^^^^^ use of undeclared crate or module `cfg_attr`
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0433, E0658.
|
||||
For more information about an error, try `rustc --explain E0433`.
|
||||
|
|
@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
|
|||
--> $DIR/const_fn_target_feature.rs:11:24
|
||||
|
|
||||
LL | const B: () = unsafe { avx2_fn() };
|
||||
| ^^^^^^^^^ calling a function that requires unavailable target features: avx2
|
||||
| ^^^^^^^^^ calling a function that requires unavailable target features: avx, avx2, sse4.1, sse4.2
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":622,"byte_end":623,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":613,"byte_end":619,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":623,"byte_end":623,"line_start":17,"line_end":17,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":622,"byte_end":623,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":613,"byte_end":619,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":623,"byte_end":623,"line_start":17,"line_end":17,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types: expected `String`, found integer
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":682,"byte_end":683,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":673,"byte_end":679,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":683,"byte_end":683,"line_start":19,"line_end":19,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":682,"byte_end":683,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":673,"byte_end":679,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":683,"byte_end":683,"line_start":19,"line_end":19,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types: expected `String`, found integer
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":746,"byte_end":747,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":736,"byte_end":742,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":747,"byte_end":747,"line_start":23,"line_end":23,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":746,"byte_end":747,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":736,"byte_end":742,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":747,"byte_end":747,"line_start":23,"line_end":23,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types: expected `String`, found integer
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":802,"byte_end":810,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":793,"byte_end":799,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":802,"byte_end":810,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":793,"byte_end":799,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types: expected `String`, found `()`
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors
|
||||
"}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":607,"byte_end":608,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":598,"byte_end":604,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":608,"byte_end":608,"line_start":16,"line_end":16,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":607,"byte_end":608,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":598,"byte_end":604,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":608,"byte_end":608,"line_start":16,"line_end":16,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types: expected `String`, found integer
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":667,"byte_end":668,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":658,"byte_end":664,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":668,"byte_end":668,"line_start":18,"line_end":18,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":667,"byte_end":668,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":658,"byte_end":664,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":668,"byte_end":668,"line_start":18,"line_end":18,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types: expected `String`, found integer
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":731,"byte_end":732,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":721,"byte_end":727,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":732,"byte_end":732,"line_start":22,"line_end":22,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":731,"byte_end":732,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":721,"byte_end":727,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":732,"byte_end":732,"line_start":22,"line_end":22,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types: expected `String`, found integer
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ This error occurs when an expression was used in a place where the compiler
|
|||
expected an expression of a different type. It can occur in several cases, the
|
||||
most common being when calling a function and passing an argument which has a
|
||||
different type than the matching type in the function declaration.
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":787,"byte_end":795,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":778,"byte_end":784,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":787,"byte_end":795,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":778,"byte_end":784,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types: expected `String`, found `()`
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors
|
||||
"}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ If you don't know the basics of Rust, you can look at the
|
|||
[Rust Book][rust-book] to get started.
|
||||
|
||||
[rust-book]: https://doc.rust-lang.org/book/
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-short.rs","byte_start":63,"byte_end":63,"line_start":1,"line_end":1,"column_start":64,"column_end":64,"is_primary":true,"text":[{"text":"//@ compile-flags: --json=diagnostic-short --error-format=json","highlight_start":64,"highlight_end":64}],"label":"consider adding a `main` function to `$DIR/json-short.rs`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-short.rs:1:64: error[E0601]: `main` function not found in crate `json_short`
|
||||
"},"level":"error","spans":[{"file_name":"$DIR/json-short.rs","byte_start":63,"byte_end":63,"line_start":1,"line_end":1,"column_start":64,"column_end":64,"is_primary":true,"text":[{"text":"//@ compile-flags: --json=diagnostic-short --error-format=json","highlight_start":64,"highlight_end":64}],"label":"consider adding a `main` function to `$DIR/json-short.rs`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-short.rs:1:64: error[E0601]: `main` function not found in crate `json_short`: consider adding a `main` function to `$DIR/json-short.rs`
|
||||
"}
|
||||
{"$message_type":"diagnostic","message":"aborting due to 1 previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 1 previous error
|
||||
"}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ fn foo() {
|
|||
|
||||
#[target_feature(enable = "sse2")]
|
||||
fn bar() {
|
||||
sse2();
|
||||
avx_bmi2();
|
||||
//~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
|
||||
Quux.avx_bmi2();
|
||||
|
|
@ -43,7 +44,6 @@ fn bar() {
|
|||
#[target_feature(enable = "avx")]
|
||||
fn baz() {
|
||||
sse2();
|
||||
//~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
|
||||
avx_bmi2();
|
||||
//~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
|
||||
Quux.avx_bmi2();
|
||||
|
|
@ -54,7 +54,8 @@ fn baz() {
|
|||
#[target_feature(enable = "bmi2")]
|
||||
fn qux() {
|
||||
sse2();
|
||||
//~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
|
||||
avx_bmi2();
|
||||
Quux.avx_bmi2();
|
||||
}
|
||||
|
||||
const _: () = sse2();
|
||||
|
|
@ -64,8 +65,6 @@ const _: () = sse2_and_fxsr();
|
|||
//~^ ERROR call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe
|
||||
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
#[target_feature(enable = "avx")]
|
||||
#[target_feature(enable = "bmi2")]
|
||||
unsafe fn needs_unsafe_block() {
|
||||
sse2();
|
||||
//~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and req
|
|||
LL | sse2();
|
||||
| ^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target feature: sse2
|
||||
= note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: sse and sse2
|
||||
= note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
|
||||
|
||||
error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:29:5
|
||||
|
|
@ -13,7 +13,8 @@ error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and
|
|||
LL | avx_bmi2();
|
||||
| ^^^^^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2
|
||||
= note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
|
||||
|
||||
error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:31:5
|
||||
|
|
@ -21,32 +22,24 @@ error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsa
|
|||
LL | Quux.avx_bmi2();
|
||||
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2
|
||||
= note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
|
||||
|
||||
error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:37:5
|
||||
--> $DIR/safe-calls.rs:38:5
|
||||
|
|
||||
LL | avx_bmi2();
|
||||
| ^^^^^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2
|
||||
|
||||
error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:39:5
|
||||
--> $DIR/safe-calls.rs:40:5
|
||||
|
|
||||
LL | Quux.avx_bmi2();
|
||||
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
|
||||
|
||||
error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:45:5
|
||||
|
|
||||
LL | sse2();
|
||||
| ^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target feature: sse2
|
||||
= note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2
|
||||
|
||||
error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:47:5
|
||||
|
|
@ -65,52 +58,43 @@ LL | Quux.avx_bmi2();
|
|||
= help: in order for the call to be safe, the context requires the following additional target feature: bmi2
|
||||
|
||||
error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:56:5
|
||||
|
|
||||
LL | sse2();
|
||||
| ^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target feature: sse2
|
||||
= note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
|
||||
|
||||
error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:60:15
|
||||
--> $DIR/safe-calls.rs:61:15
|
||||
|
|
||||
LL | const _: () = sse2();
|
||||
| ^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target feature: sse2
|
||||
= note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: sse and sse2
|
||||
= note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
|
||||
|
||||
error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block
|
||||
--> $DIR/safe-calls.rs:63:15
|
||||
--> $DIR/safe-calls.rs:64:15
|
||||
|
|
||||
LL | const _: () = sse2_and_fxsr();
|
||||
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr
|
||||
= note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: sse, sse2, and fxsr
|
||||
= note: the fxsr, sse, and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
|
||||
|
||||
error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block
|
||||
--> $DIR/safe-calls.rs:70:5
|
||||
--> $DIR/safe-calls.rs:69:5
|
||||
|
|
||||
LL | sse2();
|
||||
| ^^^^^^ call to function with `#[target_feature]`
|
||||
|
|
||||
= note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
|
||||
= help: in order for the call to be safe, the context requires the following additional target feature: sse2
|
||||
= note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
|
||||
= help: in order for the call to be safe, the context requires the following additional target features: sse and sse2
|
||||
= note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
|
||||
note: an unsafe function restricts its caller, but its body is safe by default
|
||||
--> $DIR/safe-calls.rs:69:1
|
||||
--> $DIR/safe-calls.rs:68:1
|
||||
|
|
||||
LL | unsafe fn needs_unsafe_block() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: the lint level is defined here
|
||||
--> $DIR/safe-calls.rs:66:8
|
||||
--> $DIR/safe-calls.rs:67:8
|
||||
|
|
||||
LL | #[deny(unsafe_op_in_unsafe_fn)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0133`.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
$DIR/short-error-format.rs:6:9: error[E0308]: mismatched types
|
||||
$DIR/short-error-format.rs:8:7: error[E0599]: no method named `salut` found for type `u32` in the current scope
|
||||
$DIR/short-error-format.rs:6:9: error[E0308]: mismatched types: expected `u32`, found `String`
|
||||
$DIR/short-error-format.rs:8:7: error[E0599]: no method named `salut` found for type `u32` in the current scope: method not found in `u32`
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
|
|||
10
tests/ui/target-feature/asm-implied-features-issue-128125.rs
Normal file
10
tests/ui/target-feature/asm-implied-features-issue-128125.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
//@ only-x86_64
|
||||
//@ build-pass
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn demo(v: std::arch::x86_64::__m256i) {
|
||||
std::arch::asm!("/* {v} */", v = in(ymm_reg) v);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
24
tests/ui/target-feature/implied-features.rs
Normal file
24
tests/ui/target-feature/implied-features.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//@ only-x86_64
|
||||
//@ build-pass
|
||||
#![feature(target_feature_11)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[target_feature(enable = "ssse3")]
|
||||
fn call_ssse3() {}
|
||||
|
||||
#[target_feature(enable = "avx")]
|
||||
fn call_avx() {}
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
fn test_avx2() {
|
||||
call_ssse3();
|
||||
call_avx();
|
||||
}
|
||||
|
||||
#[target_feature(enable = "fma")]
|
||||
fn test_fma() {
|
||||
call_ssse3();
|
||||
call_avx();
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -778,6 +778,14 @@ If this was unintentional then you should revert the changes before this PR is m
|
|||
Otherwise, you can ignore this comment.
|
||||
"""
|
||||
|
||||
[mentions."library/Cargo.lock"]
|
||||
message = """
|
||||
These commits modify the `library/Cargo.lock` file. Unintentional changes to `library/Cargo.lock` can be introduced when switching branches and rebasing PRs.
|
||||
|
||||
If this was unintentional then you should revert the changes before this PR is merged.
|
||||
Otherwise, you can ignore this comment.
|
||||
"""
|
||||
|
||||
[mentions."src/tools/x"]
|
||||
message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version."
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue