mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 19:58:32 +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::diagnostic_outside_of_impl)]
|
||||||
#![allow(rustc::untranslatable_diagnostic)]
|
#![allow(rustc::untranslatable_diagnostic)]
|
||||||
|
|
||||||
|
use rustc_errors::Applicability;
|
||||||
use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxtHandle};
|
use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxtHandle};
|
||||||
|
use rustc_hir as hir;
|
||||||
use rustc_middle::span_bug;
|
use rustc_middle::span_bug;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
@ -382,13 +384,35 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||||||
yield_span: Span,
|
yield_span: Span,
|
||||||
) -> Diag<'infcx> {
|
) -> Diag<'infcx> {
|
||||||
let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
|
let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
|
||||||
struct_span_code_err!(
|
let mut diag = struct_span_code_err!(
|
||||||
self.dcx(),
|
self.dcx(),
|
||||||
span,
|
span,
|
||||||
E0626,
|
E0626,
|
||||||
"borrow may still be in use when {coroutine_kind:#} yields",
|
"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> {
|
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.
|
yield point.
|
||||||
|
|
||||||
Erroneous code example:
|
Erroneous code example:
|
||||||
@ -15,19 +15,35 @@ let mut b = #[coroutine] || {
|
|||||||
Pin::new(&mut b).resume(());
|
Pin::new(&mut b).resume(());
|
||||||
```
|
```
|
||||||
|
|
||||||
At present, it is not permitted to have a yield that occurs while a
|
Coroutines may be either unmarked, or marked with `static`. If it is unmarked,
|
||||||
borrow is still in scope. To resolve this error, the borrow must
|
then the coroutine is considered "movable". At present, it is not permitted to
|
||||||
either be "contained" to a smaller scope that does not overlap the
|
have a yield in a movable coroutine that occurs while a borrow is still in
|
||||||
yield or else eliminated in another way. So, for example, we might
|
scope. To resolve this error, the coroutine may be marked `static`:
|
||||||
resolve the previous example by removing the borrow and just storing
|
|
||||||
the integer by value:
|
```
|
||||||
|
# #![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)]
|
# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
|
||||||
# use std::ops::Coroutine;
|
# use std::ops::Coroutine;
|
||||||
# use std::pin::Pin;
|
# use std::pin::Pin;
|
||||||
let mut b = #[coroutine] || {
|
let mut b = #[coroutine] || {
|
||||||
let a = 3;
|
let a = String::from("hello, world");
|
||||||
yield ();
|
yield ();
|
||||||
println!("{}", a);
|
println!("{}", a);
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,6 @@ use rustc_span::def_id::LocalDefId;
|
|||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use rustc_trait_selection::error_reporting::traits::ArgKind;
|
use rustc_trait_selection::error_reporting::traits::ArgKind;
|
||||||
use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _;
|
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
use rustc_type_ir::ClosureKind;
|
use rustc_type_ir::ClosureKind;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@ -734,13 +733,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
.map(|ty| ArgKind::from_expected_ty(*ty, None))
|
.map(|ty| ArgKind::from_expected_ty(*ty, None))
|
||||||
.collect();
|
.collect();
|
||||||
let (closure_span, closure_arg_span, found_args) =
|
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),
|
Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args),
|
||||||
None => (None, None, Vec::new()),
|
None => (None, None, Vec::new()),
|
||||||
};
|
};
|
||||||
let expected_span =
|
let expected_span =
|
||||||
expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id));
|
expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id));
|
||||||
let guar = self
|
let guar = self
|
||||||
|
.err_ctxt()
|
||||||
.report_arg_count_mismatch(
|
.report_arg_count_mismatch(
|
||||||
expected_span,
|
expected_span,
|
||||||
closure_span,
|
closure_span,
|
||||||
|
@ -755,18 +755,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
.collect()
|
.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")]
|
#[instrument(skip(self), level = "debug")]
|
||||||
pub fn sub_regions(
|
pub fn sub_regions(
|
||||||
&self,
|
&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 } => {
|
ExprKind::Deref { arg } => {
|
||||||
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
|
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
|
||||||
self.thir[arg].kind
|
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 source Rust `path::to::STATIC` is a place expr like *&ident is.
|
||||||
// a constant reference (or constant raw pointer for `static mut`) in MIR
|
// 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) => {
|
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 ty = self.tcx.static_ptr_ty(id);
|
||||||
let temp_lifetime = self
|
let temp_lifetime = self
|
||||||
.rvalue_scopes
|
.rvalue_scopes
|
||||||
|
@ -12,6 +12,7 @@ use rustc_middle::{
|
|||||||
use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
|
use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
|
||||||
|
|
||||||
use crate::error_reporting::TypeErrCtxt;
|
use crate::error_reporting::TypeErrCtxt;
|
||||||
|
use crate::infer::InferCtxtExt;
|
||||||
|
|
||||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
pub fn note_and_explain_type_err(
|
pub fn note_and_explain_type_err(
|
||||||
@ -821,7 +822,7 @@ fn foo(&self) -> Self::T { String::new() }
|
|||||||
tcx.defaultness(item.id.owner_id)
|
tcx.defaultness(item.id.owner_id)
|
||||||
{
|
{
|
||||||
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
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(
|
diag.span_label(
|
||||||
item.span,
|
item.span,
|
||||||
"associated type defaults can't be assumed inside the \
|
"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();
|
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
||||||
if let hir::Defaultness::Default { has_value: true } =
|
if let hir::Defaultness::Default { has_value: true } =
|
||||||
tcx.defaultness(item.id.owner_id)
|
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(
|
diag.span_label(
|
||||||
item.span,
|
item.span,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
|
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
|
||||||
use super::suggestions::get_explanation_based_on_obligation;
|
use super::suggestions::get_explanation_based_on_obligation;
|
||||||
use crate::error_reporting::infer::TyCategory;
|
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::traits::report_object_safety_error;
|
||||||
use crate::error_reporting::TypeErrCtxt;
|
use crate::error_reporting::TypeErrCtxt;
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
@ -2602,7 +2601,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||||||
})
|
})
|
||||||
.unwrap_or((found_span, None, found));
|
.unwrap_or((found_span, None, found));
|
||||||
|
|
||||||
self.infcx.report_arg_count_mismatch(
|
self.report_arg_count_mismatch(
|
||||||
span,
|
span,
|
||||||
closure_span,
|
closure_span,
|
||||||
expected,
|
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(
|
fn report_not_const_evaluatable_error(
|
||||||
&self,
|
&self,
|
||||||
obligation: &PredicateObligation<'tcx>,
|
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;
|
pub mod ambiguity;
|
||||||
mod fulfillment_errors;
|
mod fulfillment_errors;
|
||||||
mod infer_ctxt_ext;
|
|
||||||
pub mod on_unimplemented;
|
pub mod on_unimplemented;
|
||||||
mod overflow;
|
mod overflow;
|
||||||
pub mod suggestions;
|
pub mod suggestions;
|
||||||
@ -23,7 +22,6 @@ use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
|
|||||||
use crate::error_reporting::TypeErrCtxt;
|
use crate::error_reporting::TypeErrCtxt;
|
||||||
use crate::traits::{FulfillmentError, FulfillmentErrorCode};
|
use crate::traits::{FulfillmentError, FulfillmentErrorCode};
|
||||||
|
|
||||||
pub use self::infer_ctxt_ext::*;
|
|
||||||
pub use self::overflow::*;
|
pub use self::overflow::*;
|
||||||
|
|
||||||
// When outputting impl candidates, prefer showing those that are more similar.
|
// 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
|
// going to be cross-lang LTOed anyway. However, using expose is shorter and
|
||||||
// requires less unsafe.
|
// requires less unsafe.
|
||||||
let addr: usize = ptr.expose_provenance();
|
let addr: usize = ptr.expose_provenance();
|
||||||
|
#[cfg(bootstrap)]
|
||||||
let image_base = unsafe { addr_of!(__ImageBase) }.addr();
|
let image_base = unsafe { addr_of!(__ImageBase) }.addr();
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
let image_base = addr_of!(__ImageBase).addr();
|
||||||
let offset: usize = addr - image_base;
|
let offset: usize = addr - image_base;
|
||||||
Self(offset as u32)
|
Self(offset as u32)
|
||||||
}
|
}
|
||||||
@ -250,7 +253,10 @@ extern "C" {
|
|||||||
// This is fine since the MSVC runtime uses string comparison on the type name
|
// This is fine since the MSVC runtime uses string comparison on the type name
|
||||||
// to match TypeDescriptors rather than pointer equality.
|
// to match TypeDescriptors rather than pointer equality.
|
||||||
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
|
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
|
||||||
|
#[cfg(bootstrap)]
|
||||||
pVFTable: unsafe { addr_of!(TYPE_INFO_VTABLE) } as *const _,
|
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(),
|
spare: core::ptr::null_mut(),
|
||||||
name: TYPE_NAME,
|
name: TYPE_NAME,
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Common code for printing backtraces.
|
//! Common code for printing backtraces.
|
||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
|
use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
|
||||||
use crate::borrow::Cow;
|
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.
|
// Start immediately if we're not using a short backtrace.
|
||||||
let mut start = print_fmt != PrintFmt::Short;
|
let mut start = print_fmt != PrintFmt::Short;
|
||||||
set_image_base();
|
set_image_base();
|
||||||
backtrace_rs::trace_unsynchronized(|frame| {
|
// SAFETY: we roll our own locking in this town
|
||||||
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
|
unsafe {
|
||||||
return false;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if start {
|
let mut hit = false;
|
||||||
if omitted_count > 0 {
|
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
|
||||||
debug_assert!(print_fmt == PrintFmt::Short);
|
hit = true;
|
||||||
// only print the message between the middle of frames
|
|
||||||
if !first_omit {
|
// Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace`
|
||||||
let _ = writeln!(
|
// are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be
|
||||||
bt_fmt.formatter(),
|
// called before the panic hook, so we won't ignore any frames if there is no
|
||||||
" [... omitted {} frame{} ...]",
|
// invoke of `__rust_begin_short_backtrace`.
|
||||||
omitted_count,
|
if print_fmt == PrintFmt::Short {
|
||||||
if omitted_count > 1 { "s" } else { "" }
|
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 {
|
if !hit && start {
|
||||||
use crate::backtrace_rs::SymbolName;
|
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
|
||||||
res = bt_fmt.frame().print_raw(
|
|
||||||
frame.ip(),
|
|
||||||
Some(SymbolName::new("__my_thread_exit".as_bytes())),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if !hit && start {
|
|
||||||
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
idx += 1;
|
idx += 1;
|
||||||
res.is_ok()
|
res.is_ok()
|
||||||
});
|
})
|
||||||
|
};
|
||||||
res?;
|
res?;
|
||||||
bt_fmt.finish()?;
|
bt_fmt.finish()?;
|
||||||
if print_fmt == PrintFmt::Short {
|
if print_fmt == PrintFmt::Short {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
use crate::alloc::{GlobalAlloc, Layout, System};
|
use crate::alloc::{GlobalAlloc, Layout, System};
|
||||||
use crate::cmp;
|
use crate::cmp;
|
||||||
use crate::ptr;
|
use crate::ptr;
|
||||||
@ -46,14 +47,16 @@ pub unsafe fn realloc_fallback(
|
|||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_size: usize,
|
new_size: usize,
|
||||||
) -> *mut u8 {
|
) -> *mut u8 {
|
||||||
// Docs for GlobalAlloc::realloc require this to be valid:
|
// SAFETY: Docs for GlobalAlloc::realloc require this to be valid
|
||||||
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
unsafe {
|
||||||
|
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
||||||
|
|
||||||
let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
|
let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
|
||||||
if !new_ptr.is_null() {
|
if !new_ptr.is_null() {
|
||||||
let size = cmp::min(old_layout.size(), new_size);
|
let size = cmp::min(old_layout.size(), new_size);
|
||||||
ptr::copy_nonoverlapping(ptr, new_ptr, size);
|
ptr::copy_nonoverlapping(ptr, new_ptr, size);
|
||||||
GlobalAlloc::dealloc(alloc, ptr, old_layout);
|
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_session::declare_lint_pass;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_target::spec::abi::Abi;
|
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! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||||||
// 'cuz currently nothing changes after deleting this check.
|
// 'cuz currently nothing changes after deleting this check.
|
||||||
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
|
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,
|
cx.param_env,
|
||||||
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
|
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
|
||||||
ty::PredicatePolarity::Positive,
|
ty::PredicatePolarity::Positive,
|
||||||
|
@ -82,26 +82,22 @@ fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The platform-specific library name
|
/// 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 {
|
match aux_type {
|
||||||
AuxType::Bin => None,
|
AuxType::Bin => None,
|
||||||
// In some cases (e.g. MUSL), we build a static
|
// In some cases (e.g. MUSL), we build a static
|
||||||
// library, rather than a dynamic library.
|
// library, rather than a dynamic library.
|
||||||
// In this case, the only path we can pass
|
// In this case, the only path we can pass
|
||||||
// with '--extern-meta' is the '.rlib' file
|
// with '--extern-meta' is the '.rlib' file
|
||||||
AuxType::Lib => Some(format!("lib{}.rlib", lib)),
|
AuxType::Lib => Some(format!("lib{name}.rlib")),
|
||||||
AuxType::Dylib => Some(if cfg!(windows) {
|
AuxType::Dylib => Some(dylib_name(name)),
|
||||||
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)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>) {
|
pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
|
||||||
match &*config.target {
|
match &*config.target {
|
||||||
"arm-linux-androideabi"
|
"arm-linux-androideabi"
|
||||||
|
@ -5,5 +5,5 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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
|
error: unsupported operation: extern static `FOO` is not supported by Miri
|
||||||
--> $DIR/extern_static.rs:LL:CC
|
--> $DIR/extern_static.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let _val = unsafe { std::ptr::addr_of!(FOO) };
|
LL | let _val = std::ptr::addr_of!(FOO);
|
||||||
| ^^^ extern static `FOO` is not supported by Miri
|
| ^^^ 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
|
= 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:
|
= note: BACKTRACE:
|
||||||
|
@ -2,7 +2,7 @@ use std::ptr::addr_of;
|
|||||||
|
|
||||||
static mut FOO: i32 = 42;
|
static mut FOO: i32 = 42;
|
||||||
|
|
||||||
static BAR: Foo = Foo(unsafe { addr_of!(FOO) });
|
static BAR: Foo = Foo(addr_of!(FOO));
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct Foo(*const i32);
|
struct Foo(*const i32);
|
||||||
|
@ -9,7 +9,7 @@ const C1: &i32 = &S;
|
|||||||
const C1_READ: () = {
|
const C1_READ: () = {
|
||||||
assert!(*C1 == 0);
|
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() {
|
fn main() {
|
||||||
assert_eq!(*C1, 0);
|
assert_eq!(*C1, 0);
|
||||||
|
@ -16,12 +16,9 @@ static mut STATIC: u32 = 42;
|
|||||||
static INTERIOR_MUTABLE_STATIC: SyncUnsafeCell<u32> = SyncUnsafeCell::new(42);
|
static INTERIOR_MUTABLE_STATIC: SyncUnsafeCell<u32> = SyncUnsafeCell::new(42);
|
||||||
|
|
||||||
// A static that mutably points to STATIC.
|
// A static that mutably points to STATIC.
|
||||||
static PTR: SyncPtr = SyncPtr {
|
static PTR: SyncPtr = SyncPtr { foo: ptr::addr_of_mut!(STATIC) };
|
||||||
foo: unsafe { ptr::addr_of_mut!(STATIC) },
|
static INTERIOR_MUTABLE_PTR: SyncPtr =
|
||||||
};
|
SyncPtr { foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32 };
|
||||||
static INTERIOR_MUTABLE_PTR: SyncPtr = SyncPtr {
|
|
||||||
foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let ptr = PTR.foo;
|
let ptr = PTR.foo;
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
error[E0626]: borrow may still be in use when coroutine yields
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/coroutine-with-nll.rs:8:17
|
--> $DIR/coroutine-with-nll.rs:8:17
|
||||||
|
|
|
|
||||||
|
LL | || {
|
||||||
|
| -- within this coroutine
|
||||||
|
...
|
||||||
LL | let b = &mut true;
|
LL | let b = &mut true;
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
LL |
|
LL |
|
||||||
LL | yield ();
|
LL | yield ();
|
||||||
| -------- possible yield occurs here
|
| -------- possible yield occurs here
|
||||||
|
|
|
||||||
|
help: add `static` to mark this coroutine as unmovable
|
||||||
|
|
|
||||||
|
LL | static || {
|
||||||
|
| ++++++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
error[E0626]: borrow may still be in use when coroutine yields
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/issue-48048.rs:9:9
|
--> $DIR/issue-48048.rs:9:9
|
||||||
|
|
|
|
||||||
|
LL | #[coroutine] || {
|
||||||
|
| -- within this coroutine
|
||||||
|
...
|
||||||
LL | x.0({
|
LL | x.0({
|
||||||
| ^^^
|
| ^^^
|
||||||
LL | yield;
|
LL | yield;
|
||||||
| ----- possible yield occurs here
|
| ----- possible yield occurs here
|
||||||
|
|
|
||||||
|
help: add `static` to mark this coroutine as unmovable
|
||||||
|
|
|
||||||
|
LL | #[coroutine] static || {
|
||||||
|
| ++++++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
error[E0626]: borrow may still be in use when coroutine yields
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/pattern-borrow.rs:9:24
|
--> $DIR/pattern-borrow.rs:9:24
|
||||||
|
|
|
|
||||||
|
LL | #[coroutine] move || {
|
||||||
|
| ------- within this coroutine
|
||||||
LL | if let Test::A(ref _a) = test {
|
LL | if let Test::A(ref _a) = test {
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
LL | yield ();
|
LL | yield ();
|
||||||
| -------- possible yield occurs here
|
| -------- possible yield occurs here
|
||||||
|
|
|
||||||
|
help: add `static` to mark this coroutine as unmovable
|
||||||
|
|
|
||||||
|
LL | #[coroutine] static move || {
|
||||||
|
| ++++++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
error[E0626]: borrow may still be in use when `gen` block yields
|
error[E0626]: borrow may still be in use when `gen` block yields
|
||||||
--> $DIR/self_referential_gen_block.rs:9:21
|
--> $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 | let z = &y;
|
||||||
| ^^
|
| ^^
|
||||||
LL | yield 43;
|
LL | yield 43;
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
error[E0626]: borrow may still be in use when coroutine yields
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/yield-in-args.rs:9:13
|
--> $DIR/yield-in-args.rs:9:13
|
||||||
|
|
|
|
||||||
|
LL | || {
|
||||||
|
| -- within this coroutine
|
||||||
|
LL | let b = true;
|
||||||
LL | foo(&b, yield);
|
LL | foo(&b, yield);
|
||||||
| ^^ ----- possible yield occurs here
|
| ^^ ----- possible yield occurs here
|
||||||
|
|
|
||||||
|
help: add `static` to mark this coroutine as unmovable
|
||||||
|
|
|
||||||
|
LL | static || {
|
||||||
|
| ++++++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
error[E0626]: borrow may still be in use when coroutine yields
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/yield-while-iterating.rs:13:18
|
--> $DIR/yield-while-iterating.rs:13:18
|
||||||
|
|
|
|
||||||
|
LL | let _b =#[coroutine] move || {
|
||||||
|
| ------- within this coroutine
|
||||||
LL | for p in &x {
|
LL | for p in &x {
|
||||||
| ^^
|
| ^^
|
||||||
LL | yield();
|
LL | yield();
|
||||||
| ------- possible yield occurs here
|
| ------- 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
|
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
|
||||||
--> $DIR/yield-while-iterating.rs:58:20
|
--> $DIR/yield-while-iterating.rs:58:20
|
||||||
|
@ -1,20 +1,35 @@
|
|||||||
error[E0626]: borrow may still be in use when coroutine yields
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/yield-while-local-borrowed.rs:13:17
|
--> $DIR/yield-while-local-borrowed.rs:13:17
|
||||||
|
|
|
|
||||||
|
LL | let mut b = #[coroutine] move || {
|
||||||
|
| ------- within this coroutine
|
||||||
LL | let a = &mut 3;
|
LL | let a = &mut 3;
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
LL |
|
LL |
|
||||||
LL | yield ();
|
LL | yield ();
|
||||||
| -------- possible yield occurs here
|
| -------- 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
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/yield-while-local-borrowed.rs:40:21
|
--> $DIR/yield-while-local-borrowed.rs:40:21
|
||||||
|
|
|
|
||||||
|
LL | let mut b = #[coroutine] move || {
|
||||||
|
| ------- within this coroutine
|
||||||
|
...
|
||||||
LL | let b = &a;
|
LL | let b = &a;
|
||||||
| ^^
|
| ^^
|
||||||
LL |
|
LL |
|
||||||
LL | yield ();
|
LL | yield ();
|
||||||
| -------- possible yield occurs here
|
| -------- 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
|
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
|
error[E0626]: borrow may still be in use when coroutine yields
|
||||||
--> $DIR/issue-55850.rs:28:16
|
--> $DIR/issue-55850.rs:28:16
|
||||||
|
|
|
|
||||||
|
LL | GenIter(#[coroutine] move || {
|
||||||
|
| ------- within this coroutine
|
||||||
|
LL | let mut s = String::new();
|
||||||
LL | yield &s[..]
|
LL | yield &s[..]
|
||||||
| -------^---- possible yield occurs here
|
| -------^---- 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
|
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