Merge pull request #4680 from rust-lang/rustup-2025-11-12

Automatic Rustup
This commit is contained in:
Ralf Jung 2025-11-12 07:49:37 +00:00 committed by GitHub
commit 037db1e3c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
753 changed files with 7381 additions and 4556 deletions

View file

@ -6,7 +6,7 @@ name: Post merge analysis
on:
push:
branches:
- master
- main
jobs:
analysis:

View file

@ -600,6 +600,7 @@ dependencies = [
"serde_json",
"tempfile",
"termize",
"tikv-jemalloc-sys",
"toml 0.9.7",
"ui_test",
"walkdir",
@ -4806,6 +4807,7 @@ dependencies = [
"stringdex",
"tempfile",
"threadpool",
"tikv-jemalloc-sys",
"tracing",
"tracing-subscriber",
"tracing-tree",
@ -5537,9 +5539,9 @@ version = "0.1.0"
[[package]]
name = "tikv-jemalloc-sys"
version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d"
checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b"
dependencies = [
"cc",
"libc",

View file

@ -320,7 +320,7 @@
#build.npm = "npm"
# Python interpreter to use for various tasks throughout the build, notably
# rustdoc tests, the lldb python interpreter, and some dist bits and pieces.
# rustdoc tests, and some dist bits and pieces.
#
# Defaults to the Python interpreter used to execute x.py.
#build.python = "python"

View file

@ -21,9 +21,9 @@ rustc_public_bridge = { path = "../rustc_public_bridge" }
# tidy-alphabetical-end
[dependencies.tikv-jemalloc-sys]
version = "0.6.0"
version = "0.6.1"
optional = true
features = ['unprefixed_malloc_on_supported_platforms']
features = ['override_allocator_on_supported_platforms']
[features]
# tidy-alphabetical-start

View file

@ -7,26 +7,25 @@
// distribution. The obvious way to do this is with the `#[global_allocator]`
// mechanism. However, for complicated reasons (see
// https://github.com/rust-lang/rust/pull/81782#issuecomment-784438001 for some
// details) that mechanism doesn't work here. Also, we must use a consistent
// allocator across the rustc <-> llvm boundary, and `#[global_allocator]`
// wouldn't provide that.
// details) that mechanism doesn't work here. Also, we'd like to use a
// consistent allocator across the rustc <-> llvm boundary, and
// `#[global_allocator]` wouldn't provide that.
//
// Instead, we use a lower-level mechanism. rustc is linked with jemalloc in a
// way such that jemalloc's implementation of `malloc`, `free`, etc., override
// the libc allocator's implementation. This means that Rust's `System`
// allocator, which calls `libc::malloc()` et al., is actually calling into
// jemalloc.
// Instead, we use a lower-level mechanism, namely the
// `"override_allocator_on_supported_platforms"` Cargo feature of jemalloc-sys.
//
// This makes jemalloc-sys override the libc/system allocator's implementation
// of `malloc`, `free`, etc.. This means that Rust's `System` allocator, which
// calls `libc::malloc()` et al., is actually calling into jemalloc.
//
// A consequence of not using `GlobalAlloc` (and the `tikv-jemallocator` crate
// provides an impl of that trait, which is called `Jemalloc`) is that we
// cannot use the sized deallocation APIs (`sdallocx`) that jemalloc provides.
// It's unclear how much performance is lost because of this.
//
// As for the symbol overrides in `main` below: we're pulling in a static copy
// of jemalloc. We need to actually reference its symbols for it to get linked.
// The two crates we link to here, `std` and `rustc_driver`, are both dynamic
// libraries. So we must reference jemalloc symbols one way or another, because
// this file is the only object code in the rustc executable.
// NOTE: Even though Cargo passes `--extern` with `tikv_jemalloc_sys`, we still need to `use` the
// crate for the compiler to see the `#[used]`, see https://github.com/rust-lang/rust/issues/64402.
// This is similarly required if we used a crate with `#[global_allocator]`.
//
// NOTE: if you are reading this comment because you want to set a custom `global_allocator` for
// benchmarking, consider using the benchmarks in the `rustc-perf` collector suite instead:
@ -36,43 +35,9 @@
// to compare their performance, see
// https://github.com/rust-lang/rust/commit/b90cfc887c31c3e7a9e6d462e2464db1fe506175#diff-43914724af6e464c1da2171e4a9b6c7e607d5bc1203fa95c0ab85be4122605ef
// for an example of how to do so.
#[cfg(feature = "jemalloc")]
use tikv_jemalloc_sys as _;
fn main() {
// See the comment at the top of this file for an explanation of this.
#[cfg(feature = "jemalloc")]
{
use std::os::raw::{c_int, c_void};
use tikv_jemalloc_sys as jemalloc_sys;
#[used]
static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
#[used]
static _F2: unsafe extern "C" fn(*mut *mut c_void, usize, usize) -> c_int =
jemalloc_sys::posix_memalign;
#[used]
static _F3: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::aligned_alloc;
#[used]
static _F4: unsafe extern "C" fn(usize) -> *mut c_void = jemalloc_sys::malloc;
#[used]
static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc;
#[used]
static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free;
// On OSX, jemalloc doesn't directly override malloc/free, but instead
// registers itself with the allocator's zone APIs in a ctor. However,
// the linker doesn't seem to consider ctors as "used" when statically
// linking, so we need to explicitly depend on the function.
#[cfg(target_os = "macos")]
{
unsafe extern "C" {
fn _rjem_je_zone_register();
}
#[used]
static _F7: unsafe extern "C" fn() = _rjem_je_zone_register;
}
}
rustc_driver::main()
}

View file

@ -648,9 +648,10 @@ impl Pat {
PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
PatKind::MacCall(mac) => TyKind::MacCall(mac.clone()),
// `&mut? P` can be reinterpreted as `&mut? T` where `T` is `P` reparsed as a type.
PatKind::Ref(pat, mutbl) => {
pat.to_ty().map(|ty| TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }))?
}
PatKind::Ref(pat, pinned, mutbl) => pat.to_ty().map(|ty| match pinned {
Pinnedness::Not => TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }),
Pinnedness::Pinned => TyKind::PinnedRef(None, MutTy { ty, mutbl: *mutbl }),
})?,
// A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array,
// when `P` can be reparsed as a type `T`.
PatKind::Slice(pats) if let [pat] = pats.as_slice() => {
@ -696,7 +697,7 @@ impl Pat {
// Trivial wrappers over inner patterns.
PatKind::Box(s)
| PatKind::Deref(s)
| PatKind::Ref(s, _)
| PatKind::Ref(s, _, _)
| PatKind::Paren(s)
| PatKind::Guard(s, _) => s.walk(it),
@ -717,7 +718,7 @@ impl Pat {
/// Strip off all reference patterns (`&`, `&mut`) and return the inner pattern.
pub fn peel_refs(&self) -> &Pat {
let mut current = self;
while let PatKind::Ref(inner, _) = &current.kind {
while let PatKind::Ref(inner, _, _) = &current.kind {
current = inner;
}
current
@ -765,7 +766,9 @@ impl Pat {
PatKind::Missing => unreachable!(),
PatKind::Wild => Some("_".to_string()),
PatKind::Ident(BindingMode::NONE, ident, None) => Some(format!("{ident}")),
PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())),
PatKind::Ref(pat, pinned, mutbl) => {
pat.descr().map(|d| format!("&{}{d}", pinned.prefix_str(*mutbl)))
}
_ => None,
}
}
@ -913,7 +916,7 @@ pub enum PatKind {
Deref(Box<Pat>),
/// A reference pattern (e.g., `&mut (a, b)`).
Ref(Box<Pat>, Mutability),
Ref(Box<Pat>, Pinnedness, Mutability),
/// A literal, const block or path.
Expr(Box<Expr>),

View file

@ -645,8 +645,9 @@ macro_rules! common_visitor_and_walkers {
fn visit_fn(
&mut self,
fk: FnKind<$($lt)? $(${ignore($mut)} '_)?>,
_: &AttrVec,
_: Span,
_: NodeId
_: NodeId,
) -> Self::Result {
walk_fn(self, fk)
}
@ -740,6 +741,7 @@ macro_rules! common_visitor_and_walkers {
type Ctxt;
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
&$($lt)? $($mut)? self,
attrs: &AttrVec,
span: Span,
id: NodeId,
visibility: &$($lt)? $($mut)? Visibility,
@ -773,7 +775,7 @@ macro_rules! common_visitor_and_walkers {
) -> V::Result {
let Item { attrs, id, kind, vis, span, tokens: _ } = item;
visit_visitable!($($mut)? visitor, id, attrs, vis);
try_visit!(kind.walk(*span, *id, vis, ctxt, visitor));
try_visit!(kind.walk(attrs, *span, *id, vis, ctxt, visitor));
visit_visitable!($($mut)? visitor, span);
V::Result::output()
}
@ -799,6 +801,7 @@ macro_rules! common_visitor_and_walkers {
type Ctxt = ();
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
&$($lt)? $($mut)? self,
attrs: &AttrVec,
span: Span,
id: NodeId,
visibility: &$($lt)? $($mut)? Visibility,
@ -808,7 +811,7 @@ macro_rules! common_visitor_and_walkers {
match self {
ItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func);
try_visit!(vis.visit_fn(kind, span, id));
try_visit!(vis.visit_fn(kind, attrs, span, id));
}
ItemKind::ExternCrate(orig_name, ident) =>
visit_visitable!($($mut)? vis, orig_name, ident),
@ -856,6 +859,7 @@ macro_rules! common_visitor_and_walkers {
type Ctxt = AssocCtxt;
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
&$($lt)? $($mut)? self,
attrs: &AttrVec,
span: Span,
id: NodeId,
visibility: &$($lt)? $($mut)? Visibility,
@ -867,7 +871,7 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, item),
AssocItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func);
try_visit!(vis.visit_fn(kind, span, id))
try_visit!(vis.visit_fn(kind, attrs, span, id))
}
AssocItemKind::Type(alias) =>
visit_visitable!($($mut)? vis, alias),
@ -886,6 +890,7 @@ macro_rules! common_visitor_and_walkers {
type Ctxt = ();
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
&$($lt)? $($mut)? self,
attrs: &AttrVec,
span: Span,
id: NodeId,
visibility: &$($lt)? $($mut)? Visibility,
@ -897,7 +902,7 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, item),
ForeignItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func);
try_visit!(vis.visit_fn(kind, span, id))
try_visit!(vis.visit_fn(kind, attrs, span, id))
}
ForeignItemKind::TyAlias(alias) =>
visit_visitable!($($mut)? vis, alias),
@ -999,7 +1004,7 @@ macro_rules! common_visitor_and_walkers {
}) => {
visit_visitable!($($mut)? vis, constness, movability, capture_clause);
let kind = FnKind::Closure(binder, coroutine_kind, fn_decl, body);
try_visit!(vis.visit_fn(kind, *span, *id));
try_visit!(vis.visit_fn(kind, attrs, *span, *id));
visit_visitable!($($mut)? vis, fn_decl_span, fn_arg_span);
}
ExprKind::Block(block, opt_label) =>

View file

@ -317,4 +317,15 @@ impl Pinnedness {
pub fn is_pinned(self) -> bool {
matches!(self, Self::Pinned)
}
/// Returns `""` (empty string), "mut", `"pin mut "` or `"pin const "` depending
/// on the pinnedness and mutability.
pub fn prefix_str(self, mutbl: Mutability) -> &'static str {
match (self, mutbl) {
(Pinnedness::Pinned, Mutability::Mut) => "pin mut ",
(Pinnedness::Pinned, Mutability::Not) => "pin const ",
(Pinnedness::Not, Mutability::Mut) => "mut ",
(Pinnedness::Not, Mutability::Not) => "",
}
}
}

View file

@ -1771,8 +1771,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
let pat = self.lower_pat(pat);
let for_span =
self.mark_span_with_reason(DesugaringKind::ForLoop, self.lower_span(e.span), None);
let head_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None);
let pat_span = self.mark_span_with_reason(DesugaringKind::ForLoop, pat.span, None);
let for_ctxt = for_span.ctxt();
// Try to point both the head and pat spans to their position in the for loop
// rather than inside a macro.
let head_span =
head.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(head.span).with_ctxt(for_ctxt);
let pat_span =
pat.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(pat.span).with_ctxt(for_ctxt);
let loop_hir_id = self.lower_node_id(e.id);
let label = self.lower_label(opt_label, e.id, loop_hir_id);

View file

