Merge from rustc
This commit is contained in:
commit
2ca415c1c2
786 changed files with 8976 additions and 5907 deletions
|
@ -18,3 +18,5 @@ b39a1d6f1a30ba29f25d7141038b9a5bf0126e36
|
|||
f97fddab91fbf290ea5b691fe355d6f915220b6e
|
||||
# format let-else
|
||||
cc907f80b95c6ec530c5ee1b05b044a468f07eca
|
||||
# format let-chains
|
||||
b2d2184edea578109a48ec3d8decbee5948e8f35
|
||||
|
|
6
.mailmap
6
.mailmap
|
@ -346,7 +346,9 @@ Lindsey Kuper <lindsey@composition.al> <lindsey@rockstargirl.org>
|
|||
Lindsey Kuper <lindsey@composition.al> <lkuper@mozilla.com>
|
||||
Liu Dingming <liudingming@bytedance.com>
|
||||
Loo Maclin <loo.maclin@protonmail.com>
|
||||
Loïc BRANSTETT <lolo.branstett@numericable.fr>
|
||||
Urgau <urgau@numericable.fr>
|
||||
Urgau <urgau@numericable.fr> <lolo.branstett@numericable.fr>
|
||||
Urgau <urgau@numericable.fr> <3616612+Urgau@users.noreply.github.com>
|
||||
Lucy <luxx4x@protonmail.com>
|
||||
Lukas H. <lukaramu@users.noreply.github.com>
|
||||
Lukas Lueg <lukas.lueg@gmail.com>
|
||||
|
@ -445,6 +447,8 @@ Oliver Scherer <oli-obk@users.noreply.github.com> <public.oliver.schneider@kit.e
|
|||
Oliver Scherer <oli-obk@users.noreply.github.com> <oliver.schneider@kit.edu>
|
||||
Oliver Scherer <oli-obk@users.noreply.github.com> <obk8176014uqher834@olio-obk.de>
|
||||
Oliver Scherer <oli-obk@users.noreply.github.com>
|
||||
Onur Özkan <onurozkan.dev@outlook.com> <work@onurozkan.dev>
|
||||
Onur Özkan <onurozkan.dev@outlook.com>
|
||||
Ömer Sinan Ağacan <omeragacan@gmail.com>
|
||||
Ophir LOJKINE <pere.jobs@gmail.com>
|
||||
Ožbolt Menegatti <ozbolt.menegatti@gmail.com> gareins <ozbolt.menegatti@gmail.com>
|
||||
|
|
55
Cargo.lock
55
Cargo.lock
|
@ -2150,9 +2150,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.148"
|
||||
version = "0.2.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
dependencies = [
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
@ -2245,9 +2245,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.7"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
|
||||
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
|
@ -2418,9 +2418,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "minifier"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eb022374af2f446981254e6bf9efb6e2c9e1a53176d395fca02792fd4435729"
|
||||
checksum = "5394aa376422b4b2b6c02fd9cfcb657e4ec544ae98e43d7d5d785fd0d042fd6d"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
|
@ -2970,30 +2970,6 @@ dependencies = [
|
|||
"pad",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
|
@ -4480,6 +4456,7 @@ dependencies = [
|
|||
name = "rustc_session"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"getopts",
|
||||
"libc",
|
||||
"rustc_ast",
|
||||
|
@ -4779,9 +4756,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.14"
|
||||
version = "0.38.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f"
|
||||
checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
|
@ -5252,23 +5229,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4d38d39c754ae037a9bc3ca1580a985db7371cd14f1229172d1db9093feb6739"
|
||||
dependencies = [
|
||||
"papergrid",
|
||||
"tabled_derive",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tabled_derive"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.38"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(all(not(bootstrap), feature = "nightly"), doc(rust_logo))]
|
||||
#![cfg_attr(all(not(bootstrap), feature = "nightly"), feature(rustdoc_internals))]
|
||||
|
||||
use std::fmt;
|
||||
use std::num::{NonZeroUsize, ParseIntError};
|
||||
|
@ -681,6 +683,7 @@ impl fmt::Display for AlignFromBytesError {
|
|||
|
||||
impl Align {
|
||||
pub const ONE: Align = Align { pow2: 0 };
|
||||
// LLVM has a maximal supported alignment of 2^29, we inherit that.
|
||||
pub const MAX: Align = Align { pow2: 29 };
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -520,9 +520,7 @@ impl NestedMetaItem {
|
|||
I: Iterator<Item = &'a TokenTree>,
|
||||
{
|
||||
match tokens.peek() {
|
||||
Some(TokenTree::Token(token, _))
|
||||
if let Some(lit) = MetaItemLit::from_token(token) =>
|
||||
{
|
||||
Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => {
|
||||
tokens.next();
|
||||
return Some(NestedMetaItem::Lit(lit));
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ pub fn entry_point_type(
|
|||
} else if attr::contains_name(attrs, sym::rustc_main) {
|
||||
EntryPointType::RustcMainAttr
|
||||
} else {
|
||||
if let Some(name) = name && name == sym::main {
|
||||
if let Some(name) = name
|
||||
&& name == sym::main
|
||||
{
|
||||
if at_root {
|
||||
// This is a top-level function so it can be `main`.
|
||||
EntryPointType::MainNamed
|
||||
|
|
|
@ -107,13 +107,11 @@ impl Lit {
|
|||
/// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation.
|
||||
pub fn from_token(token: &Token) -> Option<Lit> {
|
||||
match token.uninterpolate().kind {
|
||||
Ident(name, false) if name.is_bool_lit() => {
|
||||
Some(Lit::new(Bool, name, None))
|
||||
}
|
||||
Ident(name, false) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
|
||||
Literal(token_lit) => Some(token_lit),
|
||||
Interpolated(ref nt)
|
||||
if let NtExpr(expr) | NtLiteral(expr) = &**nt
|
||||
&& let ast::ExprKind::Lit(token_lit) = expr.kind =>
|
||||
&& let ast::ExprKind::Lit(token_lit) = expr.kind =>
|
||||
{
|
||||
Some(token_lit)
|
||||
}
|
||||
|
@ -229,35 +227,61 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool {
|
|||
#[derive(PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum TokenKind {
|
||||
/* Expression-operator symbols. */
|
||||
/// `=`
|
||||
Eq,
|
||||
/// `<`
|
||||
Lt,
|
||||
/// `<=`
|
||||
Le,
|
||||
/// `==`
|
||||
EqEq,
|
||||
/// `!=`
|
||||
Ne,
|
||||
/// `>`
|
||||
Ge,
|
||||
/// `>=`
|
||||
Gt,
|
||||
/// `&&`
|
||||
AndAnd,
|
||||
/// `||`
|
||||
OrOr,
|
||||
/// `!`
|
||||
Not,
|
||||
/// `~`
|
||||
Tilde,
|
||||
BinOp(BinOpToken),
|
||||
BinOpEq(BinOpToken),
|
||||
|
||||
/* Structural symbols */
|
||||
/// `@`
|
||||
At,
|
||||
/// `.`
|
||||
Dot,
|
||||
/// `..`
|
||||
DotDot,
|
||||
/// `...`
|
||||
DotDotDot,
|
||||
/// `..=`
|
||||
DotDotEq,
|
||||
/// `,`
|
||||
Comma,
|
||||
/// `;`
|
||||
Semi,
|
||||
/// `:`
|
||||
Colon,
|
||||
/// `::`
|
||||
ModSep,
|
||||
/// `->`
|
||||
RArrow,
|
||||
/// `<-`
|
||||
LArrow,
|
||||
/// `=>`
|
||||
FatArrow,
|
||||
/// `#`
|
||||
Pound,
|
||||
/// `$`
|
||||
Dollar,
|
||||
/// `?`
|
||||
Question,
|
||||
/// Used by proc macros for representing lifetimes, not generated by lexer right now.
|
||||
SingleQuote,
|
||||
|
@ -296,6 +320,7 @@ pub enum TokenKind {
|
|||
/// similarly to symbols in string literal tokens.
|
||||
DocComment(CommentKind, ast::AttrStyle, Symbol),
|
||||
|
||||
/// End Of File
|
||||
Eof,
|
||||
}
|
||||
|
||||
|
@ -628,7 +653,9 @@ impl Token {
|
|||
|
||||
/// Returns `true` if the token is an interpolated path.
|
||||
fn is_path(&self) -> bool {
|
||||
if let Interpolated(nt) = &self.kind && let NtPath(..) = **nt {
|
||||
if let Interpolated(nt) = &self.kind
|
||||
&& let NtPath(..) = **nt
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -650,7 +677,9 @@ impl Token {
|
|||
|
||||
/// Is the token an interpolated block (`$b:block`)?
|
||||
pub fn is_whole_block(&self) -> bool {
|
||||
if let Interpolated(nt) = &self.kind && let NtBlock(..) = **nt {
|
||||
if let Interpolated(nt) = &self.kind
|
||||
&& let NtBlock(..) = **nt
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -550,7 +550,9 @@ impl TokenStream {
|
|||
|
||||
let stream_iter = stream.0.iter().cloned();
|
||||
|
||||
if let Some(first) = stream.0.first() && Self::try_glue_to_last(vec_mut, first) {
|
||||
if let Some(first) = stream.0.first()
|
||||
&& Self::try_glue_to_last(vec_mut, first)
|
||||
{
|
||||
// Now skip the first token tree from `stream`.
|
||||
vec_mut.extend(stream_iter.skip(1));
|
||||
} else {
|
||||
|
|
|
@ -136,12 +136,6 @@ ast_lowering_template_modifier = template modifier
|
|||
|
||||
ast_lowering_this_not_async = this is not `async`
|
||||
|
||||
ast_lowering_trait_fn_async =
|
||||
functions in traits cannot be declared `async`
|
||||
.label = `async` because of this
|
||||
.note = `async` trait functions are not currently supported
|
||||
.note2 = consider using the `async-trait` crate: https://crates.io/crates/async-trait
|
||||
|
||||
ast_lowering_underscore_expr_lhs_assign =
|
||||
in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
.label = `_` not allowed here
|
||||
|
|
|
@ -354,17 +354,6 @@ pub struct InclusiveRangeWithNoEnd {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic, Clone, Copy)]
|
||||
#[diag(ast_lowering_trait_fn_async, code = "E0706")]
|
||||
#[note]
|
||||
#[note(ast_lowering_note2)]
|
||||
pub struct TraitFnAsync {
|
||||
#[primary_span]
|
||||
pub fn_span: Span,
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub enum BadReturnTypeNotation {
|
||||
#[diag(ast_lowering_bad_return_type_notation_inputs)]
|
||||
|
|
|
@ -673,12 +673,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
&& let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
|
||||
&& attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
|
||||
{
|
||||
let unstable_span =
|
||||
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::Async,
|
||||
span,
|
||||
self.allow_gen_future.clone(),
|
||||
);
|
||||
self.lower_attrs(
|
||||
inner_hir_id,
|
||||
&[Attribute {
|
||||
kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new(sym::track_caller, span)))),
|
||||
kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new(
|
||||
sym::track_caller,
|
||||
span,
|
||||
)))),
|
||||
id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(),
|
||||
style: AttrStyle::Outer,
|
||||
span: unstable_span,
|
||||
|
@ -1102,7 +1108,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
if let ExprKind::Path(qself, path) = &expr.kind {
|
||||
// Does the path resolve to something disallowed in a tuple struct/variant pattern?
|
||||
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
|
||||
if let Some(res) = partial_res.full_res() && !res.expected_in_tuple_struct_pat() {
|
||||
if let Some(res) = partial_res.full_res()
|
||||
&& !res.expected_in_tuple_struct_pat()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -1122,7 +1130,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
if let ExprKind::Path(qself, path) = &expr.kind {
|
||||
// Does the path resolve to something disallowed in a unit struct/variant pattern?
|
||||
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
|
||||
if let Some(res) = partial_res.full_res() && !res.expected_in_unit_struct_pat() {
|
||||
if let Some(res) = partial_res.full_res()
|
||||
&& !res.expected_in_unit_struct_pat()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,9 +61,12 @@ fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
|
|||
let remaining_args = args.split_off(arg_index + 1);
|
||||
let old_arg_offset = args.len();
|
||||
let mut fmt2 = &mut args.pop().unwrap().expr; // The inner FormatArgs.
|
||||
let fmt2 = loop { // Unwrap the Expr to get to the FormatArgs.
|
||||
let fmt2 = loop {
|
||||
// Unwrap the Expr to get to the FormatArgs.
|
||||
match &mut fmt2.kind {
|
||||
ExprKind::Paren(inner) | ExprKind::AddrOf(BorrowKind::Ref, _, inner) => fmt2 = inner,
|
||||
ExprKind::Paren(inner) | ExprKind::AddrOf(BorrowKind::Ref, _, inner) => {
|
||||
fmt2 = inner
|
||||
}
|
||||
ExprKind::FormatArgs(fmt2) => break fmt2,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
|
@ -1387,10 +1387,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// Desugar `~const` bound in generics into an additional `const host: bool` param
|
||||
// if the effects feature is enabled. This needs to be done before we lower where
|
||||
// clauses since where clauses need to bind to the DefId of the host param
|
||||
let host_param_parts = if let Const::Yes(span) = constness && self.tcx.features().effects {
|
||||
if let Some(param) = generics.params.iter().find(|x| {
|
||||
x.attrs.iter().any(|x| x.has_name(sym::rustc_host))
|
||||
}) {
|
||||
let host_param_parts = if let Const::Yes(span) = constness
|
||||
&& self.tcx.features().effects
|
||||
{
|
||||
if let Some(param) =
|
||||
generics.params.iter().find(|x| x.attrs.iter().any(|x| x.has_name(sym::rustc_host)))
|
||||
{
|
||||
// user has manually specified a `rustc_host` param, in this case, we set
|
||||
// the param id so that lowering logic can use that. But we don't create
|
||||
// another host param, so this gives `None`.
|
||||
|
@ -1399,7 +1401,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
} else {
|
||||
let param_node_id = self.next_node_id();
|
||||
let hir_id = self.next_id();
|
||||
let def_id = self.create_def(self.local_def_id(parent_node_id), param_node_id, DefPathData::TypeNs(sym::host), span);
|
||||
let def_id = self.create_def(
|
||||
self.local_def_id(parent_node_id),
|
||||
param_node_id,
|
||||
DefPathData::TypeNs(sym::host),
|
||||
span,
|
||||
);
|
||||
self.host_param_id = Some(def_id);
|
||||
Some((span, hir_id, def_id))
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
//! get confused if the spans from leaf AST nodes occur in multiple places
|
||||
//! in the HIR, especially for multiple identifiers.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
|
@ -40,7 +43,7 @@
|
|||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait, TraitFnAsync};
|
||||
use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit;
|
||||
|
@ -271,8 +274,6 @@ enum ImplTraitPosition {
|
|||
ClosureReturn,
|
||||
PointerReturn,
|
||||
FnTraitReturn,
|
||||
TraitReturn,
|
||||
ImplReturn,
|
||||
GenericDefault,
|
||||
ConstTy,
|
||||
StaticTy,
|
||||
|
@ -302,8 +303,6 @@ impl std::fmt::Display for ImplTraitPosition {
|
|||
ImplTraitPosition::ClosureReturn => "closure return types",
|
||||
ImplTraitPosition::PointerReturn => "`fn` pointer return types",
|
||||
ImplTraitPosition::FnTraitReturn => "`Fn` trait return types",
|
||||
ImplTraitPosition::TraitReturn => "trait method return types",
|
||||
ImplTraitPosition::ImplReturn => "`impl` method return types",
|
||||
ImplTraitPosition::GenericDefault => "generic parameter defaults",
|
||||
ImplTraitPosition::ConstTy => "const types",
|
||||
ImplTraitPosition::StaticTy => "static types",
|
||||
|
@ -334,20 +333,9 @@ impl FnDeclKind {
|
|||
matches!(self, FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait)
|
||||
}
|
||||
|
||||
fn return_impl_trait_allowed(&self, tcx: TyCtxt<'_>) -> bool {
|
||||
fn return_impl_trait_allowed(&self) -> bool {
|
||||
match self {
|
||||
FnDeclKind::Fn | FnDeclKind::Inherent => true,
|
||||
FnDeclKind::Impl if tcx.features().return_position_impl_trait_in_trait => true,
|
||||
FnDeclKind::Trait if tcx.features().return_position_impl_trait_in_trait => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn async_fn_allowed(&self, tcx: TyCtxt<'_>) -> bool {
|
||||
match self {
|
||||
FnDeclKind::Fn | FnDeclKind::Inherent => true,
|
||||
FnDeclKind::Impl if tcx.features().async_fn_in_trait => true,
|
||||
FnDeclKind::Trait if tcx.features().async_fn_in_trait => true,
|
||||
FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1271,7 +1259,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
&PolyTraitRef {
|
||||
bound_generic_params: ThinVec::new(),
|
||||
trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
|
||||
span: t.span
|
||||
span: t.span,
|
||||
},
|
||||
itctx,
|
||||
ast::Const::No,
|
||||
|
@ -1805,53 +1793,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.lower_ty_direct(¶m.ty, &itctx)
|
||||
}));
|
||||
|
||||
let output = if let Some((ret_id, span)) = make_ret_async {
|
||||
if !kind.async_fn_allowed(self.tcx) {
|
||||
match kind {
|
||||
FnDeclKind::Trait | FnDeclKind::Impl => {
|
||||
self.tcx
|
||||
.sess
|
||||
.create_feature_err(
|
||||
TraitFnAsync { fn_span, span },
|
||||
sym::async_fn_in_trait,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
_ => {
|
||||
self.tcx.sess.emit_err(TraitFnAsync { fn_span, span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let output = if let Some((ret_id, _span)) = make_ret_async {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind, fn_span)
|
||||
} else {
|
||||
match &decl.output {
|
||||
FnRetTy::Ty(ty) => {
|
||||
let context = if kind.return_impl_trait_allowed(self.tcx) {
|
||||
let context = if kind.return_impl_trait_allowed() {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||
fn_kind: kind,
|
||||
}
|
||||
} else {
|
||||
let position = match kind {
|
||||
FnDeclKind::Fn | FnDeclKind::Inherent => {
|
||||
unreachable!("fn should allow in-band lifetimes")
|
||||
ImplTraitContext::Disallowed(match kind {
|
||||
FnDeclKind::Fn
|
||||
| FnDeclKind::Inherent
|
||||
| FnDeclKind::Trait
|
||||
| FnDeclKind::Impl => {
|
||||
unreachable!("fn should allow return-position impl trait in traits")
|
||||
}
|
||||
FnDeclKind::ExternFn => ImplTraitPosition::ExternFnReturn,
|
||||
FnDeclKind::Closure => ImplTraitPosition::ClosureReturn,
|
||||
FnDeclKind::Pointer => ImplTraitPosition::PointerReturn,
|
||||
FnDeclKind::Trait => ImplTraitPosition::TraitReturn,
|
||||
FnDeclKind::Impl => ImplTraitPosition::ImplReturn,
|
||||
};
|
||||
match kind {
|
||||
FnDeclKind::Trait | FnDeclKind::Impl => ImplTraitContext::FeatureGated(
|
||||
position,
|
||||
sym::return_position_impl_trait_in_trait,
|
||||
),
|
||||
_ => ImplTraitContext::Disallowed(position),
|
||||
}
|
||||
})
|
||||
};
|
||||
hir::FnRetTy::Return(self.lower_ty(ty, &context))
|
||||
}
|
||||
|
@ -1924,18 +1889,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
||||
output,
|
||||
span,
|
||||
if let FnDeclKind::Trait = fn_kind
|
||||
&& !this.tcx.features().return_position_impl_trait_in_trait
|
||||
{
|
||||
ImplTraitContext::FeatureGated(
|
||||
ImplTraitPosition::TraitReturn,
|
||||
sym::return_position_impl_trait_in_trait,
|
||||
)
|
||||
} else {
|
||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||
fn_kind,
|
||||
}
|
||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||
fn_kind,
|
||||
},
|
||||
);
|
||||
arena_vec![this; future_bound]
|
||||
|
|
|
@ -82,7 +82,8 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
|
|||
// We can sometimes encounter bare trait objects
|
||||
// which are represented in AST as paths.
|
||||
if let Some(partial_res) = self.resolver.get_partial_res(t.id)
|
||||
&& let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) = partial_res.full_res()
|
||||
&& let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) =
|
||||
partial_res.full_res()
|
||||
{
|
||||
self.current_binders.push(t.id);
|
||||
visit::walk_ty(self, t);
|
||||
|
|
|
@ -215,14 +215,15 @@ impl<'a> AstValidator<'a> {
|
|||
}
|
||||
|
||||
fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
|
||||
if let Some(ident) = field.ident &&
|
||||
ident.name == kw::Underscore {
|
||||
self.check_unnamed_field_ty(&field.ty, ident.span);
|
||||
self.visit_vis(&field.vis);
|
||||
self.visit_ident(ident);
|
||||
self.visit_ty_common(&field.ty);
|
||||
self.walk_ty(&field.ty);
|
||||
walk_list!(self, visit_attribute, &field.attrs);
|
||||
if let Some(ident) = field.ident
|
||||
&& ident.name == kw::Underscore
|
||||
{
|
||||
self.check_unnamed_field_ty(&field.ty, ident.span);
|
||||
self.visit_vis(&field.vis);
|
||||
self.visit_ident(ident);
|
||||
self.visit_ty_common(&field.ty);
|
||||
self.walk_ty(&field.ty);
|
||||
walk_list!(self, visit_attribute, &field.attrs);
|
||||
} else {
|
||||
self.visit_field_def(field);
|
||||
}
|
||||
|
@ -291,13 +292,11 @@ impl<'a> AstValidator<'a> {
|
|||
}
|
||||
|
||||
fn deny_unnamed_field(&self, field: &FieldDef) {
|
||||
if let Some(ident) = field.ident &&
|
||||
ident.name == kw::Underscore {
|
||||
self.err_handler()
|
||||
.emit_err(errors::InvalidUnnamedField {
|
||||
span: field.span,
|
||||
ident_span: ident.span
|
||||
});
|
||||
if let Some(ident) = field.ident
|
||||
&& ident.name == kw::Underscore
|
||||
{
|
||||
self.err_handler()
|
||||
.emit_err(errors::InvalidUnnamedField { span: field.span, ident_span: ident.span });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1180,28 +1179,40 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
(BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
|
||||
self.err_handler().emit_err(errors::OptionalTraitSupertrait {
|
||||
span: poly.span,
|
||||
path_str: pprust::path_to_string(&poly.trait_ref.path)
|
||||
path_str: pprust::path_to_string(&poly.trait_ref.path),
|
||||
});
|
||||
}
|
||||
(BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
|
||||
self.err_handler().emit_err(errors::OptionalTraitObject {span: poly.span});
|
||||
self.err_handler().emit_err(errors::OptionalTraitObject { span: poly.span });
|
||||
}
|
||||
(_, TraitBoundModifier::MaybeConst) if let Some(reason) = &self.disallow_tilde_const => {
|
||||
(_, TraitBoundModifier::MaybeConst)
|
||||
if let Some(reason) = &self.disallow_tilde_const =>
|
||||
{
|
||||
let reason = match reason {
|
||||
DisallowTildeConstContext::TraitObject => errors::TildeConstReason::TraitObject,
|
||||
DisallowTildeConstContext::Fn(FnKind::Closure(..)) => errors::TildeConstReason::Closure,
|
||||
DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => errors::TildeConstReason::Function { ident: ident.span },
|
||||
DisallowTildeConstContext::TraitObject => {
|
||||
errors::TildeConstReason::TraitObject
|
||||
}
|
||||
DisallowTildeConstContext::Fn(FnKind::Closure(..)) => {
|
||||
errors::TildeConstReason::Closure
|
||||
}
|
||||
DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => {
|
||||
errors::TildeConstReason::Function { ident: ident.span }
|
||||
}
|
||||
};
|
||||
self.err_handler().emit_err(errors::TildeConstDisallowed {
|
||||
span: bound.span(),
|
||||
reason
|
||||
});
|
||||
self.err_handler()
|
||||
.emit_err(errors::TildeConstDisallowed { span: bound.span(), reason });
|
||||
}
|
||||
(_, TraitBoundModifier::MaybeConstMaybe) => {
|
||||
self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "?" });
|
||||
self.err_handler().emit_err(errors::OptionalConstExclusive {
|
||||
span: bound.span(),
|
||||
modifier: "?",
|
||||
});
|
||||
}
|
||||
(_, TraitBoundModifier::MaybeConstNegative) => {
|
||||
self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span(), modifier: "!" });
|
||||
self.err_handler().emit_err(errors::OptionalConstExclusive {
|
||||
span: bound.span(),
|
||||
modifier: "!",
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -1214,7 +1225,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
{
|
||||
for arg in &args.args {
|
||||
if let ast::AngleBracketedArg::Constraint(constraint) = arg {
|
||||
self.err_handler().emit_err(errors::ConstraintOnNegativeBound { span: constraint.span });
|
||||
self.err_handler()
|
||||
.emit_err(errors::ConstraintOnNegativeBound { span: constraint.span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -658,7 +658,7 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
|
|||
|
||||
for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
|
||||
.iter()
|
||||
.filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
|
||||
.filter(|&&(f1, f2)| features.active(f1) && features.active(f2))
|
||||
{
|
||||
if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
|
||||
if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
//!
|
||||
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_is_partitioned)]
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
#![feature(associated_type_bounds)]
|
||||
|
|
|
@ -684,8 +684,8 @@ pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> St
|
|||
for piece in pieces {
|
||||
match piece {
|
||||
FormatArgsPiece::Literal(s) => {
|
||||
for c in s.as_str().escape_debug() {
|
||||
template.push(c);
|
||||
for c in s.as_str().chars() {
|
||||
template.extend(c.escape_debug());
|
||||
if let '{' | '}' = c {
|
||||
template.push(c);
|
||||
}
|
||||
|
|
|
@ -405,7 +405,9 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER {
|
||||
if let Some(s) = since
|
||||
&& s.as_str() == VERSION_PLACEHOLDER
|
||||
{
|
||||
since = Some(rust_version_symbol());
|
||||
}
|
||||
|
||||
|
@ -694,13 +696,16 @@ pub fn eval_condition(
|
|||
!eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
|
||||
}
|
||||
sym::target => {
|
||||
if let Some(features) = features && !features.cfg_target_compact {
|
||||
if let Some(features) = features
|
||||
&& !features.cfg_target_compact
|
||||
{
|
||||
feature_err(
|
||||
sess,
|
||||
sym::cfg_target_compact,
|
||||
cfg.span,
|
||||
"compact `cfg(target(..))` is experimental and subject to change"
|
||||
).emit();
|
||||
"compact `cfg(target(..))` is experimental and subject to change",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
mis.iter().fold(true, |res, mi| {
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
|
||||
//! to this crate.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![feature(let_chains)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
//! -k list/and@1 fallback/likelysubtags@1 fallback/parents@1 fallback/supplement/co@1 \
|
||||
//! --cldr-tag latest --icuexport-tag latest -o src/data
|
||||
//! ```
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![allow(elided_lifetimes_in_paths)]
|
||||
|
||||
mod data {
|
||||
|
|
|
@ -351,7 +351,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
}
|
||||
// Check if we are in a situation of `ident @ ident` where we want to suggest
|
||||
// `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
|
||||
if let Some(subpat) = sub && self.pat.is_none() {
|
||||
if let Some(subpat) = sub
|
||||
&& self.pat.is_none()
|
||||
{
|
||||
self.visit_pat(subpat);
|
||||
if self.pat.is_some() {
|
||||
self.parent_pat = Some(p);
|
||||
|
@ -370,7 +372,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
let mut finder =
|
||||
ExpressionFinder { expr_span: move_span, expr: None, pat: None, parent_pat: None };
|
||||
finder.visit_expr(expr);
|
||||
if let Some(span) = span && let Some(expr) = finder.expr {
|
||||
if let Some(span) = span
|
||||
&& let Some(expr) = finder.expr
|
||||
{
|
||||
for (_, expr) in hir.parent_iter(expr.hir_id) {
|
||||
if let hir::Node::Expr(expr) = expr {
|
||||
if expr.span.contains(span) {
|
||||
|
@ -425,10 +429,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
Some(hir::intravisit::FnKind::Method(..)) => "method",
|
||||
Some(hir::intravisit::FnKind::Closure) => "closure",
|
||||
};
|
||||
span.push_span_label(
|
||||
ident.span,
|
||||
format!("in this {descr}"),
|
||||
);
|
||||
span.push_span_label(ident.span, format!("in this {descr}"));
|
||||
err.span_note(
|
||||
span,
|
||||
format!(
|
||||
|
@ -441,15 +442,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
let ty = place.ty(self.body, self.infcx.tcx).ty;
|
||||
if let hir::Node::Expr(parent_expr) = parent
|
||||
&& let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
|
||||
&& let hir::ExprKind::Path(
|
||||
hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _)
|
||||
) = call_expr.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(
|
||||
LangItem::IntoIterIntoIter,
|
||||
_,
|
||||
_,
|
||||
)) = call_expr.kind
|
||||
{
|
||||
// Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
|
||||
} else if let UseSpans::FnSelfUse {
|
||||
kind: CallKind::Normal { .. },
|
||||
..
|
||||
} = move_spans {
|
||||
} else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } =
|
||||
move_spans
|
||||
{
|
||||
// We already suggest cloning for these cases in `explain_captures`.
|
||||
} else {
|
||||
self.suggest_cloning(err, ty, expr, move_span);
|
||||
|
@ -602,10 +604,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
if self.sugg_span.is_some() {
|
||||
return;
|
||||
}
|
||||
if let hir::StmtKind::Local(hir::Local {
|
||||
span, ty, init: None, ..
|
||||
}) = &ex.kind && span.contains(self.decl_span) {
|
||||
self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
|
||||
if let hir::StmtKind::Local(hir::Local { span, ty, init: None, .. }) = &ex.kind
|
||||
&& span.contains(self.decl_span)
|
||||
{
|
||||
self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
|
||||
}
|
||||
hir::intravisit::walk_stmt(self, ex);
|
||||
}
|
||||
|
@ -743,19 +745,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
".clone()".to_owned()
|
||||
};
|
||||
if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
|
||||
&& self.infcx
|
||||
.type_implements_trait(
|
||||
clone_trait_def,
|
||||
[ty],
|
||||
self.param_env,
|
||||
)
|
||||
&& self
|
||||
.infcx
|
||||
.type_implements_trait(clone_trait_def, [ty], self.param_env)
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
let msg = if let ty::Adt(def, _) = ty.kind()
|
||||
&& [
|
||||
tcx.get_diagnostic_item(sym::Arc),
|
||||
tcx.get_diagnostic_item(sym::Rc),
|
||||
].contains(&Some(def.did()))
|
||||
&& [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)]
|
||||
.contains(&Some(def.did()))
|
||||
{
|
||||
"clone the value to increment its reference count"
|
||||
} else {
|
||||
|
@ -1350,9 +1347,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// };
|
||||
// corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`.
|
||||
if let hir::ExprKind::Call(path, [arg]) = ex.kind
|
||||
&& let hir::ExprKind::Path(
|
||||
hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _),
|
||||
) = path.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(
|
||||
LangItem::IntoIterIntoIter,
|
||||
_,
|
||||
_,
|
||||
)) = path.kind
|
||||
&& arg.span.contains(self.issue_span)
|
||||
{
|
||||
// Find `IntoIterator::into_iter(<head>)`
|
||||
|
@ -1370,18 +1369,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
..
|
||||
}) = stmt.kind
|
||||
&& let hir::ExprKind::Call(path, _args) = call.kind
|
||||
&& let hir::ExprKind::Path(
|
||||
hir::QPath::LangItem(LangItem::IteratorNext, _, _),
|
||||
) = path.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _)) =
|
||||
path.kind
|
||||
&& let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind
|
||||
&& let hir::QPath::LangItem(LangItem::OptionSome, pat_span, _) = path
|
||||
&& call.span.contains(self.issue_span)
|
||||
{
|
||||
// Find `<pat>` and the span for the whole `for` loop.
|
||||
if let PatField { pat: hir::Pat {
|
||||
kind: hir::PatKind::Binding(_, _, ident, ..),
|
||||
if let PatField {
|
||||
pat: hir::Pat { kind: hir::PatKind::Binding(_, _, ident, ..), .. },
|
||||
..
|
||||
}, ..} = field {
|
||||
} = field
|
||||
{
|
||||
self.loop_bind = Some(ident);
|
||||
}
|
||||
self.head_span = Some(*head_span);
|
||||
|
@ -1441,18 +1440,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// A bare path doesn't need a `let` assignment, it's already a simple
|
||||
// binding access.
|
||||
// As a new binding wasn't added, we don't need to modify the advancing call.
|
||||
sugg.push((
|
||||
loop_span.with_hi(pat_span.lo()),
|
||||
format!("while let Some("),
|
||||
));
|
||||
sugg.push((loop_span.with_hi(pat_span.lo()), format!("while let Some(")));
|
||||
sugg.push((
|
||||
pat_span.shrink_to_hi().with_hi(head.span.lo()),
|
||||
") = ".to_string(),
|
||||
));
|
||||
sugg.push((
|
||||
head.span.shrink_to_hi(),
|
||||
".next()".to_string(),
|
||||
));
|
||||
sugg.push((head.span.shrink_to_hi(), ".next()".to_string()));
|
||||
} else {
|
||||
// Needs a new a `let` binding.
|
||||
let indent = if let Some(indent) = sm.indentation_before(loop_span) {
|
||||
|
@ -1483,11 +1476,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
sugg.push((recv.span, "iter".to_string()));
|
||||
}
|
||||
}
|
||||
err.multipart_suggestion(
|
||||
msg,
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
|
||||
} else {
|
||||
err.help(msg);
|
||||
}
|
||||
|
@ -1666,69 +1655,80 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
|
||||
if e.span.contains(self.capture_span) {
|
||||
if let hir::ExprKind::Closure(&hir::Closure {
|
||||
movability: None,
|
||||
body,
|
||||
fn_arg_span,
|
||||
fn_decl: hir::FnDecl{ inputs, .. },
|
||||
..
|
||||
}) = e.kind &&
|
||||
let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) {
|
||||
self.suggest_arg = "this: &Self".to_string();
|
||||
if inputs.len() > 0 {
|
||||
self.suggest_arg.push_str(", ");
|
||||
}
|
||||
self.in_closure = true;
|
||||
self.closure_arg_span = fn_arg_span;
|
||||
self.visit_expr(body);
|
||||
self.in_closure = false;
|
||||
movability: None,
|
||||
body,
|
||||
fn_arg_span,
|
||||
fn_decl: hir::FnDecl { inputs, .. },
|
||||
..
|
||||
}) = e.kind
|
||||
&& let Some(hir::Node::Expr(body)) = self.hir.find(body.hir_id)
|
||||
{
|
||||
self.suggest_arg = "this: &Self".to_string();
|
||||
if inputs.len() > 0 {
|
||||
self.suggest_arg.push_str(", ");
|
||||
}
|
||||
self.in_closure = true;
|
||||
self.closure_arg_span = fn_arg_span;
|
||||
self.visit_expr(body);
|
||||
self.in_closure = false;
|
||||
}
|
||||
}
|
||||
if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e {
|
||||
if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
|
||||
seg.ident.name == kw::SelfLower && self.in_closure {
|
||||
self.closure_change_spans.push(e.span);
|
||||
if let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path
|
||||
&& seg.ident.name == kw::SelfLower
|
||||
&& self.in_closure
|
||||
{
|
||||
self.closure_change_spans.push(e.span);
|
||||
}
|
||||
}
|
||||
hir::intravisit::walk_expr(self, e);
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, local: &'hir hir::Local<'hir>) {
|
||||
if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat &&
|
||||
let Some(init) = local.init
|
||||
if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } =
|
||||
local.pat
|
||||
&& let Some(init) = local.init
|
||||
{
|
||||
if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure {
|
||||
movability: None,
|
||||
..
|
||||
}), .. } = init &&
|
||||
init.span.contains(self.capture_span) {
|
||||
self.closure_local_id = Some(*hir_id);
|
||||
if let hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { movability: None, .. }),
|
||||
..
|
||||
} = init
|
||||
&& init.span.contains(self.capture_span)
|
||||
{
|
||||
self.closure_local_id = Some(*hir_id);
|
||||
}
|
||||
}
|
||||
hir::intravisit::walk_local(self, local);
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) {
|
||||
if let hir::StmtKind::Semi(e) = s.kind &&
|
||||
let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind &&
|
||||
let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
|
||||
let Res::Local(hir_id) = seg.res &&
|
||||
Some(hir_id) == self.closure_local_id {
|
||||
let (span, arg_str) = if args.len() > 0 {
|
||||
(args[0].span.shrink_to_lo(), "self, ".to_string())
|
||||
} else {
|
||||
let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
|
||||
(span, "(self)".to_string())
|
||||
};
|
||||
self.closure_call_changes.push((span, arg_str));
|
||||
if let hir::StmtKind::Semi(e) = s.kind
|
||||
&& let hir::ExprKind::Call(
|
||||
hir::Expr { kind: hir::ExprKind::Path(path), .. },
|
||||
args,
|
||||
) = e.kind
|
||||
&& let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path
|
||||
&& let Res::Local(hir_id) = seg.res
|
||||
&& Some(hir_id) == self.closure_local_id
|
||||
{
|
||||
let (span, arg_str) = if args.len() > 0 {
|
||||
(args[0].span.shrink_to_lo(), "self, ".to_string())
|
||||
} else {
|
||||
let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
|
||||
(span, "(self)".to_string())
|
||||
};
|
||||
self.closure_call_changes.push((span, arg_str));
|
||||
}
|
||||
hir::intravisit::walk_stmt(self, s);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(hir::Node::ImplItem(
|
||||
hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. }
|
||||
)) = hir.find(self.mir_hir_id()) &&
|
||||
let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) {
|
||||
if let Some(hir::Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(_fn_sig, body_id),
|
||||
..
|
||||
})) = hir.find(self.mir_hir_id())
|
||||
&& let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
|
||||
{
|
||||
let mut finder = ExpressionFinder {
|
||||
capture_span: *capture_kind_span,
|
||||
closure_change_spans: vec![],
|
||||
|
@ -2299,15 +2299,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
visitor.visit_stmt(stmt);
|
||||
|
||||
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
let expr_ty: Option<Ty<'_>> = visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
|
||||
let expr_ty: Option<Ty<'_>> =
|
||||
visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
|
||||
|
||||
let is_format_arguments_item =
|
||||
if let Some(expr_ty) = expr_ty
|
||||
&& let ty::Adt(adt, _) = expr_ty.kind() {
|
||||
self.infcx.tcx.lang_items().get(LangItem::FormatArguments) == Some(adt.did())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let is_format_arguments_item = if let Some(expr_ty) = expr_ty
|
||||
&& let ty::Adt(adt, _) = expr_ty.kind()
|
||||
{
|
||||
self.infcx.tcx.lang_items().get(LangItem::FormatArguments)
|
||||
== Some(adt.did())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if visitor.found == 0
|
||||
&& stmt.span.contains(proper_span)
|
||||
|
|
|
@ -76,10 +76,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
expr_finder.visit_expr(body.value);
|
||||
if let Some(mut expr) = expr_finder.result {
|
||||
while let hir::ExprKind::AddrOf(_, _, inner)
|
||||
| hir::ExprKind::Unary(hir::UnOp::Deref, inner)
|
||||
| hir::ExprKind::Field(inner, _)
|
||||
| hir::ExprKind::MethodCall(_, inner, _, _)
|
||||
| hir::ExprKind::Index(inner, _, _) = &expr.kind
|
||||
| hir::ExprKind::Unary(hir::UnOp::Deref, inner)
|
||||
| hir::ExprKind::Field(inner, _)
|
||||
| hir::ExprKind::MethodCall(_, inner, _, _)
|
||||
| hir::ExprKind::Index(inner, _, _) = &expr.kind
|
||||
{
|
||||
expr = inner;
|
||||
}
|
||||
|
@ -88,10 +88,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
&& let hir::def::Res::Local(hir_id) = p.res
|
||||
&& let Some(hir::Node::Pat(pat)) = tcx.hir().find(hir_id)
|
||||
{
|
||||
err.span_label(
|
||||
pat.span,
|
||||
format!("binding `{ident}` declared here"),
|
||||
);
|
||||
err.span_label(pat.span, format!("binding `{ident}` declared here"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -419,7 +416,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
if self.local_names[local].is_some()
|
||||
&& let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
|
||||
&& let Some(borrowed_local) = place.as_local()
|
||||
&& self.local_names[borrowed_local].is_some() && local != borrowed_local
|
||||
&& self.local_names[borrowed_local].is_some()
|
||||
&& local != borrowed_local
|
||||
{
|
||||
should_note_order = true;
|
||||
}
|
||||
|
|
|
@ -780,19 +780,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
|
||||
debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
|
||||
if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
|
||||
&& let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind
|
||||
&& let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) =
|
||||
**kind
|
||||
{
|
||||
debug!("move_spans: def_id={:?} places={:?}", def_id, places);
|
||||
let def_id = def_id.expect_local();
|
||||
if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
|
||||
self.closure_span(def_id, moved_place, places)
|
||||
{
|
||||
return ClosureUse {
|
||||
generator_kind,
|
||||
args_span,
|
||||
capture_kind_span,
|
||||
path_span,
|
||||
};
|
||||
return ClosureUse { generator_kind, args_span, capture_kind_span, path_span };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1123,7 +1119,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
&self.infcx.tcx.sess.parse_sess.span_diagnostic,
|
||||
CaptureReasonSuggest::FreshReborrow {
|
||||
span: move_span.shrink_to_hi(),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(clone_trait) = tcx.lang_items().clone_trait()
|
||||
&& let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
|
||||
|
|
|
@ -396,17 +396,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
let upvar_hir_id = captured_place.get_root_variable();
|
||||
|
||||
if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
|
||||
&& let hir::PatKind::Binding(
|
||||
hir::BindingAnnotation::NONE,
|
||||
_,
|
||||
upvar_ident,
|
||||
_,
|
||||
) = pat.kind
|
||||
&& let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, upvar_ident, _) =
|
||||
pat.kind
|
||||
{
|
||||
if upvar_ident.name == kw::SelfLower {
|
||||
for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) {
|
||||
if let Some(fn_decl) = node.fn_decl() {
|
||||
if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) {
|
||||
if !matches!(
|
||||
fn_decl.implicit_self,
|
||||
hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef
|
||||
) {
|
||||
err.span_suggestion(
|
||||
upvar_ident.span,
|
||||
"consider changing this to be mutable",
|
||||
|
@ -573,7 +572,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
self.ty,
|
||||
),
|
||||
vec![
|
||||
vec![ // val.insert(index, rv);
|
||||
vec![
|
||||
// val.insert(index, rv);
|
||||
(
|
||||
val.span.shrink_to_hi().with_hi(index.span.lo()),
|
||||
".insert(".to_string(),
|
||||
|
@ -584,7 +584,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
),
|
||||
(rv.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
vec![ // val.get_mut(index).map(|v| { *v = rv; });
|
||||
vec![
|
||||
// val.get_mut(index).map(|v| { *v = rv; });
|
||||
(
|
||||
val.span.shrink_to_hi().with_hi(index.span.lo()),
|
||||
".get_mut(".to_string(),
|
||||
|
@ -593,12 +594,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
index.span.shrink_to_hi().with_hi(place.span.hi()),
|
||||
").map(|val| { *val".to_string(),
|
||||
),
|
||||
(
|
||||
rv.span.shrink_to_hi(),
|
||||
"; })".to_string(),
|
||||
),
|
||||
(rv.span.shrink_to_hi(), "; })".to_string()),
|
||||
],
|
||||
vec![ // let x = val.entry(index).or_insert(rv);
|
||||
vec![
|
||||
// let x = val.entry(index).or_insert(rv);
|
||||
(val.span.shrink_to_lo(), "let val = ".to_string()),
|
||||
(
|
||||
val.span.shrink_to_hi().with_hi(index.span.lo()),
|
||||
|
@ -747,10 +746,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
&& let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let mut v = BindingFinder {
|
||||
span: pat_span,
|
||||
hir_id: None,
|
||||
};
|
||||
let mut v = BindingFinder { span: pat_span, hir_id: None };
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
} else {
|
||||
|
@ -766,7 +762,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
|
||||
..
|
||||
})) = hir_map.find(hir_id)
|
||||
&& let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
|
||||
&& let Ok(name) =
|
||||
self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
|
||||
{
|
||||
err.span_suggestion(
|
||||
pat_span,
|
||||
|
@ -879,12 +876,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
// `span` corresponds to the expression being iterated, find the `for`-loop desugared
|
||||
// expression with that span in order to identify potential fixes when encountering a
|
||||
// read-only iterator that should be mutable.
|
||||
let mut v = Finder {
|
||||
span,
|
||||
expr: None,
|
||||
};
|
||||
let mut v = Finder { span, expr: None };
|
||||
v.visit_block(block);
|
||||
if let Some(expr) = v.expr && let Call(_, [expr]) = expr.kind {
|
||||
if let Some(expr) = v.expr
|
||||
&& let Call(_, [expr]) = expr.kind
|
||||
{
|
||||
match expr.kind {
|
||||
MethodCall(path_segment, _, _, span) => {
|
||||
// We have `for _ in iter.read_only_iter()`, try to
|
||||
|
@ -1032,38 +1028,42 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
let source = self.body.source;
|
||||
let hir = self.infcx.tcx.hir();
|
||||
if let InstanceDef::Item(def_id) = source.instance
|
||||
&& let Some(Node::Expr(hir::Expr { hir_id, kind, ..})) = hir.get_if_local(def_id)
|
||||
&& let ExprKind::Closure(closure) = kind && closure.movability == None
|
||||
&& let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) {
|
||||
let mut cur_expr = expr;
|
||||
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
|
||||
if path_segment.ident.name == sym::iter {
|
||||
// check `_ty` has `iter_mut` method
|
||||
let res = self
|
||||
.infcx
|
||||
.tcx
|
||||
.typeck(path_segment.hir_id.owner.def_id)
|
||||
.type_dependent_def_id(cur_expr.hir_id)
|
||||
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
|
||||
.map(|def_id| self.infcx.tcx.associated_items(def_id))
|
||||
.map(|assoc_items| {
|
||||
assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
|
||||
});
|
||||
&& let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = hir.get_if_local(def_id)
|
||||
&& let ExprKind::Closure(closure) = kind
|
||||
&& closure.movability == None
|
||||
&& let Some(Node::Expr(expr)) = hir.find_parent(*hir_id)
|
||||
{
|
||||
let mut cur_expr = expr;
|
||||
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
|
||||
if path_segment.ident.name == sym::iter {
|
||||
// check `_ty` has `iter_mut` method
|
||||
let res = self
|
||||
.infcx
|
||||
.tcx
|
||||
.typeck(path_segment.hir_id.owner.def_id)
|
||||
.type_dependent_def_id(cur_expr.hir_id)
|
||||
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
|
||||
.map(|def_id| self.infcx.tcx.associated_items(def_id))
|
||||
.map(|assoc_items| {
|
||||
assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
|
||||
});
|
||||
|
||||
if let Some(mut res) = res && res.peek().is_some() {
|
||||
err.span_suggestion_verbose(
|
||||
path_segment.ident.span,
|
||||
"you may want to use `iter_mut` here",
|
||||
"iter_mut",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
cur_expr = recv;
|
||||
if let Some(mut res) = res
|
||||
&& res.peek().is_some()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
path_segment.ident.span,
|
||||
"you may want to use `iter_mut` here",
|
||||
"iter_mut",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
cur_expr = recv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_make_local_mut(
|
||||
|
@ -1200,14 +1200,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
}
|
||||
let hir_map = self.infcx.tcx.hir();
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local() &&
|
||||
let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let mut v = BindingFinder {
|
||||
span: err_label_span,
|
||||
hir_id: None,
|
||||
};
|
||||
let mut v = BindingFinder { span: err_label_span, hir_id: None };
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
} else {
|
||||
|
@ -1215,15 +1212,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
|
||||
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
|
||||
{
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => (
|
||||
"specifying",
|
||||
local.pat.span.shrink_to_hi(),
|
||||
format!(": {message}"),
|
||||
),
|
||||
None => {
|
||||
("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
|
||||
}
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
|
@ -1234,9 +1229,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
err.span_label(
|
||||
err_label_span,
|
||||
format!(
|
||||
"consider changing this binding's type to be: `{message}`"
|
||||
),
|
||||
format!("consider changing this binding's type to be: `{message}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1380,11 +1373,7 @@ fn suggest_ampmut<'tcx>(
|
|||
let ty_mut = decl_ty.builtin_deref(true).unwrap();
|
||||
assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
|
||||
|
||||
(
|
||||
false,
|
||||
span,
|
||||
format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty)
|
||||
)
|
||||
(false, span, format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty_mut.ty))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -942,9 +942,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
|||
ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {}
|
||||
_ => return false,
|
||||
}
|
||||
tcx.any_free_region_meets(pred, |r| {
|
||||
*r == ty::ReEarlyBound(region)
|
||||
})
|
||||
tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyBound(region))
|
||||
})
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
//! This query borrow-checks the MIR to (further) ensure it is not broken.
|
||||
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
|
@ -11,7 +14,6 @@
|
|||
#![feature(trusted_step)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate rustc_middle;
|
||||
|
@ -173,7 +175,9 @@ fn do_mir_borrowck<'tcx>(
|
|||
for var_debug_info in &input_body.var_debug_info {
|
||||
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
|
||||
if let Some(local) = place.as_local() {
|
||||
if let Some(prev_name) = local_names[local] && var_debug_info.name != prev_name {
|
||||
if let Some(prev_name) = local_names[local]
|
||||
&& var_debug_info.name != prev_name
|
||||
{
|
||||
span_bug!(
|
||||
var_debug_info.source_info.span,
|
||||
"local {:?} has many names (`{}` vs `{}`)",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::OpaqueTyOrigin;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
|
@ -308,20 +309,19 @@ fn check_opaque_type_well_formed<'tcx>(
|
|||
return Ok(definition_ty);
|
||||
};
|
||||
let param_env = tcx.param_env(def_id);
|
||||
// HACK This bubble is required for this tests to pass:
|
||||
// nested-return-type2-tait2.rs
|
||||
// nested-return-type2-tait3.rs
|
||||
|
||||
let mut parent_def_id = def_id;
|
||||
while tcx.def_kind(parent_def_id) == DefKind::OpaqueTy {
|
||||
parent_def_id = tcx.local_parent(parent_def_id);
|
||||
}
|
||||
|
||||
// FIXME(-Ztrait-solver=next): We probably should use `DefiningAnchor::Error`
|
||||
// and prepopulate this `InferCtxt` with known opaque values, rather than
|
||||
// using the `Bind` anchor here. For now it's fine.
|
||||
let infcx = tcx
|
||||
.infer_ctxt()
|
||||
.with_next_trait_solver(next_trait_solver)
|
||||
.with_opaque_type_inference(if next_trait_solver {
|
||||
DefiningAnchor::Bind(def_id)
|
||||
} else {
|
||||
DefiningAnchor::Bubble
|
||||
})
|
||||
.with_opaque_type_inference(DefiningAnchor::Bind(parent_def_id))
|
||||
.build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let identity_args = GenericArgs::identity_for_item(tcx, def_id);
|
||||
|
|
|
@ -49,7 +49,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
// If the query has created new universes and errors are going to be emitted, register the
|
||||
// cause of these new universes for improved diagnostics.
|
||||
let universe = self.infcx.universe();
|
||||
if old_universe != universe && let Some(error_info) = error_info {
|
||||
if old_universe != universe
|
||||
&& let Some(error_info) = error_info
|
||||
{
|
||||
let universe_info = error_info.to_universe_info(old_universe);
|
||||
for u in (old_universe + 1)..=universe {
|
||||
self.borrowck_context.constraints.universe_causes.insert(u, universe_info.clone());
|
||||
|
|
|
@ -21,20 +21,22 @@ pub fn expand(
|
|||
|
||||
// Allow using `#[alloc_error_handler]` on an item statement
|
||||
// FIXME - if we get deref patterns, use them to reduce duplication here
|
||||
let (item, is_stmt, sig_span) =
|
||||
if let Annotatable::Item(item) = &item
|
||||
&& let ItemKind::Fn(fn_kind) = &item.kind
|
||||
{
|
||||
(item, false, ecx.with_def_site_ctxt(fn_kind.sig.span))
|
||||
} else if let Annotatable::Stmt(stmt) = &item
|
||||
&& let StmtKind::Item(item) = &stmt.kind
|
||||
&& let ItemKind::Fn(fn_kind) = &item.kind
|
||||
{
|
||||
(item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
|
||||
} else {
|
||||
ecx.sess.parse_sess.span_diagnostic.emit_err(errors::AllocErrorMustBeFn {span: item.span() });
|
||||
return vec![orig_item];
|
||||
};
|
||||
let (item, is_stmt, sig_span) = if let Annotatable::Item(item) = &item
|
||||
&& let ItemKind::Fn(fn_kind) = &item.kind
|
||||
{
|
||||
(item, false, ecx.with_def_site_ctxt(fn_kind.sig.span))
|
||||
} else if let Annotatable::Stmt(stmt) = &item
|
||||
&& let StmtKind::Item(item) = &stmt.kind
|
||||
&& let ItemKind::Fn(fn_kind) = &item.kind
|
||||
{
|
||||
(item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
|
||||
} else {
|
||||
ecx.sess
|
||||
.parse_sess
|
||||
.span_diagnostic
|
||||
.emit_err(errors::AllocErrorMustBeFn { span: item.span() });
|
||||
return vec![orig_item];
|
||||
};
|
||||
|
||||
// Generate a bunch of new items using the AllocFnFactory
|
||||
let span = ecx.with_def_site_ctxt(item.span);
|
||||
|
|
|
@ -193,10 +193,9 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
|||
fn manage_cond_expr(&mut self, expr: &mut P<Expr>) {
|
||||
match &mut expr.kind {
|
||||
ExprKind::AddrOf(_, mutability, local_expr) => {
|
||||
self.with_is_consumed_management(
|
||||
matches!(mutability, Mutability::Mut),
|
||||
|this| this.manage_cond_expr(local_expr)
|
||||
);
|
||||
self.with_is_consumed_management(matches!(mutability, Mutability::Mut), |this| {
|
||||
this.manage_cond_expr(local_expr)
|
||||
});
|
||||
}
|
||||
ExprKind::Array(local_exprs) => {
|
||||
for local_expr in local_exprs {
|
||||
|
@ -223,7 +222,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
|||
|this| {
|
||||
this.manage_cond_expr(lhs);
|
||||
this.manage_cond_expr(rhs);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
ExprKind::Call(_, local_exprs) => {
|
||||
|
@ -285,10 +284,9 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
|||
}
|
||||
}
|
||||
ExprKind::Unary(un_op, local_expr) => {
|
||||
self.with_is_consumed_management(
|
||||
matches!(un_op, UnOp::Neg | UnOp::Not),
|
||||
|this| this.manage_cond_expr(local_expr)
|
||||
);
|
||||
self.with_is_consumed_management(matches!(un_op, UnOp::Neg | UnOp::Not), |this| {
|
||||
this.manage_cond_expr(local_expr)
|
||||
});
|
||||
}
|
||||
// Expressions that are not worth or can not be captured.
|
||||
//
|
||||
|
|
|
@ -33,7 +33,7 @@ pub fn expand_concat(
|
|||
accumulator.push_str(&b.to_string());
|
||||
}
|
||||
Ok(ast::LitKind::CStr(..)) => {
|
||||
cx.emit_err(errors::ConcatCStrLit{ span: e.span});
|
||||
cx.emit_err(errors::ConcatCStrLit { span: e.span });
|
||||
has_errors = true;
|
||||
}
|
||||
Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
|
||||
|
@ -49,7 +49,9 @@ pub fn expand_concat(
|
|||
}
|
||||
},
|
||||
// We also want to allow negative numeric literals.
|
||||
ast::ExprKind::Unary(ast::UnOp::Neg, ref expr) if let ast::ExprKind::Lit(token_lit) = expr.kind => {
|
||||
ast::ExprKind::Unary(ast::UnOp::Neg, ref expr)
|
||||
if let ast::ExprKind::Lit(token_lit) = expr.kind =>
|
||||
{
|
||||
match ast::LitKind::from_token_lit(token_lit) {
|
||||
Ok(ast::LitKind::Int(i, _)) => accumulator.push_str(&format!("-{i}")),
|
||||
Ok(ast::LitKind::Float(f, _)) => accumulator.push_str(&format!("-{f}")),
|
||||
|
|
|
@ -140,8 +140,8 @@ pub fn expand_concat_bytes(
|
|||
}
|
||||
ast::ExprKind::Repeat(expr, count) => {
|
||||
if let ast::ExprKind::Lit(token_lit) = count.value.kind
|
||||
&& let Ok(ast::LitKind::Int(count_val, _)) =
|
||||
ast::LitKind::from_token_lit(token_lit)
|
||||
&& let Ok(ast::LitKind::Int(count_val, _)) =
|
||||
ast::LitKind::from_token_lit(token_lit)
|
||||
{
|
||||
if let Some(elem) =
|
||||
handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
|
||||
|
@ -151,7 +151,7 @@ pub fn expand_concat_bytes(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
cx.emit_err(errors::ConcatBytesBadRepeat {span: count.value.span });
|
||||
cx.emit_err(errors::ConcatBytesBadRepeat { span: count.value.span });
|
||||
}
|
||||
}
|
||||
&ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
|
||||
|
|
|
@ -106,7 +106,9 @@ fn cs_clone_simple(
|
|||
// This basic redundancy checking only prevents duplication of
|
||||
// assertions like `AssertParamIsClone<Foo>` where the type is a
|
||||
// simple name. That's enough to get a lot of cases, though.
|
||||
if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) {
|
||||
if let Some(name) = field.ty.kind.is_simple_path()
|
||||
&& !seen_type_names.insert(name)
|
||||
{
|
||||
// Already produced an assertion for this type.
|
||||
} else {
|
||||
// let _: AssertParamIsClone<FieldTy>;
|
||||
|
|
|
@ -73,7 +73,9 @@ fn cs_total_eq_assert(
|
|||
// This basic redundancy checking only prevents duplication of
|
||||
// assertions like `AssertParamIsEq<Foo>` where the type is a
|
||||
// simple name. That's enough to get a lot of cases, though.
|
||||
if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) {
|
||||
if let Some(name) = field.ty.kind.is_simple_path()
|
||||
&& !seen_type_names.insert(name)
|
||||
{
|
||||
// Already produced an assertion for this type.
|
||||
} else {
|
||||
// let _: AssertParamIsEq<FieldTy>;
|
||||
|
|
|
@ -21,25 +21,26 @@ pub fn expand_deriving_partial_ord(
|
|||
|
||||
// Order in which to perform matching
|
||||
let tag_then_data = if let Annotatable::Item(item) = item
|
||||
&& let ItemKind::Enum(def, _) = &item.kind {
|
||||
let dataful: Vec<bool> = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect();
|
||||
match dataful.iter().filter(|&&b| b).count() {
|
||||
// No data, placing the tag check first makes codegen simpler
|
||||
0 => true,
|
||||
1..=2 => false,
|
||||
_ => {
|
||||
(0..dataful.len()-1).any(|i| {
|
||||
if dataful[i] && let Some(idx) = dataful[i+1..].iter().position(|v| *v) {
|
||||
idx >= 2
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
&& let ItemKind::Enum(def, _) = &item.kind
|
||||
{
|
||||
let dataful: Vec<bool> = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect();
|
||||
match dataful.iter().filter(|&&b| b).count() {
|
||||
// No data, placing the tag check first makes codegen simpler
|
||||
0 => true,
|
||||
1..=2 => false,
|
||||
_ => (0..dataful.len() - 1).any(|i| {
|
||||
if dataful[i]
|
||||
&& let Some(idx) = dataful[i + 1..].iter().position(|v| *v)
|
||||
{
|
||||
idx >= 2
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
let partial_cmp_def = MethodDef {
|
||||
name: sym::partial_cmp,
|
||||
generics: Bounds::empty(),
|
||||
|
@ -133,12 +134,16 @@ fn cs_partial_cmp(
|
|||
if !tag_then_data
|
||||
&& let ExprKind::Match(_, arms) = &mut expr1.kind
|
||||
&& let Some(last) = arms.last_mut()
|
||||
&& let PatKind::Wild = last.pat.kind {
|
||||
last.body = expr2;
|
||||
expr1
|
||||
&& let PatKind::Wild = last.pat.kind
|
||||
{
|
||||
last.body = expr2;
|
||||
expr1
|
||||
} else {
|
||||
let eq_arm =
|
||||
cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1);
|
||||
let eq_arm = cx.arm(
|
||||
span,
|
||||
cx.pat_some(span, cx.pat_path(span, equal_path.clone())),
|
||||
expr1,
|
||||
);
|
||||
let neq_arm =
|
||||
cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
|
||||
cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm])
|
||||
|
|
|
@ -73,7 +73,9 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
|
|||
|
||||
let first_token = &p.token;
|
||||
|
||||
let fmtstr = if let token::Literal(lit) = first_token.kind && matches!(lit.kind, token::Str | token::StrRaw(_)) {
|
||||
let fmtstr = if let token::Literal(lit) = first_token.kind
|
||||
&& matches!(lit.kind, token::Str | token::StrRaw(_))
|
||||
{
|
||||
// This allows us to properly handle cases when the first comma
|
||||
// after the format string is mistakenly replaced with any operator,
|
||||
// which cause the expression parser to eat too much tokens.
|
||||
|
@ -176,7 +178,7 @@ fn make_format_args(
|
|||
&& block.stmts.len() == 1
|
||||
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
|
||||
&& let ExprKind::Path(None, path) = &expr.kind
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"quote your inlined format argument to use as string literal",
|
||||
|
@ -184,7 +186,7 @@ fn make_format_args(
|
|||
(unexpanded_fmt_span.shrink_to_hi(), "\"".to_string()),
|
||||
(unexpanded_fmt_span.shrink_to_lo(), "\"".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
let sugg_fmt = match args.explicit_args().len() {
|
||||
|
@ -257,8 +259,13 @@ fn make_format_args(
|
|||
if let Some(note) = err.note {
|
||||
e.note_ = Some(errors::InvalidFormatStringNote { note });
|
||||
}
|
||||
if let Some((label, span)) = err.secondary_label && is_source_literal {
|
||||
e.label_ = Some(errors::InvalidFormatStringLabel { span: fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label } );
|
||||
if let Some((label, span)) = err.secondary_label
|
||||
&& is_source_literal
|
||||
{
|
||||
e.label_ = Some(errors::InvalidFormatStringLabel {
|
||||
span: fmt_span.from_inner(InnerSpan::new(span.start, span.end)),
|
||||
label,
|
||||
});
|
||||
}
|
||||
match err.suggestion {
|
||||
parse::Suggestion::None => {}
|
||||
|
|
|
@ -24,20 +24,22 @@ pub fn expand(
|
|||
|
||||
// Allow using `#[global_allocator]` on an item statement
|
||||
// FIXME - if we get deref patterns, use them to reduce duplication here
|
||||
let (item, is_stmt, ty_span) =
|
||||
if let Annotatable::Item(item) = &item
|
||||
&& let ItemKind::Static(box ast::StaticItem { ty, ..}) = &item.kind
|
||||
{
|
||||
(item, false, ecx.with_def_site_ctxt(ty.span))
|
||||
} else if let Annotatable::Stmt(stmt) = &item
|
||||
&& let StmtKind::Item(item) = &stmt.kind
|
||||
&& let ItemKind::Static(box ast::StaticItem { ty, ..}) = &item.kind
|
||||
{
|
||||
(item, true, ecx.with_def_site_ctxt(ty.span))
|
||||
} else {
|
||||
ecx.sess.parse_sess.span_diagnostic.emit_err(errors::AllocMustStatics{span: item.span()});
|
||||
return vec![orig_item];
|
||||
};
|
||||
let (item, is_stmt, ty_span) = if let Annotatable::Item(item) = &item
|
||||
&& let ItemKind::Static(box ast::StaticItem { ty, .. }) = &item.kind
|
||||
{
|
||||
(item, false, ecx.with_def_site_ctxt(ty.span))
|
||||
} else if let Annotatable::Stmt(stmt) = &item
|
||||
&& let StmtKind::Item(item) = &stmt.kind
|
||||
&& let ItemKind::Static(box ast::StaticItem { ty, .. }) = &item.kind
|
||||
{
|
||||
(item, true, ecx.with_def_site_ctxt(ty.span))
|
||||
} else {
|
||||
ecx.sess
|
||||
.parse_sess
|
||||
.span_diagnostic
|
||||
.emit_err(errors::AllocMustStatics { span: item.span() });
|
||||
return vec![orig_item];
|
||||
};
|
||||
|
||||
// Generate a bunch of new items using the AllocFnFactory
|
||||
let span = ecx.with_def_site_ctxt(item.span);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
//! This crate contains implementations of built-in macros and other code generating facilities
|
||||
//! injecting code into the crate before it is lowered to HIR.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(array_windows)]
|
||||
#![feature(box_patterns)]
|
||||
|
@ -71,33 +74,35 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
}
|
||||
|
||||
register_bang! {
|
||||
// tidy-alphabetical-start
|
||||
asm: asm::expand_asm,
|
||||
assert: assert::expand_assert,
|
||||
cfg: cfg::expand_cfg,
|
||||
column: source_util::expand_column,
|
||||
compile_error: compile_error::expand_compile_error,
|
||||
concat: concat::expand_concat,
|
||||
concat_bytes: concat_bytes::expand_concat_bytes,
|
||||
concat_idents: concat_idents::expand_concat_idents,
|
||||
concat: concat::expand_concat,
|
||||
const_format_args: format::expand_format_args,
|
||||
core_panic: edition_panic::expand_panic,
|
||||
env: env::expand_env,
|
||||
file: source_util::expand_file,
|
||||
format_args_nl: format::expand_format_args_nl,
|
||||
format_args: format::expand_format_args,
|
||||
const_format_args: format::expand_format_args,
|
||||
format_args_nl: format::expand_format_args_nl,
|
||||
global_asm: asm::expand_global_asm,
|
||||
include: source_util::expand_include,
|
||||
include_bytes: source_util::expand_include_bytes,
|
||||
include_str: source_util::expand_include_str,
|
||||
include: source_util::expand_include,
|
||||
line: source_util::expand_line,
|
||||
log_syntax: log_syntax::expand_log_syntax,
|
||||
module_path: source_util::expand_mod,
|
||||
option_env: env::expand_option_env,
|
||||
core_panic: edition_panic::expand_panic,
|
||||
std_panic: edition_panic::expand_panic,
|
||||
unreachable: edition_panic::expand_unreachable,
|
||||
stringify: source_util::expand_stringify,
|
||||
trace_macros: trace_macros::expand_trace_macros,
|
||||
type_ascribe: type_ascribe::expand_type_ascribe,
|
||||
unreachable: edition_panic::expand_unreachable,
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
||||
register_attr! {
|
||||
|
|
|
@ -61,9 +61,14 @@ pub fn expand_file(
|
|||
|
||||
let topmost = cx.expansion_cause().unwrap_or(sp);
|
||||
let loc = cx.source_map().lookup_char_pos(topmost.lo());
|
||||
base::MacEager::expr(
|
||||
cx.expr_str(topmost, Symbol::intern(&loc.file.name.prefer_remapped().to_string_lossy())),
|
||||
)
|
||||
|
||||
use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
|
||||
base::MacEager::expr(cx.expr_str(
|
||||
topmost,
|
||||
Symbol::intern(
|
||||
&loc.file.name.for_scope(cx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn expand_stringify(
|
||||
|
|
|
@ -35,11 +35,13 @@ pub fn expand_test_case(
|
|||
let sp = ecx.with_def_site_ctxt(attr_sp);
|
||||
let (mut item, is_stmt) = match anno_item {
|
||||
Annotatable::Item(item) => (item, false),
|
||||
Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
|
||||
(i, true)
|
||||
} else {
|
||||
unreachable!()
|
||||
},
|
||||
Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => {
|
||||
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
|
||||
(i, true)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
ecx.emit_err(errors::TestCaseNonItem { span: anno_item.span() });
|
||||
return vec![];
|
||||
|
|
|
@ -414,11 +414,12 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
|||
// Note: must be kept in sync with get_caller_location from cg_ssa
|
||||
pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> {
|
||||
let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| {
|
||||
use rustc_session::RemapFileNameExt;
|
||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
||||
let caller = fx.tcx.sess.source_map().lookup_char_pos(topmost.lo());
|
||||
let const_loc = fx.tcx.const_caller_location((
|
||||
rustc_span::symbol::Symbol::intern(
|
||||
&caller.file.name.prefer_remapped().to_string_lossy(),
|
||||
&caller.file.name.for_codegen(&fx.tcx.sess).to_string_lossy(),
|
||||
),
|
||||
caller.line as u32,
|
||||
caller.col_display as u32 + 1,
|
||||
|
|
|
@ -95,7 +95,11 @@ impl DebugContext {
|
|||
match &source_file.name {
|
||||
FileName::Real(path) => {
|
||||
let (dir_path, file_name) =
|
||||
split_path_dir_and_file(path.remapped_path_if_available());
|
||||
split_path_dir_and_file(if self.should_remap_filepaths {
|
||||
path.remapped_path_if_available()
|
||||
} else {
|
||||
path.local_path_if_available()
|
||||
});
|
||||
let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
|
||||
let file_name = osstr_as_utf8_bytes(file_name);
|
||||
|
||||
|
@ -116,7 +120,14 @@ impl DebugContext {
|
|||
filename => {
|
||||
let dir_id = line_program.default_directory();
|
||||
let dummy_file_name = LineString::new(
|
||||
filename.prefer_remapped().to_string().into_bytes(),
|
||||
filename
|
||||
.display(if self.should_remap_filepaths {
|
||||
FileNameDisplayPreference::Remapped
|
||||
} else {
|
||||
FileNameDisplayPreference::Local
|
||||
})
|
||||
.to_string()
|
||||
.into_bytes(),
|
||||
line_program.encoding(),
|
||||
line_strings,
|
||||
);
|
||||
|
|
|
@ -31,6 +31,8 @@ pub(crate) struct DebugContext {
|
|||
|
||||
dwarf: DwarfUnit,
|
||||
unit_range_list: RangeList,
|
||||
|
||||
should_remap_filepaths: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct FunctionDebugContext {
|
||||
|
@ -63,12 +65,18 @@ impl DebugContext {
|
|||
|
||||
let mut dwarf = DwarfUnit::new(encoding);
|
||||
|
||||
let should_remap_filepaths = tcx.sess.should_prefer_remapped_for_codegen();
|
||||
|
||||
let producer = producer();
|
||||
let comp_dir = tcx
|
||||
.sess
|
||||
.opts
|
||||
.working_dir
|
||||
.to_string_lossy(FileNameDisplayPreference::Remapped)
|
||||
.to_string_lossy(if should_remap_filepaths {
|
||||
FileNameDisplayPreference::Remapped
|
||||
} else {
|
||||
FileNameDisplayPreference::Local
|
||||
})
|
||||
.into_owned();
|
||||
let (name, file_info) = match tcx.sess.local_crate_source_file() {
|
||||
Some(path) => {
|
||||
|
@ -102,7 +110,12 @@ impl DebugContext {
|
|||
root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
|
||||
}
|
||||
|
||||
DebugContext { endian, dwarf, unit_range_list: RangeList(Vec::new()) }
|
||||
DebugContext {
|
||||
endian,
|
||||
dwarf,
|
||||
unit_range_list: RangeList(Vec::new()),
|
||||
should_remap_filepaths,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn define_function(
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![feature(rustc_private)]
|
||||
// Note: please avoid adding other feature gates where possible
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
* TODO(antoyo): remove the patches.
|
||||
*/
|
||||
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![feature(
|
||||
rustc_private,
|
||||
decl_macro,
|
||||
|
|
|
@ -259,9 +259,17 @@ pub fn target_machine_factory(
|
|||
};
|
||||
let debuginfo_compression = SmallCStr::new(&debuginfo_compression);
|
||||
|
||||
let should_prefer_remapped_for_split_debuginfo_paths =
|
||||
sess.should_prefer_remapped_for_split_debuginfo_paths();
|
||||
|
||||
Arc::new(move |config: TargetMachineFactoryConfig| {
|
||||
let path_to_cstring_helper = |path: Option<PathBuf>| -> CString {
|
||||
let path = path_mapping.map_prefix(path.unwrap_or_default()).0;
|
||||
let path = path.unwrap_or_default();
|
||||
let path = if should_prefer_remapped_for_split_debuginfo_paths {
|
||||
path_mapping.map_prefix(path).0
|
||||
} else {
|
||||
path.into()
|
||||
};
|
||||
CString::new(path.to_str().unwrap()).unwrap()
|
||||
};
|
||||
|
||||
|
|
|
@ -1521,8 +1521,13 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
llfn: &'ll Value,
|
||||
) {
|
||||
let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) };
|
||||
if self.tcx.sess.is_sanitizer_cfi_enabled() && let Some(fn_abi) = fn_abi && is_indirect_call {
|
||||
if let Some(fn_attrs) = fn_attrs && fn_attrs.no_sanitize.contains(SanitizerSet::CFI) {
|
||||
if self.tcx.sess.is_sanitizer_cfi_enabled()
|
||||
&& let Some(fn_abi) = fn_abi
|
||||
&& is_indirect_call
|
||||
{
|
||||
if let Some(fn_attrs) = fn_attrs
|
||||
&& fn_attrs.no_sanitize.contains(SanitizerSet::CFI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1559,25 +1564,29 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
llfn: &'ll Value,
|
||||
) -> Option<llvm::OperandBundleDef<'ll>> {
|
||||
let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) };
|
||||
let kcfi_bundle =
|
||||
if self.tcx.sess.is_sanitizer_kcfi_enabled() && let Some(fn_abi) = fn_abi && is_indirect_call {
|
||||
if let Some(fn_attrs) = fn_attrs && fn_attrs.no_sanitize.contains(SanitizerSet::KCFI) {
|
||||
return None;
|
||||
}
|
||||
let kcfi_bundle = if self.tcx.sess.is_sanitizer_kcfi_enabled()
|
||||
&& let Some(fn_abi) = fn_abi
|
||||
&& is_indirect_call
|
||||
{
|
||||
if let Some(fn_attrs) = fn_attrs
|
||||
&& fn_attrs.no_sanitize.contains(SanitizerSet::KCFI)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut options = TypeIdOptions::empty();
|
||||
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
|
||||
options.insert(TypeIdOptions::GENERALIZE_POINTERS);
|
||||
}
|
||||
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
|
||||
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
|
||||
}
|
||||
let mut options = TypeIdOptions::empty();
|
||||
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
|
||||
options.insert(TypeIdOptions::GENERALIZE_POINTERS);
|
||||
}
|
||||
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
|
||||
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
|
||||
}
|
||||
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options);
|
||||
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options);
|
||||
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
kcfi_bundle
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
|
|||
llfn
|
||||
} else {
|
||||
let instance_def_id = instance.def_id();
|
||||
let llfn = if tcx.sess.target.arch == "x86" &&
|
||||
let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym)
|
||||
let llfn = if tcx.sess.target.arch == "x86"
|
||||
&& let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym)
|
||||
{
|
||||
// Fix for https://github.com/rust-lang/rust/issues/104453
|
||||
// On x86 Windows, LLVM uses 'L' as the prefix for any private
|
||||
|
@ -60,8 +60,18 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
|
|||
// LLVM will prefix the name with `__imp_`. Ideally, we'd like the
|
||||
// existing logic below to set the Storage Class, but it has an
|
||||
// exemption for MinGW for backwards compatability.
|
||||
let llfn = cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi, Some(instance));
|
||||
unsafe { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); }
|
||||
let llfn = cx.declare_fn(
|
||||
&common::i686_decorated_name(
|
||||
&dllimport,
|
||||
common::is_mingw_gnu_toolchain(&tcx.sess.target),
|
||||
true,
|
||||
),
|
||||
fn_abi,
|
||||
Some(instance),
|
||||
);
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
llfn
|
||||
} else {
|
||||
cx.declare_fn(sym, fn_abi, Some(instance))
|
||||
|
|
|
@ -182,10 +182,17 @@ fn check_and_apply_linkage<'ll, 'tcx>(
|
|||
llvm::LLVMSetInitializer(g2, g1);
|
||||
g2
|
||||
}
|
||||
} else if cx.tcx.sess.target.arch == "x86" &&
|
||||
let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym)
|
||||
} else if cx.tcx.sess.target.arch == "x86"
|
||||
&& let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym)
|
||||
{
|
||||
cx.declare_global(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&cx.tcx.sess.target), true), llty)
|
||||
cx.declare_global(
|
||||
&common::i686_decorated_name(
|
||||
&dllimport,
|
||||
common::is_mingw_gnu_toolchain(&cx.tcx.sess.target),
|
||||
true,
|
||||
),
|
||||
llty,
|
||||
)
|
||||
} else {
|
||||
// Generate an external declaration.
|
||||
// FIXME(nagisa): investigate whether it can be changed into define_global
|
||||
|
|
|
@ -126,9 +126,9 @@ impl GlobalFileTable {
|
|||
// Since rustc generates coverage maps with relative paths, the
|
||||
// compilation directory can be combined with the relative paths
|
||||
// to get absolute paths, if needed.
|
||||
let working_dir = Symbol::intern(
|
||||
&tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy(),
|
||||
);
|
||||
use rustc_session::RemapFileNameExt;
|
||||
let working_dir =
|
||||
Symbol::intern(&tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy());
|
||||
global_file_table.insert(working_dir);
|
||||
Self { global_file_table }
|
||||
}
|
||||
|
|
|
@ -75,7 +75,10 @@ fn make_mir_scope<'ll, 'tcx>(
|
|||
return;
|
||||
};
|
||||
|
||||
if let Some(vars) = variables && !vars.contains(scope) && scope_data.inlined.is_none() {
|
||||
if let Some(vars) = variables
|
||||
&& !vars.contains(scope)
|
||||
&& scope_data.inlined.is_none()
|
||||
{
|
||||
// Do not create a DIScope if there are no variables defined in this
|
||||
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
|
||||
debug_context.scopes[scope] = parent_scope;
|
||||
|
|
|
@ -547,48 +547,77 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) ->
|
|||
) -> &'ll DIFile {
|
||||
debug!(?source_file.name);
|
||||
|
||||
use rustc_session::RemapFileNameExt;
|
||||
let (directory, file_name) = match &source_file.name {
|
||||
FileName::Real(filename) => {
|
||||
let working_directory = &cx.sess().opts.working_dir;
|
||||
debug!(?working_directory);
|
||||
|
||||
let filename = cx
|
||||
.sess()
|
||||
.source_map()
|
||||
.path_mapping()
|
||||
.to_embeddable_absolute_path(filename.clone(), working_directory);
|
||||
if cx.sess().should_prefer_remapped_for_codegen() {
|
||||
let filename = cx
|
||||
.sess()
|
||||
.source_map()
|
||||
.path_mapping()
|
||||
.to_embeddable_absolute_path(filename.clone(), working_directory);
|
||||
|
||||
// Construct the absolute path of the file
|
||||
let abs_path = filename.remapped_path_if_available();
|
||||
debug!(?abs_path);
|
||||
// Construct the absolute path of the file
|
||||
let abs_path = filename.remapped_path_if_available();
|
||||
debug!(?abs_path);
|
||||
|
||||
if let Ok(rel_path) =
|
||||
abs_path.strip_prefix(working_directory.remapped_path_if_available())
|
||||
{
|
||||
// If the compiler's working directory (which also is the DW_AT_comp_dir of
|
||||
// the compilation unit) is a prefix of the path we are about to emit, then
|
||||
// only emit the part relative to the working directory.
|
||||
// Because of path remapping we sometimes see strange things here: `abs_path`
|
||||
// might actually look like a relative path
|
||||
// (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without
|
||||
// taking the working directory into account, downstream tooling will
|
||||
// interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`,
|
||||
// which makes no sense. Usually in such cases the working directory will also
|
||||
// be remapped to `<crate-name-and-version>` or some other prefix of the path
|
||||
// we are remapping, so we end up with
|
||||
// `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
|
||||
// By moving the working directory portion into the `directory` part of the
|
||||
// DIFile, we allow LLVM to emit just the relative path for DWARF, while
|
||||
// still emitting the correct absolute path for CodeView.
|
||||
(
|
||||
working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
|
||||
rel_path.to_string_lossy().into_owned(),
|
||||
)
|
||||
if let Ok(rel_path) =
|
||||
abs_path.strip_prefix(working_directory.remapped_path_if_available())
|
||||
{
|
||||
// If the compiler's working directory (which also is the DW_AT_comp_dir of
|
||||
// the compilation unit) is a prefix of the path we are about to emit, then
|
||||
// only emit the part relative to the working directory.
|
||||
// Because of path remapping we sometimes see strange things here: `abs_path`
|
||||
// might actually look like a relative path
|
||||
// (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without
|
||||
// taking the working directory into account, downstream tooling will
|
||||
// interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`,
|
||||
// which makes no sense. Usually in such cases the working directory will also
|
||||
// be remapped to `<crate-name-and-version>` or some other prefix of the path
|
||||
// we are remapping, so we end up with
|
||||
// `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
|
||||
// By moving the working directory portion into the `directory` part of the
|
||||
// DIFile, we allow LLVM to emit just the relative path for DWARF, while
|
||||
// still emitting the correct absolute path for CodeView.
|
||||
(
|
||||
working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
|
||||
rel_path.to_string_lossy().into_owned(),
|
||||
)
|
||||
} else {
|
||||
("".into(), abs_path.to_string_lossy().into_owned())
|
||||
}
|
||||
} else {
|
||||
("".into(), abs_path.to_string_lossy().into_owned())
|
||||
let working_directory = working_directory.local_path_if_available();
|
||||
let filename = filename.local_path_if_available();
|
||||
|
||||
debug!(?working_directory, ?filename);
|
||||
|
||||
let abs_path: Cow<'_, Path> = if filename.is_absolute() {
|
||||
filename.into()
|
||||
} else {
|
||||
let mut p = PathBuf::new();
|
||||
p.push(working_directory);
|
||||
p.push(filename);
|
||||
p.into()
|
||||
};
|
||||
|
||||
if let Ok(rel_path) = abs_path.strip_prefix(working_directory) {
|
||||
(
|
||||
working_directory.to_string_lossy().into(),
|
||||
rel_path.to_string_lossy().into_owned(),
|
||||
)
|
||||
} else {
|
||||
("".into(), abs_path.to_string_lossy().into_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
other => ("".into(), other.prefer_remapped().to_string_lossy().into_owned()),
|
||||
other => {
|
||||
debug!(?other);
|
||||
("".into(), other.for_codegen(cx.sess()).to_string_lossy().into_owned())
|
||||
}
|
||||
};
|
||||
|
||||
let hash_kind = match source_file.src_hash.kind {
|
||||
|
@ -822,8 +851,9 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
|
|||
// FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice.
|
||||
let producer = format!("clang LLVM ({rustc_producer})");
|
||||
|
||||
use rustc_session::RemapFileNameExt;
|
||||
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
|
||||
let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped);
|
||||
let work_dir = tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy();
|
||||
let flags = "\0";
|
||||
let output_filenames = tcx.output_filenames(());
|
||||
let split_name = if tcx.sess.target_can_use_split_dwarf() {
|
||||
|
@ -834,7 +864,13 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
|
|||
Some(codegen_unit_name),
|
||||
)
|
||||
// We get a path relative to the working directory from split_dwarf_path
|
||||
.map(|f| tcx.sess.source_map().path_mapping().map_prefix(f).0)
|
||||
.map(|f| {
|
||||
if tcx.sess.should_prefer_remapped_for_split_debuginfo_paths() {
|
||||
tcx.sess.source_map().path_mapping().map_prefix(f).0
|
||||
} else {
|
||||
f.into()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -537,7 +537,9 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
|
||||
// Only "class" methods are generally understood by LLVM,
|
||||
// so avoid methods on other types (e.g., `<*mut T>::null`).
|
||||
if let ty::Adt(def, ..) = impl_self_ty.kind() && !def.is_box() {
|
||||
if let ty::Adt(def, ..) = impl_self_ty.kind()
|
||||
&& !def.is_box()
|
||||
{
|
||||
// Again, only create type information if full debuginfo is enabled
|
||||
if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.has_param()
|
||||
{
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(extern_types)]
|
||||
#![feature(hash_raw_entry)]
|
||||
|
|
|
@ -368,17 +368,25 @@ fn link_rlib<'a>(
|
|||
let NativeLibKind::Static { bundle: None | Some(true), .. } = lib.kind else {
|
||||
continue;
|
||||
};
|
||||
if flavor == RlibFlavor::Normal && let Some(filename) = lib.filename {
|
||||
if flavor == RlibFlavor::Normal
|
||||
&& let Some(filename) = lib.filename
|
||||
{
|
||||
let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
|
||||
let src = read(path).map_err(|e| sess.emit_fatal(errors::ReadFileError {message: e }))?;
|
||||
let src =
|
||||
read(path).map_err(|e| sess.emit_fatal(errors::ReadFileError { message: e }))?;
|
||||
let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src);
|
||||
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
|
||||
packed_bundled_libs.push(wrapper_file);
|
||||
} else {
|
||||
let path =
|
||||
find_native_static_library(lib.name.as_str(), lib.verbatim, &lib_search_paths, sess);
|
||||
let path = find_native_static_library(
|
||||
lib.name.as_str(),
|
||||
lib.verbatim,
|
||||
&lib_search_paths,
|
||||
sess,
|
||||
);
|
||||
ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| {
|
||||
sess.emit_fatal(errors::AddNativeLibrary { library_path: path, error })});
|
||||
sess.emit_fatal(errors::AddNativeLibrary { library_path: path, error })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -626,6 +626,15 @@ impl<'a> Linker for GccLinker<'a> {
|
|||
self.linker_arg("--strip-all");
|
||||
}
|
||||
}
|
||||
match self.sess.opts.unstable_opts.debuginfo_compression {
|
||||
config::DebugInfoCompression::None => {}
|
||||
config::DebugInfoCompression::Zlib => {
|
||||
self.linker_arg("--compress-debug-sections=zlib");
|
||||
}
|
||||
config::DebugInfoCompression::Zstd => {
|
||||
self.linker_arg("--compress-debug-sections=zstd");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn no_crt_objects(&mut self) {
|
||||
|
|
|
@ -238,8 +238,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
&& let Some(fn_sig) = fn_sig()
|
||||
&& fn_sig.skip_binder().abi() != abi::Abi::Rust
|
||||
{
|
||||
struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
|
||||
.emit();
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
attr.span,
|
||||
E0737,
|
||||
"`#[track_caller]` requires Rust ABI"
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
if is_closure
|
||||
&& !tcx.features().closure_track_caller
|
||||
|
@ -435,17 +440,18 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
&& let [item] = items.as_slice()
|
||||
&& let Some((sym::align, literal)) = item.name_value_literal()
|
||||
{
|
||||
rustc_attr::parse_alignment(&literal.kind).map_err(|msg| {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0589,
|
||||
"invalid `repr(align)` attribute: {}",
|
||||
msg
|
||||
)
|
||||
.emit();
|
||||
})
|
||||
.ok()
|
||||
rustc_attr::parse_alignment(&literal.kind)
|
||||
.map_err(|msg| {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0589,
|
||||
"invalid `repr(align)` attribute: {}",
|
||||
msg
|
||||
)
|
||||
.emit();
|
||||
})
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -626,10 +632,7 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
|||
&& let ty::AssocItemContainer::ImplContainer = impl_item.container
|
||||
&& let Some(trait_item) = impl_item.trait_item_def_id
|
||||
{
|
||||
return tcx
|
||||
.codegen_fn_attrs(trait_item)
|
||||
.flags
|
||||
.intersects(CodegenFnAttrFlags::TRACK_CALLER);
|
||||
return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
|
||||
}
|
||||
|
||||
false
|
||||
|
|
|
@ -1454,10 +1454,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let tcx = bx.tcx();
|
||||
|
||||
let mut span_to_caller_location = |span: Span| {
|
||||
use rustc_session::RemapFileNameExt;
|
||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
||||
let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
|
||||
let const_loc = tcx.const_caller_location((
|
||||
Symbol::intern(&caller.file.name.prefer_remapped().to_string_lossy()),
|
||||
Symbol::intern(&caller.file.name.for_codegen(self.cx.sess()).to_string_lossy()),
|
||||
caller.line as u32,
|
||||
caller.col_display as u32 + 1,
|
||||
));
|
||||
|
@ -1555,7 +1556,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
|
||||
fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock {
|
||||
if let Some((cached_bb, cached_reason)) = self.terminate_block && reason == cached_reason {
|
||||
if let Some((cached_bb, cached_reason)) = self.terminate_block
|
||||
&& reason == cached_reason
|
||||
{
|
||||
return cached_bb;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,9 +117,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
sym::vtable_size => {
|
||||
let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
|
||||
bx.range_metadata(value, WrappingRange { start: 0, end: size_bound });
|
||||
},
|
||||
}
|
||||
// Alignment is always nonzero.
|
||||
sym::vtable_align => bx.range_metadata(value, WrappingRange { start: 1, end: !0 }),
|
||||
sym::vtable_align => {
|
||||
bx.range_metadata(value, WrappingRange { start: 1, end: !0 })
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
value
|
||||
|
@ -220,9 +222,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
} else {
|
||||
bx.exactudiv(args[0].immediate(), args[1].immediate())
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType {
|
||||
span,
|
||||
name,
|
||||
ty,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -238,7 +244,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
_ => bug!(),
|
||||
},
|
||||
None => {
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::BasicFloatType { span, name, ty: arg_tys[0] });
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::BasicFloatType {
|
||||
span,
|
||||
name,
|
||||
ty: arg_tys[0],
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -246,11 +256,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
sym::float_to_int_unchecked => {
|
||||
if float_type_width(arg_tys[0]).is_none() {
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::FloatToIntUnchecked { span, ty: arg_tys[0] });
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::FloatToIntUnchecked {
|
||||
span,
|
||||
ty: arg_tys[0],
|
||||
});
|
||||
return;
|
||||
}
|
||||
let Some((_width, signed)) = int_type_width_signed(ret_ty, bx.tcx()) else {
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::FloatToIntUnchecked { span, ty: ret_ty });
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::FloatToIntUnchecked {
|
||||
span,
|
||||
ty: ret_ty,
|
||||
});
|
||||
return;
|
||||
};
|
||||
if signed {
|
||||
|
@ -299,7 +315,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
};
|
||||
|
||||
let invalid_monomorphization = |ty| {
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
|
||||
bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType {
|
||||
span,
|
||||
name,
|
||||
ty,
|
||||
});
|
||||
};
|
||||
|
||||
match instruction {
|
||||
|
@ -319,7 +339,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
cmp = bx.ptrtoint(cmp, bx.type_isize());
|
||||
src = bx.ptrtoint(src, bx.type_isize());
|
||||
}
|
||||
let pair = bx.atomic_cmpxchg(dst, cmp, src, parse_ordering(bx, success), parse_ordering(bx, failure), weak);
|
||||
let pair = bx.atomic_cmpxchg(
|
||||
dst,
|
||||
cmp,
|
||||
src,
|
||||
parse_ordering(bx, success),
|
||||
parse_ordering(bx, failure),
|
||||
weak,
|
||||
);
|
||||
let val = bx.extract_value(pair, 0);
|
||||
let success = bx.extract_value(pair, 1);
|
||||
let val = bx.from_immediate(val);
|
||||
|
@ -345,11 +372,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
// Some platforms do not support atomic operations on pointers,
|
||||
// so we cast to integer first...
|
||||
let llty = bx.type_isize();
|
||||
let result = bx.atomic_load(llty, source, parse_ordering(bx, ordering), size);
|
||||
let result = bx.atomic_load(
|
||||
llty,
|
||||
source,
|
||||
parse_ordering(bx, ordering),
|
||||
size,
|
||||
);
|
||||
// ... and then cast the result back to a pointer
|
||||
bx.inttoptr(result, bx.backend_type(layout))
|
||||
} else {
|
||||
bx.atomic_load(bx.backend_type(layout), source, parse_ordering(bx, ordering), size)
|
||||
bx.atomic_load(
|
||||
bx.backend_type(layout),
|
||||
source,
|
||||
parse_ordering(bx, ordering),
|
||||
size,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return invalid_monomorphization(ty);
|
||||
|
@ -375,12 +412,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
|
||||
"fence" => {
|
||||
bx.atomic_fence(parse_ordering(bx, ordering), SynchronizationScope::CrossThread);
|
||||
bx.atomic_fence(
|
||||
parse_ordering(bx, ordering),
|
||||
SynchronizationScope::CrossThread,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
"singlethreadfence" => {
|
||||
bx.atomic_fence(parse_ordering(bx, ordering), SynchronizationScope::SingleThread);
|
||||
bx.atomic_fence(
|
||||
parse_ordering(bx, ordering),
|
||||
SynchronizationScope::SingleThread,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -239,17 +239,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
};
|
||||
if let OperandValueKind::Immediate(out_scalar) = cast_kind
|
||||
&& in_scalar.size(self.cx) == out_scalar.size(self.cx)
|
||||
{
|
||||
let operand_bty = bx.backend_type(operand.layout);
|
||||
let cast_bty = bx.backend_type(cast);
|
||||
Some(OperandValue::Immediate(self.transmute_immediate(
|
||||
bx,
|
||||
imm,
|
||||
in_scalar,
|
||||
operand_bty,
|
||||
out_scalar,
|
||||
cast_bty,
|
||||
)))
|
||||
{
|
||||
let operand_bty = bx.backend_type(operand.layout);
|
||||
let cast_bty = bx.backend_type(cast);
|
||||
Some(OperandValue::Immediate(self.transmute_immediate(
|
||||
bx,
|
||||
imm,
|
||||
in_scalar,
|
||||
operand_bty,
|
||||
out_scalar,
|
||||
cast_bty,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
const_eval_address_space_full =
|
||||
there are no more free addresses in the address space
|
||||
const_eval_align_check_failed = accessing memory with alignment {$has}, but alignment {$required} is required
|
||||
|
||||
const_eval_align_offset_invalid_align =
|
||||
`align_offset` called with non-power-of-two align: {$target_align}
|
||||
|
||||
const_eval_alignment_check_failed =
|
||||
accessing memory with alignment {$has}, but alignment {$required} is required
|
||||
{$msg ->
|
||||
[AccessedPtr] accessing memory
|
||||
*[other] accessing memory based on pointer
|
||||
} with alignment {$has}, but alignment {$required} is required
|
||||
|
||||
const_eval_already_reported =
|
||||
an error has already been reported elsewhere (this should not usually be printed)
|
||||
const_eval_assume_false =
|
||||
|
@ -61,7 +65,6 @@ const_eval_deref_coercion_non_const =
|
|||
.target_note = deref defined here
|
||||
const_eval_deref_function_pointer =
|
||||
accessing {$allocation} which contains a function
|
||||
const_eval_deref_test = dereferencing pointer failed
|
||||
const_eval_deref_vtable_pointer =
|
||||
accessing {$allocation} which contains a vtable
|
||||
const_eval_different_allocations =
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::mem;
|
||||
|
||||
use either::{Left, Right};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
|
@ -73,9 +75,9 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
|||
None => InternKind::Constant,
|
||||
}
|
||||
};
|
||||
ecx.machine.check_alignment = CheckAlignment::No; // interning doesn't need to respect alignment
|
||||
let check_alignment = mem::replace(&mut ecx.machine.check_alignment, CheckAlignment::No); // interning doesn't need to respect alignment
|
||||
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
|
||||
// we leave alignment checks off, since this `ecx` will not be used for further evaluation anyway
|
||||
ecx.machine.check_alignment = check_alignment;
|
||||
|
||||
debug!("eval_body_using_ecx done: {:?}", ret);
|
||||
Ok(ret)
|
||||
|
|
|
@ -39,8 +39,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
|
|||
hir::Node::Ctor(_)
|
||||
| hir::Node::AnonConst(_)
|
||||
| hir::Node::ConstBlock(_)
|
||||
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => hir::Constness::Const,
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => tcx.generics_of(def_id).host_effect_index.map_or(hir::Constness::NotConst, |_| hir::Constness::Const),
|
||||
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => {
|
||||
hir::Constness::Const
|
||||
}
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => tcx
|
||||
.generics_of(def_id)
|
||||
.host_effect_index
|
||||
.map_or(hir::Constness::NotConst, |_| hir::Constness::Const),
|
||||
hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => {
|
||||
// Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other
|
||||
// foreign items cannot be evaluated at compile-time.
|
||||
|
|
|
@ -5,8 +5,9 @@ use rustc_errors::{
|
|||
use rustc_hir::ConstContext;
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::mir::interpret::{
|
||||
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, PointerKind,
|
||||
ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
|
||||
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, Misalignment,
|
||||
PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
|
||||
ValidationErrorInfo,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
|
@ -389,15 +390,6 @@ pub struct LiveDrop<'tcx> {
|
|||
pub dropped_at: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(const_eval_align_check_failed)]
|
||||
pub struct AlignmentCheckFailed {
|
||||
pub has: u64,
|
||||
pub required: u64,
|
||||
#[subdiagnostic]
|
||||
pub frames: Vec<FrameNote>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_error, code = "E0080")]
|
||||
pub struct ConstEvalError {
|
||||
|
@ -459,7 +451,6 @@ fn bad_pointer_message(msg: CheckInAllocMsg, handler: &Handler) -> String {
|
|||
use crate::fluent_generated::*;
|
||||
|
||||
let msg = match msg {
|
||||
CheckInAllocMsg::DerefTest => const_eval_deref_test,
|
||||
CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test,
|
||||
CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test,
|
||||
CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test,
|
||||
|
@ -568,9 +559,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
|
||||
builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
|
||||
}
|
||||
AlignmentCheckFailed { required, has } => {
|
||||
AlignmentCheckFailed(Misalignment { required, has }, msg) => {
|
||||
builder.set_arg("required", required.bytes());
|
||||
builder.set_arg("has", has.bytes());
|
||||
builder.set_arg("msg", format!("{msg:?}"));
|
||||
}
|
||||
WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
|
||||
builder.set_arg("allocation", alloc);
|
||||
|
|
|
@ -161,7 +161,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
|
||||
#[inline(always)]
|
||||
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
|
||||
&self.ecx
|
||||
self.ecx
|
||||
}
|
||||
|
||||
fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
|
||||
|
@ -259,7 +259,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
// to avoid could be expensive: on the potentially larger types, arrays and slices,
|
||||
// rather than on all aggregates unconditionally.
|
||||
if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) {
|
||||
let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
|
||||
let Some((size, _align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
|
||||
// We do the walk if we can't determine the size of the mplace: we may be
|
||||
// dealing with extern types here in the future.
|
||||
return Ok(true);
|
||||
|
@ -267,7 +267,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
|
||||
// If there is no provenance in this allocation, it does not contain references
|
||||
// that point to another allocation, and we can avoid the interning walk.
|
||||
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size, align)? {
|
||||
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size)? {
|
||||
if !alloc.has_provenance() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
|
|||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{Abi, Align, Primitive, Size};
|
||||
use rustc_target::abi::{Abi, Primitive, Size};
|
||||
|
||||
use super::{
|
||||
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
|
||||
|
@ -349,10 +349,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// Check that the range between them is dereferenceable ("in-bounds or one past the
|
||||
// end of the same allocation"). This is like the check in ptr_offset_inbounds.
|
||||
let min_ptr = if dist >= 0 { b } else { a };
|
||||
self.check_ptr_access_align(
|
||||
self.check_ptr_access(
|
||||
min_ptr,
|
||||
Size::from_bytes(dist.unsigned_abs()),
|
||||
Align::ONE,
|
||||
CheckInAllocMsg::OffsetFromTest,
|
||||
)?;
|
||||
|
||||
|
@ -571,16 +570,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
pub fn ptr_offset_inbounds(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
pointee_ty: Ty<'tcx>,
|
||||
offset_count: i64,
|
||||
offset_bytes: i64,
|
||||
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
|
||||
// We cannot overflow i64 as a type's size must be <= isize::MAX.
|
||||
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
|
||||
// The computed offset, in bytes, must not overflow an isize.
|
||||
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
|
||||
// the difference to be noticeable.
|
||||
let offset_bytes =
|
||||
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
|
||||
// The offset being in bounds cannot rely on "wrapping around" the address space.
|
||||
// So, first rule out overflows in the pointer arithmetic.
|
||||
let offset_ptr = ptr.signed_offset(offset_bytes, self)?;
|
||||
|
@ -589,10 +580,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// pointers to be properly aligned (unlike a read/write operation).
|
||||
let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr };
|
||||
// This call handles checking for integer/null pointers.
|
||||
self.check_ptr_access_align(
|
||||
self.check_ptr_access(
|
||||
min_ptr,
|
||||
Size::from_bytes(offset_bytes.unsigned_abs()),
|
||||
Align::ONE,
|
||||
CheckInAllocMsg::PointerArithmeticTest,
|
||||
)?;
|
||||
Ok(offset_ptr)
|
||||
|
@ -621,7 +611,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let src = self.read_pointer(src)?;
|
||||
let dst = self.read_pointer(dst)?;
|
||||
|
||||
self.mem_copy(src, align, dst, align, size, nonoverlapping)
|
||||
self.check_ptr_align(src, align)?;
|
||||
self.check_ptr_align(dst, align)?;
|
||||
|
||||
self.mem_copy(src, dst, size, nonoverlapping)
|
||||
}
|
||||
|
||||
pub(crate) fn write_bytes_intrinsic(
|
||||
|
@ -677,7 +670,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
size|
|
||||
-> InterpResult<'tcx, &[u8]> {
|
||||
let ptr = this.read_pointer(op)?;
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else {
|
||||
// zero-sized access
|
||||
return Ok(&[]);
|
||||
};
|
||||
|
|
|
@ -114,8 +114,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
|
||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
||||
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
|
||||
|
||||
use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
|
||||
(
|
||||
Symbol::intern(&caller.file.name.prefer_remapped().to_string_lossy()),
|
||||
Symbol::intern(
|
||||
&caller
|
||||
.file
|
||||
.name
|
||||
.for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS)
|
||||
.to_string_lossy(),
|
||||
),
|
||||
u32::try_from(caller.line).unwrap(),
|
||||
u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),
|
||||
)
|
||||
|
|
|
@ -436,6 +436,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
place: &PlaceTy<'tcx, Self::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Without an aliasing model, all we can do is put `Uninit` into the place.
|
||||
// Conveniently this also ensures that the place actually points to suitable memory.
|
||||
ecx.write_uninit(place)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ use rustc_target::abi::{Align, HasDataLayout, Size};
|
|||
use crate::fluent_generated as fluent;
|
||||
|
||||
use super::{
|
||||
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
|
||||
GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance,
|
||||
Scalar,
|
||||
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg,
|
||||
CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer,
|
||||
PointerArithmetic, Provenance, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
|
@ -258,14 +258,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
None => self.get_alloc_raw(alloc_id)?.size(),
|
||||
};
|
||||
// This will also call the access hooks.
|
||||
self.mem_copy(
|
||||
ptr,
|
||||
Align::ONE,
|
||||
new_ptr.into(),
|
||||
Align::ONE,
|
||||
old_size.min(new_size),
|
||||
/*nonoverlapping*/ true,
|
||||
)?;
|
||||
self.mem_copy(ptr, new_ptr.into(), old_size.min(new_size), /*nonoverlapping*/ true)?;
|
||||
self.deallocate_ptr(ptr, old_size_and_align, kind)?;
|
||||
|
||||
Ok(new_ptr)
|
||||
|
@ -367,12 +360,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> {
|
||||
self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let (size, align) = self
|
||||
|
@ -382,18 +373,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Check if the given pointer points to live memory of given `size` and `align`
|
||||
/// (ignoring `M::enforce_alignment`). The caller can control the error message for the
|
||||
/// out-of-bounds case.
|
||||
/// Check if the given pointer points to live memory of the given `size`.
|
||||
/// The caller can control the error message for the out-of-bounds case.
|
||||
#[inline(always)]
|
||||
pub fn check_ptr_access_align(
|
||||
pub fn check_ptr_access(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
msg: CheckInAllocMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| {
|
||||
self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
})?;
|
||||
|
@ -402,15 +391,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
|
||||
/// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
|
||||
/// to the allocation it points to. Supports both shared and mutable references, as the actual
|
||||
/// checking is offloaded to a helper closure. `align` defines whether and which alignment check
|
||||
/// is done.
|
||||
/// checking is offloaded to a helper closure.
|
||||
///
|
||||
/// If this returns `None`, the size is 0; it can however return `Some` even for size 0.
|
||||
fn check_and_deref_ptr<T>(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Option<Align>,
|
||||
msg: CheckInAllocMsg,
|
||||
alloc_size: impl FnOnce(
|
||||
AllocId,
|
||||
|
@ -425,14 +412,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
if size.bytes() > 0 || addr == 0 {
|
||||
throw_ub!(DanglingIntPointer(addr, msg));
|
||||
}
|
||||
// Must be aligned.
|
||||
if let Some(align) = align {
|
||||
self.check_offset_align(addr, align)?;
|
||||
}
|
||||
None
|
||||
}
|
||||
Ok((alloc_id, offset, prov)) => {
|
||||
let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
|
||||
let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
|
||||
// Test bounds. This also ensures non-null.
|
||||
// It is sufficient to check this for the end pointer. Also check for overflow!
|
||||
if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
|
||||
|
@ -448,20 +431,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
if M::Provenance::OFFSET_IS_ADDR {
|
||||
assert_ne!(ptr.addr(), Size::ZERO);
|
||||
}
|
||||
// Test align. Check this last; if both bounds and alignment are violated
|
||||
// we want the error to be about the bounds.
|
||||
if let Some(align) = align {
|
||||
if M::use_addr_for_alignment_check(self) {
|
||||
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
||||
self.check_offset_align(ptr.addr().bytes(), align)?;
|
||||
} else {
|
||||
// Check allocation alignment and offset alignment.
|
||||
if alloc_align.bytes() < align.bytes() {
|
||||
throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
|
||||
}
|
||||
self.check_offset_align(offset.bytes(), align)?;
|
||||
}
|
||||
}
|
||||
|
||||
// We can still be zero-sized in this branch, in which case we have to
|
||||
// return `None`.
|
||||
|
@ -470,17 +439,65 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
})
|
||||
}
|
||||
|
||||
fn check_offset_align(&self, offset: u64, align: Align) -> InterpResult<'tcx> {
|
||||
if offset % align.bytes() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// The biggest power of two through which `offset` is divisible.
|
||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||
throw_ub!(AlignmentCheckFailed {
|
||||
has: Align::from_bytes(offset_pow2).unwrap(),
|
||||
required: align
|
||||
});
|
||||
pub(super) fn check_misalign(
|
||||
&self,
|
||||
misaligned: Option<Misalignment>,
|
||||
msg: CheckAlignMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
if let Some(misaligned) = misaligned {
|
||||
throw_ub!(AlignmentCheckFailed(misaligned, msg))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn is_ptr_misaligned(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
align: Align,
|
||||
) -> Option<Misalignment> {
|
||||
if !M::enforce_alignment(self) || align.bytes() == 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn offset_misalignment(offset: u64, align: Align) -> Option<Misalignment> {
|
||||
if offset % align.bytes() == 0 {
|
||||
None
|
||||
} else {
|
||||
// The biggest power of two through which `offset` is divisible.
|
||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||
Some(Misalignment { has: Align::from_bytes(offset_pow2).unwrap(), required: align })
|
||||
}
|
||||
}
|
||||
|
||||
match self.ptr_try_get_alloc_id(ptr) {
|
||||
Err(addr) => offset_misalignment(addr, align),
|
||||
Ok((alloc_id, offset, _prov)) => {
|
||||
let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
|
||||
if M::use_addr_for_alignment_check(self) {
|
||||
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
||||
offset_misalignment(ptr.addr().bytes(), align)
|
||||
} else {
|
||||
// Check allocation alignment and offset alignment.
|
||||
if alloc_align.bytes() < align.bytes() {
|
||||
Some(Misalignment { has: alloc_align, required: align })
|
||||
} else {
|
||||
offset_misalignment(offset.bytes(), align)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks a pointer for misalignment.
|
||||
///
|
||||
/// The error assumes this is checking the pointer used directly for an access.
|
||||
pub fn check_ptr_align(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_misalign(self.is_ptr_misaligned(ptr, align), CheckAlignMsg::AccessedPtr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,17 +555,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Get the base address for the bytes in an `Allocation` specified by the
|
||||
/// `AllocID` passed in; error if no such allocation exists.
|
||||
///
|
||||
/// It is up to the caller to take sufficient care when using this address:
|
||||
/// there could be provenance or uninit memory in there, and other memory
|
||||
/// accesses could invalidate the exposed pointer.
|
||||
pub fn alloc_base_addr(&self, id: AllocId) -> InterpResult<'tcx, *const u8> {
|
||||
let alloc = self.get_alloc_raw(id)?;
|
||||
Ok(alloc.base_addr())
|
||||
}
|
||||
|
||||
/// Gives raw access to the `Allocation`, without bounds or alignment checks.
|
||||
/// The caller is responsible for calling the access hooks!
|
||||
///
|
||||
|
@ -586,18 +592,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
/// "Safe" (bounds and align-checked) allocation access.
|
||||
/// Bounds-checked *but not align-checked* allocation access.
|
||||
pub fn get_ptr_alloc<'a>(
|
||||
&'a self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
|
||||
{
|
||||
let ptr_and_alloc = self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let alloc = self.get_alloc_raw(alloc_id)?;
|
||||
|
@ -658,15 +662,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
Ok((alloc, &mut self.machine))
|
||||
}
|
||||
|
||||
/// "Safe" (bounds and align-checked) allocation access.
|
||||
/// Bounds-checked *but not align-checked* allocation access.
|
||||
pub fn get_ptr_alloc_mut<'a>(
|
||||
&'a mut self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
|
||||
{
|
||||
let parts = self.get_ptr_access(ptr, size, align)?;
|
||||
let parts = self.get_ptr_access(ptr, size)?;
|
||||
if let Some((alloc_id, offset, prov)) = parts {
|
||||
let tcx = *self.tcx;
|
||||
// FIXME: can we somehow avoid looking up the allocation twice here?
|
||||
|
@ -1023,7 +1026,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
) -> InterpResult<'tcx, &[u8]> {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else {
|
||||
// zero-sized access
|
||||
return Ok(&[]);
|
||||
};
|
||||
|
@ -1049,7 +1052,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
assert_eq!(lower, len, "can only write iterators with a precise length");
|
||||
|
||||
let size = Size::from_bytes(len);
|
||||
let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size, Align::ONE)? else {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size)? else {
|
||||
// zero-sized access
|
||||
assert_matches!(src.next(), None, "iterator said it was empty but returned an element");
|
||||
return Ok(());
|
||||
|
@ -1074,29 +1077,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
pub fn mem_copy(
|
||||
&mut self,
|
||||
src: Pointer<Option<M::Provenance>>,
|
||||
src_align: Align,
|
||||
dest: Pointer<Option<M::Provenance>>,
|
||||
dest_align: Align,
|
||||
size: Size,
|
||||
nonoverlapping: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.mem_copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
|
||||
self.mem_copy_repeatedly(src, dest, size, 1, nonoverlapping)
|
||||
}
|
||||
|
||||
pub fn mem_copy_repeatedly(
|
||||
&mut self,
|
||||
src: Pointer<Option<M::Provenance>>,
|
||||
src_align: Align,
|
||||
dest: Pointer<Option<M::Provenance>>,
|
||||
dest_align: Align,
|
||||
size: Size,
|
||||
num_copies: u64,
|
||||
nonoverlapping: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
// We need to do our own bounds-checks.
|
||||
let src_parts = self.get_ptr_access(src, size, src_align)?;
|
||||
let dest_parts = self.get_ptr_access(dest, size * num_copies, dest_align)?; // `Size` multiplication
|
||||
let src_parts = self.get_ptr_access(src, size)?;
|
||||
let dest_parts = self.get_ptr_access(dest, size * num_copies)?; // `Size` multiplication
|
||||
|
||||
// FIXME: we look up both allocations twice here, once before for the `check_ptr_access`
|
||||
// and once below to get the underlying `&[mut] Allocation`.
|
||||
|
|
|
@ -26,7 +26,7 @@ pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackP
|
|||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
|
||||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||
pub use self::projection::Projectable;
|
||||
pub use self::projection::{OffsetMode, Projectable};
|
||||
pub use self::terminator::FnArg;
|
||||
pub use self::validity::{CtfeValidationMode, RefTracking};
|
||||
pub use self::visitor::ValueVisitor;
|
||||
|
|
|
@ -10,11 +10,12 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
|||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
||||
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
|
||||
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
|
||||
|
||||
use super::{
|
||||
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
|
||||
MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, Projectable, Provenance, Scalar,
|
||||
MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable,
|
||||
Provenance, Scalar,
|
||||
};
|
||||
|
||||
/// An `Immediate` represents a single immediate self-contained Rust value.
|
||||
|
@ -43,12 +44,16 @@ impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
|
|||
}
|
||||
|
||||
impl<Prov: Provenance> Immediate<Prov> {
|
||||
pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
|
||||
Immediate::Scalar(Scalar::from_pointer(ptr, cx))
|
||||
}
|
||||
|
||||
pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
|
||||
Immediate::Scalar(Scalar::from_maybe_pointer(ptr, cx))
|
||||
pub fn new_pointer_with_meta(
|
||||
ptr: Pointer<Option<Prov>>,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> Self {
|
||||
let ptr = Scalar::from_maybe_pointer(ptr, cx);
|
||||
match meta {
|
||||
MemPlaceMeta::None => Immediate::from(ptr),
|
||||
MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(ptr, meta),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_slice(ptr: Pointer<Option<Prov>>, len: u64, cx: &impl HasDataLayout) -> Self {
|
||||
|
@ -219,6 +224,17 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||
/// given layout.
|
||||
// Not called `offset` to avoid confusion with the trait method.
|
||||
fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
|
||||
debug_assert!(layout.is_sized(), "unsized immediates are not a thing");
|
||||
// `ImmTy` have already been checked to be in-bounds, so we can just check directly if this
|
||||
// remains in-bounds. This cannot actually be violated since projections are type-checked
|
||||
// and bounds-checked.
|
||||
assert!(
|
||||
offset + layout.size <= self.layout.size,
|
||||
"attempting to project to field at offset {} with size {} into immediate with layout {:#?}",
|
||||
offset.bytes(),
|
||||
layout.size.bytes(),
|
||||
self.layout,
|
||||
);
|
||||
// This makes several assumptions about what layouts we will encounter; we match what
|
||||
// codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
|
||||
let inner_val: Immediate<_> = match (**self, self.layout.abi) {
|
||||
|
@ -286,6 +302,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
|||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
_mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
|
@ -315,14 +332,6 @@ pub(super) enum Operand<Prov: Provenance = AllocId> {
|
|||
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
|
||||
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for an `OpTy`!
|
||||
/// `None` means "alignment does not matter since this is a by-value operand"
|
||||
/// (`Operand::Immediate`); this field is only relevant for `Operand::Indirect`.
|
||||
/// Also CTFE ignores alignment anyway, so this is for Miri only.
|
||||
pub align: Option<Align>,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
|
||||
|
@ -338,18 +347,14 @@ impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
|
|||
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: ImmTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
|
||||
OpTy { op: Operand::Immediate(val.imm), layout: val.layout }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
OpTy {
|
||||
op: Operand::Indirect(*mplace.mplace()),
|
||||
layout: mplace.layout,
|
||||
align: Some(mplace.align),
|
||||
}
|
||||
OpTy { op: Operand::Indirect(*mplace.mplace()), layout: mplace.layout }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,14 +385,14 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
|||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
match self.as_mplace_or_imm() {
|
||||
Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, ecx)?.into()),
|
||||
Left(mplace) => Ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()),
|
||||
Right(imm) => {
|
||||
debug_assert!(layout.is_sized(), "unsized immediates are not a thing");
|
||||
assert_matches!(meta, MemPlaceMeta::None); // no place to store metadata here
|
||||
// Every part of an uninit is uninit.
|
||||
Ok(imm.offset_(offset, layout, ecx).into())
|
||||
|
@ -622,7 +627,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
throw_inval!(ConstPropNonsense);
|
||||
}
|
||||
}
|
||||
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
|
||||
Ok(OpTy { op, layout })
|
||||
}
|
||||
|
||||
/// Every place can be read from, so we can turn them into an operand.
|
||||
|
@ -637,16 +642,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
Right((frame, local, offset)) => {
|
||||
debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`.
|
||||
let base = self.local_to_op(&self.stack()[frame], local, None)?;
|
||||
let mut field = match offset {
|
||||
Ok(match offset {
|
||||
Some(offset) => base.offset(offset, place.layout, self)?,
|
||||
None => {
|
||||
// In the common case this hasn't been projected.
|
||||
debug_assert_eq!(place.layout, base.layout);
|
||||
base
|
||||
}
|
||||
};
|
||||
field.align = Some(place.align);
|
||||
Ok(field)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -734,27 +737,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
})
|
||||
};
|
||||
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
|
||||
let op = match val_val {
|
||||
let imm = match val_val {
|
||||
mir::ConstValue::Indirect { alloc_id, offset } => {
|
||||
// We rely on mutability being set correctly in that allocation to prevent writes
|
||||
// where none should happen.
|
||||
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
|
||||
Operand::Indirect(MemPlace::from_ptr(ptr.into()))
|
||||
return Ok(self.ptr_to_mplace(ptr.into(), layout).into());
|
||||
}
|
||||
mir::ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
|
||||
mir::ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
|
||||
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
|
||||
mir::ConstValue::ZeroSized => Immediate::Uninit,
|
||||
mir::ConstValue::Slice { data, meta } => {
|
||||
// We rely on mutability being set correctly in `data` to prevent writes
|
||||
// where none should happen.
|
||||
let ptr = Pointer::new(self.tcx.reserve_and_set_memory_alloc(data), Size::ZERO);
|
||||
Operand::Immediate(Immediate::new_slice(
|
||||
self.global_base_pointer(ptr)?.into(),
|
||||
meta,
|
||||
self,
|
||||
))
|
||||
Immediate::new_slice(self.global_base_pointer(ptr)?.into(), meta, self)
|
||||
}
|
||||
};
|
||||
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
|
||||
Ok(OpTy { op: Operand::Immediate(imm), layout })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -767,6 +766,6 @@ mod size_asserts {
|
|||
static_assert_size!(Immediate, 48);
|
||||
static_assert_size!(ImmTy<'_>, 64);
|
||||
static_assert_size!(Operand, 56);
|
||||
static_assert_size!(OpTy<'_>, 80);
|
||||
static_assert_size!(OpTy<'_>, 72);
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_apfloat::{Float, FloatConvert};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{InterpResult, Scalar};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::abi::Abi;
|
||||
|
@ -337,7 +337,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let offset_count = right.to_scalar().to_target_isize(self)?;
|
||||
let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty;
|
||||
|
||||
let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?;
|
||||
// We cannot overflow i64 as a type's size must be <= isize::MAX.
|
||||
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
|
||||
// The computed offset, in bytes, must not overflow an isize.
|
||||
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
|
||||
// the difference to be noticeable.
|
||||
let offset_bytes =
|
||||
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
|
||||
|
||||
let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
|
||||
Ok((
|
||||
ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout),
|
||||
false,
|
||||
|
|
|
@ -15,9 +15,9 @@ use rustc_middle::ty::Ty;
|
|||
use rustc_target::abi::{Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
|
||||
|
||||
use super::{
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ImmTy,
|
||||
Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, Pointer,
|
||||
PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckAlignMsg, ImmTy,
|
||||
Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy,
|
||||
Operand, Pointer, PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
|
@ -57,19 +57,11 @@ pub(super) struct MemPlace<Prov: Provenance = AllocId> {
|
|||
/// Must not be present for sized types, but can be missing for unsized types
|
||||
/// (e.g., `extern type`).
|
||||
pub meta: MemPlaceMeta<Prov>,
|
||||
/// Stores whether this place was created based on a sufficiently aligned pointer.
|
||||
misaligned: Option<Misalignment>,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> MemPlace<Prov> {
|
||||
#[inline(always)]
|
||||
pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self {
|
||||
MemPlace { ptr, meta: MemPlaceMeta::None }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_ptr_with_meta(ptr: Pointer<Option<Prov>>, meta: MemPlaceMeta<Prov>) -> Self {
|
||||
MemPlace { ptr, meta }
|
||||
}
|
||||
|
||||
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
||||
pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
|
||||
MemPlace { ptr: self.ptr.map_provenance(f), ..self }
|
||||
|
@ -78,27 +70,32 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
|||
/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
|
||||
#[inline]
|
||||
pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
|
||||
match self.meta {
|
||||
MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)),
|
||||
MemPlaceMeta::Meta(meta) => {
|
||||
Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx), meta)
|
||||
}
|
||||
}
|
||||
Immediate::new_pointer_with_meta(self.ptr, self.meta, cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// Not called `offset_with_meta` to avoid confusion with the trait method.
|
||||
fn offset_with_meta_<'tcx>(
|
||||
fn offset_with_meta_<'mir, 'tcx, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
cx: &impl HasDataLayout,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
debug_assert!(
|
||||
!meta.has_meta() || self.meta.has_meta(),
|
||||
"cannot use `offset_with_meta` to add metadata to a place"
|
||||
);
|
||||
Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta })
|
||||
if offset > ecx.data_layout().max_size_of_val() {
|
||||
throw_ub!(PointerArithOverflow);
|
||||
}
|
||||
let ptr = match mode {
|
||||
OffsetMode::Inbounds => {
|
||||
ecx.ptr_offset_inbounds(self.ptr, offset.bytes().try_into().unwrap())?
|
||||
}
|
||||
OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
|
||||
};
|
||||
Ok(MemPlace { ptr, meta, misaligned: self.misaligned })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,11 +104,6 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
|||
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
mplace: MemPlace<Prov>,
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for a `MPlaceTy`!
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for MPlaceTy<'_, Prov> {
|
||||
|
@ -133,25 +125,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
|
|||
assert!(layout.is_zst());
|
||||
let align = layout.align.abi;
|
||||
let ptr = Pointer::from_addr_invalid(align.bytes()); // no provenance, absolute address
|
||||
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self {
|
||||
MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_aligned_ptr_with_meta(
|
||||
ptr: Pointer<Option<Prov>>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
) -> Self {
|
||||
MPlaceTy {
|
||||
mplace: MemPlace::from_ptr_with_meta(ptr, meta),
|
||||
layout,
|
||||
align: layout.align.abi,
|
||||
}
|
||||
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None, misaligned: None }, layout }
|
||||
}
|
||||
|
||||
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
||||
|
@ -189,15 +163,12 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
|||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
Ok(MPlaceTy {
|
||||
mplace: self.mplace.offset_with_meta_(offset, meta, ecx)?,
|
||||
align: self.align.restrict_for_offset(offset),
|
||||
layout,
|
||||
})
|
||||
Ok(MPlaceTy { mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, layout })
|
||||
}
|
||||
|
||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
|
@ -228,11 +199,6 @@ pub(super) enum Place<Prov: Provenance = AllocId> {
|
|||
pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
place: Place<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for a `PlaceTy`!
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
|
||||
|
@ -248,7 +214,7 @@ impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
|
|||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout, align: mplace.align }
|
||||
PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,7 +230,7 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
|
|||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
|
||||
match self.place {
|
||||
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }),
|
||||
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }),
|
||||
Place::Local { frame, local, offset } => Right((frame, local, offset)),
|
||||
}
|
||||
}
|
||||
|
@ -301,27 +267,27 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
|||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
Ok(match self.as_mplace_or_local() {
|
||||
Left(mplace) => mplace.offset_with_meta(offset, meta, layout, ecx)?.into(),
|
||||
Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(),
|
||||
Right((frame, local, old_offset)) => {
|
||||
debug_assert!(layout.is_sized(), "unsized locals should live in memory");
|
||||
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
|
||||
let new_offset = ecx
|
||||
.data_layout()
|
||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?;
|
||||
PlaceTy {
|
||||
place: Place::Local {
|
||||
frame,
|
||||
local,
|
||||
offset: Some(Size::from_bytes(new_offset)),
|
||||
},
|
||||
align: self.align.restrict_for_offset(offset),
|
||||
layout,
|
||||
}
|
||||
// `Place::Local` are always in-bounds of their surrounding local, so we can just
|
||||
// check directly if this remains in-bounds. This cannot actually be violated since
|
||||
// projections are type-checked and bounds-checked.
|
||||
assert!(offset + layout.size <= self.layout.size);
|
||||
|
||||
let new_offset = Size::from_bytes(
|
||||
ecx.data_layout()
|
||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?,
|
||||
);
|
||||
|
||||
PlaceTy { place: Place::Local { frame, local, offset: Some(new_offset) }, layout }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -339,9 +305,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
|||
#[inline(always)]
|
||||
pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
match self.op() {
|
||||
Operand::Indirect(mplace) => {
|
||||
Left(MPlaceTy { mplace: *mplace, layout: self.layout, align: self.align.unwrap() })
|
||||
}
|
||||
Operand::Indirect(mplace) => Left(MPlaceTy { mplace: *mplace, layout: self.layout }),
|
||||
Operand::Immediate(imm) => Right(ImmTy::from_immediate(*imm, self.layout)),
|
||||
}
|
||||
}
|
||||
|
@ -362,7 +326,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
|||
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>;
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)>;
|
||||
|
||||
fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
|
@ -374,10 +338,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
|||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
|
||||
{
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)> {
|
||||
self.as_mplace_or_local()
|
||||
.map_right(|(frame, local, offset)| (frame, local, offset, self.align, self.layout))
|
||||
.map_right(|(frame, local, offset)| (frame, local, offset, self.layout))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -393,8 +356,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
|||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
|
||||
{
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)> {
|
||||
Left(self.clone())
|
||||
}
|
||||
|
||||
|
@ -413,6 +375,25 @@ where
|
|||
Prov: Provenance,
|
||||
M: Machine<'mir, 'tcx, Provenance = Prov>,
|
||||
{
|
||||
pub fn ptr_with_meta_to_mplace(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
meta: MemPlaceMeta<M::Provenance>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> MPlaceTy<'tcx, M::Provenance> {
|
||||
let misaligned = self.is_ptr_misaligned(ptr, layout.align.abi);
|
||||
MPlaceTy { mplace: MemPlace { ptr, meta, misaligned }, layout }
|
||||
}
|
||||
|
||||
pub fn ptr_to_mplace(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> MPlaceTy<'tcx, M::Provenance> {
|
||||
assert!(layout.is_sized());
|
||||
self.ptr_with_meta_to_mplace(ptr, MemPlaceMeta::None, layout)
|
||||
}
|
||||
|
||||
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
||||
/// Alignment is just based on the type. This is the inverse of `mplace_to_ref()`.
|
||||
///
|
||||
|
@ -434,7 +415,8 @@ where
|
|||
|
||||
// `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
|
||||
// we hence can't call `size_and_align_of` since that asserts more validity than we want.
|
||||
Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta))
|
||||
let ptr = ptr.to_pointer(self)?;
|
||||
Ok(self.ptr_with_meta_to_mplace(ptr, meta, layout))
|
||||
}
|
||||
|
||||
/// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space.
|
||||
|
@ -464,7 +446,6 @@ where
|
|||
}
|
||||
|
||||
let mplace = self.ref_to_mplace(&val)?;
|
||||
self.check_mplace(&mplace)?;
|
||||
Ok(mplace)
|
||||
}
|
||||
|
||||
|
@ -477,8 +458,11 @@ where
|
|||
let (size, _align) = self
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
self.get_ptr_alloc(mplace.ptr(), size, mplace.align)
|
||||
// We check alignment separately, and *after* checking everything else.
|
||||
// If an access is both OOB and misaligned, we want to see the bounds error.
|
||||
let a = self.get_ptr_alloc(mplace.ptr(), size)?;
|
||||
self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn)?;
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -490,19 +474,13 @@ where
|
|||
let (size, _align) = self
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
self.get_ptr_alloc_mut(mplace.ptr(), size, mplace.align)
|
||||
}
|
||||
|
||||
/// Check if this mplace is dereferenceable and sufficiently aligned.
|
||||
pub fn check_mplace(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
let (size, _align) = self
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
let align = if M::enforce_alignment(self) { mplace.align } else { Align::ONE };
|
||||
self.check_ptr_access_align(mplace.ptr(), size, align, CheckInAllocMsg::DerefTest)?;
|
||||
Ok(())
|
||||
// We check alignment separately, and raise that error *after* checking everything else.
|
||||
// If an access is both OOB and misaligned, we want to see the bounds error.
|
||||
// However we have to call `check_misalign` first to make the borrow checker happy.
|
||||
let misalign_err = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn);
|
||||
let a = self.get_ptr_alloc_mut(mplace.ptr(), size)?;
|
||||
misalign_err?;
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
||||
|
@ -517,8 +495,8 @@ where
|
|||
let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
|
||||
let array = Ty::new_array(self.tcx.tcx, e_ty, len);
|
||||
let layout = self.layout_of(array)?;
|
||||
assert_eq!(layout.size, mplace.layout.size);
|
||||
Ok((MPlaceTy { layout, ..*mplace }, len))
|
||||
let mplace = mplace.transmute(layout, self)?;
|
||||
Ok((mplace, len))
|
||||
}
|
||||
|
||||
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
||||
|
@ -554,7 +532,7 @@ where
|
|||
Operand::Indirect(mplace) => Place::Ptr(*mplace),
|
||||
}
|
||||
};
|
||||
Ok(PlaceTy { place, layout, align: layout.align.abi })
|
||||
Ok(PlaceTy { place, layout })
|
||||
}
|
||||
|
||||
/// Computes a place. You should only use this if you intend to write into this
|
||||
|
@ -644,7 +622,7 @@ where
|
|||
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
|
||||
// but not factored as a separate function.
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Right((frame, local, offset, align, layout)) => {
|
||||
Right((frame, local, offset, layout)) => {
|
||||
if offset.is_some() {
|
||||
// This has been projected to a part of this local. We could have complicated
|
||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||
|
@ -685,7 +663,7 @@ where
|
|||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
MPlaceTy { mplace: *mplace, align, layout }
|
||||
MPlaceTy { mplace: *mplace, layout }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -694,7 +672,7 @@ where
|
|||
};
|
||||
|
||||
// This is already in memory, write there.
|
||||
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.align, mplace.mplace)
|
||||
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)
|
||||
}
|
||||
|
||||
/// Write an immediate to memory.
|
||||
|
@ -704,7 +682,6 @@ where
|
|||
&mut self,
|
||||
value: Immediate<M::Provenance>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
align: Align,
|
||||
dest: MemPlace<M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Note that it is really important that the type here is the right one, and matches the
|
||||
|
@ -713,9 +690,7 @@ where
|
|||
// wrong type.
|
||||
|
||||
let tcx = *self.tcx;
|
||||
let Some(mut alloc) =
|
||||
self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout, align })?
|
||||
else {
|
||||
let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else {
|
||||
// zero-sized access
|
||||
return Ok(());
|
||||
};
|
||||
|
@ -733,9 +708,6 @@ where
|
|||
alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
|
||||
}
|
||||
Immediate::ScalarPair(a_val, b_val) => {
|
||||
// We checked `ptr_align` above, so all fields will have the alignment they need.
|
||||
// We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
|
||||
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
|
||||
let Abi::ScalarPair(a, b) = layout.abi else {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
|
@ -764,7 +736,7 @@ where
|
|||
) -> InterpResult<'tcx> {
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Left(mplace) => mplace,
|
||||
Right((frame, local, offset, align, layout)) => {
|
||||
Right((frame, local, offset, layout)) => {
|
||||
if offset.is_some() {
|
||||
// This has been projected to a part of this local. We could have complicated
|
||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||
|
@ -780,7 +752,7 @@ where
|
|||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
MPlaceTy { mplace: *mplace, layout, align }
|
||||
MPlaceTy { mplace: *mplace, layout }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -873,7 +845,6 @@ where
|
|||
self.write_immediate_to_mplace_no_validate(
|
||||
*src_val,
|
||||
src.layout(),
|
||||
dest_mem.align,
|
||||
dest_mem.mplace,
|
||||
)
|
||||
};
|
||||
|
@ -900,14 +871,12 @@ where
|
|||
// type does not have Scalar/ScalarPair layout.
|
||||
// (Or as the `Assign` docs put it, assignments "not producing primitives" must be
|
||||
// non-overlapping.)
|
||||
self.mem_copy(
|
||||
src.ptr(),
|
||||
src.align,
|
||||
dest.ptr(),
|
||||
dest.align,
|
||||
dest_size,
|
||||
/*nonoverlapping*/ true,
|
||||
)
|
||||
// We check alignment separately, and *after* checking everything else.
|
||||
// If an access is both OOB and misaligned, we want to see the bounds error.
|
||||
self.mem_copy(src.ptr(), dest.ptr(), dest_size, /*nonoverlapping*/ true)?;
|
||||
self.check_misalign(src.mplace.misaligned, CheckAlignMsg::BasedOn)?;
|
||||
self.check_misalign(dest.mplace.misaligned, CheckAlignMsg::BasedOn)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that a place is in memory, and returns where it is.
|
||||
|
@ -941,7 +910,6 @@ where
|
|||
self.write_immediate_to_mplace_no_validate(
|
||||
local_val,
|
||||
local_layout,
|
||||
local_layout.align.abi,
|
||||
mplace.mplace,
|
||||
)?;
|
||||
}
|
||||
|
@ -956,7 +924,13 @@ where
|
|||
&mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
|
||||
};
|
||||
if let Some(offset) = offset {
|
||||
whole_local.offset_with_meta_(offset, MemPlaceMeta::None, self)?
|
||||
// This offset is always inbounds, no need to check it again.
|
||||
whole_local.offset_with_meta_(
|
||||
offset,
|
||||
OffsetMode::Wrapping,
|
||||
MemPlaceMeta::None,
|
||||
self,
|
||||
)?
|
||||
} else {
|
||||
// Preserve wide place metadata, do not call `offset`.
|
||||
whole_local
|
||||
|
@ -965,7 +939,7 @@ where
|
|||
Place::Ptr(mplace) => mplace,
|
||||
};
|
||||
// Return with the original layout and align, so that the caller can go on
|
||||
Ok(MPlaceTy { mplace, layout: place.layout, align: place.align })
|
||||
Ok(MPlaceTy { mplace, layout: place.layout })
|
||||
}
|
||||
|
||||
pub fn allocate_dyn(
|
||||
|
@ -978,7 +952,7 @@ where
|
|||
span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known")
|
||||
};
|
||||
let ptr = self.allocate_ptr(size, align, kind)?;
|
||||
Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.into(), layout, meta))
|
||||
Ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout))
|
||||
}
|
||||
|
||||
pub fn allocate(
|
||||
|
@ -990,7 +964,7 @@ where
|
|||
self.allocate_dyn(layout, kind, MemPlaceMeta::None)
|
||||
}
|
||||
|
||||
/// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation.
|
||||
/// Returns a wide MPlace of type `str` to a new 1-aligned allocation.
|
||||
pub fn allocate_str(
|
||||
&mut self,
|
||||
str: &str,
|
||||
|
@ -999,15 +973,8 @@ where
|
|||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?;
|
||||
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
|
||||
let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
|
||||
|
||||
let ty = Ty::new_ref(
|
||||
self.tcx.tcx,
|
||||
self.tcx.lifetimes.re_static,
|
||||
ty::TypeAndMut { ty: self.tcx.types.str_, mutbl },
|
||||
);
|
||||
let layout = self.layout_of(ty).unwrap();
|
||||
Ok(MPlaceTy { mplace, layout, align: layout.align.abi })
|
||||
let layout = self.layout_of(self.tcx.types.str_).unwrap();
|
||||
Ok(self.ptr_with_meta_to_mplace(ptr.into(), MemPlaceMeta::Meta(meta), layout))
|
||||
}
|
||||
|
||||
/// Writes the aggregate to the destination.
|
||||
|
@ -1046,7 +1013,7 @@ where
|
|||
let _ = self.tcx.global_alloc(raw.alloc_id);
|
||||
let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?;
|
||||
let layout = self.layout_of(raw.ty)?;
|
||||
Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
|
||||
Ok(self.ptr_to_mplace(ptr.into(), layout))
|
||||
}
|
||||
|
||||
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
|
||||
|
@ -1062,12 +1029,10 @@ where
|
|||
let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
|
||||
let (ty, _) = self.get_ptr_vtable(vtable)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
|
||||
let mplace = MPlaceTy {
|
||||
mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace },
|
||||
layout,
|
||||
align: layout.align.abi,
|
||||
};
|
||||
// This is a kind of transmute, from a place with unsized type and metadata to
|
||||
// a place with sized type and no metadata.
|
||||
let mplace =
|
||||
MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout };
|
||||
Ok((mplace, vtable))
|
||||
}
|
||||
|
||||
|
@ -1099,10 +1064,10 @@ mod size_asserts {
|
|||
use super::*;
|
||||
use rustc_data_structures::static_assert_size;
|
||||
// tidy-alphabetical-start
|
||||
static_assert_size!(MemPlace, 40);
|
||||
static_assert_size!(MemPlace, 48);
|
||||
static_assert_size!(MemPlaceMeta, 24);
|
||||
static_assert_size!(MPlaceTy<'_>, 64);
|
||||
static_assert_size!(Place, 40);
|
||||
static_assert_size!(Place, 48);
|
||||
static_assert_size!(PlaceTy<'_>, 64);
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
|
|
@ -19,6 +19,15 @@ use rustc_target::abi::{self, VariantIdx};
|
|||
|
||||
use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
|
||||
|
||||
/// Describes the constraints placed on offset-projections.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum OffsetMode {
|
||||
/// The offset has to be inbounds, like `ptr::offset`.
|
||||
Inbounds,
|
||||
/// No constraints, just wrap around the edge of the address space.
|
||||
Wrapping,
|
||||
}
|
||||
|
||||
/// A thing that we can project into, and that has a layout.
|
||||
pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
||||
/// Get the layout.
|
||||
|
@ -53,12 +62,12 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self>;
|
||||
|
||||
#[inline]
|
||||
fn offset<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
|
@ -66,10 +75,9 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
assert!(layout.is_sized());
|
||||
self.offset_with_meta(offset, MemPlaceMeta::None, layout, ecx)
|
||||
self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn transmute<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
|
@ -77,7 +85,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||
) -> InterpResult<'tcx, Self> {
|
||||
assert!(self.layout().is_sized() && layout.is_sized());
|
||||
assert_eq!(self.layout().size, layout.size);
|
||||
self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, ecx)
|
||||
self.offset_with_meta(Size::ZERO, OffsetMode::Wrapping, MemPlaceMeta::None, layout, ecx)
|
||||
}
|
||||
|
||||
/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
|
||||
|
@ -104,7 +112,17 @@ impl<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'tcx,
|
|||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Option<(u64, P)>> {
|
||||
let Some(idx) = self.range.next() else { return Ok(None) };
|
||||
Ok(Some((idx, self.base.offset(self.stride * idx, self.field_layout, ecx)?)))
|
||||
// We use `Wrapping` here since the offset has already been checked when the iterator was created.
|
||||
Ok(Some((
|
||||
idx,
|
||||
self.base.offset_with_meta(
|
||||
self.stride * idx,
|
||||
OffsetMode::Wrapping,
|
||||
MemPlaceMeta::None,
|
||||
self.field_layout,
|
||||
ecx,
|
||||
)?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +177,7 @@ where
|
|||
(MemPlaceMeta::None, offset)
|
||||
};
|
||||
|
||||
base.offset_with_meta(offset, meta, field_layout, self)
|
||||
base.offset_with_meta(offset, OffsetMode::Inbounds, meta, field_layout, self)
|
||||
}
|
||||
|
||||
/// Downcasting to an enum variant.
|
||||
|
@ -248,6 +266,10 @@ where
|
|||
};
|
||||
let len = base.len(self)?;
|
||||
let field_layout = base.layout().field(self, 0);
|
||||
// Ensure that all the offsets are in-bounds once, up-front.
|
||||
debug!("project_array_fields: {base:?} {len}");
|
||||
base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?;
|
||||
// Create the iterator.
|
||||
Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData })
|
||||
}
|
||||
|
||||
|
@ -305,7 +327,7 @@ where
|
|||
};
|
||||
let layout = self.layout_of(ty)?;
|
||||
|
||||
base.offset_with_meta(from_offset, meta, layout, self)
|
||||
base.offset_with_meta(from_offset, OffsetMode::Inbounds, meta, layout, self)
|
||||
}
|
||||
|
||||
/// Applying a general projection
|
||||
|
|
|
@ -206,15 +206,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let elem_size = first.layout.size;
|
||||
let first_ptr = first.ptr();
|
||||
let rest_ptr = first_ptr.offset(elem_size, self)?;
|
||||
// For the alignment of `rest_ptr`, we crucially do *not* use `first.align` as
|
||||
// that place might be more aligned than its type mandates (a `u8` array could
|
||||
// be 4-aligned if it sits at the right spot in a struct). We have to also factor
|
||||
// in element size.
|
||||
// No alignment requirement since `copy_op` above already checked it.
|
||||
self.mem_copy_repeatedly(
|
||||
first_ptr,
|
||||
dest.align,
|
||||
rest_ptr,
|
||||
dest.align.restrict_for_offset(elem_size),
|
||||
elem_size,
|
||||
length - 1,
|
||||
/*nonoverlapping:*/ true,
|
||||
|
@ -268,7 +263,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
NullaryOp(ref null_op, ty) => {
|
||||
let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
if let mir::NullOp::SizeOf | mir::NullOp::AlignOf = null_op && layout.is_unsized() {
|
||||
if let mir::NullOp::SizeOf | mir::NullOp::AlignOf = null_op
|
||||
&& layout.is_unsized()
|
||||
{
|
||||
span_bug!(
|
||||
self.frame().current_span(),
|
||||
"{null_op:?} MIR operator called for unsized type {ty}",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use either::Either;
|
||||
use rustc_ast::ast::InlineAsmOptions;
|
||||
use rustc_middle::{
|
||||
mir,
|
||||
|
@ -729,13 +728,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
callee_ty: callee_fn_abi.ret.layout.ty
|
||||
});
|
||||
}
|
||||
// Ensure the return place is aligned and dereferenceable, and protect it for
|
||||
// in-place return value passing.
|
||||
if let Either::Left(mplace) = destination.as_mplace_or_local() {
|
||||
self.check_mplace(&mplace)?;
|
||||
} else {
|
||||
// Nothing to do for locals, they are always properly allocated and aligned.
|
||||
}
|
||||
// Protect return place for in-place return value passing.
|
||||
M::protect_in_place_function_argument(self, destination)?;
|
||||
|
||||
// Don't forget to mark "initially live" locals as live.
|
||||
|
@ -890,11 +883,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> {
|
||||
// Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988
|
||||
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
|
||||
if attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.any(|feature| !self.tcx.sess.target_features.contains(feature))
|
||||
if !self.tcx.sess.target.is_like_wasm
|
||||
&& attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.any(|feature| !self.tcx.sess.target_features.contains(feature))
|
||||
{
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_unavailable_target_features_for_fn,
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_ast::Mutability;
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::interpret::{
|
||||
ExpectedKind, InterpError, InvalidMetaKind, PointerKind, ValidationErrorInfo,
|
||||
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, ValidationErrorInfo,
|
||||
ValidationErrorKind, ValidationErrorKind::*,
|
||||
};
|
||||
use rustc_middle::ty;
|
||||
|
@ -355,7 +355,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
value: &OpTy<'tcx, M::Provenance>,
|
||||
ptr_kind: PointerKind,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Not using `deref_pointer` since we do the dereferenceable check ourselves below.
|
||||
// Not using `deref_pointer` since we want to use our `read_immediate` wrapper.
|
||||
let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?;
|
||||
// Handle wide pointers.
|
||||
// Check metadata early, for better diagnostics
|
||||
|
@ -378,18 +378,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
|
||||
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
|
||||
try_validation!(
|
||||
self.ecx.check_ptr_access_align(
|
||||
self.ecx.check_ptr_access(
|
||||
place.ptr(),
|
||||
size,
|
||||
align,
|
||||
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
|
||||
),
|
||||
self.path,
|
||||
Ub(AlignmentCheckFailed { required, has }) => UnalignedPtr {
|
||||
ptr_kind,
|
||||
required_bytes: required.bytes(),
|
||||
found_bytes: has.bytes()
|
||||
},
|
||||
Ub(DanglingIntPointer(0, _)) => NullPtr { ptr_kind },
|
||||
Ub(DanglingIntPointer(i, _)) => DanglingPtrNoProvenance {
|
||||
ptr_kind,
|
||||
|
@ -405,6 +399,18 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
ptr_kind,
|
||||
},
|
||||
);
|
||||
try_validation!(
|
||||
self.ecx.check_ptr_align(
|
||||
place.ptr(),
|
||||
align,
|
||||
),
|
||||
self.path,
|
||||
Ub(AlignmentCheckFailed(Misalignment { required, has }, _msg)) => UnalignedPtr {
|
||||
ptr_kind,
|
||||
required_bytes: required.bytes(),
|
||||
found_bytes: has.bytes()
|
||||
},
|
||||
);
|
||||
// Do not allow pointers to uninhabited types.
|
||||
if place.layout.abi.is_uninhabited() {
|
||||
let ty = place.layout.ty;
|
||||
|
@ -645,7 +651,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
|
||||
#[inline(always)]
|
||||
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
|
||||
&self.ecx
|
||||
self.ecx
|
||||
}
|
||||
|
||||
fn read_discriminant(
|
||||
|
@ -781,14 +787,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
// Optimization: we just check the entire range at once.
|
||||
// NOTE: Keep this in sync with the handling of integer and float
|
||||
// types above, in `visit_primitive`.
|
||||
// In run-time mode, we accept pointers in here. This is actually more
|
||||
// permissive than a per-element check would be, e.g., we accept
|
||||
// a &[u8] that contains a pointer even though bytewise checking would
|
||||
// reject it. However, that's good: We don't inherently want
|
||||
// to reject those pointers, we just do not have the machinery to
|
||||
// talk about parts of a pointer.
|
||||
// We also accept uninit, for consistency with the slow path.
|
||||
let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size, mplace.align)?.expect("we already excluded size 0");
|
||||
// No need for an alignment check here, this is not an actual memory access.
|
||||
let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0");
|
||||
|
||||
match alloc.get_bytes_strip_provenance() {
|
||||
// In the happy case, we needn't check anything else.
|
||||
|
|
|
@ -4,6 +4,9 @@ Rust MIR: a lowered representation of Rust.
|
|||
|
||||
*/
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
|
|
|
@ -9,16 +9,17 @@ use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
|
|||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::traits::BuiltinImplSource;
|
||||
use rustc_middle::ty::GenericArgs;
|
||||
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{GenericArgKind, GenericArgs};
|
||||
use rustc_middle::ty::{TraitRef, TypeVisitableExt};
|
||||
use rustc_mir_dataflow::{self, Analysis};
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
|
||||
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
|
||||
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ops::{ControlFlow, Deref};
|
||||
|
||||
use super::ops::{self, NonConstOp, Status};
|
||||
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
|
||||
|
@ -188,6 +189,24 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
|
||||
kind: LocalKind,
|
||||
checker: &'ck mut Checker<'mir, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'ck, 'mir, 'tcx> TypeVisitor<TyCtxt<'tcx>> for LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match t.kind() {
|
||||
ty::FnPtr(_) => ControlFlow::Continue(()),
|
||||
ty::Ref(_, _, hir::Mutability::Mut) => {
|
||||
self.checker.check_op(ops::ty::MutRef(self.kind));
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
_ => t.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Checker<'mir, 'tcx> {
|
||||
ccx: &'mir ConstCx<'mir, 'tcx>,
|
||||
qualifs: Qualifs<'mir, 'tcx>,
|
||||
|
@ -304,7 +323,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
|||
let gate = match op.status_in_item(self.ccx) {
|
||||
Status::Allowed => return,
|
||||
|
||||
Status::Unstable(gate) if self.tcx.features().enabled(gate) => {
|
||||
Status::Unstable(gate) if self.tcx.features().active(gate) => {
|
||||
let unstable_in_stable = self.ccx.is_const_stable_const_fn()
|
||||
&& !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate);
|
||||
if unstable_in_stable {
|
||||
|
@ -346,20 +365,9 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
|||
fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) {
|
||||
let kind = self.body.local_kind(local);
|
||||
|
||||
for ty in ty.walk() {
|
||||
let ty = match ty.unpack() {
|
||||
GenericArgKind::Type(ty) => ty,
|
||||
let mut visitor = LocalReturnTyVisitor { kind, checker: self };
|
||||
|
||||
// No constraints on lifetimes or constants, except potentially
|
||||
// constants' types, but `walk` will get to them as well.
|
||||
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
|
||||
};
|
||||
|
||||
match *ty.kind() {
|
||||
ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef(kind)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
visitor.visit_ty(ty);
|
||||
}
|
||||
|
||||
fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
|
||||
|
@ -456,7 +464,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
|
||||
Rvalue::Aggregate(kind, ..) => {
|
||||
if let AggregateKind::Generator(def_id, ..) = kind.as_ref()
|
||||
&& let Some(generator_kind @ hir::GeneratorKind::Async(..)) = self.tcx.generator_kind(def_id)
|
||||
&& let Some(generator_kind @ hir::GeneratorKind::Async(..)) =
|
||||
self.tcx.generator_kind(def_id)
|
||||
{
|
||||
self.check_op(ops::Generator(generator_kind));
|
||||
}
|
||||
|
@ -571,8 +580,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
Rvalue::BinaryOp(op, box (lhs, rhs))
|
||||
| Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => {
|
||||
Rvalue::BinaryOp(op, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => {
|
||||
let lhs_ty = lhs.ty(self.body, self.tcx);
|
||||
let rhs_ty = rhs.ty(self.body, self.tcx);
|
||||
|
||||
|
@ -580,18 +588,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
// Int, bool, and char operations are fine.
|
||||
} else if lhs_ty.is_fn_ptr() || lhs_ty.is_unsafe_ptr() {
|
||||
assert_eq!(lhs_ty, rhs_ty);
|
||||
assert!(
|
||||
matches!(
|
||||
op,
|
||||
BinOp::Eq
|
||||
assert!(matches!(
|
||||
op,
|
||||
BinOp::Eq
|
||||
| BinOp::Ne
|
||||
| BinOp::Le
|
||||
| BinOp::Lt
|
||||
| BinOp::Ge
|
||||
| BinOp::Gt
|
||||
| BinOp::Offset
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
self.check_op(ops::RawPtrComparison);
|
||||
} else if lhs_ty.is_floating_point() || rhs_ty.is_floating_point() {
|
||||
|
@ -939,7 +945,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
if self.span.allows_unstable(gate) {
|
||||
return;
|
||||
}
|
||||
if let Some(implied_by_gate) = implied_by && self.span.allows_unstable(implied_by_gate) {
|
||||
if let Some(implied_by_gate) = implied_by
|
||||
&& self.span.allows_unstable(implied_by_gate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -311,10 +311,10 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
|||
ccx.const_kind(),
|
||||
));
|
||||
|
||||
if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() {
|
||||
err.help(format!(
|
||||
"add `#![feature({feature})]` to the crate attributes to enable",
|
||||
));
|
||||
if let Some(feature) = feature
|
||||
&& ccx.tcx.sess.is_nightly_build()
|
||||
{
|
||||
err.help(format!("add `#![feature({feature})]` to the crate attributes to enable",));
|
||||
}
|
||||
|
||||
if let ConstContext::Static(_) = ccx.const_kind() {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(array_windows)]
|
||||
#![feature(associated_type_bounds)]
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
// This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in
|
||||
// `rustc_driver_impl` to be compiled in parallel with other crates.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
|
||||
pub use rustc_driver_impl::*;
|
||||
|
|
|
@ -6,53 +6,55 @@ edition = "2021"
|
|||
[lib]
|
||||
|
||||
[dependencies]
|
||||
time = { version = "0.3", default-features = false, features = ["formatting", ] }
|
||||
tracing = { version = "0.1.35" }
|
||||
serde_json = "1.0.59"
|
||||
rustc_log = { path = "../rustc_log" }
|
||||
# tidy-alphabetical-start
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_lowering = { path = "../rustc_ast_lowering" }
|
||||
rustc_ast_passes = { path = "../rustc_ast_passes" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_borrowck = { path = "../rustc_borrowck" }
|
||||
rustc_builtin_macros = { path = "../rustc_builtin_macros" }
|
||||
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
||||
rustc_const_eval = { path = "../rustc_const_eval" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_error_codes = { path = "../rustc_error_codes" }
|
||||
rustc_error_messages = { path = "../rustc_error_messages" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_expand = { path = "../rustc_expand" }
|
||||
rustc_hir_typeck = { path = "../rustc_hir_typeck" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
|
||||
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
|
||||
rustc_hir_typeck = { path = "../rustc_hir_typeck" }
|
||||
rustc_incremental = { path = "../rustc_incremental" }
|
||||
rustc_infer = { path = "../rustc_infer" }
|
||||
rustc_interface = { path = "../rustc_interface" }
|
||||
rustc_lint = { path = "../rustc_lint" }
|
||||
rustc_log = { path = "../rustc_log" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_metadata = { path = "../rustc_metadata" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_mir_build = { path = "../rustc_mir_build" }
|
||||
rustc_mir_dataflow = { path = "../rustc_mir_dataflow" }
|
||||
rustc_mir_transform = { path = "../rustc_mir_transform" }
|
||||
rustc_monomorphize = { path = "../rustc_monomorphize" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_passes = { path = "../rustc_passes" }
|
||||
rustc_plugin_impl = { path = "../rustc_plugin_impl" }
|
||||
rustc_privacy = { path = "../rustc_privacy" }
|
||||
rustc_query_system = { path = "../rustc_query_system" }
|
||||
rustc_resolve = { path = "../rustc_resolve" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
rustc_ty_utils = { path = "../rustc_ty_utils" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_lint = { path = "../rustc_lint" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_metadata = { path = "../rustc_metadata" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_plugin_impl = { path = "../rustc_plugin_impl" }
|
||||
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_error_codes = { path = "../rustc_error_codes" }
|
||||
rustc_interface = { path = "../rustc_interface" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
|
||||
rustc_mir_transform = { path = "../rustc_mir_transform" }
|
||||
serde_json = "1.0.59"
|
||||
time = { version = "0.3", default-features = false, features = ["formatting", ] }
|
||||
tracing = { version = "0.1.35" }
|
||||
# tidy-alphabetical-end
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
|
|
|
@ -312,6 +312,7 @@ fn run_compiler(
|
|||
locale_resources: DEFAULT_LOCALE_RESOURCES,
|
||||
lint_caps: Default::default(),
|
||||
parse_sess_created: None,
|
||||
hash_untracked_state: None,
|
||||
register_lints: None,
|
||||
override_queries: None,
|
||||
make_codegen_backend,
|
||||
|
@ -1290,7 +1291,9 @@ fn ice_path() -> &'static Option<PathBuf> {
|
|||
if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
|
||||
return None;
|
||||
}
|
||||
if let Some(s) = std::env::var_os("RUST_BACKTRACE") && s == "0" {
|
||||
if let Some(s) = std::env::var_os("RUST_BACKTRACE")
|
||||
&& s == "0"
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let mut path = match std::env::var_os("RUSTC_ICE") {
|
||||
|
@ -1357,8 +1360,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
|
|||
eprintln!();
|
||||
|
||||
if let Some(ice_path) = ice_path()
|
||||
&& let Ok(mut out) =
|
||||
File::options().create(true).append(true).open(&ice_path)
|
||||
&& let Ok(mut out) = File::options().create(true).append(true).open(&ice_path)
|
||||
{
|
||||
// The current implementation always returns `Some`.
|
||||
let location = info.location().unwrap();
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
`async fn`s are not yet supported in traits in Rust.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,edition2018
|
||||
```ignore,edition2018
|
||||
trait T {
|
||||
// Neither case is currently supported.
|
||||
async fn foo() {}
|
||||
|
@ -13,7 +15,7 @@ trait T {
|
|||
`async fn`s return an `impl Future`, making the following two examples
|
||||
equivalent:
|
||||
|
||||
```edition2018,ignore (example-of-desugaring-equivalence)
|
||||
```ignore,edition2018 (example-of-desugaring-equivalence)
|
||||
async fn foo() -> User {
|
||||
unimplemented!()
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![deny(rustdoc::invalid_codeblock_attributes)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![feature(let_chains)]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
|
|
@ -337,9 +337,7 @@ pub trait Emitter: Translate {
|
|||
&& last_name != name
|
||||
{
|
||||
let descr = macro_kind.descr();
|
||||
format!(
|
||||
" which comes from the expansion of the {descr} `{last_name}`",
|
||||
)
|
||||
format!(" which comes from the expansion of the {descr} `{last_name}`",)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
@ -1935,7 +1933,9 @@ impl EmitterWriter {
|
|||
is_multiline,
|
||||
)
|
||||
}
|
||||
if let DisplaySuggestion::Add = show_code_change && is_item_attribute {
|
||||
if let DisplaySuggestion::Add = show_code_change
|
||||
&& is_item_attribute
|
||||
{
|
||||
// The suggestion adds an entire line of code, ending on a newline, so we'll also
|
||||
// print the *following* line, to provide context of what we're advising people to
|
||||
// do. Otherwise you would only see contextless code that can be confused for
|
||||
|
|
|
@ -507,6 +507,7 @@ pub enum StashKey {
|
|||
CallAssocMethod,
|
||||
TraitMissingMethod,
|
||||
OpaqueHiddenTypeMismatch,
|
||||
MaybeForgetReturn,
|
||||
}
|
||||
|
||||
fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) {
|
||||
|
@ -1673,7 +1674,11 @@ impl HandlerInner {
|
|||
let _ = write!(
|
||||
&mut out,
|
||||
"delayed span bug: {}\n{}\n",
|
||||
bug.inner.styled_message().iter().filter_map(|(msg, _)| msg.as_str()).collect::<String>(),
|
||||
bug.inner
|
||||
.styled_message()
|
||||
.iter()
|
||||
.filter_map(|(msg, _)| msg.as_str())
|
||||
.collect::<String>(),
|
||||
&bug.note
|
||||
);
|
||||
}
|
||||
|
|
|
@ -151,12 +151,14 @@ fn misformed_fluent() {
|
|||
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
|
||||
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
|
||||
} = &err
|
||||
&& let [FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. }
|
||||
| ReferenceKind::Variable { id, .. },
|
||||
))] = &**errs
|
||||
&& let [
|
||||
FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. } | ReferenceKind::Variable { id, .. },
|
||||
)),
|
||||
] = &**errs
|
||||
&& id == "name"
|
||||
{} else {
|
||||
{
|
||||
} else {
|
||||
panic!("{err:#?}")
|
||||
};
|
||||
assert_eq!(
|
||||
|
@ -176,12 +178,14 @@ fn misformed_fluent() {
|
|||
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
|
||||
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
|
||||
} = &err
|
||||
&& let [FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. }
|
||||
| ReferenceKind::Variable { id, .. },
|
||||
))] = &**errs
|
||||
&& let [
|
||||
FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. } | ReferenceKind::Variable { id, .. },
|
||||
)),
|
||||
] = &**errs
|
||||
&& id == "oops"
|
||||
{} else {
|
||||
{
|
||||
} else {
|
||||
panic!("{err:#?}")
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
|
@ -14,12 +14,12 @@ use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem
|
|||
use rustc_attr as attr;
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_feature::{Feature, Features, State as FeatureState};
|
||||
use rustc_feature::{ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES};
|
||||
use rustc_feature::Features;
|
||||
use rustc_feature::{ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES};
|
||||
use rustc_parse::validate_attr;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::edition::{Edition, ALL_EDITIONS};
|
||||
use rustc_span::edition::ALL_EDITIONS;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
use thin_vec::ThinVec;
|
||||
|
@ -36,18 +36,10 @@ pub struct StripUnconfigured<'a> {
|
|||
}
|
||||
|
||||
pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
|
||||
fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
|
||||
ACTIVE_FEATURES.iter().filter(move |feature| {
|
||||
if let Some(feature_edition) = feature.edition {
|
||||
feature_edition <= edition
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn feature_list(attr: &Attribute) -> ThinVec<ast::NestedMetaItem> {
|
||||
if attr.has_name(sym::feature) && let Some(list) = attr.meta_item_list() {
|
||||
if attr.has_name(sym::feature)
|
||||
&& let Some(list) = attr.meta_item_list()
|
||||
{
|
||||
list
|
||||
} else {
|
||||
ThinVec::new()
|
||||
|
@ -69,7 +61,9 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
|
|||
if mi.is_word() {
|
||||
let name = mi.name_or_empty();
|
||||
let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
|
||||
if let Some(edition) = edition && edition > features_edition {
|
||||
if let Some(edition) = edition
|
||||
&& edition > features_edition
|
||||
{
|
||||
features_edition = edition;
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +73,13 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
|
|||
// Enable edition-dependent features based on `features_edition`.
|
||||
// - E.g. enable `test_2018_feature` if `features_edition` is 2018 or higher
|
||||
let mut edition_enabled_features = FxHashSet::default();
|
||||
for feature in active_features_up_to(features_edition) {
|
||||
// FIXME(Manishearth) there is currently no way to set lib features by
|
||||
// edition.
|
||||
edition_enabled_features.insert(feature.name);
|
||||
feature.set(&mut features);
|
||||
for f in UNSTABLE_FEATURES {
|
||||
if let Some(edition) = f.feature.edition && edition <= features_edition {
|
||||
// FIXME(Manishearth) there is currently no way to set lib features by
|
||||
// edition.
|
||||
edition_enabled_features.insert(f.feature.name);
|
||||
(f.set_enabled)(&mut features);
|
||||
}
|
||||
}
|
||||
|
||||
// Process all features declared in the code.
|
||||
|
@ -143,19 +139,17 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
|
|||
}
|
||||
|
||||
// If the declared feature has been removed, issue an error.
|
||||
if let Some(Feature { state, .. }) = REMOVED_FEATURES.iter().find(|f| name == f.name) {
|
||||
if let FeatureState::Removed { reason } = state {
|
||||
sess.emit_err(FeatureRemoved {
|
||||
span: mi.span(),
|
||||
reason: reason.map(|reason| FeatureRemovedReason { reason }),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
|
||||
sess.emit_err(FeatureRemoved {
|
||||
span: mi.span(),
|
||||
reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the declared feature is stable, record it.
|
||||
if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
|
||||
let since = Some(Symbol::intern(since));
|
||||
if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
|
||||
let since = Some(Symbol::intern(f.since));
|
||||
features.set_declared_lang_feature(name, mi.span(), since);
|
||||
continue;
|
||||
}
|
||||
|
@ -171,8 +165,8 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
|
|||
}
|
||||
|
||||
// If the declared feature is unstable, record it.
|
||||
if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
|
||||
f.set(&mut features);
|
||||
if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
|
||||
(f.set_enabled)(&mut features);
|
||||
features.set_declared_lang_feature(name, mi.span(), None);
|
||||
continue;
|
||||
}
|
||||
|
@ -248,7 +242,8 @@ impl<'a> StripUnconfigured<'a> {
|
|||
let trees: Vec<_> = stream
|
||||
.0
|
||||
.iter()
|
||||
.flat_map(|tree| match tree.clone() {
|
||||
.flat_map(|tree| {
|
||||
match tree.clone() {
|
||||
AttrTokenTree::Attributes(mut data) => {
|
||||
data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr));
|
||||
|
||||
|
@ -263,18 +258,17 @@ impl<'a> StripUnconfigured<'a> {
|
|||
}
|
||||
AttrTokenTree::Delimited(sp, delim, mut inner) => {
|
||||
inner = self.configure_tokens(&inner);
|
||||
Some(AttrTokenTree::Delimited(sp, delim, inner))
|
||||
.into_iter()
|
||||
Some(AttrTokenTree::Delimited(sp, delim, inner)).into_iter()
|
||||
}
|
||||
AttrTokenTree::Token(ref token, _) if let TokenKind::Interpolated(nt) = &token.kind => {
|
||||
panic!(
|
||||
"Nonterminal should have been flattened at {:?}: {:?}",
|
||||
token.span, nt
|
||||
);
|
||||
AttrTokenTree::Token(ref token, _)
|
||||
if let TokenKind::Interpolated(nt) = &token.kind =>
|
||||
{
|
||||
panic!("Nonterminal should have been flattened at {:?}: {:?}", token.span, nt);
|
||||
}
|
||||
AttrTokenTree::Token(token, spacing) => {
|
||||
Some(AttrTokenTree::Token(token, spacing)).into_iter()
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
AttrTokenStream::new(trees)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![feature(array_windows)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(associated_type_defaults)]
|
||||
|
|
|
@ -716,18 +716,18 @@ fn has_compile_error_macro(rhs: &mbe::TokenTree) -> bool {
|
|||
match rhs {
|
||||
mbe::TokenTree::Delimited(_sp, d) => {
|
||||
let has_compile_error = d.tts.array_windows::<3>().any(|[ident, bang, args]| {
|
||||
if let mbe::TokenTree::Token(ident) = ident &&
|
||||
let TokenKind::Ident(ident, _) = ident.kind &&
|
||||
ident == sym::compile_error &&
|
||||
let mbe::TokenTree::Token(bang) = bang &&
|
||||
let TokenKind::Not = bang.kind &&
|
||||
let mbe::TokenTree::Delimited(_, del) = args &&
|
||||
del.delim != Delimiter::Invisible
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if let mbe::TokenTree::Token(ident) = ident
|
||||
&& let TokenKind::Ident(ident, _) = ident.kind
|
||||
&& ident == sym::compile_error
|
||||
&& let mbe::TokenTree::Token(bang) = bang
|
||||
&& let TokenKind::Not = bang.kind
|
||||
&& let mbe::TokenTree::Delimited(_, del) = args
|
||||
&& del.delim != Delimiter::Invisible
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
if has_compile_error { true } else { d.tts.iter().any(has_compile_error_macro) }
|
||||
}
|
||||
|
|
|
@ -124,8 +124,7 @@ fn parse_depth<'sess>(
|
|||
&& let Ok(n_usize) = usize::try_from(n_u128)
|
||||
{
|
||||
Ok(n_usize)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
|
||||
Err(sess.span_diagnostic.struct_span_err(span, msg))
|
||||
}
|
||||
|
@ -137,15 +136,16 @@ fn parse_ident<'sess>(
|
|||
sess: &'sess ParseSess,
|
||||
span: Span,
|
||||
) -> PResult<'sess, Ident> {
|
||||
if let Some(tt) = iter.next() && let TokenTree::Token(token, _) = tt {
|
||||
if let Some(tt) = iter.next()
|
||||
&& let TokenTree::Token(token, _) = tt
|
||||
{
|
||||
if let Some((elem, false)) = token.ident() {
|
||||
return Ok(elem);
|
||||
}
|
||||
let token_str = pprust::token_to_string(token);
|
||||
let mut err = sess.span_diagnostic.struct_span_err(
|
||||
span,
|
||||
format!("expected identifier, found `{}`", &token_str)
|
||||
);
|
||||
let mut err = sess
|
||||
.span_diagnostic
|
||||
.struct_span_err(span, format!("expected identifier, found `{}`", &token_str));
|
||||
err.span_suggestion(
|
||||
token.span,
|
||||
format!("try removing `{}`", &token_str),
|
||||
|
|
|
@ -91,7 +91,9 @@ pub(crate) fn mod_dir_path(
|
|||
inline: Inline,
|
||||
) -> (PathBuf, DirOwnership) {
|
||||
match inline {
|
||||
Inline::Yes if let Some(file_path) = mod_file_path_from_attr(sess, attrs, &module.dir_path) => {
|
||||
Inline::Yes
|
||||
if let Some(file_path) = mod_file_path_from_attr(sess, attrs, &module.dir_path) =>
|
||||
{
|
||||
// For inline modules file path from `#[path]` is actually the directory path
|
||||
// for historical reasons, so we don't pop the last segment here.
|
||||
(file_path, DirOwnership::Owned { relative: None })
|
||||
|
|
|
@ -226,9 +226,8 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
|
|||
}));
|
||||
}
|
||||
|
||||
Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => {
|
||||
trees.push(TokenTree::Ident(Ident { sym: ident.name, is_raw, span: ident.span }))
|
||||
}
|
||||
Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => trees
|
||||
.push(TokenTree::Ident(Ident { sym: ident.name, is_raw, span: ident.span })),
|
||||
|
||||
Interpolated(nt) => {
|
||||
let stream = TokenStream::from_nonterminal_ast(&nt);
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
//! List of the accepted feature gates.
|
||||
|
||||
use super::{to_nonzero, Feature, State};
|
||||
use super::{to_nonzero, Feature};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
macro_rules! declare_features {
|
||||
($(
|
||||
$(#[doc = $doc:tt])* (accepted, $feature:ident, $ver:expr, $issue:expr, None),
|
||||
)+) => {
|
||||
/// Those language feature has since been Accepted (it was once Active)
|
||||
/// Formerly unstable features that have now been accepted (stabilized).
|
||||
pub const ACCEPTED_FEATURES: &[Feature] = &[
|
||||
$(
|
||||
Feature {
|
||||
state: State::Accepted,
|
||||
name: sym::$feature,
|
||||
since: $ver,
|
||||
issue: to_nonzero($issue),
|
||||
edition: None,
|
||||
}
|
||||
),+
|
||||
$(Feature {
|
||||
name: sym::$feature,
|
||||
since: $ver,
|
||||
issue: to_nonzero($issue),
|
||||
edition: None,
|
||||
}),+
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +64,8 @@ declare_features! (
|
|||
(accepted, associated_types, "1.0.0", None, None),
|
||||
/// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions.
|
||||
(accepted, async_await, "1.39.0", Some(50547), None),
|
||||
/// Allows async functions to be declared, implemented, and used in traits.
|
||||
(accepted, async_fn_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None),
|
||||
/// Allows all literals in attribute lists and values of key-value pairs.
|
||||
(accepted, attr_literals, "1.30.0", Some(34981), None),
|
||||
/// Allows overloading augmented assignment operations like `a += b`.
|
||||
|
@ -198,7 +197,7 @@ declare_features! (
|
|||
/// + `impl Debug for Foo<'_>`
|
||||
(accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None),
|
||||
/// Allows referencing `Self` and projections in impl-trait.
|
||||
(accepted, impl_trait_projections, "CURRENT_RUSTC_VERSION", Some(103532), None),
|
||||
(accepted, impl_trait_projections, "1.74.0", Some(103532), None),
|
||||
/// Allows using `a..=b` and `..=b` as inclusive range syntaxes.
|
||||
(accepted, inclusive_range_syntax, "1.26.0", Some(28237), None),
|
||||
/// Allows inferring outlives requirements (RFC 2093).
|
||||
|
@ -270,7 +269,7 @@ declare_features! (
|
|||
/// Allows the use of or-patterns (e.g., `0 | 1`).
|
||||
(accepted, or_patterns, "1.53.0", Some(54883), None),
|
||||
/// Allows using `+bundle,+whole-archive` link modifiers with native libs.
|
||||
(accepted, packed_bundled_libs, "CURRENT_RUSTC_VERSION", Some(108081), None),
|
||||
(accepted, packed_bundled_libs, "1.74.0", Some(108081), None),
|
||||
/// Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`.
|
||||
/// This defines the behavior of panics.
|
||||
(accepted, panic_handler, "1.30.0", Some(44489), None),
|
||||
|
@ -306,6 +305,8 @@ declare_features! (
|
|||
(accepted, repr_packed, "1.33.0", Some(33158), None),
|
||||
/// Allows `#[repr(transparent)]` attribute on newtype structs.
|
||||
(accepted, repr_transparent, "1.28.0", Some(43036), None),
|
||||
/// Allows return-position `impl Trait` in traits.
|
||||
(accepted, return_position_impl_trait_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None),
|
||||
/// Allows code like `let x: &'static u32 = &42` to work (RFC 1414).
|
||||
(accepted, rvalue_static_promotion, "1.21.0", Some(38865), None),
|
||||
/// Allows `Self` in type definitions (RFC 2300).
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue