mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #128093 - matthiaskrgr:rollup-1snye4b, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #125834 (treat `&raw (const|mut) UNSAFE_STATIC` implied deref as safe) - #127962 (Cleanup compiletest dylib name calculation) - #128049 (Reword E0626 to mention static coroutine, add structured suggestion for adding `static`) - #128067 (Get rid of `can_eq_shallow`) - #128076 (Get rid of `InferCtxtExt` from `error_reporting::traits`) - #128089 (std: Unsafe-wrap actually-universal platform code) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
d53dc752d2
@ -1,7 +1,9 @@
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxtHandle};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
@ -382,13 +384,35 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
yield_span: Span,
|
||||
) -> Diag<'infcx> {
|
||||
let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
|
||||
struct_span_code_err!(
|
||||
let mut diag = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
E0626,
|
||||
"borrow may still be in use when {coroutine_kind:#} yields",
|
||||
)
|
||||
.with_span_label(yield_span, "possible yield occurs here")
|
||||
);
|
||||
diag.span_label(
|
||||
self.infcx.tcx.def_span(self.body.source.def_id()),
|
||||
format!("within this {coroutine_kind:#}"),
|
||||
);
|
||||
diag.span_label(yield_span, "possible yield occurs here");
|
||||
if matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_)) {
|
||||
let hir::Closure { capture_clause, fn_decl_span, .. } = self
|
||||
.infcx
|
||||
.tcx
|
||||
.hir_node_by_def_id(self.body.source.def_id().expect_local())
|
||||
.expect_closure();
|
||||
let span = match capture_clause {
|
||||
rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(),
|
||||
rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(),
|
||||
};
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"add `static` to mark this coroutine as unmovable",
|
||||
"static ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
diag
|
||||
}
|
||||
|
||||
pub(crate) fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> Diag<'infcx> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
This error occurs because a borrow in a coroutine persists across a
|
||||
This error occurs because a borrow in a movable coroutine persists across a
|
||||
yield point.
|
||||
|
||||
Erroneous code example:
|
||||
@ -15,19 +15,35 @@ let mut b = #[coroutine] || {
|
||||
Pin::new(&mut b).resume(());
|
||||
```
|
||||
|
||||
At present, it is not permitted to have a yield that occurs while a
|
||||
borrow is still in scope. To resolve this error, the borrow must
|
||||
either be "contained" to a smaller scope that does not overlap the
|
||||
yield or else eliminated in another way. So, for example, we might
|
||||
resolve the previous example by removing the borrow and just storing
|
||||
the integer by value:
|
||||
Coroutines may be either unmarked, or marked with `static`. If it is unmarked,
|
||||
then the coroutine is considered "movable". At present, it is not permitted to
|
||||
have a yield in a movable coroutine that occurs while a borrow is still in
|
||||
scope. To resolve this error, the coroutine may be marked `static`:
|
||||
|
||||
```
|
||||
# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
|
||||
# use std::ops::Coroutine;
|
||||
# use std::pin::Pin;
|
||||
let mut b = #[coroutine] static || { // <-- note the static keyword
|
||||
let a = &String::from("hello, world");
|
||||
yield ();
|
||||
println!("{}", a);
|
||||
};
|
||||
let mut b = std::pin::pin!(b);
|
||||
b.as_mut().resume(());
|
||||
```
|
||||
|
||||
If the coroutine must remain movable, for example to be used as `Unpin`
|
||||
without pinning it on the stack or in an allocation, we can alternatively
|
||||
resolve the previous example by removing the borrow and just storing the
|
||||
type by value:
|
||||
|
||||
```
|
||||
# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
|
||||
# use std::ops::Coroutine;
|
||||
# use std::pin::Pin;
|
||||
let mut b = #[coroutine] || {
|
||||
let a = 3;
|
||||
let a = String::from("hello, world");
|
||||
yield ();
|
||||
println!("{}", a);
|
||||
};
|
||||
|
@ -18,7 +18,6 @@ use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::error_reporting::traits::ArgKind;
|
||||
use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_type_ir::ClosureKind;
|
||||
use std::iter;
|
||||
@ -734,13 +733,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.map(|ty| ArgKind::from_expected_ty(*ty, None))
|
||||
.collect();
|
||||
let (closure_span, closure_arg_span, found_args) =
|
||||
match self.get_fn_like_arguments(expr_map_node) {
|
||||
match self.err_ctxt().get_fn_like_arguments(expr_map_node) {
|
||||
Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args),
|
||||
None => (None, None, Vec::new()),
|
||||
};
|
||||
let expected_span =
|
||||
expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id));
|
||||
let guar = self
|
||||
.err_ctxt()
|
||||
.report_arg_count_mismatch(
|
||||
expected_span,
|
||||
closure_span,
|
||||
|
@ -755,18 +755,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
// FIXME(-Znext-solver): Get rid of this method, it's never correct. Either that,
|
||||
// or we need to process the obligations.
|
||||
pub fn can_eq_shallow<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool
|
||||
where
|
||||
T: at::ToTrace<'tcx>,
|
||||
{
|
||||
let origin = &ObligationCause::dummy();
|
||||
// We're only answering whether the types could be the same, and with
|
||||
// opaque types, "they can be the same", via registering a hidden type.
|
||||
self.probe(|_| self.at(origin, param_env).eq(DefineOpaqueTypes::Yes, a, b).is_ok())
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn sub_regions(
|
||||
&self,
|
||||
|
@ -466,6 +466,24 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::AddressOf { arg, .. } => {
|
||||
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
|
||||
// THIR desugars UNSAFE_STATIC into *UNSAFE_STATIC_REF, where
|
||||
// UNSAFE_STATIC_REF holds the addr of the UNSAFE_STATIC, so: take two steps
|
||||
&& let ExprKind::Deref { arg } = self.thir[arg].kind
|
||||
// FIXME(workingjubiee): we lack a clear reason to reject ThreadLocalRef here,
|
||||
// but we also have no conclusive reason to allow it either!
|
||||
&& let ExprKind::StaticRef { .. } = self.thir[arg].kind
|
||||
{
|
||||
// A raw ref to a place expr, even an "unsafe static", is okay!
|
||||
// We short-circuit to not recursively traverse this expression.
|
||||
return;
|
||||
// note: const_mut_refs enables this code, and it currently remains unsafe:
|
||||
// static mut BYTE: u8 = 0;
|
||||
// static mut BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(BYTE) };
|
||||
// static mut DEREF_BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(*BYTE_PTR) };
|
||||
}
|
||||
}
|
||||
ExprKind::Deref { arg } => {
|
||||
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
|
||||
self.thir[arg].kind
|
||||
|
@ -939,9 +939,11 @@ impl<'tcx> Cx<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// We encode uses of statics as a `*&STATIC` where the `&STATIC` part is
|
||||
// a constant reference (or constant raw pointer for `static mut`) in MIR
|
||||
// A source Rust `path::to::STATIC` is a place expr like *&ident is.
|
||||
// In THIR, we make them exactly equivalent by inserting the implied *& or *&raw,
|
||||
// but distinguish between &STATIC and &THREAD_LOCAL as they have different semantics
|
||||
Res::Def(DefKind::Static { .. }, id) => {
|
||||
// this is &raw for extern static or static mut, and & for other statics
|
||||
let ty = self.tcx.static_ptr_ty(id);
|
||||
let temp_lifetime = self
|
||||
.rvalue_scopes
|
||||
|
@ -12,6 +12,7 @@ use rustc_middle::{
|
||||
use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
|
||||
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::infer::InferCtxtExt;
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub fn note_and_explain_type_err(
|
||||
@ -821,7 +822,7 @@ fn foo(&self) -> Self::T { String::new() }
|
||||
tcx.defaultness(item.id.owner_id)
|
||||
{
|
||||
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
||||
if self.infcx.can_eq_shallow(param_env, assoc_ty, found) {
|
||||
if self.infcx.can_eq(param_env, assoc_ty, found) {
|
||||
diag.span_label(
|
||||
item.span,
|
||||
"associated type defaults can't be assumed inside the \
|
||||
@ -844,7 +845,7 @@ fn foo(&self) -> Self::T { String::new() }
|
||||
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
||||
if let hir::Defaultness::Default { has_value: true } =
|
||||
tcx.defaultness(item.id.owner_id)
|
||||
&& self.infcx.can_eq_shallow(param_env, assoc_ty, found)
|
||||
&& self.infcx.can_eq(param_env, assoc_ty, found)
|
||||
{
|
||||
diag.span_label(
|
||||
item.span,
|
||||
|
@ -1,7 +1,6 @@
|
||||
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
|
||||
use super::suggestions::get_explanation_based_on_obligation;
|
||||
use crate::error_reporting::infer::TyCategory;
|
||||
use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
|
||||
use crate::error_reporting::traits::report_object_safety_error;
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::errors::{
|
||||
@ -2602,7 +2601,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
})
|
||||
.unwrap_or((found_span, None, found));
|
||||
|
||||
self.infcx.report_arg_count_mismatch(
|
||||
self.report_arg_count_mismatch(
|
||||
span,
|
||||
closure_span,
|
||||
expected,
|
||||
@ -2614,6 +2613,238 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Given some node representing a fn-like thing in the HIR map,
|
||||
/// returns a span and `ArgKind` information that describes the
|
||||
/// arguments it expects. This can be supplied to
|
||||
/// `report_arg_count_mismatch`.
|
||||
pub fn get_fn_like_arguments(
|
||||
&self,
|
||||
node: Node<'_>,
|
||||
) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
let hir = self.tcx.hir();
|
||||
Some(match node {
|
||||
Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
|
||||
..
|
||||
}) => (
|
||||
fn_decl_span,
|
||||
fn_arg_span,
|
||||
hir.body(body)
|
||||
.params
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat
|
||||
{
|
||||
Some(ArgKind::Tuple(
|
||||
Some(span),
|
||||
args.iter()
|
||||
.map(|pat| {
|
||||
sm.span_to_snippet(pat.span)
|
||||
.ok()
|
||||
.map(|snippet| (snippet, "_".to_owned()))
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()?,
|
||||
))
|
||||
} else {
|
||||
let name = sm.span_to_snippet(arg.pat.span).ok()?;
|
||||
Some(ArgKind::Arg(name, "_".to_owned()))
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<ArgKind>>>()?,
|
||||
),
|
||||
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. })
|
||||
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
|
||||
| Node::TraitItem(&hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(ref sig, _), ..
|
||||
}) => (
|
||||
sig.span,
|
||||
None,
|
||||
sig.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| match arg.kind {
|
||||
hir::TyKind::Tup(tys) => ArgKind::Tuple(
|
||||
Some(arg.span),
|
||||
vec![("_".to_owned(), "_".to_owned()); tys.len()],
|
||||
),
|
||||
_ => ArgKind::empty(),
|
||||
})
|
||||
.collect::<Vec<ArgKind>>(),
|
||||
),
|
||||
Node::Ctor(variant_data) => {
|
||||
let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
|
||||
(span, None, vec![ArgKind::empty(); variant_data.fields().len()])
|
||||
}
|
||||
_ => panic!("non-FnLike node found: {node:?}"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Reports an error when the number of arguments needed by a
|
||||
/// trait match doesn't match the number that the expression
|
||||
/// provides.
|
||||
pub fn report_arg_count_mismatch(
|
||||
&self,
|
||||
span: Span,
|
||||
found_span: Option<Span>,
|
||||
expected_args: Vec<ArgKind>,
|
||||
found_args: Vec<ArgKind>,
|
||||
is_closure: bool,
|
||||
closure_arg_span: Option<Span>,
|
||||
) -> Diag<'a> {
|
||||
let kind = if is_closure { "closure" } else { "function" };
|
||||
|
||||
let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
|
||||
let arg_length = arguments.len();
|
||||
let distinct = matches!(other, &[ArgKind::Tuple(..)]);
|
||||
match (arg_length, arguments.get(0)) {
|
||||
(1, Some(ArgKind::Tuple(_, fields))) => {
|
||||
format!("a single {}-tuple as argument", fields.len())
|
||||
}
|
||||
_ => format!(
|
||||
"{} {}argument{}",
|
||||
arg_length,
|
||||
if distinct && arg_length > 1 { "distinct " } else { "" },
|
||||
pluralize!(arg_length)
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let expected_str = args_str(&expected_args, &found_args);
|
||||
let found_str = args_str(&found_args, &expected_args);
|
||||
|
||||
let mut err = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
E0593,
|
||||
"{} is expected to take {}, but it takes {}",
|
||||
kind,
|
||||
expected_str,
|
||||
found_str,
|
||||
);
|
||||
|
||||
err.span_label(span, format!("expected {kind} that takes {expected_str}"));
|
||||
|
||||
if let Some(found_span) = found_span {
|
||||
err.span_label(found_span, format!("takes {found_str}"));
|
||||
|
||||
// Suggest to take and ignore the arguments with expected_args_length `_`s if
|
||||
// found arguments is empty (assume the user just wants to ignore args in this case).
|
||||
// For example, if `expected_args_length` is 2, suggest `|_, _|`.
|
||||
if found_args.is_empty() && is_closure {
|
||||
let underscores = vec!["_"; expected_args.len()].join(", ");
|
||||
err.span_suggestion_verbose(
|
||||
closure_arg_span.unwrap_or(found_span),
|
||||
format!(
|
||||
"consider changing the closure to take and ignore the expected argument{}",
|
||||
pluralize!(expected_args.len())
|
||||
),
|
||||
format!("|{underscores}|"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
|
||||
if fields.len() == expected_args.len() {
|
||||
let sugg = fields
|
||||
.iter()
|
||||
.map(|(name, _)| name.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
err.span_suggestion_verbose(
|
||||
found_span,
|
||||
"change the closure to take multiple arguments instead of a single tuple",
|
||||
format!("|{sugg}|"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..]
|
||||
&& fields.len() == found_args.len()
|
||||
&& is_closure
|
||||
{
|
||||
let sugg = format!(
|
||||
"|({}){}|",
|
||||
found_args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
ArgKind::Arg(name, _) => name.to_owned(),
|
||||
_ => "_".to_owned(),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
// add type annotations if available
|
||||
if found_args.iter().any(|arg| match arg {
|
||||
ArgKind::Arg(_, ty) => ty != "_",
|
||||
_ => false,
|
||||
}) {
|
||||
format!(
|
||||
": ({})",
|
||||
fields
|
||||
.iter()
|
||||
.map(|(_, ty)| ty.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
found_span,
|
||||
"change the closure to accept a tuple instead of individual arguments",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
|
||||
/// in that order, and returns the generic type corresponding to the
|
||||
/// argument of that trait (corresponding to the closure arguments).
|
||||
pub fn type_implements_fn_trait(
|
||||
&self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: ty::Binder<'tcx, Ty<'tcx>>,
|
||||
polarity: ty::PredicatePolarity,
|
||||
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
|
||||
self.commit_if_ok(|_| {
|
||||
for trait_def_id in [
|
||||
self.tcx.lang_items().fn_trait(),
|
||||
self.tcx.lang_items().fn_mut_trait(),
|
||||
self.tcx.lang_items().fn_once_trait(),
|
||||
] {
|
||||
let Some(trait_def_id) = trait_def_id else { continue };
|
||||
// Make a fresh inference variable so we can determine what the generic parameters
|
||||
// of the trait are.
|
||||
let var = self.next_ty_var(DUMMY_SP);
|
||||
// FIXME(effects)
|
||||
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
|
||||
let obligation = Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
|
||||
);
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
ocx.register_obligation(obligation);
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok((
|
||||
self.tcx
|
||||
.fn_trait_kind_from_def_id(trait_def_id)
|
||||
.expect("expected to map DefId to ClosureKind"),
|
||||
ty.rebind(self.resolve_vars_if_possible(var)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
})
|
||||
}
|
||||
|
||||
fn report_not_const_evaluatable_error(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -1,244 +0,0 @@
|
||||
// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`.
|
||||
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
|
||||
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::Node;
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
use super::ArgKind;
|
||||
|
||||
#[extension(pub trait InferCtxtExt<'tcx>)]
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Given some node representing a fn-like thing in the HIR map,
|
||||
/// returns a span and `ArgKind` information that describes the
|
||||
/// arguments it expects. This can be supplied to
|
||||
/// `report_arg_count_mismatch`.
|
||||
fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
let hir = self.tcx.hir();
|
||||
Some(match node {
|
||||
Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
|
||||
..
|
||||
}) => (
|
||||
fn_decl_span,
|
||||
fn_arg_span,
|
||||
hir.body(body)
|
||||
.params
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat
|
||||
{
|
||||
Some(ArgKind::Tuple(
|
||||
Some(span),
|
||||
args.iter()
|
||||
.map(|pat| {
|
||||
sm.span_to_snippet(pat.span)
|
||||
.ok()
|
||||
.map(|snippet| (snippet, "_".to_owned()))
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()?,
|
||||
))
|
||||
} else {
|
||||
let name = sm.span_to_snippet(arg.pat.span).ok()?;
|
||||
Some(ArgKind::Arg(name, "_".to_owned()))
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<ArgKind>>>()?,
|
||||
),
|
||||
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. })
|
||||
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
|
||||
| Node::TraitItem(&hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(ref sig, _), ..
|
||||
}) => (
|
||||
sig.span,
|
||||
None,
|
||||
sig.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| match arg.kind {
|
||||
hir::TyKind::Tup(tys) => ArgKind::Tuple(
|
||||
Some(arg.span),
|
||||
vec![("_".to_owned(), "_".to_owned()); tys.len()],
|
||||
),
|
||||
_ => ArgKind::empty(),
|
||||
})
|
||||
.collect::<Vec<ArgKind>>(),
|
||||
),
|
||||
Node::Ctor(variant_data) => {
|
||||
let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
|
||||
(span, None, vec![ArgKind::empty(); variant_data.fields().len()])
|
||||
}
|
||||
_ => panic!("non-FnLike node found: {node:?}"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Reports an error when the number of arguments needed by a
|
||||
/// trait match doesn't match the number that the expression
|
||||
/// provides.
|
||||
fn report_arg_count_mismatch(
|
||||
&self,
|
||||
span: Span,
|
||||
found_span: Option<Span>,
|
||||
expected_args: Vec<ArgKind>,
|
||||
found_args: Vec<ArgKind>,
|
||||
is_closure: bool,
|
||||
closure_arg_span: Option<Span>,
|
||||
) -> Diag<'_> {
|
||||
let kind = if is_closure { "closure" } else { "function" };
|
||||
|
||||
let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
|
||||
let arg_length = arguments.len();
|
||||
let distinct = matches!(other, &[ArgKind::Tuple(..)]);
|
||||
match (arg_length, arguments.get(0)) {
|
||||
(1, Some(ArgKind::Tuple(_, fields))) => {
|
||||
format!("a single {}-tuple as argument", fields.len())
|
||||
}
|
||||
_ => format!(
|
||||
"{} {}argument{}",
|
||||
arg_length,
|
||||
if distinct && arg_length > 1 { "distinct " } else { "" },
|
||||
pluralize!(arg_length)
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let expected_str = args_str(&expected_args, &found_args);
|
||||
let found_str = args_str(&found_args, &expected_args);
|
||||
|
||||
let mut err = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
E0593,
|
||||
"{} is expected to take {}, but it takes {}",
|
||||
kind,
|
||||
expected_str,
|
||||
found_str,
|
||||
);
|
||||
|
||||
err.span_label(span, format!("expected {kind} that takes {expected_str}"));
|
||||
|
||||
if let Some(found_span) = found_span {
|
||||
err.span_label(found_span, format!("takes {found_str}"));
|
||||
|
||||
// Suggest to take and ignore the arguments with expected_args_length `_`s if
|
||||
// found arguments is empty (assume the user just wants to ignore args in this case).
|
||||
// For example, if `expected_args_length` is 2, suggest `|_, _|`.
|
||||
if found_args.is_empty() && is_closure {
|
||||
let underscores = vec!["_"; expected_args.len()].join(", ");
|
||||
err.span_suggestion_verbose(
|
||||
closure_arg_span.unwrap_or(found_span),
|
||||
format!(
|
||||
"consider changing the closure to take and ignore the expected argument{}",
|
||||
pluralize!(expected_args.len())
|
||||
),
|
||||
format!("|{underscores}|"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
|
||||
if fields.len() == expected_args.len() {
|
||||
let sugg = fields
|
||||
.iter()
|
||||
.map(|(name, _)| name.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
err.span_suggestion_verbose(
|
||||
found_span,
|
||||
"change the closure to take multiple arguments instead of a single tuple",
|
||||
format!("|{sugg}|"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..]
|
||||
&& fields.len() == found_args.len()
|
||||
&& is_closure
|
||||
{
|
||||
let sugg = format!(
|
||||
"|({}){}|",
|
||||
found_args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
ArgKind::Arg(name, _) => name.to_owned(),
|
||||
_ => "_".to_owned(),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
// add type annotations if available
|
||||
if found_args.iter().any(|arg| match arg {
|
||||
ArgKind::Arg(_, ty) => ty != "_",
|
||||
_ => false,
|
||||
}) {
|
||||
format!(
|
||||
": ({})",
|
||||
fields
|
||||
.iter()
|
||||
.map(|(_, ty)| ty.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
found_span,
|
||||
"change the closure to accept a tuple instead of individual arguments",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
|
||||
/// in that order, and returns the generic type corresponding to the
|
||||
/// argument of that trait (corresponding to the closure arguments).
|
||||
fn type_implements_fn_trait(
|
||||
&self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: ty::Binder<'tcx, Ty<'tcx>>,
|
||||
polarity: ty::PredicatePolarity,
|
||||
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
|
||||
self.commit_if_ok(|_| {
|
||||
for trait_def_id in [
|
||||
self.tcx.lang_items().fn_trait(),
|
||||
self.tcx.lang_items().fn_mut_trait(),
|
||||
self.tcx.lang_items().fn_once_trait(),
|
||||
] {
|
||||
let Some(trait_def_id) = trait_def_id else { continue };
|
||||
// Make a fresh inference variable so we can determine what the generic parameters
|
||||
// of the trait are.
|
||||
let var = self.next_ty_var(DUMMY_SP);
|
||||
// FIXME(effects)
|
||||
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
|
||||
let obligation = Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
|
||||
);
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
ocx.register_obligation(obligation);
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok((
|
||||
self.tcx
|
||||
.fn_trait_kind_from_def_id(trait_def_id)
|
||||
.expect("expected to map DefId to ClosureKind"),
|
||||
ty.rebind(self.resolve_vars_if_possible(var)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
})
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
pub mod ambiguity;
|
||||
mod fulfillment_errors;
|
||||
mod infer_ctxt_ext;
|
||||
pub mod on_unimplemented;
|
||||
mod overflow;
|
||||
pub mod suggestions;
|
||||
@ -23,7 +22,6 @@ use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::traits::{FulfillmentError, FulfillmentErrorCode};
|
||||
|
||||
pub use self::infer_ctxt_ext::*;
|
||||
pub use self::overflow::*;
|
||||
|
||||
// When outputting impl candidates, prefer showing those that are more similar.
|
||||
|
@ -157,7 +157,10 @@ mod imp {
|
||||
// going to be cross-lang LTOed anyway. However, using expose is shorter and
|
||||
// requires less unsafe.
|
||||
let addr: usize = ptr.expose_provenance();
|
||||
#[cfg(bootstrap)]
|
||||
let image_base = unsafe { addr_of!(__ImageBase) }.addr();
|
||||
#[cfg(not(bootstrap))]
|
||||
let image_base = addr_of!(__ImageBase).addr();
|
||||
let offset: usize = addr - image_base;
|
||||
Self(offset as u32)
|
||||
}
|
||||
@ -250,7 +253,10 @@ extern "C" {
|
||||
// This is fine since the MSVC runtime uses string comparison on the type name
|
||||
// to match TypeDescriptors rather than pointer equality.
|
||||
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
|
||||
#[cfg(bootstrap)]
|
||||
pVFTable: unsafe { addr_of!(TYPE_INFO_VTABLE) } as *const _,
|
||||
#[cfg(not(bootstrap))]
|
||||
pVFTable: addr_of!(TYPE_INFO_VTABLE) as *const _,
|
||||
spare: core::ptr::null_mut(),
|
||||
name: TYPE_NAME,
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Common code for printing backtraces.
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
|
||||
use crate::borrow::Cow;
|
||||
@ -62,73 +63,76 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
|
||||
// Start immediately if we're not using a short backtrace.
|
||||
let mut start = print_fmt != PrintFmt::Short;
|
||||
set_image_base();
|
||||
backtrace_rs::trace_unsynchronized(|frame| {
|
||||
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut hit = false;
|
||||
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
|
||||
hit = true;
|
||||
|
||||
// Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace`
|
||||
// are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be
|
||||
// called before the panic hook, so we won't ignore any frames if there is no
|
||||
// invoke of `__rust_begin_short_backtrace`.
|
||||
if print_fmt == PrintFmt::Short {
|
||||
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
|
||||
if start && sym.contains("__rust_begin_short_backtrace") {
|
||||
start = false;
|
||||
return;
|
||||
}
|
||||
if sym.contains("__rust_end_short_backtrace") {
|
||||
start = true;
|
||||
return;
|
||||
}
|
||||
if !start {
|
||||
omitted_count += 1;
|
||||
}
|
||||
}
|
||||
// SAFETY: we roll our own locking in this town
|
||||
unsafe {
|
||||
backtrace_rs::trace_unsynchronized(|frame| {
|
||||
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
|
||||
return false;
|
||||
}
|
||||
|
||||
if start {
|
||||
if omitted_count > 0 {
|
||||
debug_assert!(print_fmt == PrintFmt::Short);
|
||||
// only print the message between the middle of frames
|
||||
if !first_omit {
|
||||
let _ = writeln!(
|
||||
bt_fmt.formatter(),
|
||||
" [... omitted {} frame{} ...]",
|
||||
omitted_count,
|
||||
if omitted_count > 1 { "s" } else { "" }
|
||||
);
|
||||
let mut hit = false;
|
||||
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
|
||||
hit = true;
|
||||
|
||||
// Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace`
|
||||
// are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be
|
||||
// called before the panic hook, so we won't ignore any frames if there is no
|
||||
// invoke of `__rust_begin_short_backtrace`.
|
||||
if print_fmt == PrintFmt::Short {
|
||||
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
|
||||
if start && sym.contains("__rust_begin_short_backtrace") {
|
||||
start = false;
|
||||
return;
|
||||
}
|
||||
if sym.contains("__rust_end_short_backtrace") {
|
||||
start = true;
|
||||
return;
|
||||
}
|
||||
if !start {
|
||||
omitted_count += 1;
|
||||
}
|
||||
}
|
||||
first_omit = false;
|
||||
omitted_count = 0;
|
||||
}
|
||||
res = bt_fmt.frame().symbol(frame, symbol);
|
||||
|
||||
if start {
|
||||
if omitted_count > 0 {
|
||||
debug_assert!(print_fmt == PrintFmt::Short);
|
||||
// only print the message between the middle of frames
|
||||
if !first_omit {
|
||||
let _ = writeln!(
|
||||
bt_fmt.formatter(),
|
||||
" [... omitted {} frame{} ...]",
|
||||
omitted_count,
|
||||
if omitted_count > 1 { "s" } else { "" }
|
||||
);
|
||||
}
|
||||
first_omit = false;
|
||||
omitted_count = 0;
|
||||
}
|
||||
res = bt_fmt.frame().symbol(frame, symbol);
|
||||
}
|
||||
});
|
||||
#[cfg(target_os = "nto")]
|
||||
if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
|
||||
if !hit && start {
|
||||
use crate::backtrace_rs::SymbolName;
|
||||
res = bt_fmt.frame().print_raw(
|
||||
frame.ip(),
|
||||
Some(SymbolName::new("__my_thread_exit".as_bytes())),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
#[cfg(target_os = "nto")]
|
||||
if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
|
||||
if !hit && start {
|
||||
use crate::backtrace_rs::SymbolName;
|
||||
res = bt_fmt.frame().print_raw(
|
||||
frame.ip(),
|
||||
Some(SymbolName::new("__my_thread_exit".as_bytes())),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if !hit && start {
|
||||
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
res.is_ok()
|
||||
});
|
||||
idx += 1;
|
||||
res.is_ok()
|
||||
})
|
||||
};
|
||||
res?;
|
||||
bt_fmt.finish()?;
|
||||
if print_fmt == PrintFmt::Short {
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
use crate::alloc::{GlobalAlloc, Layout, System};
|
||||
use crate::cmp;
|
||||
use crate::ptr;
|
||||
@ -46,14 +47,16 @@ pub unsafe fn realloc_fallback(
|
||||
old_layout: Layout,
|
||||
new_size: usize,
|
||||
) -> *mut u8 {
|
||||
// Docs for GlobalAlloc::realloc require this to be valid:
|
||||
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
||||
// SAFETY: Docs for GlobalAlloc::realloc require this to be valid
|
||||
unsafe {
|
||||
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
||||
|
||||
let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
|
||||
if !new_ptr.is_null() {
|
||||
let size = cmp::min(old_layout.size(), new_size);
|
||||
ptr::copy_nonoverlapping(ptr, new_ptr, size);
|
||||
GlobalAlloc::dealloc(alloc, ptr, old_layout);
|
||||
let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
|
||||
if !new_ptr.is_null() {
|
||||
let size = cmp::min(old_layout.size(), new_size);
|
||||
ptr::copy_nonoverlapping(ptr, new_ptr, size);
|
||||
GlobalAlloc::dealloc(alloc, ptr, old_layout);
|
||||
}
|
||||
new_ptr
|
||||
}
|
||||
new_ptr
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use rustc_middle::ty::{
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _;
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||
// 'cuz currently nothing changes after deleting this check.
|
||||
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
|
||||
}) {
|
||||
match cx.tcx.infer_ctxt().build().type_implements_fn_trait(
|
||||
match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait(
|
||||
cx.param_env,
|
||||
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
|
||||
ty::PredicatePolarity::Positive,
|
||||
|
@ -82,26 +82,22 @@ fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
|
||||
}
|
||||
|
||||
/// The platform-specific library name
|
||||
fn get_lib_name(lib: &str, aux_type: AuxType) -> Option<String> {
|
||||
fn get_lib_name(name: &str, aux_type: AuxType) -> Option<String> {
|
||||
match aux_type {
|
||||
AuxType::Bin => None,
|
||||
// In some cases (e.g. MUSL), we build a static
|
||||
// library, rather than a dynamic library.
|
||||
// In this case, the only path we can pass
|
||||
// with '--extern-meta' is the '.rlib' file
|
||||
AuxType::Lib => Some(format!("lib{}.rlib", lib)),
|
||||
AuxType::Dylib => Some(if cfg!(windows) {
|
||||
format!("{}.dll", lib)
|
||||
} else if cfg!(target_vendor = "apple") {
|
||||
format!("lib{}.dylib", lib)
|
||||
} else if cfg!(target_os = "aix") {
|
||||
format!("lib{}.a", lib)
|
||||
} else {
|
||||
format!("lib{}.so", lib)
|
||||
}),
|
||||
AuxType::Lib => Some(format!("lib{name}.rlib")),
|
||||
AuxType::Dylib => Some(dylib_name(name)),
|
||||
}
|
||||
}
|
||||
|
||||
fn dylib_name(name: &str) -> String {
|
||||
format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION)
|
||||
}
|
||||
|
||||
pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
|
||||
match &*config.target {
|
||||
"arm-linux-androideabi"
|
||||
|
@ -5,5 +5,5 @@ extern "C" {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val = unsafe { std::ptr::addr_of!(FOO) }; //~ ERROR: is not supported by Miri
|
||||
let _val = std::ptr::addr_of!(FOO); //~ ERROR: is not supported by Miri
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: unsupported operation: extern static `FOO` is not supported by Miri
|
||||
--> $DIR/extern_static.rs:LL:CC
|
||||
|
|
||||
LL | let _val = unsafe { std::ptr::addr_of!(FOO) };
|
||||
| ^^^ extern static `FOO` is not supported by Miri
|
||||
LL | let _val = std::ptr::addr_of!(FOO);
|
||||
| ^^^ extern static `FOO` is not supported by Miri
|
||||
|
|
||||
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
|
||||
= note: BACKTRACE:
|
||||
|
@ -2,7 +2,7 @@ use std::ptr::addr_of;
|
||||
|
||||
static mut FOO: i32 = 42;
|
||||
|
||||
static BAR: Foo = Foo(unsafe { addr_of!(FOO) });
|
||||
static BAR: Foo = Foo(addr_of!(FOO));
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Foo(*const i32);
|
||||
|
@ -9,7 +9,7 @@ const C1: &i32 = &S;
|
||||
const C1_READ: () = {
|
||||
assert!(*C1 == 0);
|
||||
};
|
||||
const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) };
|
||||
const C2: *const i32 = std::ptr::addr_of!(S_MUT);
|
||||
|
||||
fn main() {
|
||||
assert_eq!(*C1, 0);
|
||||
|
@ -16,12 +16,9 @@ static mut STATIC: u32 = 42;
|
||||
static INTERIOR_MUTABLE_STATIC: SyncUnsafeCell<u32> = SyncUnsafeCell::new(42);
|
||||
|
||||
// A static that mutably points to STATIC.
|
||||
static PTR: SyncPtr = SyncPtr {
|
||||
foo: unsafe { ptr::addr_of_mut!(STATIC) },
|
||||
};
|
||||
static INTERIOR_MUTABLE_PTR: SyncPtr = SyncPtr {
|
||||
foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32,
|
||||
};
|
||||
static PTR: SyncPtr = SyncPtr { foo: ptr::addr_of_mut!(STATIC) };
|
||||
static INTERIOR_MUTABLE_PTR: SyncPtr =
|
||||
SyncPtr { foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32 };
|
||||
|
||||
fn main() {
|
||||
let ptr = PTR.foo;
|
||||
|
@ -1,11 +1,19 @@
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/coroutine-with-nll.rs:8:17
|
||||
|
|
||||
LL | || {
|
||||
| -- within this coroutine
|
||||
...
|
||||
LL | let b = &mut true;
|
||||
| ^^^^^^^^^
|
||||
LL |
|
||||
LL | yield ();
|
||||
| -------- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | static || {
|
||||
| ++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -1,10 +1,18 @@
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/issue-48048.rs:9:9
|
||||
|
|
||||
LL | #[coroutine] || {
|
||||
| -- within this coroutine
|
||||
...
|
||||
LL | x.0({
|
||||
| ^^^
|
||||
LL | yield;
|
||||
| ----- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | #[coroutine] static || {
|
||||
| ++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -1,10 +1,17 @@
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/pattern-borrow.rs:9:24
|
||||
|
|
||||
LL | #[coroutine] move || {
|
||||
| ------- within this coroutine
|
||||
LL | if let Test::A(ref _a) = test {
|
||||
| ^^^^^^
|
||||
LL | yield ();
|
||||
| -------- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | #[coroutine] static move || {
|
||||
| ++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
error[E0626]: borrow may still be in use when `gen` block yields
|
||||
--> $DIR/self_referential_gen_block.rs:9:21
|
||||
|
|
||||
LL | let mut x = gen {
|
||||
| --- within this `gen` block
|
||||
LL | let y = 42;
|
||||
LL | let z = &y;
|
||||
| ^^
|
||||
LL | yield 43;
|
||||
|
@ -1,8 +1,16 @@
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/yield-in-args.rs:9:13
|
||||
|
|
||||
LL | || {
|
||||
| -- within this coroutine
|
||||
LL | let b = true;
|
||||
LL | foo(&b, yield);
|
||||
| ^^ ----- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | static || {
|
||||
| ++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -1,10 +1,17 @@
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/yield-while-iterating.rs:13:18
|
||||
|
|
||||
LL | let _b =#[coroutine] move || {
|
||||
| ------- within this coroutine
|
||||
LL | for p in &x {
|
||||
| ^^
|
||||
LL | yield();
|
||||
| ------- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | let _b =#[coroutine] static move || {
|
||||
| ++++++
|
||||
|
||||
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
|
||||
--> $DIR/yield-while-iterating.rs:58:20
|
||||
|
@ -1,20 +1,35 @@
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/yield-while-local-borrowed.rs:13:17
|
||||
|
|
||||
LL | let mut b = #[coroutine] move || {
|
||||
| ------- within this coroutine
|
||||
LL | let a = &mut 3;
|
||||
| ^^^^^^
|
||||
LL |
|
||||
LL | yield ();
|
||||
| -------- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | let mut b = #[coroutine] static move || {
|
||||
| ++++++
|
||||
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/yield-while-local-borrowed.rs:40:21
|
||||
|
|
||||
LL | let mut b = #[coroutine] move || {
|
||||
| ------- within this coroutine
|
||||
...
|
||||
LL | let b = &a;
|
||||
| ^^
|
||||
LL |
|
||||
LL | yield ();
|
||||
| -------- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | let mut b = #[coroutine] static move || {
|
||||
| ++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -10,8 +10,16 @@ LL | yield &s[..]
|
||||
error[E0626]: borrow may still be in use when coroutine yields
|
||||
--> $DIR/issue-55850.rs:28:16
|
||||
|
|
||||
LL | GenIter(#[coroutine] move || {
|
||||
| ------- within this coroutine
|
||||
LL | let mut s = String::new();
|
||||
LL | yield &s[..]
|
||||
| -------^---- possible yield occurs here
|
||||
|
|
||||
help: add `static` to mark this coroutine as unmovable
|
||||
|
|
||||
LL | GenIter(#[coroutine] static move || {
|
||||
| ++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
16
tests/ui/static/raw-ref-deref-with-unsafe.rs
Normal file
16
tests/ui/static/raw-ref-deref-with-unsafe.rs
Normal file
@ -0,0 +1,16 @@
|
||||
//@ check-pass
|
||||
#![feature(const_mut_refs)]
|
||||
use std::ptr;
|
||||
|
||||
// This code should remain unsafe because of the two unsafe operations here,
|
||||
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
|
||||
|
||||
static mut BYTE: u8 = 0;
|
||||
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
|
||||
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
|
||||
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref
|
||||
static mut DEREF_BYTE_PTR: *mut u8 = unsafe { ptr::addr_of_mut!(*BYTE_PTR) };
|
||||
|
||||
fn main() {
|
||||
let _ = unsafe { DEREF_BYTE_PTR };
|
||||
}
|
18
tests/ui/static/raw-ref-deref-without-unsafe.rs
Normal file
18
tests/ui/static/raw-ref-deref-without-unsafe.rs
Normal file
@ -0,0 +1,18 @@
|
||||
#![feature(const_mut_refs)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
// This code should remain unsafe because of the two unsafe operations here,
|
||||
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
|
||||
|
||||
static mut BYTE: u8 = 0;
|
||||
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
|
||||
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
|
||||
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref!
|
||||
static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
|
||||
//~^ ERROR: use of mutable static
|
||||
//~| ERROR: dereference of raw pointer
|
||||
|
||||
fn main() {
|
||||
let _ = unsafe { DEREF_BYTE_PTR };
|
||||
}
|
19
tests/ui/static/raw-ref-deref-without-unsafe.stderr
Normal file
19
tests/ui/static/raw-ref-deref-without-unsafe.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
|
||||
--> $DIR/raw-ref-deref-without-unsafe.rs:12:56
|
||||
|
|
||||
LL | static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
|
||||
| ^^^^^^^^^ dereference of raw pointer
|
||||
|
|
||||
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
|
||||
|
||||
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
|
||||
--> $DIR/raw-ref-deref-without-unsafe.rs:12:57
|
||||
|
|
||||
LL | static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
|
||||
| ^^^^^^^^ use of mutable static
|
||||
|
|
||||
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0133`.
|
27
tests/ui/static/raw-ref-extern-static.rs
Normal file
27
tests/ui/static/raw-ref-extern-static.rs
Normal file
@ -0,0 +1,27 @@
|
||||
//@ check-pass
|
||||
#![feature(raw_ref_op)]
|
||||
use std::ptr;
|
||||
|
||||
// see https://github.com/rust-lang/rust/issues/125833
|
||||
// notionally, taking the address of an extern static is a safe operation,
|
||||
// as we only point at it instead of generating a true reference to it
|
||||
|
||||
// it may potentially induce linker errors, but the safety of that is not about taking addresses!
|
||||
// any safety obligation of the extern static's correctness in declaration is on the extern itself,
|
||||
// see RFC 3484 for more on that: https://rust-lang.github.io/rfcs/3484-unsafe-extern-blocks.html
|
||||
|
||||
extern "C" {
|
||||
static THERE: u8;
|
||||
static mut SOMEWHERE: u8;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ptr2there = ptr::addr_of!(THERE);
|
||||
let ptr2somewhere = ptr::addr_of!(SOMEWHERE);
|
||||
let ptr2somewhere = ptr::addr_of_mut!(SOMEWHERE);
|
||||
|
||||
// testing both addr_of and the expression it directly expands to
|
||||
let raw2there = &raw const THERE;
|
||||
let raw2somewhere = &raw const SOMEWHERE;
|
||||
let raw2somewhere = &raw mut SOMEWHERE;
|
||||
}
|
17
tests/ui/static/raw-ref-static-mut.rs
Normal file
17
tests/ui/static/raw-ref-static-mut.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//@ check-pass
|
||||
#![feature(raw_ref_op)]
|
||||
use std::ptr;
|
||||
|
||||
// see https://github.com/rust-lang/rust/issues/125833
|
||||
// notionally, taking the address of a static mut is a safe operation,
|
||||
// as we only point at it instead of generating a true reference to it
|
||||
static mut NOWHERE: usize = 0;
|
||||
|
||||
fn main() {
|
||||
let p2nowhere = ptr::addr_of!(NOWHERE);
|
||||
let p2nowhere = ptr::addr_of_mut!(NOWHERE);
|
||||
|
||||
// testing both addr_of and the expression it directly expands to
|
||||
let raw2nowhere = &raw const NOWHERE;
|
||||
let raw2nowhere = &raw mut NOWHERE;
|
||||
}
|
Loading…
Reference in New Issue
Block a user