@ -125,9 +125,9 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
}
impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
/// Because we want to track parent items and so forth, enable
/// deep walking so that we walk nested items in the context of
/// their outer items.
// Because we want to track parent items and so forth, enable
// deep walking so that we walk nested items in the context of
// their outer items.
fn visit_nested_item(&mut self, item: ItemId) {
debug!("visit_nested_item: {:?}", item);

View file

@ -124,8 +124,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
PatKind::Deref(inner) => {
break hir::PatKind::Deref(self.lower_pat(inner));
}
PatKind::Ref(inner, mutbl) => {
break hir::PatKind::Ref(self.lower_pat(inner), *mutbl);
PatKind::Ref(inner, pinned, mutbl) => {
break hir::PatKind::Ref(self.lower_pat(inner), *pinned, *mutbl);
}
PatKind::Range(e1, e2, Spanned { node: end, .. }) => {
break hir::PatKind::Range(

View file

@ -68,6 +68,10 @@ ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"`
.label = `extern "{$abi}"` because of this
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
ast_passes_c_variadic_bad_naked_extern = `...` is not supported for `extern "{$abi}"` naked functions
.label = `extern "{$abi}"` because of this
.help = C-variadic function must have a compatible calling convention
ast_passes_c_variadic_must_be_unsafe =
functions with a C variable argument list must be unsafe
.suggestion = add the `unsafe` keyword to this definition

View file

@ -21,7 +21,7 @@ use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use itertools::{Either, Itertools};
use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
use rustc_abi::{CVariadicStatus, CanonAbi, ExternAbi, InterruptKind};
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
use rustc_ast::*;
use rustc_ast_pretty::pprust::{self, State};
@ -35,6 +35,7 @@ use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
PATTERNS_IN_FNS_WITHOUT_BODY,
};
use rustc_session::parse::feature_err;
use rustc_span::{Ident, Span, kw, sym};
use rustc_target::spec::{AbiMap, AbiMapping};
use thin_vec::thin_vec;
@ -661,7 +662,7 @@ impl<'a> AstValidator<'a> {
/// C-variadics must be:
/// - Non-const
/// - Either foreign, or free and `unsafe extern "C"` semantically
fn check_c_variadic_type(&self, fk: FnKind<'a>) {
fn check_c_variadic_type(&self, fk: FnKind<'a>, attrs: &'a AttrVec) {
// `...` is already rejected when it is not the final parameter.
let variadic_param = match fk.decl().inputs.last() {
Some(param) if matches!(param.ty.kind, TyKind::CVarArgs) => param,
@ -693,36 +694,92 @@ impl<'a> AstValidator<'a> {
match fn_ctxt {
FnCtxt::Foreign => return,
FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext {
Extern::Implicit(_) => {
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
span: variadic_param.span,
unsafe_span: sig.safety_span(),
});
FnCtxt::Free | FnCtxt::Assoc(_) => {
match sig.header.ext {
Extern::Implicit(_) => {
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
span: variadic_param.span,
unsafe_span: sig.safety_span(),
});
}
}
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
// Just bail if the ABI is not even recognized.
let Ok(abi) = ExternAbi::from_str(symbol_unescaped.as_str()) else {
return;
};
self.check_c_variadic_abi(abi, attrs, variadic_param.span, sig);
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
span: variadic_param.span,
unsafe_span: sig.safety_span(),
});
}
}
Extern::None => {
let err = errors::CVariadicNoExtern { span: variadic_param.span };
self.dcx().emit_err(err);
}
}
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
self.dcx().emit_err(errors::CVariadicBadExtern {
span: variadic_param.span,
abi: symbol_unescaped,
extern_span: sig.extern_span(),
});
}
}
}
fn check_c_variadic_abi(
&self,
abi: ExternAbi,
attrs: &'a AttrVec,
dotdotdot_span: Span,
sig: &FnSig,
) {
// For naked functions we accept any ABI that is accepted on c-variadic
// foreign functions, if the c_variadic_naked_functions feature is enabled.
if attr::contains_name(attrs, sym::naked) {
match abi.supports_c_variadic() {
CVariadicStatus::Stable if let ExternAbi::C { .. } = abi => {
// With `c_variadic` naked c-variadic `extern "C"` functions are allowed.
}
CVariadicStatus::Stable => {
// For e.g. aapcs or sysv64 `c_variadic_naked_functions` must also be enabled.
if !self.features.enabled(sym::c_variadic_naked_functions) {
let msg = format!("Naked c-variadic `extern {abi}` functions are unstable");
feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg)
.emit();
}
}
CVariadicStatus::Unstable { feature } => {
// Some ABIs need additional features.
if !self.features.enabled(sym::c_variadic_naked_functions) {
let msg = format!("Naked c-variadic `extern {abi}` functions are unstable");
feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg)
.emit();
}
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
span: variadic_param.span,
unsafe_span: sig.safety_span(),
});
if !self.features.enabled(feature) {
let msg = format!(
"C-variadic functions with the {abi} calling convention are unstable"
);
feature_err(&self.sess, feature, sig.span, msg).emit();
}
}
Extern::None => {
let err = errors::CVariadicNoExtern { span: variadic_param.span };
self.dcx().emit_err(err);
CVariadicStatus::NotSupported => {
// Some ABIs, e.g. `extern "Rust"`, never support c-variadic functions.
self.dcx().emit_err(errors::CVariadicBadNakedExtern {
span: dotdotdot_span,
abi: abi.as_str(),
extern_span: sig.extern_span(),
});
}
},
}
} else if !matches!(abi, ExternAbi::C { .. }) {
self.dcx().emit_err(errors::CVariadicBadExtern {
span: dotdotdot_span,
abi: abi.as_str(),
extern_span: sig.extern_span(),
});
}
}
@ -1106,7 +1163,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func);
self.visit_fn(kind, item.span, item.id);
self.visit_fn(kind, &item.attrs, item.span, item.id);
}
ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => {
let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span));
@ -1473,7 +1530,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_param_bound(self, bound)
}
fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) {
fn visit_fn(&mut self, fk: FnKind<'a>, attrs: &AttrVec, span: Span, id: NodeId) {
// Only associated `fn`s can have `self` parameters.
let self_semantic = match fk.ctxt() {
Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes,
@ -1492,7 +1549,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig);
}
self.check_c_variadic_type(fk);
self.check_c_variadic_type(fk, attrs);
// Functions cannot both be `const async` or `const gen`
if let Some(&FnHeader {
@ -1643,7 +1700,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
{
self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func);
self.visit_fn(kind, item.span, item.id);
self.visit_fn(kind, &item.attrs, item.span, item.id);
}
AssocItemKind::Type(_) => {
let disallowed = (!parent_is_const).then(|| match self.outer_trait_or_trait_impl {

View file

@ -347,7 +347,18 @@ pub(crate) struct CVariadicMustBeUnsafe {
pub(crate) struct CVariadicBadExtern {
#[primary_span]
pub span: Span,
pub abi: Symbol,
pub abi: &'static str,
#[label]
pub extern_span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_c_variadic_bad_naked_extern)]
#[help]
pub(crate) struct CVariadicBadNakedExtern {
#[primary_span]
pub span: Span,
pub abi: &'static str,
#[label]
pub extern_span: Span,
}

View file

@ -1,6 +1,5 @@
use rustc_ast as ast;
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::{NodeId, PatKind, attr, token};
use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token};
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
use rustc_session::Session;
use rustc_session::parse::{feature_err, feature_warn};
@ -392,7 +391,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
visit::walk_poly_trait_ref(self, t);
}
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) {
if let Some(_header) = fn_kind.header() {
// Stability of const fn methods are covered in `visit_assoc_item` below.
}

View file

@ -1807,8 +1807,14 @@ impl<'a> State<'a> {
self.print_pat(inner);
self.pclose();
}
PatKind::Ref(inner, mutbl) => {
PatKind::Ref(inner, pinned, mutbl) => {
self.word("&");
if pinned.is_pinned() {
self.word("pin ");
if mutbl.is_not() {
self.word("const ");
}
}
if mutbl.is_mut() {
self.word("mut ");
}

View file

@ -0,0 +1,89 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrStyle, NodeId, token};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::AttrPath;
use rustc_hir::attrs::CfgEntry;
use rustc_parse::exp;
use rustc_parse::parser::Parser;
use rustc_session::Session;
use rustc_span::{ErrorGuaranteed, Ident, Span};
use crate::parser::MetaItemOrLitParser;
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
pub enum CfgSelectPredicate {
Cfg(CfgEntry),
Wildcard(Token),
}
#[derive(Default)]
pub struct CfgSelectBranches {
/// All the conditional branches.
pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
/// The first wildcard `_ => { ... }` branch.
pub wildcard: Option<(Token, TokenStream, Span)>,
/// All branches after the first wildcard, including further wildcards.
/// These branches are kept for formatting.
pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
}
pub fn parse_cfg_select(
p: &mut Parser<'_>,
sess: &Session,
features: Option<&Features>,
lint_node_id: NodeId,
) -> Result<CfgSelectBranches, ErrorGuaranteed> {
let mut branches = CfgSelectBranches::default();
while p.token != token::Eof {
if p.eat_keyword(exp!(Underscore)) {
let underscore = p.prev_token;
p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
let span = underscore.span.to(p.token.span);
match branches.wildcard {
None => branches.wildcard = Some((underscore, tts, span)),
Some(_) => {
branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
}
}
} else {
let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints)
.map_err(|diag| diag.emit())?;
let cfg_span = meta.span();
let cfg = AttributeParser::parse_single_args(
sess,
cfg_span,
cfg_span,
AttrStyle::Inner,
AttrPath {
segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
span: cfg_span,
},
ParsedDescription::Macro,
cfg_span,
lint_node_id,
features,
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&AttributeTemplate::default(),
)?;
p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
let span = cfg_span.to(p.token.span);
match branches.wildcard {
None => branches.reachable.push((cfg, tts, span)),
Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
}
}
}
Ok(branches)
}

View file

@ -33,6 +33,7 @@ pub(crate) mod allow_unstable;
pub(crate) mod body;
pub(crate) mod cfg;
pub(crate) mod cfg_old;
pub(crate) mod cfg_select;
pub(crate) mod codegen_attrs;
pub(crate) mod confusables;
pub(crate) mod crate_level;

View file

@ -106,6 +106,7 @@ pub use attributes::cfg::{
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, parse_cfg_entry,
};
pub use attributes::cfg_old::*;
pub use attributes::cfg_select::*;
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};
pub use interface::AttributeParser;

View file

@ -209,7 +209,7 @@ pub fn check_attribute_safety(
// - Normal builtin attribute
// - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
(Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
(None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_item.path.clone(),
@ -218,15 +218,10 @@ pub fn check_attribute_safety(
// - Normal builtin attribute
// - No explicit `#[unsafe(..)]` written.
(Some(AttributeSafety::Normal), Safety::Default) => {
(None | Some(AttributeSafety::Normal), Safety::Default) => {
// OK
}
// - Non-builtin attribute
(None, Safety::Unsafe(_) | Safety::Default) => {
// OK (not checked here)
}
(
Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
Safety::Safe(..),

View file

@ -3087,6 +3087,39 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
});
explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
// Detect buffer reuse pattern
if let BorrowExplanation::UsedLater(_dropped_local, _, _, _) = explanation {
// Check all locals at the borrow location to find Vec<&T> types
for (local, local_decl) in self.body.local_decls.iter_enumerated() {
if let ty::Adt(adt_def, args) = local_decl.ty.kind()
&& self.infcx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())
&& args.len() > 0
{
let vec_inner_ty = args.type_at(0);
// Check if Vec contains references
if vec_inner_ty.is_ref() {
let local_place = local.into();
if let Some(local_name) = self.describe_place(local_place) {
err.span_label(
local_decl.source_info.span,
format!("variable `{local_name}` declared here"),
);
err.note(
format!(
"`{local_name}` is a collection that stores borrowed references, \
but {name} does not live long enough to be stored in it"
)
);
err.help(
"buffer reuse with borrowed references requires unsafe code or restructuring"
);
break;
}
}
}
}
}
}
err

View file

@ -867,11 +867,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
for (binding_span, opt_ref_pat) in finder.ref_pat_for_binding {
if let Some(ref_pat) = opt_ref_pat
&& !finder.cannot_remove.contains(&ref_pat.hir_id)
&& let hir::PatKind::Ref(subpat, mutbl) = ref_pat.kind
&& let hir::PatKind::Ref(subpat, pinned, mutbl) = ref_pat.kind
&& let Some(ref_span) = ref_pat.span.trim_end(subpat.span)
{
let pinned_str = if pinned.is_pinned() { "pinned " } else { "" };
let mutable_str = if mutbl.is_mut() { "mutable " } else { "" };
let msg = format!("consider removing the {mutable_str}borrow");
let msg = format!("consider removing the {pinned_str}{mutable_str}borrow");
suggestions.push((ref_span, msg, "".to_string()));
} else {
let msg = "consider borrowing the pattern binding".to_string();

View file

@ -213,7 +213,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
AccessKind::Mutate => {
err = self.cannot_assign(span, &(item_msg + &reason));
act = "assign";
acted_on = "written";
acted_on = "written to";
span
}
AccessKind::MutableBorrow => {
@ -518,8 +518,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
err.span_label(
span,
format!(
"`{name}` is a `{pointer_sigil}` {pointer_desc}, \
so the data it refers to cannot be {acted_on}",
"`{name}` is a `{pointer_sigil}` {pointer_desc}, so it cannot be \
{acted_on}",
),
);
@ -542,7 +542,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.expected_fn_found_fn_mut_call(&mut err, span, act);
}
PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
PlaceRef { local, projection: [.., ProjectionElem::Deref] } => {
err.span_label(span, format!("cannot {act}"));
match opt_source {
@ -559,11 +559,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
));
self.suggest_map_index_mut_alternatives(ty, &mut err, span);
}
_ => (),
_ => {
let local = &self.body.local_decls[local];
match local.local_info() {
LocalInfo::StaticRef { def_id, .. } => {
let span = self.infcx.tcx.def_span(def_id);
err.span_label(span, format!("this `static` cannot be {acted_on}"));
}
LocalInfo::ConstRef { def_id } => {
let span = self.infcx.tcx.def_span(def_id);
err.span_label(span, format!("this `const` cannot be {acted_on}"));
}
LocalInfo::BlockTailTemp(_) | LocalInfo::Boring
if !local.source_info.span.overlaps(span) =>
{
err.span_label(
local.source_info.span,
format!("this cannot be {acted_on}"),
);
}
_ => {}
}
}
}
}
_ => {
PlaceRef { local, .. } => {
let local = &self.body.local_decls[local];
if !local.source_info.span.overlaps(span) {
err.span_label(local.source_info.span, format!("this cannot be {acted_on}"));
}
err.span_label(span, format!("cannot {act}"));
}
}
@ -772,11 +797,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
&& let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
&& let node = self.infcx.tcx.hir_node(hir_id)
&& let hir::Node::LetStmt(hir::LetStmt {
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
..
})
| hir::Node::Param(Param {
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. },
..
}) = node
{
@ -1494,7 +1519,7 @@ impl<'tcx> Visitor<'tcx> for BindingFinder {
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
if let hir::Pat { kind: hir::PatKind::Ref(_, _), span, .. } = param.pat
if let hir::Pat { kind: hir::PatKind::Ref(_, _, _), span, .. } = param.pat
&& *span == self.span
{
ControlFlow::Break(param.hir_id)

View file

@ -1,7 +1,9 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_attr_parsing as attr;
use rustc_attr_parsing::{
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, ShouldEmit, parse_cfg_select,
};
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectPredicate, parse_cfg_select};
use rustc_span::{Ident, Span, sym};
use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
@ -9,11 +11,11 @@ use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
/// Selects the first arm whose predicate evaluates to true.
fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> {
for (cfg, tt, arm_span) in branches.reachable {
if attr::cfg_matches(
&cfg,
if let EvalConfigResult::True = attr::eval_config_entry(
&ecx.sess,
&cfg,
ecx.current_expansion.lint_node_id,
Some(ecx.ecfg.features),
ShouldEmit::ErrorsAndLints,
) {
return Some((tt, arm_span));
}
@ -27,37 +29,41 @@ pub(super) fn expand_cfg_select<'cx>(
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_cfg_select(&mut ecx.new_parser_from_tts(tts)) {
Ok(branches) => {
if let Some((underscore, _, _)) = branches.wildcard {
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
for (predicate, _, _) in &branches.unreachable {
let span = match predicate {
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
};
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
ecx.dcx().emit_warn(err);
ExpandResult::Ready(
match parse_cfg_select(
&mut ecx.new_parser_from_tts(tts),
ecx.sess,
Some(ecx.ecfg.features),
ecx.current_expansion.lint_node_id,
) {
Ok(branches) => {
if let Some((underscore, _, _)) = branches.wildcard {
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
for (predicate, _, _) in &branches.unreachable {
let span = match predicate {
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
};
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
ecx.dcx().emit_warn(err);
}
}
if let Some((tts, arm_span)) = select_arm(ecx, branches) {
return ExpandResult::from_tts(
ecx,
tts,
sp,
arm_span,
Ident::with_dummy_span(sym::cfg_select),
);
} else {
// Emit a compiler error when none of the predicates matched.
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
DummyResult::any(sp, guar)
}
}
if let Some((tts, arm_span)) = select_arm(ecx, branches) {
return ExpandResult::from_tts(
ecx,
tts,
sp,
arm_span,
Ident::with_dummy_span(sym::cfg_select),
);
} else {
// Emit a compiler error when none of the predicates matched.
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
DummyResult::any(sp, guar)
}
}
Err(err) => {
let guar = err.emit();
DummyResult::any(sp, guar)
}
})
Err(guar) => DummyResult::any(sp, guar),
},
)
}

View file

@ -1,4 +1,4 @@
use rustc_ast::MetaItem;
use rustc_ast::{MetaItem, Safety};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::Span;
@ -24,6 +24,8 @@ pub(crate) fn expand_deriving_copy(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand(cx, mitem, item, push);
@ -48,6 +50,8 @@ pub(crate) fn expand_deriving_const_param_ty(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand(cx, mitem, item, push);

View file

@ -1,7 +1,7 @@
use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData};
use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, Safety, VariantData};
use rustc_data_structures::fx::FxHashSet;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Ident, Span, kw, sym};
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use crate::deriving::generic::ty::*;
@ -68,6 +68,29 @@ pub(crate) fn expand_deriving_clone(
_ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
}
// If the clone method is just copying the value, also mark the type as
// `TrivialClone` to allow some library optimizations.
if is_simple {
let trivial_def = TraitDef {
span,
path: path_std!(clone::TrivialClone),
skip_path_as_bound: false,
needs_copy_as_bound_if_packed: true,
additional_bounds: bounds.clone(),
supports_unions: true,
methods: Vec::new(),
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Unsafe(DUMMY_SP),
// `TrivialClone` is not part of an API guarantee, so it shouldn't
// appear in rustdoc output.
document: false,
};
trivial_def.expand_ext(cx, mitem, item, push, true);
}
let trait_def = TraitDef {
span,
path: path_std!(clone::Clone),
@ -88,6 +111,8 @@ pub(crate) fn expand_deriving_clone(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand_ext(cx, mitem, item, push, is_simple)

View file

@ -1,4 +1,4 @@
use rustc_ast::{self as ast, MetaItem};
use rustc_ast::{self as ast, MetaItem, Safety};
use rustc_data_structures::fx::FxHashSet;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Span, sym};
@ -44,6 +44,8 @@ pub(crate) fn expand_deriving_eq(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand_ext(cx, mitem, item, push, true)
}

View file

@ -1,4 +1,4 @@
use rustc_ast::MetaItem;
use rustc_ast::{MetaItem, Safety};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Ident, Span, sym};
use thin_vec::thin_vec;
@ -35,6 +35,8 @@ pub(crate) fn expand_deriving_ord(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand(cx, mitem, item, push)

View file

@ -1,4 +1,4 @@
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability, Safety};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Span, sym};
use thin_vec::thin_vec;
@ -30,6 +30,8 @@ pub(crate) fn expand_deriving_partial_eq(
associated_types: Vec::new(),
is_const: false,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
structural_trait_def.expand(cx, mitem, item, push);
@ -59,6 +61,8 @@ pub(crate) fn expand_deriving_partial_eq(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand(cx, mitem, item, push)
}

View file

@ -1,4 +1,4 @@
use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind};
use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Ident, Span, sym};
use thin_vec::thin_vec;
@ -65,6 +65,8 @@ pub(crate) fn expand_deriving_partial_ord(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand(cx, mitem, item, push)
}

View file

@ -1,4 +1,4 @@
use rustc_ast::{self as ast, EnumDef, MetaItem};
use rustc_ast::{self as ast, EnumDef, MetaItem, Safety};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_session::config::FmtDebug;
use rustc_span::{Ident, Span, Symbol, sym};
@ -42,6 +42,8 @@ pub(crate) fn expand_deriving_debug(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand(cx, mitem, item, push)
}

View file

@ -1,8 +1,7 @@
use core::ops::ControlFlow;
use rustc_ast as ast;
use rustc_ast::visit::visit_opt;
use rustc_ast::{EnumDef, VariantData, attr};
use rustc_ast::{self as ast, EnumDef, Safety, VariantData, attr};
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
use smallvec::SmallVec;
@ -52,6 +51,8 @@ pub(crate) fn expand_deriving_default(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
trait_def.expand(cx, mitem, item, push)
}

View file

@ -1,5 +1,5 @@
use rustc_ast as ast;
use rustc_ast::{ItemKind, VariantData};
use rustc_ast::{ItemKind, Safety, VariantData};
use rustc_errors::MultiSpan;
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::{Ident, Span, kw, sym};
@ -127,6 +127,8 @@ pub(crate) fn expand_deriving_from(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
from_trait_def.expand(cx, mitem, annotatable, push);

View file

@ -225,6 +225,12 @@ pub(crate) struct TraitDef<'a> {
pub is_const: bool,
pub is_staged_api_crate: bool,
/// The safety of the `impl`.
pub safety: Safety,
/// Whether the added `impl` should appear in rustdoc output.
pub document: bool,
}
pub(crate) struct MethodDef<'a> {
@ -826,13 +832,17 @@ impl<'a> TraitDef<'a> {
)
}
if !self.document {
attrs.push(cx.attr_nested_word(sym::doc, sym::hidden, self.span));
}
cx.item(
self.span,
attrs,
ast::ItemKind::Impl(ast::Impl {
generics: trait_generics,
of_trait: Some(Box::new(ast::TraitImplHeader {
safety: ast::Safety::Default,
safety: self.safety,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
constness: if self.is_const {

View file

@ -1,4 +1,4 @@
use rustc_ast::{MetaItem, Mutability};
use rustc_ast::{MetaItem, Mutability, Safety};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Span, sym};
use thin_vec::thin_vec;
@ -42,6 +42,8 @@ pub(crate) fn expand_deriving_hash(
associated_types: Vec::new(),
is_const,
is_staged_api_crate: cx.ecfg.features.staged_api(),
safety: Safety::Default,
document: true,
};
hash_trait_def.expand(cx, mitem, item, push);

View file

@ -916,8 +916,8 @@ pub(crate) fn codegen_call_with_unwind_action(
pub(crate) fn lib_call_arg_param(tcx: TyCtxt<'_>, ty: Type, is_signed: bool) -> AbiParam {
let param = AbiParam::new(ty);
if ty.is_int() && u64::from(ty.bits()) < tcx.data_layout.pointer_size().bits() {
match (&tcx.sess.target.arch, tcx.sess.target.vendor.as_ref()) {
(Arch::X86_64, _) | (Arch::AArch64, "apple") => match (ty, is_signed) {
match (&tcx.sess.target.arch, tcx.sess.target.is_like_darwin) {
(Arch::X86_64, _) | (Arch::AArch64, true) => match (ty, is_signed) {
(types::I8 | types::I16, true) => param.sext(),
(types::I8 | types::I16, false) => param.uext(),
_ => param,

View file

@ -5,7 +5,7 @@ use crate::prelude::*;
pub(crate) fn f16_to_f32(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
let (value, arg_ty) =
if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == Arch::X86_64 {
if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == Arch::X86_64 {
(
fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value),
lib_call_arg_param(fx.tcx, types::I16, false),
@ -22,8 +22,7 @@ fn f16_to_f64(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
}
pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == Arch::X86_64
{
let ret_ty = if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == Arch::X86_64 {
types::I16
} else {
types::F16
@ -38,8 +37,7 @@ pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value
}
fn f64_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == Arch::X86_64
{
let ret_ty = if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == Arch::X86_64 {
types::I16
} else {
types::F16

View file

@ -49,7 +49,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_session::Session;
use rustc_session::config::OutputFilenames;
use rustc_span::{Symbol, sym};
use rustc_target::spec::Arch;
use rustc_target::spec::{Abi, Arch, Env, Os};
pub use crate::config::*;
use crate::prelude::*;
@ -163,15 +163,15 @@ impl CodegenBackend for CraneliftCodegenBackend {
fn target_config(&self, sess: &Session) -> TargetConfig {
// FIXME return the actually used target features. this is necessary for #[cfg(target_feature)]
let target_features = match sess.target.arch {
Arch::X86_64 if sess.target.os != "none" => {
Arch::X86_64 if sess.target.os != Os::None => {
// x86_64 mandates SSE2 support and rustc requires the x87 feature to be enabled
vec![sym::fxsr, sym::sse, sym::sse2, Symbol::intern("x87")]
}
Arch::AArch64 => match &*sess.target.os {
"none" => vec![],
Arch::AArch64 => match &sess.target.os {
Os::None => vec![],
// On macOS the aes, sha2 and sha3 features are enabled by default and ring
// fails to compile on macOS when they are not present.
"macos" => vec![sym::neon, sym::aes, sym::sha2, sym::sha3],
Os::MacOs => vec![sym::neon, sym::aes, sym::sha2, sym::sha3],
// AArch64 mandates Neon support
_ => vec![sym::neon],
},
@ -184,9 +184,9 @@ impl CodegenBackend for CraneliftCodegenBackend {
// targets due to GCC using a different ABI than LLVM. Therefore `f16` and `f128`
// won't be available when using a LLVM-built sysroot.
let has_reliable_f16_f128 = !(sess.target.arch == Arch::X86_64
&& sess.target.os == "windows"
&& sess.target.env == "gnu"
&& sess.target.abi != "llvm");
&& sess.target.os == Os::Windows
&& sess.target.env == Env::Gnu
&& sess.target.abi != Abi::Llvm);
TargetConfig {
target_features,

View file

@ -7,7 +7,7 @@
use rustc_codegen_ssa::common;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv};
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
use rustc_target::spec::Arch;
use rustc_target::spec::{Arch, Env};
use tracing::debug;
use crate::context::CodegenCx;
@ -145,7 +145,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
if cx.use_dll_storage_attrs
&& let Some(library) = tcx.native_library(instance_def_id)
&& library.kind.is_dllimport()
&& !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc")
&& !matches!(tcx.sess.target.env, Env::Gnu | Env::Uclibc)
{
llvm::set_dllimport_storage_class(llfn);
}

View file

@ -29,7 +29,7 @@ use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_symbol_mangling::mangle_internal_symbol;
use rustc_target::spec::{
Arch, HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel,
Abi, Arch, Env, HasTargetSpec, Os, RelocModel, SmallDataThresholdSupport, Target, TlsModel,
};
use smallvec::SmallVec;
@ -335,9 +335,9 @@ pub(crate) unsafe fn create_module<'ll>(
// Control Flow Guard is currently only supported by MSVC and LLVM on Windows.
if sess.target.is_like_msvc
|| (sess.target.options.os == "windows"
&& sess.target.options.env == "gnu"
&& sess.target.options.abi == "llvm")
|| (sess.target.options.os == Os::Windows
&& sess.target.options.env == Env::Gnu
&& sess.target.options.abi == Abi::Llvm)
{
match sess.opts.cg.control_flow_guard {
CFGuard::Disabled => {}
@ -669,7 +669,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
/// This corresponds to the `-fobjc-abi-version=` flag in Clang / GCC.
pub(crate) fn objc_abi_version(&self) -> u32 {
assert!(self.tcx.sess.target.is_like_darwin);
if self.tcx.sess.target.arch == Arch::X86 && self.tcx.sess.target.os == "macos" {
if self.tcx.sess.target.arch == Arch::X86 && self.tcx.sess.target.os == Os::MacOs {
// 32-bit x86 macOS uses ABI version 1 (a.k.a. the "fragile ABI").
1
} else {
@ -710,7 +710,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
},
);
if self.tcx.sess.target.env == "sim" {
if self.tcx.sess.target.env == Env::Sim {
llvm::add_module_flag_u32(
self.llmod,
llvm::ModuleFlagMergeBehavior::Error,
@ -963,7 +963,7 @@ impl<'ll> CodegenCx<'ll, '_> {
return eh_catch_typeinfo;
}
let tcx = self.tcx;
assert!(self.sess().target.os == "emscripten");
assert!(self.sess().target.os == Os::Emscripten);
let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() {
Some(def_id) => self.get_static(def_id),
_ => {

View file

@ -18,6 +18,7 @@ use rustc_middle::{bug, span_bug};
use rustc_span::{Span, Symbol, sym};
use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate};
use rustc_target::callconv::PassMode;
use rustc_target::spec::Os;
use tracing::debug;
use crate::abi::FnAbiLlvmExt;
@ -681,7 +682,7 @@ fn catch_unwind_intrinsic<'ll, 'tcx>(
codegen_msvc_try(bx, try_func, data, catch_func, dest);
} else if wants_wasm_eh(bx.sess()) {
codegen_wasm_try(bx, try_func, data, catch_func, dest);
} else if bx.sess().target.os == "emscripten" {
} else if bx.sess().target.os == Os::Emscripten {
codegen_emcc_try(bx, try_func, data, catch_func, dest);
} else {
codegen_gnu_try(bx, try_func, data, catch_func, dest);

View file

@ -15,7 +15,9 @@ use rustc_fs_util::path_to_c_string;
use rustc_middle::bug;
use rustc_session::Session;
use rustc_session::config::{PrintKind, PrintRequest};
use rustc_target::spec::{Arch, MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
use rustc_target::spec::{
Abi, Arch, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport,
};
use smallvec::{SmallVec, smallvec};
use crate::back::write::create_informational_target_machine;
@ -104,7 +106,7 @@ unsafe fn configure_llvm(sess: &Session) {
add("-wasm-enable-eh", false);
}
if sess.target.os == "emscripten"
if sess.target.os == Os::Emscripten
&& !sess.opts.unstable_opts.emscripten_wasm_eh
&& sess.panic_strategy().unwinds()
{
@ -351,9 +353,9 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig {
/// Determine whether or not experimental float types are reliable based on known bugs.
fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
let target_arch = &sess.target.arch;
let target_os = sess.target.options.os.as_ref();
let target_env = sess.target.options.env.as_ref();
let target_abi = sess.target.options.abi.as_ref();
let target_os = &sess.target.options.os;
let target_env = &sess.target.options.env;
let target_abi = &sess.target.options.abi;
let target_pointer_width = sess.target.pointer_width;
let version = get_version();
let lt_20_1_1 = version < (20, 1, 1);
@ -371,7 +373,7 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
// Selection failure <https://github.com/llvm/llvm-project/issues/50374> (fixed in llvm21)
(Arch::S390x, _) if lt_21_0_0 => false,
// MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
(Arch::X86_64, "windows") if target_env == "gnu" && target_abi != "llvm" => false,
(Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false,
// Infinite recursion <https://github.com/llvm/llvm-project/issues/97981>
(Arch::CSky, _) => false,
(Arch::Hexagon, _) if lt_21_0_0 => false, // (fixed in llvm21)
@ -403,7 +405,7 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
// not fail if our compiler-builtins is linked. (fixed in llvm21)
(Arch::X86, _) if lt_21_0_0 => false,
// MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
(Arch::X86_64, "windows") if target_env == "gnu" && target_abi != "llvm" => false,
(Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false,
// There are no known problems on other platforms, so the only requirement is that symbols
// are available. `compiler-builtins` provides all symbols required for core `f128`
// support, so this should work for everything else.
@ -424,9 +426,9 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
// (ld is 80-bit extended precision).
//
// musl does not implement the symbols required for f128 math at all.
_ if target_env == "musl" => false,
_ if *target_env == Env::Musl => false,
(Arch::X86_64, _) => false,
(_, "linux") if target_pointer_width == 64 => true,
(_, Os::Linux) if target_pointer_width == 64 => true,
_ => false,
} && cfg.has_reliable_f128;
}

View file

@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::{
};
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_target::spec::Arch;
use rustc_target::spec::{Abi, Arch};
use crate::builder::Builder;
use crate::llvm::{Type, Value};
@ -270,7 +270,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>(
// Rust does not currently support any powerpc softfloat targets.
let target = &bx.cx.tcx.sess.target;
let is_soft_float_abi = target.abi == "softfloat";
let is_soft_float_abi = target.abi == Abi::SoftFloat;
assert!(!is_soft_float_abi);
// All instances of VaArgSafe are passed directly.

View file

@ -6,7 +6,7 @@ use itertools::Itertools;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::Session;
pub(super) use rustc_target::spec::apple::OSVersion;
use rustc_target::spec::{Arch, Target};
use rustc_target::spec::{Arch, Env, Os, Target};
use tracing::debug;
use crate::errors::{XcrunError, XcrunSdkPathWarning};
@ -17,35 +17,35 @@ mod tests;
/// The canonical name of the desired SDK for a given target.
pub(super) fn sdk_name(target: &Target) -> &'static str {
match (&*target.os, &*target.env) {
("macos", "") => "MacOSX",
("ios", "") => "iPhoneOS",
("ios", "sim") => "iPhoneSimulator",
match (&target.os, &target.env) {
(Os::MacOs, Env::Unspecified) => "MacOSX",
(Os::IOs, Env::Unspecified) => "iPhoneOS",
(Os::IOs, Env::Sim) => "iPhoneSimulator",
// Mac Catalyst uses the macOS SDK
("ios", "macabi") => "MacOSX",
("tvos", "") => "AppleTVOS",
("tvos", "sim") => "AppleTVSimulator",
("visionos", "") => "XROS",
("visionos", "sim") => "XRSimulator",
("watchos", "") => "WatchOS",
("watchos", "sim") => "WatchSimulator",
(Os::IOs, Env::MacAbi) => "MacOSX",
(Os::TvOs, Env::Unspecified) => "AppleTVOS",
(Os::TvOs, Env::Sim) => "AppleTVSimulator",
(Os::VisionOs, Env::Unspecified) => "XROS",
(Os::VisionOs, Env::Sim) => "XRSimulator",
(Os::WatchOs, Env::Unspecified) => "WatchOS",
(Os::WatchOs, Env::Sim) => "WatchSimulator",
(os, abi) => unreachable!("invalid os '{os}' / abi '{abi}' combination for Apple target"),
}
}
pub(super) fn macho_platform(target: &Target) -> u32 {
match (&*target.os, &*target.env) {
("macos", _) => object::macho::PLATFORM_MACOS,
("ios", "macabi") => object::macho::PLATFORM_MACCATALYST,
("ios", "sim") => object::macho::PLATFORM_IOSSIMULATOR,
("ios", _) => object::macho::PLATFORM_IOS,
("watchos", "sim") => object::macho::PLATFORM_WATCHOSSIMULATOR,
("watchos", _) => object::macho::PLATFORM_WATCHOS,
("tvos", "sim") => object::macho::PLATFORM_TVOSSIMULATOR,
("tvos", _) => object::macho::PLATFORM_TVOS,
("visionos", "sim") => object::macho::PLATFORM_XROSSIMULATOR,
("visionos", _) => object::macho::PLATFORM_XROS,
_ => unreachable!("tried to get Mach-O platform for non-Apple target"),
match (&target.os, &target.env) {
(Os::MacOs, _) => object::macho::PLATFORM_MACOS,
(Os::IOs, Env::MacAbi) => object::macho::PLATFORM_MACCATALYST,
(Os::IOs, Env::Sim) => object::macho::PLATFORM_IOSSIMULATOR,
(Os::IOs, _) => object::macho::PLATFORM_IOS,
(Os::WatchOs, Env::Sim) => object::macho::PLATFORM_WATCHOSSIMULATOR,
(Os::WatchOs, _) => object::macho::PLATFORM_WATCHOS,
(Os::TvOs, Env::Sim) => object::macho::PLATFORM_TVOSSIMULATOR,
(Os::TvOs, _) => object::macho::PLATFORM_TVOS,
(Os::VisionOs, Env::Sim) => object::macho::PLATFORM_XROSSIMULATOR,
(Os::VisionOs, _) => object::macho::PLATFORM_XROS,
(os, env) => unreachable!("invalid os '{os}' / env '{env}' combination for Apple target"),
}
}

View file

@ -46,9 +46,9 @@ use rustc_session::{Session, filesearch};
use rustc_span::Symbol;
use rustc_target::spec::crt_objects::CrtObjects;
use rustc_target::spec::{
BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault,
LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, RelocModel, RelroLevel, SanitizerSet,
SplitDebuginfo,
Abi, BinaryFormat, Cc, Env, LinkOutputKind, LinkSelfContainedComponents,
LinkSelfContainedDefault, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, Os, RelocModel,
RelroLevel, SanitizerSet, SplitDebuginfo,
};
use tracing::{debug, info, warn};
@ -1407,11 +1407,9 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
Some(LinkerFlavorCli::Llbc) => Some(LinkerFlavor::Llbc),
Some(LinkerFlavorCli::Ptx) => Some(LinkerFlavor::Ptx),
// The linker flavors that corresponds to targets needs logic that keeps the base LinkerFlavor
_ => sess
.opts
.cg
.linker_flavor
.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)),
linker_flavor => {
linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor))
}
};
if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor, features) {
return ret;
@ -1819,7 +1817,7 @@ fn self_contained_components(
LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)),
LinkSelfContainedDefault::InferredForMingw => {
sess.host == sess.target
&& sess.target.vendor != "uwp"
&& sess.target.abi != Abi::Uwp
&& detect_self_contained_mingw(sess, linker)
}
}
@ -1845,7 +1843,7 @@ fn add_pre_link_objects(
let empty = Default::default();
let objects = if self_contained {
&opts.pre_link_objects_self_contained
} else if !(sess.target.os == "fuchsia" && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) {
} else if !(sess.target.os == Os::Fuchsia && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) {
&opts.pre_link_objects
} else {
&empty
@ -2496,7 +2494,7 @@ fn add_order_independent_options(
let apple_sdk_root = add_apple_sdk(cmd, sess, flavor);
if sess.target.os == "fuchsia"
if sess.target.os == Os::Fuchsia
&& crate_type == CrateType::Executable
&& !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
{
@ -2515,7 +2513,7 @@ fn add_order_independent_options(
cmd.no_crt_objects();
}
if sess.target.os == "emscripten" {
if sess.target.os == Os::Emscripten {
cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh {
"-fwasm-exceptions"
} else if sess.panic_strategy().unwinds() {
@ -3070,8 +3068,8 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
// `sess.target.arch` (`target_arch`) is not detailed enough.
let llvm_arch = sess.target.llvm_target.split_once('-').expect("LLVM target must have arch").0;
let target_os = &*sess.target.os;
let target_env = &*sess.target.env;
let target_os = &sess.target.os;
let target_env = &sess.target.env;
// The architecture name to forward to the linker.
//
@ -3123,12 +3121,12 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
// > - xros-simulator
// > - driverkit
let platform_name = match (target_os, target_env) {
(os, "") => os,
("ios", "macabi") => "mac-catalyst",
("ios", "sim") => "ios-simulator",
("tvos", "sim") => "tvos-simulator",
("watchos", "sim") => "watchos-simulator",
("visionos", "sim") => "visionos-simulator",
(os, Env::Unspecified) => os.desc(),
(Os::IOs, Env::MacAbi) => "mac-catalyst",
(Os::IOs, Env::Sim) => "ios-simulator",
(Os::TvOs, Env::Sim) => "tvos-simulator",
(Os::WatchOs, Env::Sim) => "watchos-simulator",
(Os::VisionOs, Env::Sim) => "visionos-simulator",
_ => bug!("invalid OS/env combination for Apple target: {target_os}, {target_env}"),
};
@ -3192,7 +3190,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
// fairly safely use `-target`. See also the following, where it is
// made explicit that the recommendation by LLVM developers is to use
// `-target`: <https://github.com/llvm/llvm-project/issues/88271>
if target_os == "macos" {
if *target_os == Os::MacOs {
// `-arch` communicates the architecture.
//
// CC forwards the `-arch` to the linker, so we use the same value

View file

@ -17,7 +17,7 @@ use rustc_middle::middle::exported_symbols::{
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
use rustc_target::spec::{Arch, Cc, LinkOutputKind, LinkerFlavor, Lld};
use rustc_target::spec::{Abi, Arch, Cc, LinkOutputKind, LinkerFlavor, Lld, Os};
use tracing::{debug, warn};
use super::command::Command;
@ -83,7 +83,7 @@ pub(crate) fn get_linker<'a>(
// To comply with the Windows App Certification Kit,
// MSVC needs to link with the Store versions of the runtime libraries (vcruntime, msvcrt, etc).
let t = &sess.target;
if matches!(flavor, LinkerFlavor::Msvc(..)) && t.vendor == "uwp" {
if matches!(flavor, LinkerFlavor::Msvc(..)) && t.abi == Abi::Uwp {
if let Some(ref tool) = msvc_tool {
let original_path = tool.path();
if let Some(root_lib_path) = original_path.ancestors().nth(4) {
@ -134,12 +134,12 @@ pub(crate) fn get_linker<'a>(
// FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction
// to the linker args construction.
assert!(cmd.get_args().is_empty() || sess.target.vendor == "uwp");
assert!(cmd.get_args().is_empty() || sess.target.abi == Abi::Uwp);
match flavor {
LinkerFlavor::Unix(Cc::No) if sess.target.os == "l4re" => {
LinkerFlavor::Unix(Cc::No) if sess.target.os == Os::L4Re => {
Box::new(L4Bender::new(cmd, sess)) as Box<dyn Linker>
}
LinkerFlavor::Unix(Cc::No) if sess.target.os == "aix" => {
LinkerFlavor::Unix(Cc::No) if sess.target.os == Os::Aix => {
Box::new(AixLinker::new(cmd, sess)) as Box<dyn Linker>
}
LinkerFlavor::WasmLld(Cc::No) => Box::new(WasmLd::new(cmd, sess)) as Box<dyn Linker>,
@ -573,7 +573,7 @@ impl<'a> Linker for GccLinker<'a> {
// any `#[link]` attributes in the `libc` crate, see #72782 for details.
// FIXME: Switch to using `#[link]` attributes in the `libc` crate
// similarly to other targets.
if self.sess.target.os == "vxworks"
if self.sess.target.os == Os::VxWorks
&& matches!(
output_kind,
LinkOutputKind::StaticNoPicExe
@ -595,7 +595,7 @@ impl<'a> Linker for GccLinker<'a> {
}
fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool) {
if self.sess.target.os == "illumos" && name == "c" {
if self.sess.target.os == Os::Illumos && name == "c" {
// libc will be added via late_link_args on illumos so that it will
// appear last in the library search order.
// FIXME: This should be replaced by a more complete and generic
@ -1439,7 +1439,7 @@ impl<'a> Linker for WasmLd<'a> {
// symbols explicitly passed via the `--export` flags above and hides all
// others. Various bits and pieces of wasm32-unknown-unknown tooling use
// this, so be sure these symbols make their way out of the linker as well.
if self.sess.target.os == "unknown" || self.sess.target.os == "none" {
if matches!(self.sess.target.os, Os::Unknown | Os::None) {
self.link_args(&["--export=__heap_base", "--export=__data_end"]);
}
}

View file

@ -20,7 +20,7 @@ use rustc_metadata::fs::METADATA_FILENAME;
use rustc_middle::bug;
use rustc_session::Session;
use rustc_span::sym;
use rustc_target::spec::{RelocModel, Target, ef_avr_arch};
use rustc_target::spec::{Abi, Os, RelocModel, Target, ef_avr_arch};
use tracing::debug;
use super::apple;
@ -260,10 +260,10 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
}
pub(super) fn elf_os_abi(sess: &Session) -> u8 {
match sess.target.options.os.as_ref() {
"hermit" => elf::ELFOSABI_STANDALONE,
"freebsd" => elf::ELFOSABI_FREEBSD,
"solaris" => elf::ELFOSABI_SOLARIS,
match sess.target.options.os {
Os::Hermit => elf::ELFOSABI_STANDALONE,
Os::FreeBsd => elf::ELFOSABI_FREEBSD,
Os::Solaris => elf::ELFOSABI_SOLARIS,
_ => elf::ELFOSABI_NONE,
}
}
@ -379,11 +379,11 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
}
}
Architecture::Csky => {
let e_flags = match sess.target.options.abi.as_ref() {
"abiv2" => elf::EF_CSKY_ABIV2,
_ => elf::EF_CSKY_ABIV1,
};
e_flags
if matches!(sess.target.options.abi, Abi::AbiV2) {
elf::EF_CSKY_ABIV2
} else {
elf::EF_CSKY_ABIV1
}
}
Architecture::PowerPc64 => {
const EF_PPC64_ABI_UNKNOWN: u32 = 0;

View file

@ -17,7 +17,7 @@ use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Instance, SymbolNam
use rustc_middle::util::Providers;
use rustc_session::config::{CrateType, OomStrategy};
use rustc_symbol_mangling::mangle_internal_symbol;
use rustc_target::spec::{Arch, TlsModel};
use rustc_target::spec::{Arch, Os, TlsModel};
use tracing::debug;
use crate::back::symbol_export;
@ -711,7 +711,7 @@ pub(crate) fn extend_exported_symbols<'tcx>(
) {
let (callconv, _) = calling_convention_for_symbol(tcx, symbol);
if callconv != CanonAbi::GpuKernel || tcx.sess.target.os != "amdhsa" {
if callconv != CanonAbi::GpuKernel || tcx.sess.target.os != Os::AmdHsa {
return;
}

View file

@ -33,7 +33,7 @@ use rustc_session::Session;
use rustc_session::config::{self, CrateType, EntryFnType};
use rustc_span::{DUMMY_SP, Symbol, sym};
use rustc_symbol_mangling::mangle_internal_symbol;
use rustc_target::spec::Arch;
use rustc_target::spec::{Arch, Os};
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
use tracing::{debug, info};
@ -366,7 +366,7 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// us
pub fn wants_wasm_eh(sess: &Session) -> bool {
sess.target.is_like_wasm
&& (sess.target.os != "emscripten" || sess.opts.unstable_opts.emscripten_wasm_eh)
&& (sess.target.os != Os::Emscripten || sess.opts.unstable_opts.emscripten_wasm_eh)
}
/// Returns `true` if this session's target will use SEH-based unwinding.
@ -500,7 +500,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
) -> Bx::Function {
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`, or
// `usize efi_main(void *handle, void *system_table)` depending on the target.
let llfty = if cx.sess().target.os.contains("uefi") {
let llfty = if cx.sess().target.os == Os::Uefi {
cx.type_func(&[cx.type_ptr(), cx.type_ptr()], cx.type_isize())
} else if cx.sess().target.main_needs_argc_argv {
cx.type_func(&[cx.type_int(), cx.type_ptr()], cx.type_int())
@ -562,7 +562,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
};
let result = bx.call(start_ty, None, None, start_fn, &args, None, instance);
if cx.sess().target.os.contains("uefi") {
if cx.sess().target.os == Os::Uefi {
bx.ret(result);
} else {
let cast = bx.intcast(result, cx.type_int(), true);
@ -576,7 +576,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
/// Obtain the `argc` and `argv` values to pass to the rust start function
/// (i.e., the "start" lang item).
fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx) -> (Bx::Value, Bx::Value) {
if bx.cx().sess().target.os.contains("uefi") {
if bx.cx().sess().target.os == Os::Uefi {
// Params for UEFI
let param_handle = bx.get_param(0);
let param_system_table = bx.get_param(1);

View file

@ -16,6 +16,7 @@ use rustc_middle::ty::{self as ty, TyCtxt};
use rustc_session::lint;
use rustc_session::parse::feature_err;
use rustc_span::{Ident, Span, sym};
use rustc_target::spec::Os;
use crate::errors;
use crate::target_features::{
@ -258,7 +259,7 @@ fn process_builtin_attrs(
UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER,
UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER,
UsedBy::Default => {
let used_form = if tcx.sess.target.os == "illumos" {
let used_form = if tcx.sess.target.os == Os::Illumos {
// illumos' `ld` doesn't support a section header that would represent
// `#[used(linker)]`, see
// https://github.com/rust-lang/rust/issues/146169. For that target,

View file

@ -7,7 +7,7 @@ use rustc_middle::ty::{self, Instance, TyCtxt};
use rustc_middle::{bug, mir, span_bug};
use rustc_session::cstore::{DllCallingConvention, DllImport};
use rustc_span::Span;
use rustc_target::spec::Target;
use rustc_target::spec::{Abi, Env, Os, Target};
use crate::traits::*;
@ -171,7 +171,7 @@ pub fn asm_const_to_str<'tcx>(
}
pub fn is_mingw_gnu_toolchain(target: &Target) -> bool {
target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty()
target.os == Os::Windows && target.env == Env::Gnu && target.abi == Abi::Unspecified
}
pub fn i686_decorated_name(

View file

@ -53,13 +53,13 @@ impl !DynSend for std::env::VarsOs {}
macro_rules! already_send {
($([$ty: ty])*) => {
$(unsafe impl DynSend for $ty where $ty: Send {})*
$(unsafe impl DynSend for $ty where Self: Send {})*
};
}
// These structures are already `Send`.
already_send!(
[std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File]
[std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File][std::panic::Location<'_>]
[rustc_arena::DroplessArena][jobserver_crate::Client][jobserver_crate::HelperThread]
[crate::memmap::Mmap][crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice]
);
@ -127,14 +127,14 @@ impl !DynSync for std::env::VarsOs {}
macro_rules! already_sync {
($([$ty: ty])*) => {
$(unsafe impl DynSync for $ty where $ty: Sync {})*
$(unsafe impl DynSync for $ty where Self: Sync {})*
};
}
// These structures are already `Sync`.
already_sync!(
[std::sync::atomic::AtomicBool][std::sync::atomic::AtomicUsize][std::sync::atomic::AtomicU8]
[std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File]
[std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File][std::panic::Location<'_>]
[jobserver_crate::Client][jobserver_crate::HelperThread][crate::memmap::Mmap]
[crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice]
);

View file

@ -1,5 +1,5 @@
use crate::stable_hasher::{HashStable, StableHasher};
use crate::sync::{MappedReadGuard, ReadGuard, RwLock};
use crate::sync::{MappedReadGuard, MappedWriteGuard, ReadGuard, RwLock, WriteGuard};
/// The `Steal` struct is intended to used as the value for a query.
/// Specifically, we sometimes have queries (*cough* MIR *cough*)
@ -40,9 +40,17 @@ impl<T> Steal<T> {
ReadGuard::map(borrow, |opt| opt.as_ref().unwrap())
}
/// An escape hatch for rustc drivers to mutate `Steal` caches.
///
/// Use at your own risk. This can badly break incremental compilation
/// and anything else that relies on the immutability of query caches.
#[track_caller]
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut().as_mut().expect("attempt to read from stolen value")
pub fn risky_hack_borrow_mut(&self) -> MappedWriteGuard<'_, T> {
let borrow = self.value.borrow_mut();
if borrow.is_none() {
panic!("attempted to read from stolen value: {}", std::any::type_name::<T>());
}
WriteGuard::map(borrow, |opt| opt.as_mut().unwrap())
}
#[track_caller]

View file

@ -152,7 +152,8 @@ pub(super) fn install() {
libc::sigaltstack(&alt_stack, ptr::null_mut());
let mut sa: libc::sigaction = mem::zeroed();
sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
sa.sa_sigaction =
print_stack_trace as unsafe extern "C" fn(libc::c_int) as libc::sighandler_t;
sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
libc::sigemptyset(&mut sa.sa_mask);
for (signum, _signame) in KILL_SIGNALS {

View file

@ -1704,7 +1704,7 @@ impl HumanEmitter {
}
// print out the span location and spacer before we print the annotated source
// to do this, we need to know if this span will be primary
let is_primary = primary_lo.file.name == annotated_file.file.name;
let is_primary = primary_lo.file.stable_id == annotated_file.file.stable_id;
if is_primary {
let loc = primary_lo.clone();
if !self.short_message {
@ -2322,11 +2322,6 @@ impl HumanEmitter {
show_code_change
{
for part in parts {
let snippet = if let Ok(snippet) = sm.span_to_snippet(part.span) {
snippet
} else {
String::new()
};
let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display;
let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display;
@ -2402,7 +2397,7 @@ impl HumanEmitter {
// LL - REMOVED <- row_num - 2 - (newlines - first_i - 1)
// LL + NEWER
// | <- row_num
let snippet = sm.span_to_snippet(part.span).unwrap_or_default();
let newlines = snippet.lines().count();
if newlines > 0 && row_num > newlines {
// Account for removals where the part being removed spans multiple
@ -3108,7 +3103,7 @@ impl FileWithAnnotatedLines {
) {
for slot in file_vec.iter_mut() {
// Look through each of our files for the one we're adding to
if slot.file.name == file.name {
if slot.file.stable_id == file.stable_id {
// See if we already have a line for it
for line_slot in &mut slot.lines {
if line_slot.line_index == line_index {
@ -3471,14 +3466,9 @@ impl Drop for Buffy {
pub fn stderr_destination(color: ColorConfig) -> Destination {
let buffer_writer = std::io::stderr();
let choice = color.to_color_choice();
// We need to resolve `ColorChoice::Auto` before `Box`ing since
// `ColorChoice::Auto` on `dyn Write` will always resolve to `Never`
let choice = if matches!(choice, ColorChoice::Auto) {
AutoStream::choice(&buffer_writer)
} else {
choice
};
let choice = get_stderr_color_choice(color, &buffer_writer);
// On Windows we'll be performing global synchronization on the entire
// system for emitting rustc errors, so there's no need to buffer
// anything.
@ -3493,6 +3483,11 @@ pub fn stderr_destination(color: ColorConfig) -> Destination {
}
}
pub fn get_stderr_color_choice(color: ColorConfig, stderr: &std::io::Stderr) -> ColorChoice {
let choice = color.to_color_choice();
if matches!(choice, ColorChoice::Auto) { AutoStream::choice(stderr) } else { choice }
}
/// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead.
///
/// See #36178.

View file

@ -886,9 +886,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
}
} else if let SyntaxExtensionKind::NonMacroAttr = ext {
if let ast::Safety::Unsafe(span) = attr.get_normal_item().unsafety {
self.cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
}
// `-Zmacro-stats` ignores these because they don't do any real expansion.
self.cx.expanded_inert_attrs.mark(&attr);
item.visit_attrs(|attrs| attrs.insert(pos, attr));

View file

@ -749,7 +749,7 @@ impl server::Span for Rustc<'_, '_> {
let self_loc = self.psess().source_map().lookup_char_pos(first.lo());
let other_loc = self.psess().source_map().lookup_char_pos(second.lo());
if self_loc.file.name != other_loc.file.name {
if self_loc.file.stable_id != other_loc.file.stable_id {
return None;
}

View file

@ -410,6 +410,9 @@ declare_features! (
(unstable, avx10_target_feature, "1.88.0", Some(138843)),
/// Allows using C-variadics.
(unstable, c_variadic, "1.34.0", Some(44930)),
/// Allows defining c-variadic naked functions with any extern ABI that is allowed
/// on c-variadic foreign functions.
(unstable, c_variadic_naked_functions, "CURRENT_RUSTC_VERSION", Some(148767)),
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
(unstable, cfg_contract_checks, "1.86.0", Some(128044)),
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.

View file

@ -194,6 +194,18 @@ pub enum CfgEntry {
Version(Option<RustcVersion>, Span),
}
impl CfgEntry {
pub fn span(&self) -> Span {
let (CfgEntry::All(_, span)
| CfgEntry::Any(_, span)
| CfgEntry::Not(_, span)
| CfgEntry::Bool(_, span)
| CfgEntry::NameValue { span, .. }
| CfgEntry::Version(_, span)) = self;
*span
}
}
/// Possible values for the `#[linkage]` attribute, allowing to specify the
/// linkage type for a `MonoItem`.
///

View file

@ -1722,7 +1722,9 @@ impl<'hir> Pat<'hir> {
match self.kind {
Missing => unreachable!(),
Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true,
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it),
Box(s) | Deref(s) | Ref(s, _, _) | Binding(.., Some(s)) | Guard(s, _) => {
s.walk_short_(it)
}
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
Slice(before, slice, after) => {
@ -1749,7 +1751,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*;
match self.kind {
Missing | Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {}
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
Box(s) | Deref(s) | Ref(s, _, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
Slice(before, slice, after) => {
@ -1938,7 +1940,7 @@ pub enum PatKind<'hir> {
Deref(&'hir Pat<'hir>),
/// A reference pattern (e.g., `&mut (a, b)`).
Ref(&'hir Pat<'hir>, Mutability),
Ref(&'hir Pat<'hir>, Pinnedness, Mutability),
/// A literal, const block or path.
Expr(&'hir PatExpr<'hir>),

View file

@ -752,7 +752,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
}
PatKind::Box(ref subpattern)
| PatKind::Deref(ref subpattern)
| PatKind::Ref(ref subpattern, _) => {
| PatKind::Ref(ref subpattern, _, _) => {
try_visit!(visitor.visit_pat(subpattern));
}
PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => {

View file

@ -176,6 +176,7 @@ language_item_table! {
Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
UseCloned, sym::use_cloned, use_cloned_trait, Target::Trait, GenericRequirement::None;
TrivialClone, sym::trivial_clone, trivial_clone_trait, Target::Trait, GenericRequirement::None;
Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
/// The associated item of the `DiscriminantKind` trait.

View file

@ -516,12 +516,10 @@ fn resolve_local<'tcx>(
if let Some(pat) = pat {
if is_binding_pat(pat) {
visitor.scope_tree.record_rvalue_candidate(
expr.hir_id,
RvalueCandidate {
target: expr.hir_id.local_id,
lifetime: visitor.cx.var_parent,
},
record_subexpr_extended_temp_scopes(
&mut visitor.scope_tree,
expr,
visitor.cx.var_parent,
);
}
}
@ -593,7 +591,7 @@ fn resolve_local<'tcx>(
is_binding_pat(subpat)
}
PatKind::Ref(_, _)
PatKind::Ref(_, _, _)
| PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..)
| PatKind::Missing
| PatKind::Wild
@ -604,7 +602,7 @@ fn resolve_local<'tcx>(
}
}
/// If `expr` matches the `E&` grammar, then records an extended rvalue scope as appropriate:
/// If `expr` matches the `E&` grammar, then records an extended temporary scope as appropriate:
///
/// ```text
/// E& = & ET
@ -627,10 +625,7 @@ fn resolve_local<'tcx>(
match expr.kind {
hir::ExprKind::AddrOf(_, _, subexpr) => {
record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
visitor.scope_tree.record_rvalue_candidate(
subexpr.hir_id,
RvalueCandidate { target: subexpr.hir_id.local_id, lifetime: blk_id },
);
record_subexpr_extended_temp_scopes(&mut visitor.scope_tree, subexpr, blk_id);
}
hir::ExprKind::Struct(_, fields, _) => {
for field in fields {
@ -687,6 +682,53 @@ fn resolve_local<'tcx>(
}
}
/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by
/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that
/// case, the "temporary lifetime" of `expr` is extended to be the block enclosing the `let`
/// statement.
///
/// More formally, if `expr` matches the grammar `ET`, record the temporary scope of the matching
/// `<rvalue>` as `lifetime`:
///
/// ```text
/// ET = *ET
/// | ET[...]
/// | ET.f
/// | (ET)
/// | <rvalue>
/// ```
///
/// Note: ET is intended to match "rvalues or places based on rvalues".
fn record_subexpr_extended_temp_scopes(
scope_tree: &mut ScopeTree,
mut expr: &hir::Expr<'_>,
lifetime: Option<Scope>,
) {
debug!(?expr, ?lifetime);
loop {
// Note: give all the expressions matching `ET` with the
// extended temporary lifetime, not just the innermost rvalue,
// because in MIR building if we must compile e.g., `*rvalue()`
// into a temporary, we request the temporary scope of the
// outer expression.
scope_tree.record_extended_temp_scope(expr.hir_id.local_id, lifetime);
match expr.kind {
hir::ExprKind::AddrOf(_, _, subexpr)
| hir::ExprKind::Unary(hir::UnOp::Deref, subexpr)
| hir::ExprKind::Field(subexpr, _)
| hir::ExprKind::Index(subexpr, _, _) => {
expr = subexpr;
}
_ => {
return;
}
}
}
}
impl<'tcx> ScopeResolutionVisitor<'tcx> {
/// Records the current parent (if any) as the parent of `child_scope`.
fn record_child_scope(&mut self, child_scope: Scope) {

View file

@ -1487,11 +1487,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
match assoc_tag {
// Don't attempt to look up inherent associated types when the feature is not
// enabled. Theoretically it'd be fine to do so since we feature-gate their
// definition site. However, due to current limitations of the implementation
// (caused by us performing selection during HIR ty lowering instead of in the
// trait solver), IATs can lead to cycle errors (#108491) which mask the
// feature-gate error, needlessly confusing users who use IATs by accident
// (#113265).
// definition site. However, the current implementation of inherent associated
// items is somewhat brittle, so let's not run it by default.
ty::AssocTag::Type => return Ok(None),
ty::AssocTag::Const => {
// We also gate the mgca codepath for type-level uses of inherent consts
@ -1520,9 +1517,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
.collect();
// At the moment, we actually bail out with a hard error if the selection of an inherent
// associated item fails (see below). This means we never consider trait associated items
// as potential fallback candidates (#142006). To temporarily mask that issue, let's not
// select at all if there are no early inherent candidates.
if candidates.is_empty() {
return Ok(None);
}
let (applicable_candidates, fulfillment_errors) =
self.select_inherent_assoc_candidates(span, self_ty, candidates.clone());
// FIXME(#142006): Don't eagerly error here, there might be applicable trait candidates.
let InherentAssocCandidate { impl_, assoc_item, scope: def_scope } =
match &applicable_candidates[..] {
&[] => Err(self.report_unresolved_inherent_assoc_item(
@ -1543,6 +1549,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
)),
}?;
// FIXME(#142006): Don't eagerly validate here, there might be trait candidates that are
// accessible (visible and stable) contrary to the inherent candidate.
self.check_assoc_item(assoc_item, name, def_scope, block, span);
// FIXME(fmease): Currently creating throwaway `parent_args` to please

View file

@ -44,7 +44,7 @@ impl<'a> fmt::Debug for VarianceTerm<'a> {
}
}
/// The first pass over the crate simply builds up the set of inferreds.
// The first pass over the crate simply builds up the set of inferreds.
pub(crate) struct TermsContext<'a, 'tcx> {
pub tcx: TyCtxt<'tcx>,

View file

@ -2028,9 +2028,15 @@ impl<'a> State<'a> {
self.print_pat(inner);
self.pclose();
}
PatKind::Ref(inner, mutbl) => {
PatKind::Ref(inner, pinned, mutbl) => {
let is_range_inner = matches!(inner.kind, PatKind::Range(..));
self.word("&");
if pinned.is_pinned() {
self.word("pin ");
if mutbl.is_not() {
self.word("const ");
}
}
self.word(mutbl.prefix_str());
if is_range_inner {
self.popen();

View file

@ -523,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| hir::PatKind::TupleStruct(_, _, _)
| hir::PatKind::Tuple(_, _)
| hir::PatKind::Box(_)
| hir::PatKind::Ref(_, _)
| hir::PatKind::Ref(_, _, _)
| hir::PatKind::Deref(_)
| hir::PatKind::Expr(_)
| hir::PatKind::Range(_, _, _)

View file

@ -1231,7 +1231,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
return Ok(first_adjust.source);
}
} else if let PatKind::Ref(subpat, _) = pat.kind
} else if let PatKind::Ref(subpat, _, _) = pat.kind
&& self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id)
{
return self.pat_ty_adjusted(subpat);
@ -1817,13 +1817,13 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.cat_pattern(place_with_id, subpat, op)?;
}
PatKind::Ref(subpat, _)
PatKind::Ref(subpat, _, _)
if self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) =>
{
self.cat_pattern(place_with_id, subpat, op)?;
}
PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
PatKind::Box(subpat) | PatKind::Ref(subpat, _, _) => {
// box p1, &p1, &mut p1. we can ignore the mutability of
// PatKind::Ref since that information is already contained
// in the type.

View file

@ -40,7 +40,7 @@ use tracing::{debug, instrument};
use crate::callee::{self, DeferredCallResolution};
use crate::errors::{self, CtorIsPrivate};
use crate::method::{self, MethodCallee};
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, rvalue_scopes};
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Produces warning on the given node, if the current point in the
@ -604,13 +604,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.normalize(span, field.ty(self.tcx, args))
}
pub(crate) fn resolve_rvalue_scopes(&self, def_id: DefId) {
let scope_tree = self.tcx.region_scope_tree(def_id);
let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, scope_tree, def_id) };
let mut typeck_results = self.typeck_results.borrow_mut();
typeck_results.rvalue_scopes = rvalue_scopes;
}
/// Drain all obligations that are stalled on coroutines defined in this body.
#[instrument(level = "debug", skip(self))]
pub(crate) fn drain_stalled_coroutine_obligations(&self) {

View file

@ -37,7 +37,6 @@ mod op;
mod opaque_types;
mod pat;
mod place_op;
mod rvalue_scopes;
mod typeck_root_ctxt;
mod upvar;
mod writeback;
@ -237,9 +236,6 @@ fn typeck_with_inspect<'tcx>(
// because they don't constrain other type variables.
fcx.closure_analyze(body);
assert!(fcx.deferred_call_resolutions.borrow().is_empty());
// Before the coroutine analysis, temporary scopes shall be marked to provide more
// precise information on types to be captured.
fcx.resolve_rvalue_scopes(def_id.to_def_id());
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
let ty = fcx.normalize(span, ty);

View file

@ -1079,6 +1079,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
self.suggest_bounds_for_range_to_method(&mut err, source, item_ident);
err.emit()
}
@ -2610,7 +2611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut current_node = parent_node;
while let Node::Pat(parent_pat) = current_node {
if let hir::PatKind::Ref(_, mutability) = parent_pat.kind {
if let hir::PatKind::Ref(_, _, mutability) = parent_pat.kind {
ref_muts.push(mutability);
current_node = self.tcx.parent_hir_node(parent_pat.hir_id);
} else {
@ -3260,6 +3261,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
fn suggest_bounds_for_range_to_method(
&self,
err: &mut Diag<'_>,
source: SelfSource<'tcx>,
item_ident: Ident,
) {
let SelfSource::MethodCall(rcvr_expr) = source else { return };
let hir::ExprKind::Struct(qpath, fields, _) = rcvr_expr.kind else { return };
let Some(lang_item) = self.tcx.qpath_lang_item(*qpath) else {
return;
};
let is_inclusive = match lang_item {
hir::LangItem::RangeTo => false,
hir::LangItem::RangeToInclusive | hir::LangItem::RangeInclusiveCopy => true,
_ => return,
};
let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) else { return };
let Some(_) = self
.tcx
.associated_items(iterator_trait)
.filter_by_name_unhygienic(item_ident.name)
.next()
else {
return;
};
let source_map = self.tcx.sess.source_map();
let range_type = if is_inclusive { "RangeInclusive" } else { "Range" };
let Some(end_field) = fields.iter().find(|f| f.ident.name == rustc_span::sym::end) else {
return;
};
let element_ty = self.typeck_results.borrow().expr_ty_opt(end_field.expr);
let is_integral = element_ty.is_some_and(|ty| ty.is_integral());
let end_is_negative = is_integral
&& matches!(end_field.expr.kind, hir::ExprKind::Unary(rustc_ast::UnOp::Neg, _));
let Ok(snippet) = source_map.span_to_snippet(rcvr_expr.span) else { return };
let offset = snippet
.chars()
.take_while(|&c| c == '(' || c.is_whitespace())
.map(|c| c.len_utf8())
.sum::<usize>();
let insert_span = rcvr_expr
.span
.with_lo(rcvr_expr.span.lo() + rustc_span::BytePos(offset as u32))
.shrink_to_lo();
let (value, appl) = if is_integral && !end_is_negative {
("0", Applicability::MachineApplicable)
} else {
("/* start */", Applicability::HasPlaceholders)
};
err.span_suggestion_verbose(
insert_span,
format!("consider using a bounded `{range_type}` by adding a concrete starting value"),
value,
appl,
);
}
/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
@ -4212,7 +4278,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err: &mut Diag<'_>,
item_def_id: DefId,
hir_id: hir::HirId,
rcvr_ty: Option<Ty<'_>>,
rcvr_ty: Option<Ty<'tcx>>,
) -> bool {
let hir_id = self.tcx.parent_hir_id(hir_id);
let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false };
@ -4223,49 +4289,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !self.tcx.is_trait(trait_def_id) {
return false;
}
let krate = self.tcx.crate_name(trait_def_id.krate);
let name = self.tcx.item_name(trait_def_id);
let candidates: Vec<_> = traits
.iter()
.filter(|c| {
c.def_id.krate != trait_def_id.krate
&& self.tcx.crate_name(c.def_id.krate) == krate
&& self.tcx.item_name(c.def_id) == name
})
.map(|c| (c.def_id, c.import_ids.get(0).cloned()))
.collect();
if candidates.is_empty() {
let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else {
return false;
}
let item_span = self.tcx.def_span(item_def_id);
let msg = format!(
"there are multiple different versions of crate `{krate}` in the dependency graph",
);
let trait_span = self.tcx.def_span(trait_def_id);
let mut multi_span: MultiSpan = trait_span.into();
multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string());
let descr = self.tcx.associated_item(item_def_id).descr();
let rcvr_ty =
rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string());
multi_span
.push_span_label(item_span, format!("the {descr} is available for {rcvr_ty} here"));
for (def_id, import_def_id) in candidates {
if let Some(import_def_id) = import_def_id {
multi_span.push_span_label(
self.tcx.def_span(import_def_id),
format!(
"`{name}` imported here doesn't correspond to the right version of crate \
`{krate}`",
),
);
}
multi_span.push_span_label(
self.tcx.def_span(def_id),
"this is the trait that was imported".to_string(),
);
}
err.span_note(multi_span, msg);
true
};
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter());
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
trait_ref,
polarity: ty::PredicatePolarity::Positive,
});
let obligation = Obligation::new(self.tcx, self.misc(rcvr.span), self.param_env, trait_ref);
self.err_ctxt().note_different_trait_with_same_name(err, &obligation, trait_pred)
}
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`

View file

@ -91,6 +91,7 @@ struct TopInfo<'tcx> {
#[derive(Copy, Clone)]
struct PatInfo<'tcx> {
binding_mode: ByRef,
max_pinnedness: PinnednessCap,
max_ref_mutbl: MutblCap,
top_info: TopInfo<'tcx>,
decl_origin: Option<DeclOrigin<'tcx>>,
@ -241,6 +242,19 @@ impl MutblCap {
}
}
/// `ref` or `ref mut` bindings (not pinned, explicitly or match-ergonomics) are only allowed behind
/// an `&pin` reference if the binding's type is `Unpin`.
///
/// Normally, the borrow checker enforces this (not implemented yet), but we track it here for better
/// diagnostics.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum PinnednessCap {
/// No restriction on pinnedness.
Not,
/// Pinnedness restricted to pinned.
Pinned,
}
/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references?
///
/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e.
@ -374,6 +388,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let top_info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id };
let pat_info = PatInfo {
binding_mode: ByRef::No,
max_pinnedness: PinnednessCap::Not,
max_ref_mutbl: MutblCap::Mut,
top_info,
decl_origin,
@ -489,22 +504,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let old_pat_info = pat_info;
let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info };
let adjust_binding_mode = |inner_pinnedness, inner_mutability| {
match pat_info.binding_mode {
// If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const`
// or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or
// `&pin mut`).
ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability),
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
// Pinnedness is preserved.
ByRef::Yes(pinnedness, Mutability::Mut) => ByRef::Yes(pinnedness, inner_mutability),
// Once a `ref`, always a `ref`.
// This is because a `& &mut` cannot mutate the underlying value.
// Pinnedness is preserved.
ByRef::Yes(pinnedness, Mutability::Not) => ByRef::Yes(pinnedness, Mutability::Not),
}
};
match pat.kind {
// Peel off a `&` or `&mut`from the scrutinee type. See the examples in
// `tests/ui/rfcs/rfc-2005-default-binding-mode`.
@ -524,19 +523,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.or_default()
.push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected });
let mut binding_mode = adjust_binding_mode(Pinnedness::Not, inner_mutability);
let mut max_ref_mutbl = pat_info.max_ref_mutbl;
if self.downgrade_mut_inside_shared() {
binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
}
if matches!(binding_mode, ByRef::Yes(_, Mutability::Not)) {
max_ref_mutbl = MutblCap::Not;
}
debug!("default binding mode is now {:?}", binding_mode);
// Use the old pat info to keep `current_depth` to its old value.
let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info };
let new_pat_info =
self.adjust_pat_info(Pinnedness::Not, inner_mutability, old_pat_info);
// Recurse with the new expected type.
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
}
@ -569,20 +559,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
}
let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability);
// If the pinnedness is `Not`, it means the pattern is unpinned
// and thus requires an `Unpin` bound.
if matches!(binding_mode, ByRef::Yes(Pinnedness::Not, _)) {
self.register_bound(
inner_ty,
self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span),
self.misc(pat.span),
)
}
debug!("default binding mode is now {:?}", binding_mode);
// Use the old pat info to keep `current_depth` to its old value.
let new_pat_info = PatInfo { binding_mode, ..old_pat_info };
let new_pat_info =
self.adjust_pat_info(Pinnedness::Pinned, inner_mutability, old_pat_info);
self.check_deref_pattern(
pat,
@ -689,13 +668,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
PatKind::Ref(inner, pinned, mutbl) => {
self.check_pat_ref(pat, inner, pinned, mutbl, expected, pat_info)
}
PatKind::Slice(before, slice, after) => {
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
}
}
}
fn adjust_pat_info(
&self,
inner_pinnedness: Pinnedness,
inner_mutability: Mutability,
pat_info: PatInfo<'tcx>,
) -> PatInfo<'tcx> {
let mut binding_mode = match pat_info.binding_mode {
// If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const`
// or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or
// `&pin mut`).
ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability),
ByRef::Yes(pinnedness, mutability) => {
let pinnedness = match pinnedness {
// When `ref`, stay a `ref` (on `&`) or downgrade to `ref pin` (on `&pin`).
Pinnedness::Not => inner_pinnedness,
// When `ref pin`, stay a `ref pin`.
// This is because we cannot get an `&mut T` from `&mut &pin mut T` unless `T: Unpin`.
// Note that `&T` and `&mut T` are `Unpin`, which implies
// `& &pin const T` <-> `&pin const &T` and `&mut &pin mut T` <-> `&pin mut &mut T`
// (i.e. mutually coercible).
Pinnedness::Pinned => Pinnedness::Pinned,
};
let mutability = match mutability {
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
Mutability::Mut => inner_mutability,
// Once a `ref`, always a `ref`.
// This is because a `& &mut` cannot mutate the underlying value.
Mutability::Not => Mutability::Not,
};
ByRef::Yes(pinnedness, mutability)
}
};
let PatInfo { mut max_ref_mutbl, mut max_pinnedness, .. } = pat_info;
if self.downgrade_mut_inside_shared() {
binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
}
match binding_mode {
ByRef::Yes(_, Mutability::Not) => max_ref_mutbl = MutblCap::Not,
ByRef::Yes(Pinnedness::Pinned, _) => max_pinnedness = PinnednessCap::Pinned,
_ => {}
}
debug!("default binding mode is now {:?}", binding_mode);
PatInfo { binding_mode, max_pinnedness, max_ref_mutbl, ..pat_info }
}
fn check_deref_pattern(
&self,
pat: &'tcx Pat<'tcx>,
@ -1195,6 +1223,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
// If there exists a pinned reference in the pattern but the binding is not pinned,
// it means the binding is unpinned and thus requires an `Unpin` bound.
if pat_info.max_pinnedness == PinnednessCap::Pinned
&& matches!(bm.0, ByRef::Yes(Pinnedness::Not, _))
{
self.register_bound(
expected,
self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span),
self.misc(pat.span),
)
}
if matches!(bm.0, ByRef::Yes(_, Mutability::Mut))
&& let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl
{
@ -1223,22 +1263,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let local_ty = self.local_ty(pat.span, pat.hir_id);
let eq_ty = match bm.0 {
ByRef::Yes(Pinnedness::Not, mutbl) => {
ByRef::Yes(pinnedness, mutbl) => {
// If the binding is like `ref x | ref mut x`,
// then `x` is assigned a value of type `&M T` where M is the
// mutability and T is the expected type.
//
// Under pin ergonomics, if the binding is like `ref pin const|mut x`,
// then `x` is assigned a value of type `&pin M T` where M is the
// mutability and T is the expected type.
//
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)`
// is required. However, we use equality, which is stronger.
// See (note_1) for an explanation.
self.new_ref_ty(pat.span, mutbl, expected)
self.new_ref_ty(pat.span, pinnedness, mutbl, expected)
}
// Wrapping the type into `Pin` if the binding is like `ref pin const|mut x`
ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt(
self.tcx,
self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, pat.span)),
self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]),
),
// Otherwise, the type of x is the expected type `T`.
ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
};
@ -1331,18 +1369,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// Precondition: pat is a `Ref(_)` pattern
// FIXME(pin_ergonomics): add suggestions for `&pin mut` or `&pin const` patterns
fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) {
let tcx = self.tcx;
if let PatKind::Ref(inner, mutbl) = pat.kind
if let PatKind::Ref(inner, pinned, mutbl) = pat.kind
&& let PatKind::Binding(_, _, binding, ..) = inner.kind
{
let binding_parent = tcx.parent_hir_node(pat.hir_id);
debug!(?inner, ?pat, ?binding_parent);
let mutability = match mutbl {
ast::Mutability::Mut => "mut",
ast::Mutability::Not => "",
};
let pin_and_mut = pinned.prefix_str(mutbl).trim_end();
let mut_var_suggestion = 'block: {
if mutbl.is_not() {
@ -1392,7 +1428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// so we don't suggest moving something to the type that does not exist
hir::Node::Param(hir::Param { ty_span, pat, .. }) if pat.span != *ty_span => {
err.multipart_suggestion_verbose(
format!("to take parameter `{binding}` by reference, move `&{mutability}` to the type"),
format!("to take parameter `{binding}` by reference, move `&{pin_and_mut}` to the type"),
vec![
(pat.span.until(inner.span), "".to_owned()),
(ty_span.shrink_to_lo(), mutbl.ref_prefix_str().to_owned()),
@ -1406,13 +1442,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
hir::Node::Pat(pt) if let PatKind::TupleStruct(_, pat_arr, _) = pt.kind => {
for i in pat_arr.iter() {
if let PatKind::Ref(the_ref, _) = i.kind
if let PatKind::Ref(the_ref, _, _) = i.kind
&& let PatKind::Binding(mt, _, ident, _) = the_ref.kind
{
let BindingMode(_, mtblty) = mt;
err.span_suggestion_verbose(
i.span,
format!("consider removing `&{mutability}` from the pattern"),
format!("consider removing `&{pin_and_mut}` from the pattern"),
mtblty.prefix_str().to_string() + &ident.name.to_string(),
Applicability::MaybeIncorrect,
);
@ -1426,7 +1462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// rely on match ergonomics or it might be nested `&&pat`
err.span_suggestion_verbose(
pat.span.until(inner.span),
format!("consider removing `&{mutability}` from the pattern"),
format!("consider removing `&{pin_and_mut}` from the pattern"),
"",
Applicability::MaybeIncorrect,
);
@ -2677,6 +2713,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
pat: &'tcx Pat<'tcx>,
inner: &'tcx Pat<'tcx>,
pat_pinned: Pinnedness,
pat_mutbl: Mutability,
mut expected: Ty<'tcx>,
mut pat_info: PatInfo<'tcx>,
@ -2699,9 +2736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Determine whether we're consuming an inherited reference and resetting the default
// binding mode, based on edition and enabled experimental features.
if let ByRef::Yes(inh_pin, inh_mut) = pat_info.binding_mode
// FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here
// should be adjusted to `pat_pin == inh_pin`
&& (!self.tcx.features().pin_ergonomics() || inh_pin == Pinnedness::Not)
&& pat_pinned == inh_pin
{
match self.ref_pat_matches_inherited_ref(pat.span.edition()) {
InheritedRefMatchRule::EatOuter => {
@ -2821,21 +2856,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// to avoid creating needless variables. This also helps with
// the bad interactions of the given hack detailed in (note_1).
debug!("check_pat_ref: expected={:?}", expected);
match *expected.kind() {
ty::Ref(_, r_ty, r_mutbl)
if (ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
|| r_mutbl == pat_mutbl =>
match expected.maybe_pinned_ref() {
Some((r_ty, r_pinned, r_mutbl))
if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
|| r_mutbl == pat_mutbl)
&& pat_pinned == r_pinned =>
{
if r_mutbl == Mutability::Not {
pat_info.max_ref_mutbl = MutblCap::Not;
}
if r_pinned == Pinnedness::Pinned {
pat_info.max_pinnedness = PinnednessCap::Pinned;
}
(expected, r_ty)
}
_ => {
let inner_ty = self.next_ty_var(inner.span);
let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty);
let ref_ty = self.new_ref_ty(pat.span, pat_pinned, pat_mutbl, inner_ty);
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
let err = self.demand_eqtype_pat_diag(
pat.span,
@ -2864,10 +2902,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ref_ty
}
/// Create a reference type with a fresh region variable.
fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> {
/// Create a reference or pinned reference type with a fresh region variable.
fn new_ref_ty(
&self,
span: Span,
pinnedness: Pinnedness,
mutbl: Mutability,
ty: Ty<'tcx>,
) -> Ty<'tcx> {
let region = self.next_region_var(RegionVariableOrigin::PatternRegion(span));
Ty::new_ref(self.tcx, region, ty, mutbl)
let ref_ty = Ty::new_ref(self.tcx, region, ty, mutbl);
if pinnedness.is_pinned() {
return self.new_pinned_ty(span, ref_ty);
}
ref_ty
}
/// Create a pinned type.
fn new_pinned_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
Ty::new_adt(
self.tcx,
self.tcx.adt_def(self.tcx.require_lang_item(LangItem::Pin, span)),
self.tcx.mk_args(&[ty.into()]),
)
}
fn error_inherited_ref_mutability_mismatch(

View file

@ -1,79 +0,0 @@
use hir::Node;
use hir::def_id::DefId;
use rustc_hir as hir;
use rustc_middle::bug;
use rustc_middle::middle::region::{RvalueCandidate, Scope, ScopeTree};
use rustc_middle::ty::RvalueScopes;
use tracing::debug;
use super::FnCtxt;
/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by
/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that
/// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let`
/// statement.
///
/// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching
/// `<rvalue>` as `blk_id`:
///
/// ```text
/// ET = *ET
/// | ET[...]
/// | ET.f
/// | (ET)
/// | <rvalue>
/// ```
///
/// Note: ET is intended to match "rvalues or places based on rvalues".
fn record_rvalue_scope_rec(
rvalue_scopes: &mut RvalueScopes,
mut expr: &hir::Expr<'_>,
lifetime: Option<Scope>,
) {
loop {
// Note: give all the expressions matching `ET` with the
// extended temporary lifetime, not just the innermost rvalue,
// because in codegen if we must compile e.g., `*rvalue()`
// into a temporary, we request the temporary scope of the
// outer expression.
rvalue_scopes.record_rvalue_scope(expr.hir_id.local_id, lifetime);
match expr.kind {
hir::ExprKind::AddrOf(_, _, subexpr)
| hir::ExprKind::Unary(hir::UnOp::Deref, subexpr)
| hir::ExprKind::Field(subexpr, _)
| hir::ExprKind::Index(subexpr, _, _) => {
expr = subexpr;
}
_ => {
return;
}
}
}
}
fn record_rvalue_scope(
rvalue_scopes: &mut RvalueScopes,
expr: &hir::Expr<'_>,
candidate: &RvalueCandidate,
) {
debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})");
record_rvalue_scope_rec(rvalue_scopes, expr, candidate.lifetime)
// FIXME(@dingxiangfei2009): handle the candidates in the function call arguments
}
pub(crate) fn resolve_rvalue_scopes<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
scope_tree: &'a ScopeTree,
def_id: DefId,
) -> RvalueScopes {
let tcx = &fcx.tcx;
let mut rvalue_scopes = RvalueScopes::new();
debug!("start resolving rvalue scopes, def_id={def_id:?}");
debug!("rvalue_scope: rvalue_candidates={:?}", scope_tree.rvalue_candidates);
for (&hir_id, candidate) in &scope_tree.rvalue_candidates {
let Node::Expr(expr) = tcx.hir_node(hir_id) else { bug!("hir node does not exist") };
record_rvalue_scope(&mut rvalue_scopes, expr, candidate);
}
rvalue_scopes
}

View file

@ -79,9 +79,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
wbcx.visit_offset_of_container_types();
wbcx.visit_potentially_region_dependent_goals();
wbcx.typeck_results.rvalue_scopes =
mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes);
let used_trait_imports =
mem::take(&mut self.typeck_results.borrow_mut().used_trait_imports);
debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);

View file

@ -265,6 +265,9 @@ lint_forgetting_copy_types = calls to `std::mem::forget` with a value that imple
lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing
.label = argument has type `{$arg_ty}`
lint_function_casts_as_integer = direct cast of function item into an integer
.cast_as_fn = first cast to a pointer `as *const ()`
lint_hidden_glob_reexport = private item shadows public glob re-export
.note_glob_reexport = the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here
.note_private_item = but the private item here shadows it

View file

@ -1705,7 +1705,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
}
let (parentheses, endpoints) = match &pat.kind {
PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(subpat)),
PatKind::Ref(subpat, _, _) => (true, matches_ellipsis_pat(subpat)),
_ => (false, matches_ellipsis_pat(pat)),
};

View file

@ -5,7 +5,7 @@
//! syntactical lints.
use rustc_ast::visit::{self as ast_visit, Visitor, walk_list};
use rustc_ast::{self as ast, HasAttrs};
use rustc_ast::{self as ast, AttrVec, HasAttrs};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
use rustc_feature::Features;
@ -135,7 +135,7 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast>
});
}
fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) {
fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, _: &AttrVec, span: Span, id: ast::NodeId) {
lint_callback!(self, check_fn, fk, span, id);
ast_visit::walk_fn(self, fk);
}

View file

@ -0,0 +1,63 @@
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::BytePos;
use crate::lints::{FunctionCastsAsIntegerDiag, FunctionCastsAsIntegerSugg};
use crate::{LateContext, LateLintPass};
declare_lint! {
/// The `function_casts_as_integer` lint detects cases where a function item is cast
/// to an integer.
///
/// ### Example
///
/// ```rust
/// fn foo() {}
/// let x = foo as usize;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// When casting a function item to an integer, it implicitly creates a
/// function pointer that will in turn be cast to an integer. By making
/// it explicit, it improves readability of the code and prevents bugs.
pub FUNCTION_CASTS_AS_INTEGER,
Warn,
"casting a function into an integer",
}
declare_lint_pass!(
/// Lint for casts of functions into integers.
FunctionCastsAsInteger => [FUNCTION_CASTS_AS_INTEGER]
);
impl<'tcx> LateLintPass<'tcx> for FunctionCastsAsInteger {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
let hir::ExprKind::Cast(cast_from_expr, cast_to_expr) = expr.kind else { return };
let cast_to_ty = cx.typeck_results().expr_ty(expr);
// Casting to a function (pointer?), so all good.
//
// Normally, only casts to integers is possible, but if it ever changed, this condition
// will likely need to be updated.
if matches!(cast_to_ty.kind(), ty::FnDef(..) | ty::FnPtr(..) | ty::RawPtr(..)) {
return;
}
let cast_from_ty = cx.typeck_results().expr_ty(cast_from_expr);
if matches!(cast_from_ty.kind(), ty::FnDef(..)) {
cx.tcx.emit_node_span_lint(
FUNCTION_CASTS_AS_INTEGER,
expr.hir_id,
cast_to_expr.span.with_lo(cast_from_expr.span.hi() + BytePos(1)),
FunctionCastsAsIntegerDiag {
sugg: FunctionCastsAsIntegerSugg {
suggestion: cast_from_expr.span.shrink_to_hi(),
cast_to_ty,
},
},
);
}
}
}

View file

@ -45,6 +45,7 @@ mod errors;
mod expect;
mod for_loops_over_fallibles;
mod foreign_modules;
mod function_cast_as_integer;
mod if_let_rescope;
mod impl_trait_overcaptures;
mod internal;
@ -89,6 +90,7 @@ use deref_into_dyn_supertrait::*;
use drop_forget_useless::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use for_loops_over_fallibles::*;
use function_cast_as_integer::*;
use if_let_rescope::IfLetRescope;
use impl_trait_overcaptures::ImplTraitOvercaptures;
use internal::*;
@ -241,6 +243,7 @@ late_lint_methods!(
IfLetRescope: IfLetRescope::default(),
StaticMutRefs: StaticMutRefs,
UnqualifiedLocalImports: UnqualifiedLocalImports,
FunctionCastsAsInteger: FunctionCastsAsInteger,
CheckTransmutes: CheckTransmutes,
LifetimeSyntax: LifetimeSyntax,
]

View file

@ -3019,6 +3019,26 @@ pub(crate) struct ReservedMultihash {
pub suggestion: Span,
}
#[derive(LintDiagnostic)]
#[diag(lint_function_casts_as_integer)]
pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> {
#[subdiagnostic]
pub(crate) sugg: FunctionCastsAsIntegerSugg<'tcx>,
}
#[derive(Subdiagnostic)]
#[suggestion(
lint_cast_as_fn,
code = " as *const ()",
applicability = "machine-applicable",
style = "verbose"
)]
pub(crate) struct FunctionCastsAsIntegerSugg<'tcx> {
#[primary_span]
pub suggestion: Span,
pub cast_to_ty: Ty<'tcx>,
}
#[derive(Debug)]
pub(crate) struct MismatchedLifetimeSyntaxes {
pub inputs: LifetimeSyntaxCategories<Vec<Span>>,

View file

@ -55,10 +55,10 @@ macro_rules! late_lint_methods {
/// Each `check` method checks a single syntax node, and should not
/// invoke methods recursively (unlike `Visitor`). By default they
/// do nothing.
//
///
// FIXME: eliminate the duplication with `Visitor`. But this also
// contains a few lint-specific methods with no equivalent in `Visitor`.
//
macro_rules! declare_late_lint_pass {
([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
pub trait LateLintPass<'tcx>: LintPass {

View file

@ -16,6 +16,7 @@ use rustc_middle::ty::{
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, sym};
use rustc_target::spec::Os;
use tracing::debug;
use super::repr_nullable_ptr;
@ -177,7 +178,7 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
/// the Power alignment Rule (see the `check_struct_for_power_alignment` function).
fn check_arg_for_power_alignment<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
let tcx = cx.tcx;
assert!(tcx.sess.target.os == "aix");
assert!(tcx.sess.target.os == Os::Aix);
// Structs (under repr(C)) follow the power alignment rule if:
// - the first field of the struct is a floating-point type that
// is greater than 4-bytes, or
@ -222,7 +223,7 @@ fn check_struct_for_power_alignment<'tcx>(
let tcx = cx.tcx;
// Only consider structs (not enums or unions) on AIX.
if tcx.sess.target.os != "aix" || !adt_def.is_struct() {
if tcx.sess.target.os != Os::Aix || !adt_def.is_struct() {
return;
}

View file

@ -1307,7 +1307,8 @@ impl EarlyLintPass for UnusedParens {
Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
// Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
// Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
// FIXME(pin_ergonomics): check pinned patterns
Ref(p, _, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
}
}

View file

@ -144,6 +144,7 @@ declare_lint_pass! {
UNUSED_UNSAFE,
UNUSED_VARIABLES,
USELESS_DEPRECATED,
VARARGS_WITHOUT_PATTERN,
WARNINGS,
// tidy-alphabetical-end
]
@ -5295,3 +5296,50 @@ declare_lint! {
report_in_deps: false,
};
}
declare_lint! {
/// The `varargs_without_pattern` lint detects when `...` is used as an argument to a
/// non-foreign function without any pattern being specified.
///
/// ### Example
///
/// ```rust
/// // Using `...` in non-foreign function definitions is unstable, however stability is
/// // currently only checked after attributes are expanded, so using `#[cfg(false)]` here will
/// // allow this to compile on stable Rust.
/// #[cfg(false)]
/// fn foo(...) {
///
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Patterns are currently required for all non-`...` arguments in function definitions (with
/// some exceptions in the 2015 edition). Requiring `...` arguments to have patterns in
/// non-foreign function definitions makes the language more consistent, and removes a source of
/// confusion for the unstable C variadic feature. `...` arguments without a pattern are already
/// stable and widely used in foreign function definitions; this lint only affects non-foreign
/// function definitions.
///
/// Using `...` (C varargs) in a non-foreign function definition is currently unstable. However,
/// stability checking for the `...` syntax in non-foreign function definitions is currently
/// implemented after attributes have been expanded, meaning that if the attribute removes the
/// use of the unstable syntax (e.g. `#[cfg(false)]`, or a procedural macro), the code will
/// compile on stable Rust; this is the only situation where this lint affects code that
/// compiles on stable Rust.
///
/// This is a [future-incompatible] lint to transition this to a hard error in the future.
///
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub VARARGS_WITHOUT_PATTERN,
Warn,
"detects usage of `...` arguments without a pattern in non-foreign items",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #145544 <https://github.com/rust-lang/rust/issues/145544>",
report_in_deps: false,
};
}

View file

@ -15,7 +15,7 @@ use rustc_session::cstore::{DllCallingConvention, DllImport, ForeignModule, Nati
use rustc_session::search_paths::PathKind;
use rustc_span::Symbol;
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_target::spec::{Arch, BinaryFormat, LinkSelfContainedComponents};
use rustc_target::spec::{Abi, Arch, BinaryFormat, Env, LinkSelfContainedComponents, Os};
use crate::errors;
@ -67,9 +67,9 @@ pub fn walk_native_lib_search_dirs<R>(
// FIXME: On AIX this also has the side-effect of making the list of library search paths
// non-empty, which is needed or the linker may decide to record the LIBPATH env, if
// defined, as the search path instead of appending the default search paths.
if sess.target.vendor == "fortanix"
|| sess.target.os == "linux"
|| sess.target.os == "fuchsia"
if sess.target.abi == Abi::Fortanix
|| sess.target.os == Os::Linux
|| sess.target.os == Os::Fuchsia
|| sess.target.is_like_aix
|| sess.target.is_like_darwin && !sess.sanitizers().is_empty()
{
@ -79,7 +79,7 @@ pub fn walk_native_lib_search_dirs<R>(
// Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks
// we must have the support library stubs in the library search path (#121430).
if let Some(sdk_root) = apple_sdk_root
&& sess.target.env == "macabi"
&& sess.target.env == Env::MacAbi
{
f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;

View file

@ -11,7 +11,7 @@ use std::fmt;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::UnordMap;
use rustc_hir as hir;
use rustc_hir::{HirId, HirIdMap, Node};
use rustc_hir::{HirId, ItemLocalMap, Node};
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_span::{DUMMY_SP, Span};
use tracing::debug;
@ -221,12 +221,12 @@ pub struct ScopeTree {
/// variable is declared.
var_map: FxIndexMap<hir::ItemLocalId, Scope>,
/// Identifies expressions which, if captured into a temporary, ought to
/// have a temporary whose lifetime extends to the end of the enclosing *block*,
/// and not the enclosing *statement*. Expressions that are not present in this
/// table are not rvalue candidates. The set of rvalue candidates is computed
/// during type check based on a traversal of the AST.
pub rvalue_candidates: HirIdMap<RvalueCandidate>,
/// Tracks expressions with extended temporary scopes, based on the syntactic rules for
/// temporary lifetime extension. Further details may be found in
/// `rustc_hir_analysis::check::region` and in the [Reference].
///
/// [Reference]: https://doc.rust-lang.org/nightly/reference/destructors.html#temporary-lifetime-extension
extended_temp_scopes: ItemLocalMap<Option<Scope>>,
/// Backwards incompatible scoping that will be introduced in future editions.
/// This information is used later for linting to identify locals and
@ -234,16 +234,6 @@ pub struct ScopeTree {
pub backwards_incompatible_scope: UnordMap<hir::ItemLocalId, Scope>,
}
/// See the `rvalue_candidates` field for more information on rvalue
/// candidates in general.
/// The `lifetime` field is None to indicate that certain expressions escape
/// into 'static and should have no local cleanup scope.
#[derive(Debug, Copy, Clone, HashStable)]
pub struct RvalueCandidate {
pub target: hir::ItemLocalId,
pub lifetime: Option<Scope>,
}
impl ScopeTree {
pub fn record_scope_parent(&mut self, child: Scope, parent: Option<Scope>) {
debug!("{:?}.parent = {:?}", child, parent);
@ -260,12 +250,13 @@ impl ScopeTree {
self.var_map.insert(var, lifetime);
}
pub fn record_rvalue_candidate(&mut self, var: HirId, candidate: RvalueCandidate) {
debug!("record_rvalue_candidate(var={var:?}, candidate={candidate:?})");
if let Some(lifetime) = &candidate.lifetime {
assert!(var.local_id != lifetime.local_id)
/// Make an association between a sub-expression and an extended lifetime
pub fn record_extended_temp_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
debug!(?var, ?lifetime);
if let Some(lifetime) = lifetime {
assert!(var != lifetime.local_id);
}
self.rvalue_candidates.insert(var, candidate);
self.extended_temp_scopes.insert(var, lifetime);
}
/// Returns the narrowest scope that encloses `id`, if any.
@ -337,4 +328,20 @@ impl ScopeTree {
span_bug!(ty::tls::with(|tcx| inner.span(tcx, self)), "no enclosing temporary scope")
}
/// Returns the scope when the temp created by `expr_id` will be cleaned up.
/// It also emits a lint on potential backwards incompatible change to the temporary scope
/// which is *for now* always shortening.
pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> (Option<Scope>, Option<Scope>) {
// Check for a designated extended temporary scope.
if let Some(&s) = self.extended_temp_scopes.get(&expr_id) {
debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
return (s, None);
}
// Otherwise, locate the innermost terminating scope.
let (scope, backward_incompatible) =
self.default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node });
(Some(scope), backward_incompatible)
}
}

View file

@ -16,7 +16,7 @@ use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt};
///////////////////////////////////////////////////////////////////////////
/// Evaluated Constants
///
/// Represents the result of const evaluation via the `eval_to_allocation` query.
/// Not to be confused with `ConstAllocation`, which directly refers to the underlying data!
/// Here we indirect via an `AllocId`.

View file

@ -11,7 +11,7 @@ pub use basic_blocks::{BasicBlocks, SwitchTargetValue};
use either::Either;
use polonius_engine::Atom;
use rustc_abi::{FieldIdx, VariantIdx};
pub use rustc_ast::Mutability;
pub use rustc_ast::{Mutability, Pinnedness};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg};

View file

@ -856,6 +856,7 @@ bidirectional_lang_item_map! {
PointeeTrait,
Sized,
TransmuteTrait,
TrivialClone,
Tuple,
Unpin,
Unsize,

View file

@ -97,7 +97,6 @@ pub use self::region::{
BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region,
RegionKind, RegionVid,
};
pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::{
AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst,
@ -156,7 +155,6 @@ mod list;
mod opaque_types;
mod predicate;
mod region;
mod rvalue_scopes;
mod structural_impls;
#[allow(hidden_glob_reexports)]
mod sty;

View file

@ -1,48 +0,0 @@
use rustc_hir as hir;
use rustc_hir::ItemLocalMap;
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use tracing::debug;
use crate::middle::region::{Scope, ScopeData, ScopeTree};
/// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by
/// rules laid out in `rustc_hir_analysis::check::rvalue_scopes`.
#[derive(TyEncodable, TyDecodable, Clone, Debug, Default, Eq, PartialEq, HashStable)]
pub struct RvalueScopes {
map: ItemLocalMap<Option<Scope>>,
}
impl RvalueScopes {
pub fn new() -> Self {
Self { map: <_>::default() }
}
/// Returns the scope when the temp created by `expr_id` will be cleaned up.
/// It also emits a lint on potential backwards incompatible change to the temporary scope
/// which is *for now* always shortening.
pub fn temporary_scope(
&self,
region_scope_tree: &ScopeTree,
expr_id: hir::ItemLocalId,
) -> (Option<Scope>, Option<Scope>) {
// Check for a designated rvalue scope.
if let Some(&s) = self.map.get(&expr_id) {
debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
return (s, None);
}
// Otherwise, locate the innermost terminating scope.
let (scope, backward_incompatible) = region_scope_tree
.default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node });
(Some(scope), backward_incompatible)
}
/// Make an association between a sub-expression and an extended lifetime
pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
if let Some(lifetime) = lifetime {
assert!(var != lifetime.local_id);
}
self.map.insert(var, lifetime);
}
}

View file

@ -1366,6 +1366,19 @@ impl<'tcx> Ty<'tcx> {
None
}
pub fn maybe_pinned_ref(self) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability)> {
match *self.kind() {
Adt(def, args)
if def.is_pin()
&& let ty::Ref(_, ty, mutbl) = *args.type_at(0).kind() =>
{
Some((ty, ty::Pinnedness::Pinned, mutbl))
}
ty::Ref(_, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl)),
_ => None,
}
}
/// Panics if called on any type other than `Box<T>`.
pub fn expect_boxed_ty(self) -> Ty<'tcx> {
self.boxed_ty()

View file

@ -18,7 +18,6 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisit
use rustc_session::Session;
use rustc_span::Span;
use super::RvalueScopes;
use crate::hir::place::Place as HirPlace;
use crate::infer::canonical::Canonical;
use crate::mir::FakeReadCause;
@ -198,11 +197,6 @@ pub struct TypeckResults<'tcx> {
/// issue by fake reading `t`.
pub closure_fake_reads: LocalDefIdMap<Vec<(HirPlace<'tcx>, FakeReadCause, HirId)>>,
/// Tracks the rvalue scoping rules which defines finer scoping for rvalue expressions
/// by applying extended parameter rules.
/// Details may be found in `rustc_hir_analysis::check::rvalue_scopes`.
pub rvalue_scopes: RvalueScopes,
/// Stores the predicates that apply on coroutine witness types.
/// formatting modified file tests/ui/coroutine/retain-resume-ref.rs
pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>,
@ -254,7 +248,6 @@ impl<'tcx> TypeckResults<'tcx> {
hidden_types: Default::default(),
closure_min_captures: Default::default(),
closure_fake_reads: Default::default(),
rvalue_scopes: Default::default(),
coroutine_stalled_predicates: Default::default(),
potentially_region_dependent_goals: Default::default(),
closure_size_eval: Default::default(),

View file

@ -607,9 +607,9 @@ impl<'tcx> TyCtxt<'tcx> {
/// have the same `DefKind`.
///
/// Note that closures have a `DefId`, but the closure *expression* also has a
// `HirId` that is located within the context where the closure appears (and, sadly,
// a corresponding `NodeId`, since those are not yet phased out). The parent of
// the closure's `DefId` will also be the context where it appears.
/// `HirId` that is located within the context where the closure appears (and, sadly,
/// a corresponding `NodeId`, since those are not yet phased out). The parent of
/// the closure's `DefId` will also be the context where it appears.
pub fn is_closure_like(self, def_id: DefId) -> bool {
matches!(self.def_kind(def_id), DefKind::Closure)
}

View file

@ -779,8 +779,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place),
);
// See the comment in `expr_as_temp` and on the `rvalue_scopes` field for why
// this can be `None`.
// This can be `None` if the expression's temporary scope was extended so that it can be
// borrowed by a `const` or `static`. In that case, it's never dropped.
if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp);
}

View file

@ -284,14 +284,12 @@ impl<'tcx> MatchPairTree<'tcx> {
}
PatKind::Deref { ref subpattern }
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) {
// Only deref patterns on boxes can be lowered using a built-in deref.
debug_assert!(pattern.ty.is_box());
}
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) }
if let Some(ref_ty) = pattern.ty.pinned_ty()
&& ref_ty.is_ref() =>
{
MatchPairTree::for_pattern(
place_builder.deref(),
place_builder.field(FieldIdx::ZERO, ref_ty).deref(),
subpattern,
cx,
&mut subpairs,
@ -300,12 +298,14 @@ impl<'tcx> MatchPairTree<'tcx> {
None
}
PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => {
let Some(ref_ty) = pattern.ty.pinned_ty() else {
rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty);
};
PatKind::DerefPattern { borrow: ByRef::Yes(Pinnedness::Pinned, _), .. } => {
rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty)
}
PatKind::Deref { ref subpattern }
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
MatchPairTree::for_pattern(
place_builder.field(FieldIdx::ZERO, ref_ty).deref(),
place_builder.deref(),
subpattern,
cx,
&mut subpairs,

View file

@ -337,7 +337,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
let tcx = self.tcx;
let expr_ty = self.typeck_results.expr_ty(expr);
let (temp_lifetime, backwards_incompatible) =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let kind = match expr.kind {
// Here comes the interesting stuff:
@ -502,9 +502,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
expr: Some(arg),
safety_mode: BlockSafety::Safe,
});
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, arg_expr.hir_id.local_id);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(arg_expr.hir_id.local_id);
arg = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
ty: arg_ty,
@ -996,9 +995,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
}
} else {
let block_ty = self.typeck_results.node_type(body.hir_id);
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, body.hir_id.local_id);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(body.hir_id.local_id);
let block = self.mirror_block(body);
let body = self.thir.exprs.push(Expr {
ty: block_ty,
@ -1143,7 +1141,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
overloaded_callee: Option<Ty<'tcx>>,
) -> Expr<'tcx> {
let (temp_lifetime, backwards_incompatible) =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let (ty, user_ty) = match overloaded_callee {
Some(fn_def) => (fn_def, None),
None => {
@ -1237,9 +1235,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
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, self.typing_env);
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let kind = if self.tcx.is_thread_local_static(id) {
ExprKind::ThreadLocalRef(id)
} else {
@ -1321,7 +1318,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
// construct the complete expression `foo()` for the overloaded call,
// which will yield the &T type
let (temp_lifetime, backwards_incompatible) =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let fun = self.method_callee(expr, span, overloaded_callee);
let fun = self.thir.exprs.push(fun);
let fun_ty = self.thir[fun].ty;
@ -1341,9 +1338,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
closure_expr: &'tcx hir::Expr<'tcx>,
place: HirPlace<'tcx>,
) -> Expr<'tcx> {
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
let var_ty = place.base_ty;
// The result of capture analysis in `rustc_hir_typeck/src/upvar.rs` represents a captured path
@ -1405,9 +1401,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
let upvar_capture = captured_place.info.capture_kind;
let captured_place_expr =
self.convert_captured_hir_place(closure_expr, captured_place.place.clone());
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
match upvar_capture {
ty::UpvarCapture::ByValue => captured_place_expr,

View file

@ -12,7 +12,7 @@ use rustc_hir::{self as hir, HirId, find_attr};
use rustc_middle::bug;
use rustc_middle::middle::region;
use rustc_middle::thir::*;
use rustc_middle::ty::{self, RvalueScopes, TyCtxt};
use rustc_middle::ty::{self, TyCtxt};
use tracing::instrument;
use crate::thir::pattern::pat_from_hir;
@ -62,7 +62,6 @@ struct ThirBuildCx<'tcx> {
region_scope_tree: &'tcx region::ScopeTree,
typeck_results: &'tcx ty::TypeckResults<'tcx>,
rvalue_scopes: &'tcx RvalueScopes,
/// False to indicate that adjustments should not be applied. Only used for `custom_mir`
apply_adjustments: bool,
@ -109,7 +108,6 @@ impl<'tcx> ThirBuildCx<'tcx> {
typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
region_scope_tree: tcx.region_scope_tree(def),
typeck_results,
rvalue_scopes: &typeck_results.rvalue_scopes,
body_owner: def.to_def_id(),
apply_adjustments:
!find_attr!(tcx.hir_attrs(hir_id), AttributeKind::CustomMir(..) => ()).is_some(),

View file

@ -97,7 +97,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
// adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
// gets the least-dereferenced type).
let unadjusted_pat = match pat.kind {
hir::PatKind::Ref(inner, _)
hir::PatKind::Ref(inner, _, _)
if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) =>
{
self.lower_pattern(inner)
@ -319,7 +319,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern);
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow }
}
hir::PatKind::Ref(subpattern, _) => {
hir::PatKind::Ref(subpattern, _, _) => {
// Track the default binding mode for the Rust 2024 migration suggestion.
let opt_old_mode_span =
self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref());
@ -370,10 +370,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
if let Some(pty) = ty.pinned_ty()
&& let &ty::Ref(_, rty, _) = pty.kind() =>
{
debug_assert!(
self.tcx.features().pin_ergonomics(),
"`pin_ergonomics` must be enabled to have a by-pin-ref binding"
);
ty = rty;
}
hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => {

View file

@ -547,9 +547,11 @@ where
Some(SolverTraitLangItem::PointeeSized) => {
unreachable!("`PointeeSized` is removed during lowering");
}
Some(SolverTraitLangItem::Copy | SolverTraitLangItem::Clone) => {
G::consider_builtin_copy_clone_candidate(self, goal)
}
Some(
SolverTraitLangItem::Copy
| SolverTraitLangItem::Clone
| SolverTraitLangItem::TrivialClone,
) => G::consider_builtin_copy_clone_candidate(self, goal),
Some(SolverTraitLangItem::Fn) => {
G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::Fn)
}

Some files were not shown because too many files have changed in this diff Show more