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:
bors 2024-07-23 12:10:45 +00:00
commit d53dc752d2
33 changed files with 569 additions and 368 deletions

View File

@ -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> {

View File

@ -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);
}; };

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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>,

View File

@ -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(())
})
}
}

View File

@ -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.

View File

@ -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,
}; };

View File

@ -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 {

View File

@ -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
} }

View File

@ -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,

View File

@ -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"

View File

@ -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
} }

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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 };
}

View 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 };
}

View 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`.

View 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;
}

View 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;
}