Merge from rustc

This commit is contained in:
The Miri Conjob Bot 2023-07-26 06:32:12 +00:00
commit eb3ccfd841
338 changed files with 9244 additions and 3352 deletions

View file

@ -641,9 +641,9 @@ dependencies = [
[[package]]
name = "compiler_builtins"
version = "0.1.95"
version = "0.1.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6866e0f3638013234db3c89ead7a14d278354338e7237257407500009012b23f"
checksum = "dfbefa16407456e5cad1ad066c84dfcb980afe4437103a0007d1c2f136130210"
dependencies = [
"cc",
"rustc-std-workspace-core",

View file

@ -763,7 +763,9 @@ pub trait LayoutCalculator {
let mut size = Size::ZERO;
let only_variant = &variants[FIRST_VARIANT];
for field in only_variant {
assert!(field.0.is_sized());
if field.0.is_unsized() {
self.delay_bug("unsized field in union".to_string());
}
align = align.max(field.align());
max_repr_align = max_repr_align.max(field.max_repr_align());

View file

@ -1189,7 +1189,7 @@ impl FieldsShape {
}
FieldsShape::Array { stride, count } => {
let i = u64::try_from(i).unwrap();
assert!(i < count);
assert!(i < count, "tried to access field {} of array with {} fields", i, count);
stride * i
}
FieldsShape::Arbitrary { ref offsets, .. } => offsets[FieldIdx::from_usize(i)],
@ -1345,7 +1345,6 @@ impl Abi {
/// Discard validity range information and allow undef.
pub fn to_union(&self) -> Self {
assert!(self.is_sized());
match *self {
Abi::Scalar(s) => Abi::Scalar(s.to_union()),
Abi::ScalarPair(s1, s2) => Abi::ScalarPair(s1.to_union(), s2.to_union()),

View file

@ -62,7 +62,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
CommentKind::Block => {
// Whatever happens, we skip the first line.
let mut i = lines
.get(0)
.first()
.map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 })
.unwrap_or(0);
let mut j = lines.len();

View file

@ -286,7 +286,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf(
self.lower_ty(
container,
&mut ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf),
&ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf),
),
self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))),
),

View file

@ -697,15 +697,15 @@ pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> St
write!(template, "{n}").unwrap();
if p.format_options != Default::default() || p.format_trait != FormatTrait::Display
{
template.push_str(":");
template.push(':');
}
if let Some(fill) = p.format_options.fill {
template.push(fill);
}
match p.format_options.alignment {
Some(FormatAlignment::Left) => template.push_str("<"),
Some(FormatAlignment::Right) => template.push_str(">"),
Some(FormatAlignment::Center) => template.push_str("^"),
Some(FormatAlignment::Left) => template.push('<'),
Some(FormatAlignment::Right) => template.push('>'),
Some(FormatAlignment::Center) => template.push('^'),
None => {}
}
match p.format_options.sign {

View file

@ -2306,11 +2306,10 @@ mod error {
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) {
if let None = self.tainted_by_errors {
self.tainted_by_errors = Some(
self.tcx
.sess
.delay_span_bug(t.span.clone(), "diagnostic buffered but not emitted"),
)
self.tainted_by_errors = Some(self.tcx.sess.delay_span_bug(
t.span.clone_ignoring_labels(),
"diagnostic buffered but not emitted",
))
}
t.buffer(&mut self.buffered);
}

View file

@ -339,8 +339,8 @@ fn check_opaque_type_well_formed<'tcx>(
// version.
let errors = ocx.select_all_or_error();
// This is still required for many(half of the tests in ui/type-alias-impl-trait)
// tests to pass
// This is fishy, but we check it again in `check_opaque_meets_bounds`.
// Remove once we can prepopulate with known hidden types.
let _ = infcx.take_opaque_types();
if errors.is_empty() {

View file

@ -109,8 +109,8 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
.suggestion = remove the value
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
.cargo = Cargo sets build script variables at run time. Use `std::env::var("{$var}")` instead
.other = use `std::env::var("{$var}")` to read the variable at run time
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
.custom = use `std::env::var({$var_expr})` to read the variable at run time
builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments

View file

@ -401,7 +401,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
// should have errored above during parsing
[] => unreachable!(),
[(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
[abis @ ..] => {
abis => {
for (abi, span) in abis {
args.clobber_abis.push((*abi, *span));
}

View file

@ -4,7 +4,7 @@
//
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{self as ast, GenericArg};
use rustc_ast::{self as ast, AstDeref, GenericArg};
use rustc_expand::base::{self, *};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
@ -76,27 +76,36 @@ pub fn expand_env<'cx>(
},
};
let sp = cx.with_def_site_ctxt(sp);
let span = cx.with_def_site_ctxt(sp);
let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern);
cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
let e = match value {
None => {
// Use the string literal in the code in the diagnostic to avoid confusing diagnostics,
// e.g. when the literal contains escape sequences.
let ast::ExprKind::Lit(ast::token::Lit {
kind: ast::token::LitKind::Str,
symbol: original_var,
kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..),
symbol,
..
}) = &var_expr.kind
else {
unreachable!("`expr_to_string` ensures this is a string lit")
};
cx.emit_err(errors::EnvNotDefined {
span: sp,
msg: custom_msg,
var: *original_var,
help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())),
});
if let Some(msg_from_user) = custom_msg {
cx.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user });
} else if is_cargo_env_var(var.as_str()) {
cx.emit_err(errors::EnvNotDefined::CargoEnvVar {
span,
var: *symbol,
var_expr: var_expr.ast_deref(),
});
} else {
cx.emit_err(errors::EnvNotDefined::CustomEnvVar {
span,
var: *symbol,
var_expr: var_expr.ast_deref(),
});
}
return DummyResult::any(sp);
}
Some(value) => cx.expr_str(sp, value),
@ -104,13 +113,9 @@ pub fn expand_env<'cx>(
MacEager::expr(e)
}
fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp {
if var.starts_with("CARGO_")
/// Returns `true` if an environment variable from `env!` is one used by Cargo.
fn is_cargo_env_var(var: &str) -> bool {
var.starts_with("CARGO_")
|| var.starts_with("DEP_")
|| matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET")
{
errors::EnvNotDefinedHelp::CargoVar
} else {
errors::EnvNotDefinedHelp::Other
}
}

View file

@ -440,43 +440,43 @@ pub(crate) struct EnvTakesArgs {
pub(crate) span: Span,
}
//#[derive(Diagnostic)]
//#[diag(builtin_macros_env_not_defined)]
pub(crate) struct EnvNotDefined {
pub(crate) struct EnvNotDefinedWithUserMessage {
pub(crate) span: Span,
pub(crate) msg: Option<Symbol>,
pub(crate) var: Symbol,
pub(crate) help: Option<EnvNotDefinedHelp>,
pub(crate) msg_from_user: Symbol,
}
// Hand-written implementation to support custom user messages
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined {
// Hand-written implementation to support custom user messages.
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage {
#[track_caller]
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
let mut diag = if let Some(msg) = self.msg {
#[expect(
rustc::untranslatable_diagnostic,
reason = "cannot translate user-provided messages"
)]
handler.struct_diagnostic(msg.to_string())
} else {
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined)
};
diag.set_arg("var", self.var);
#[expect(
rustc::untranslatable_diagnostic,
reason = "cannot translate user-provided messages"
)]
let mut diag = handler.struct_diagnostic(self.msg_from_user.to_string());
diag.set_span(self.span);
if let Some(help) = self.help {
diag.subdiagnostic(help);
}
diag
}
}
#[derive(Subdiagnostic)]
pub(crate) enum EnvNotDefinedHelp {
#[derive(Diagnostic)]
pub(crate) enum EnvNotDefined<'a> {
#[diag(builtin_macros_env_not_defined)]
#[help(builtin_macros_cargo)]
CargoVar,
#[help(builtin_macros_other)]
Other,
CargoEnvVar {
#[primary_span]
span: Span,
var: Symbol,
var_expr: &'a rustc_ast::Expr,
},
#[diag(builtin_macros_env_not_defined)]
#[help(builtin_macros_custom)]
CustomEnvVar {
#[primary_span]
span: Span,
var: Symbol,
var_expr: &'a rustc_ast::Expr,
},
}
#[derive(Diagnostic)]

View file

@ -1,63 +1,6 @@
codegen_gcc_invalid_minimum_alignment =
invalid minimum global alignment: {$err}
codegen_gcc_invalid_monomorphization_basic_integer =
invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}`
codegen_gcc_invalid_monomorphization_expected_signed_unsigned =
invalid monomorphization of `{$name}` intrinsic: expected element type `{$elem_ty}` of vector type `{$vec_ty}` to be a signed or unsigned integer type
codegen_gcc_invalid_monomorphization_expected_simd =
invalid monomorphization of `{$name}` intrinsic: expected SIMD {$expected_ty} type, found non-SIMD `{$found_ty}`
codegen_gcc_invalid_monomorphization_inserted_type =
invalid monomorphization of `{$name}` intrinsic: expected inserted type `{$in_elem}` (element of input `{$in_ty}`), found `{$out_ty}`
codegen_gcc_invalid_monomorphization_invalid_bitmask =
invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
codegen_gcc_invalid_monomorphization_invalid_float_vector =
invalid monomorphization of `{$name}` intrinsic: unsupported element type `{$elem_ty}` of floating-point vector `{$vec_ty}`
codegen_gcc_invalid_monomorphization_mask_type =
invalid monomorphization of `{$name}` intrinsic: mask element type is `{$ty}`, expected `i_`
codegen_gcc_invalid_monomorphization_mismatched_lengths =
invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}`
codegen_gcc_invalid_monomorphization_not_float =
invalid monomorphization of `{$name}` intrinsic: `{$ty}` is not a floating-point type
codegen_gcc_invalid_monomorphization_return_element =
invalid monomorphization of `{$name}` intrinsic: expected return element type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}` with element type `{$out_ty}`
codegen_gcc_invalid_monomorphization_return_integer_type =
invalid monomorphization of `{$name}` intrinsic: expected return type with integer elements, found `{$ret_ty}` with non-integer `{$out_ty}`
codegen_gcc_invalid_monomorphization_return_length =
invalid monomorphization of `{$name}` intrinsic: expected return type of length {$in_len}, found `{$ret_ty}` with length {$out_len}
codegen_gcc_invalid_monomorphization_return_length_input_type =
invalid monomorphization of `{$name}` intrinsic: expected return type with length {$in_len} (same as input type `{$in_ty}`), found `{$ret_ty}` with length {$out_len}
codegen_gcc_invalid_monomorphization_return_type =
invalid monomorphization of `{$name}` intrinsic: expected return type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}`
codegen_gcc_invalid_monomorphization_simd_shuffle =
invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be an array of `u32`, got `{$ty}`
codegen_gcc_invalid_monomorphization_unrecognized =
invalid monomorphization of `{$name}` intrinsic: unrecognized intrinsic `{$name}`
codegen_gcc_invalid_monomorphization_unsupported_cast =
invalid monomorphization of `{$name}` intrinsic: unsupported cast from `{$in_ty}` with element `{$in_elem}` to `{$ret_ty}` with element `{$out_elem}`
codegen_gcc_invalid_monomorphization_unsupported_element =
invalid monomorphization of `{$name}` intrinsic: unsupported {$name} from `{$in_ty}` with element `{$elem_ty}` to `{$ret_ty}`
codegen_gcc_invalid_monomorphization_unsupported_operation =
invalid monomorphization of `{$name}` intrinsic: unsupported operation on `{$in_ty}` with element `{$in_elem}`
codegen_gcc_lto_not_supported =
LTO is not supported. You may get a linker error.

View file

@ -1,7 +1,6 @@
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_macros::Diagnostic;
use rustc_middle::ty::Ty;
use rustc_span::{Span, Symbol};
use rustc_span::Span;
use std::borrow::Cow;
struct ExitCode(Option<i32>);
@ -16,201 +15,6 @@ impl IntoDiagnosticArg for ExitCode {
}
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_basic_integer, code = "E0511")]
pub(crate) struct InvalidMonomorphizationBasicInteger<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_invalid_float_vector, code = "E0511")]
pub(crate) struct InvalidMonomorphizationInvalidFloatVector<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub elem_ty: &'a str,
pub vec_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_not_float, code = "E0511")]
pub(crate) struct InvalidMonomorphizationNotFloat<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_unrecognized, code = "E0511")]
pub(crate) struct InvalidMonomorphizationUnrecognized {
#[primary_span]
pub span: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_expected_signed_unsigned, code = "E0511")]
pub(crate) struct InvalidMonomorphizationExpectedSignedUnsigned<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub elem_ty: Ty<'a>,
pub vec_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_unsupported_element, code = "E0511")]
pub(crate) struct InvalidMonomorphizationUnsupportedElement<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_ty: Ty<'a>,
pub elem_ty: Ty<'a>,
pub ret_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_invalid_bitmask, code = "E0511")]
pub(crate) struct InvalidMonomorphizationInvalidBitmask<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub ty: Ty<'a>,
pub expected_int_bits: u64,
pub expected_bytes: u64,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_simd_shuffle, code = "E0511")]
pub(crate) struct InvalidMonomorphizationSimdShuffle<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_expected_simd, code = "E0511")]
pub(crate) struct InvalidMonomorphizationExpectedSimd<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub position: &'a str,
pub found_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_mask_type, code = "E0511")]
pub(crate) struct InvalidMonomorphizationMaskType<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_return_length, code = "E0511")]
pub(crate) struct InvalidMonomorphizationReturnLength<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_len: u64,
pub ret_ty: Ty<'a>,
pub out_len: u64,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_return_length_input_type, code = "E0511")]
pub(crate) struct InvalidMonomorphizationReturnLengthInputType<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_len: u64,
pub in_ty: Ty<'a>,
pub ret_ty: Ty<'a>,
pub out_len: u64,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_return_element, code = "E0511")]
pub(crate) struct InvalidMonomorphizationReturnElement<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_elem: Ty<'a>,
pub in_ty: Ty<'a>,
pub ret_ty: Ty<'a>,
pub out_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_return_type, code = "E0511")]
pub(crate) struct InvalidMonomorphizationReturnType<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_elem: Ty<'a>,
pub in_ty: Ty<'a>,
pub ret_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_inserted_type, code = "E0511")]
pub(crate) struct InvalidMonomorphizationInsertedType<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_elem: Ty<'a>,
pub in_ty: Ty<'a>,
pub out_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_return_integer_type, code = "E0511")]
pub(crate) struct InvalidMonomorphizationReturnIntegerType<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub ret_ty: Ty<'a>,
pub out_ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_mismatched_lengths, code = "E0511")]
pub(crate) struct InvalidMonomorphizationMismatchedLengths {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub m_len: u64,
pub v_len: u64,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_unsupported_cast, code = "E0511")]
pub(crate) struct InvalidMonomorphizationUnsupportedCast<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_ty: Ty<'a>,
pub in_elem: Ty<'a>,
pub ret_ty: Ty<'a>,
pub out_elem: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_unsupported_operation, code = "E0511")]
pub(crate) struct InvalidMonomorphizationUnsupportedOperation<'a> {
#[primary_span]
pub span: Span,
pub name: Symbol,
pub in_ty: Ty<'a>,
pub in_elem: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_lto_not_supported)]
pub(crate) struct LTONotSupported;

View file

@ -13,6 +13,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef;
use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
#[cfg(feature="master")]
use rustc_codegen_ssa::traits::{DerivedTypeMethods, MiscMethods};
use rustc_codegen_ssa::errors::InvalidMonomorphization;
use rustc_middle::bug;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::ty::layout::LayoutOf;
@ -31,7 +32,6 @@ use crate::abi::FnAbiGccExt;
use crate::builder::Builder;
use crate::common::{SignType, TypeReflection};
use crate::context::CodegenCx;
use crate::errors::InvalidMonomorphizationBasicInteger;
use crate::type_of::LayoutGccExt;
use crate::intrinsic::simd::generic_simd_intrinsic;
@ -256,7 +256,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
_ => bug!(),
},
None => {
tcx.sess.emit_err(InvalidMonomorphizationBasicInteger { span, name, ty });
tcx.sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
return;
}
}

View file

@ -21,19 +21,6 @@ use rustc_target::abi::Align;
use crate::builder::Builder;
#[cfg(feature="master")]
use crate::context::CodegenCx;
#[cfg(feature="master")]
use crate::errors::{InvalidMonomorphizationExpectedSignedUnsigned, InvalidMonomorphizationInsertedType};
use crate::errors::{
InvalidMonomorphizationExpectedSimd,
InvalidMonomorphizationInvalidBitmask,
InvalidMonomorphizationInvalidFloatVector, InvalidMonomorphizationMaskType,
InvalidMonomorphizationMismatchedLengths, InvalidMonomorphizationNotFloat,
InvalidMonomorphizationReturnElement, InvalidMonomorphizationReturnIntegerType,
InvalidMonomorphizationReturnLength, InvalidMonomorphizationReturnLengthInputType,
InvalidMonomorphizationReturnType, InvalidMonomorphizationSimdShuffle,
InvalidMonomorphizationUnrecognized, InvalidMonomorphizationUnsupportedElement,
InvalidMonomorphizationUnsupportedOperation,
};
pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
bx: &mut Builder<'a, 'gcc, 'tcx>,
@ -59,16 +46,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
};
}
macro_rules! require_simd {
($ty: expr, $position: expr) => {
require!(
$ty.is_simd(),
InvalidMonomorphizationExpectedSimd {
span,
name,
position: $position,
found_ty: $ty
}
)
($ty: expr, $diag: expr) => {
require!($ty.is_simd(), $diag)
};
}
@ -78,7 +57,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let arg_tys = sig.inputs();
if name == sym::simd_select_bitmask {
require_simd!(arg_tys[1], "argument");
require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] });
let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
let expected_int_bits = (len.max(8) - 1).next_power_of_two();
@ -99,10 +78,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty));
bx.load(int_ty, ptr, Align::ONE)
}
_ => return_error!(InvalidMonomorphizationInvalidBitmask {
_ => return_error!(InvalidMonomorphization::InvalidBitmask {
span,
name,
ty: mask_ty,
mask_ty,
expected_int_bits,
expected_bytes
}),
@ -131,7 +110,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
}
// every intrinsic below takes a SIMD vector as its first argument
require_simd!(arg_tys[0], "input");
require_simd!(arg_tys[0], InvalidMonomorphization::SimdInput { span, name, ty: arg_tys[0] });
let in_ty = arg_tys[0];
let comparison = match name {
@ -146,12 +125,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx());
if let Some(cmp_op) = comparison {
require_simd!(ret_ty, "return");
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
require!(
in_len == out_len,
InvalidMonomorphizationReturnLengthInputType {
InvalidMonomorphization::ReturnLengthInputType {
span,
name,
in_len,
@ -162,7 +141,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
);
require!(
bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
InvalidMonomorphizationReturnIntegerType { span, name, ret_ty, out_ty }
InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty }
);
let arg1 = args[0].immediate();
@ -190,7 +169,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
|| span_bug!(span, "could not evaluate shuffle index array length"),
)
}
_ => return_error!(InvalidMonomorphizationSimdShuffle {
_ => return_error!(InvalidMonomorphization::SimdShuffle {
span,
name,
ty: args[2].layout.ty
@ -202,16 +181,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
})
};
require_simd!(ret_ty, "return");
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
require!(
out_len == n,
InvalidMonomorphizationReturnLength { span, name, in_len: n, ret_ty, out_len }
InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
);
require!(
in_elem == out_ty,
InvalidMonomorphizationReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
);
let vector = args[2].immediate();
@ -223,7 +202,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
if name == sym::simd_insert {
require!(
in_elem == arg_tys[2],
InvalidMonomorphizationInsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] }
InvalidMonomorphization::InsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] }
);
let vector = args[0].immediate();
let index = args[1].immediate();
@ -240,7 +219,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
if name == sym::simd_extract {
require!(
ret_ty == in_elem,
InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
);
let vector = args[0].immediate();
return Ok(bx.context.new_vector_access(None, vector, args[1].immediate()).to_rvalue());
@ -249,26 +228,26 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
if name == sym::simd_select {
let m_elem_ty = in_elem;
let m_len = in_len;
require_simd!(arg_tys[1], "argument");
require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] });
let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
require!(
m_len == v_len,
InvalidMonomorphizationMismatchedLengths { span, name, m_len, v_len }
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
);
match m_elem_ty.kind() {
ty::Int(_) => {}
_ => return_error!(InvalidMonomorphizationMaskType { span, name, ty: m_elem_ty }),
_ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }),
}
return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate()));
}
#[cfg(feature="master")]
if name == sym::simd_cast || name == sym::simd_as {
require_simd!(ret_ty, "return");
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx());
require!(
in_len == out_len,
InvalidMonomorphizationReturnLengthInputType {
InvalidMonomorphization::ReturnLengthInputType {
span,
name,
in_len,
@ -329,7 +308,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
})*
_ => {},
}
return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem })
return_error!(InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem })
})*
}
}
@ -422,12 +401,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
32 => ("f", elem_ty),
64 => ("", elem_ty),
_ => {
return_error!(InvalidMonomorphizationInvalidFloatVector { span, name, elem_ty: f.name_str(), vec_ty: in_ty });
return_error!(InvalidMonomorphization::FloatingPointVector { span, name, f_ty: *f, in_ty });
}
}
}
else {
return_error!(InvalidMonomorphizationNotFloat { span, name, ty: in_ty });
return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty });
};
let vec_ty = bx.cx.type_vector(elem_ty, in_len);
@ -450,7 +429,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
sym::simd_fsqrt => "sqrt",
sym::simd_round => "round",
sym::simd_trunc => "trunc",
_ => return_error!(InvalidMonomorphizationUnrecognized { span, name })
_ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name })
};
let builtin_name = format!("{}{}", intr_name, elem_ty_str);
let funcs = bx.cx.functions.borrow();
@ -566,10 +545,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// * M: any integer width is supported, will be truncated to i1
// All types must be simd vector types
require_simd!(in_ty, "first");
require_simd!(arg_tys[1], "second");
require_simd!(arg_tys[2], "third");
require_simd!(ret_ty, "return");
require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty });
require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] });
require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] });
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
// Of the same length:
let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
@ -674,9 +653,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// * M: any integer width is supported, will be truncated to i1
// All types must be simd vector types
require_simd!(in_ty, "first");
require_simd!(arg_tys[1], "second");
require_simd!(arg_tys[2], "third");
require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty });
require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] });
require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] });
// Of the same length:
let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx());
@ -815,7 +794,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
})*
_ => {},
}
return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem })
return_error!(InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem })
})*
}
}
@ -835,11 +814,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_int_from_ty(i)),
ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_uint_from_ty(i)),
_ => {
return_error!(InvalidMonomorphizationExpectedSignedUnsigned {
return_error!(InvalidMonomorphization::ExpectedVectorElementType {
span,
name,
elem_ty: arg_tys[0].simd_size_and_type(bx.tcx()).1,
vec_ty: arg_tys[0],
expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1,
vector_type: arg_tys[0],
});
}
};
@ -925,7 +904,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
if name == sym::$name {
require!(
ret_ty == in_elem,
InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
);
return match in_elem.kind() {
ty::Int(_) | ty::Uint(_) => {
@ -947,11 +926,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
Ok(bx.vector_reduce_op(args[0].immediate(), $vec_op))
}
}
_ => return_error!(InvalidMonomorphizationUnsupportedElement {
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
span,
name,
symbol: sym::$name,
in_ty,
elem_ty: in_elem,
in_elem,
ret_ty
}),
};
@ -994,12 +974,19 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
if name == sym::$name {
require!(
ret_ty == in_elem,
InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
);
return match in_elem.kind() {
ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())),
ty::Float(_) => Ok(bx.$float_red(args[0].immediate())),
_ => return_error!(InvalidMonomorphizationUnsupportedElement { span, name, in_ty, elem_ty: in_elem, ret_ty }),
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
span,
name,
symbol: sym::$name,
in_ty,
in_elem,
ret_ty
}),
};
}
};
@ -1017,17 +1004,18 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let input = if !$boolean {
require!(
ret_ty == in_elem,
InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
);
args[0].immediate()
} else {
match in_elem.kind() {
ty::Int(_) | ty::Uint(_) => {}
_ => return_error!(InvalidMonomorphizationUnsupportedElement {
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
span,
name,
symbol: sym::$name,
in_ty,
elem_ty: in_elem,
in_elem,
ret_ty
}),
}
@ -1039,11 +1027,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let r = bx.vector_reduce_op(input, $op);
Ok(if !$boolean { r } else { bx.icmp(IntPredicate::IntNE, r, bx.context.new_rvalue_zero(r.get_type())) })
}
_ => return_error!(InvalidMonomorphizationUnsupportedElement {
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
span,
name,
symbol: sym::$name,
in_ty,
elem_ty: in_elem,
in_elem,
ret_ty
}),
};

View file

@ -100,9 +100,11 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val);
let covfun_section_name = coverageinfo::covfun_section_name(cx);
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
save_function_record(
cx,
&covfun_section_name,
mangled_function_name,
source_hash,
filenames_ref,
@ -228,6 +230,7 @@ impl CoverageMapGenerator {
/// specific, well-known section and name.
fn save_function_record(
cx: &CodegenCx<'_, '_>,
covfun_section_name: &str,
mangled_function_name: &str,
source_hash: u64,
filenames_ref: u64,
@ -254,7 +257,13 @@ fn save_function_record(
/*packed=*/ true,
);
coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used);
coverageinfo::save_func_record_to_mod(
cx,
covfun_section_name,
func_name_hash,
func_record_val,
is_used,
);
}
/// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for

View file

@ -408,6 +408,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
covfun_section_name: &str,
func_name_hash: u64,
func_record_val: &'ll llvm::Value,
is_used: bool,
@ -423,20 +424,33 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
let func_record_var_name =
format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" });
debug!("function record var name: {:?}", func_record_var_name);
let func_record_section_name = llvm::build_string(|s| unsafe {
llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
})
.expect("Rust Coverage function record section name failed UTF-8 conversion");
debug!("function record section name: {:?}", func_record_section_name);
debug!("function record section name: {:?}", covfun_section_name);
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
llvm::set_initializer(llglobal, func_record_val);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
llvm::set_section(llglobal, &func_record_section_name);
llvm::set_section(llglobal, covfun_section_name);
llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
cx.add_used_global(llglobal);
}
/// Returns the section name string to pass through to the linker when embedding
/// per-function coverage information in the object file, according to the target
/// platform's object file format.
///
/// LLVM's coverage tools read coverage mapping details from this section when
/// producing coverage reports.
///
/// Typical values are:
/// - `__llvm_covfun` on Linux
/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String {
llvm::build_string(|s| unsafe {
llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
})
.expect("Rust Coverage function record section name failed UTF-8 conversion")
}

View file

@ -353,7 +353,7 @@ impl IntoDiagnostic<'_> for LinkingFailed<'_> {
let contains_undefined_ref = self.escaped_output.contains("undefined reference to");
diag.note(format!("{:?}", self.command)).note(self.escaped_output.to_string());
diag.note(format!("{:?}", self.command)).note(self.escaped_output);
// Trying to match an error from OS linkers
// which by now we have no way to translate.

View file

@ -408,8 +408,11 @@ const_eval_undefined_behavior =
const_eval_undefined_behavior_note =
The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
const_eval_uninhabited_enum_tag = {$front_matter}: encountered an uninhabited enum variant
const_eval_uninhabited_enum_variant_read =
read discriminant of an uninhabited enum variant
const_eval_uninhabited_enum_variant_written =
writing discriminant of an uninhabited enum
writing discriminant of an uninhabited enum variant
const_eval_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}`
const_eval_uninit = {$front_matter}: encountered uninitialized bytes
const_eval_uninit_bool = {$front_matter}: encountered uninitialized memory, but expected a boolean
@ -423,8 +426,6 @@ const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but e
const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer
const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference
const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str`
const_eval_uninit_unsized_local =
unsized local is used while uninitialized
const_eval_unreachable = entering unreachable code
const_eval_unreachable_unwind =
unwinding past a stack frame that does not allow unwinding

View file

@ -101,8 +101,8 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
return None;
}
ty::Adt(def, _) => {
let variant = ecx.read_discriminant(&op).ok()?.1;
let down = ecx.operand_downcast(&op, variant).ok()?;
let variant = ecx.read_discriminant(&op).ok()?;
let down = ecx.project_downcast(&op, variant).ok()?;
(def.variants()[variant].fields.len(), Some(variant), down)
}
ty::Tuple(args) => (args.len(), None, op),
@ -111,7 +111,7 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
let fields_iter = (0..field_count)
.map(|i| {
let field_op = ecx.operand_field(&down, i).ok()?;
let field_op = ecx.project_field(&down, i).ok()?;
let val = op_to_const(&ecx, &field_op);
Some((val, field_op.layout.ty))
})

View file

@ -2,11 +2,11 @@ use super::eval_queries::{mk_eval_cx, op_to_const};
use super::machine::CompileTimeEvalContext;
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
use crate::const_eval::CanAccessStatics;
use crate::interpret::MPlaceTy;
use crate::interpret::{
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
MemoryKind, PlaceTy, Scalar,
MemoryKind, PlaceTy, Projectable, Scalar,
};
use crate::interpret::{MPlaceTy, Value};
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::source_map::DUMMY_SP;
use rustc_target::abi::{Align, FieldIdx, VariantIdx, FIRST_VARIANT};
@ -20,7 +20,7 @@ fn branches<'tcx>(
num_nodes: &mut usize,
) -> ValTreeCreationResult<'tcx> {
let place = match variant {
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
Some(variant) => ecx.project_downcast(place, variant).unwrap(),
None => *place,
};
let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
@ -28,7 +28,7 @@ fn branches<'tcx>(
let mut fields = Vec::with_capacity(n);
for i in 0..n {
let field = ecx.mplace_field(&place, i).unwrap();
let field = ecx.project_field(&place, i).unwrap();
let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?;
fields.push(Some(valtree));
}
@ -55,13 +55,11 @@ fn slice_branches<'tcx>(
place: &MPlaceTy<'tcx>,
num_nodes: &mut usize,
) -> ValTreeCreationResult<'tcx> {
let n = place
.len(&ecx.tcx.tcx)
.unwrap_or_else(|_| panic!("expected to use len of place {:?}", place));
let n = place.len(ecx).unwrap_or_else(|_| panic!("expected to use len of place {:?}", place));
let mut elems = Vec::with_capacity(n as usize);
for i in 0..n {
let place_elem = ecx.mplace_index(place, i).unwrap();
let place_elem = ecx.project_index(place, i).unwrap();
let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?;
elems.push(valtree);
}
@ -132,7 +130,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
bug!("uninhabited types should have errored and never gotten converted to valtree")
}
let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else {
let Ok(variant) = ecx.read_discriminant(&place.into()) else {
return Err(ValTreeCreationError::Other);
};
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
@ -386,7 +384,7 @@ fn valtree_into_mplace<'tcx>(
debug!(?variant);
(
place.project_downcast(ecx, variant_idx).unwrap(),
ecx.project_downcast(place, variant_idx).unwrap(),
&branches[1..],
Some(variant_idx),
)
@ -401,7 +399,7 @@ fn valtree_into_mplace<'tcx>(
debug!(?i, ?inner_valtree);
let mut place_inner = match ty.kind() {
ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(),
ty::Str | ty::Slice(_) => ecx.project_index(place, i as u64).unwrap(),
_ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty())
&& i == branches.len() - 1 =>
{
@ -441,7 +439,7 @@ fn valtree_into_mplace<'tcx>(
)
.unwrap()
}
_ => ecx.mplace_field(&place_adjusted, i).unwrap(),
_ => ecx.project_field(&place_adjusted, i).unwrap(),
};
debug!(?place_inner);

View file

@ -511,7 +511,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes,
DeadLocal => const_eval_dead_local,
ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
UninhabitedEnumVariantWritten => const_eval_uninhabited_enum_variant_written,
UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
Validation(e) => e.diagnostic_message(),
Custom(x) => (x.msg)(),
}
@ -535,7 +536,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
| InvalidMeta(InvalidMetaKind::TooBig)
| InvalidUninitBytes(None)
| DeadLocal
| UninhabitedEnumVariantWritten => {}
| UninhabitedEnumVariantWritten(_)
| UninhabitedEnumVariantRead(_) => {}
BoundsCheckFailed { len, index } => {
builder.set_arg("len", len);
builder.set_arg("index", index);
@ -623,6 +625,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
UnsafeCell => const_eval_unsafe_cell,
UninhabitedVal { .. } => const_eval_uninhabited_val,
InvalidEnumTag { .. } => const_eval_invalid_enum_tag,
UninhabitedEnumTag => const_eval_uninhabited_enum_tag,
UninitEnumTag => const_eval_uninit_enum_tag,
UninitStr => const_eval_uninit_str,
Uninit { expected: ExpectedKind::Bool } => const_eval_uninit_bool,
@ -760,7 +763,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
| InvalidMetaSliceTooLarge { .. }
| InvalidMetaTooLarge { .. }
| DanglingPtrUseAfterFree { .. }
| DanglingPtrOutOfBounds { .. } => {}
| DanglingPtrOutOfBounds { .. }
| UninhabitedEnumTag => {}
}
}
}
@ -835,7 +839,9 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
rustc_middle::error::middle_adjust_for_foreign_abi_error
}
InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized,
InvalidProgramInfo::UninitUnsizedLocal => const_eval_uninit_unsized_local,
InvalidProgramInfo::ConstPropNonsense => {
panic!("We had const-prop nonsense, this should never be printed")
}
}
}
fn add_args<G: EmissionGuarantee>(
@ -846,7 +852,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
match self {
InvalidProgramInfo::TooGeneric
| InvalidProgramInfo::AlreadyReported(_)
| InvalidProgramInfo::UninitUnsizedLocal => {}
| InvalidProgramInfo::ConstPropNonsense => {}
InvalidProgramInfo::Layout(e) => {
let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler);
for (name, val) in diag.args() {

View file

@ -420,8 +420,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if cast_ty_field.is_zst() {
continue;
}
let src_field = self.operand_field(src, i)?;
let dst_field = self.place_field(dest, i)?;
let src_field = self.project_field(src, i)?;
let dst_field = self.project_field(dest, i)?;
if src_field.layout.ty == cast_ty_field.ty {
self.copy_op(&src_field, &dst_field, /*allow_transmute*/ false)?;
} else {

View file

@ -1,6 +1,6 @@
//! Functions for reading and writing discriminants of multi-variant layouts (enums and generators).
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
use rustc_middle::{mir, ty};
use rustc_target::abi::{self, TagEncoding};
use rustc_target::abi::{VariantIdx, Variants};
@ -22,7 +22,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// When evaluating we will always error before even getting here, but ConstProp 'executes'
// dead code, so we cannot ICE here.
if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
throw_ub!(UninhabitedEnumVariantWritten)
throw_ub!(UninhabitedEnumVariantWritten(variant_index))
}
match dest.layout.variants {
@ -47,7 +47,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let size = tag_layout.size(self);
let tag_val = size.truncate(discr_val);
let tag_dest = self.place_field(dest, tag_field)?;
let tag_dest = self.project_field(dest, tag_field)?;
self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
}
abi::Variants::Multiple {
@ -78,7 +78,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&niche_start_val,
)?;
// Write result.
let niche_dest = self.place_field(dest, tag_field)?;
let niche_dest = self.project_field(dest, tag_field)?;
self.write_immediate(*tag_val, &niche_dest)?;
}
}
@ -93,7 +93,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn read_discriminant(
&self,
op: &OpTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> {
) -> InterpResult<'tcx, VariantIdx> {
trace!("read_discriminant_value {:#?}", op.layout);
// Get type and layout of the discriminant.
let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
@ -106,19 +106,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
Variants::Single { index } => {
let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
Some(discr) => {
// This type actually has discriminants.
assert_eq!(discr.ty, discr_layout.ty);
Scalar::from_uint(discr.val, discr_layout.size)
// Do some extra checks on enums.
if op.layout.ty.is_enum() {
// Hilariously, `Single` is used even for 0-variant enums.
// (See https://github.com/rust-lang/rust/issues/89765).
if matches!(op.layout.ty.kind(), ty::Adt(def, ..) if def.variants().is_empty())
{
throw_ub!(UninhabitedEnumVariantRead(index))
}
None => {
// On a type without actual discriminants, variant is 0.
assert_eq!(index.as_u32(), 0);
Scalar::from_uint(index.as_u32(), discr_layout.size)
// For consisteny with `write_discriminant`, and to make sure that
// `project_downcast` cannot fail due to strange layouts, we declare immediate UB
// for uninhabited variants.
if op.layout.for_variant(self, index).abi.is_uninhabited() {
throw_ub!(UninhabitedEnumVariantRead(index))
}
};
return Ok((discr, index));
}
return Ok(index);
}
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
(tag, tag_encoding, tag_field)
@ -138,13 +141,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?;
// Read tag and sanity-check `tag_layout`.
let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
let tag_val = self.read_immediate(&self.project_field(op, tag_field)?)?;
assert_eq!(tag_layout.size, tag_val.layout.size);
assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
trace!("tag value: {}", tag_val);
// Figure out which discriminant and variant this corresponds to.
Ok(match *tag_encoding {
let index = match *tag_encoding {
TagEncoding::Direct => {
let scalar = tag_val.to_scalar();
// Generate a specific error if `tag_val` is not an integer.
@ -172,7 +175,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
.ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
// Return the cast value, and the index.
(discr_val, index.0)
index.0
}
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
let tag_val = tag_val.to_scalar();
@ -230,7 +233,32 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Compute the size of the scalar we need to return.
// No need to cast, because the variant index directly serves as discriminant and is
// encoded in the tag.
(Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
variant
}
};
// For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants.
if op.layout.for_variant(self, index).abi.is_uninhabited() {
throw_ub!(UninhabitedEnumVariantRead(index))
}
Ok(index)
}
pub fn discriminant_for_variant(
&self,
layout: TyAndLayout<'tcx>,
variant: VariantIdx,
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
let discr_layout = self.layout_of(layout.ty.discriminant_ty(*self.tcx))?;
Ok(match layout.ty.discriminant_for_variant(*self.tcx, variant) {
Some(discr) => {
// This type actually has discriminants.
assert_eq!(discr.ty, discr_layout.ty);
Scalar::from_uint(discr.val, discr_layout.size)
}
None => {
// On a type without actual discriminants, variant is 0.
assert_eq!(variant.as_u32(), 0);
Scalar::from_uint(variant.as_u32(), discr_layout.size)
}
})
}

View file

@ -1014,9 +1014,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
{
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.place {
Place::Local { frame, local } => {
Place::Local { frame, local, offset } => {
let mut allocs = Vec::new();
write!(fmt, "{:?}", local)?;
if let Some(offset) = offset {
write!(fmt, "+{:#x}", offset.bytes())?;
}
if frame != self.ecx.frame_idx() {
write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?;
}

View file

@ -164,75 +164,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
&self.ecx
}
fn visit_aggregate(
&mut self,
mplace: &MPlaceTy<'tcx>,
fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
) -> InterpResult<'tcx> {
// We want to walk the aggregate to look for references to intern. While doing that we
// also need to take special care of interior mutability.
//
// As an optimization, however, if the allocation does not contain any references: we don't
// need to do the walk. It can be costly for big arrays for example (e.g. issue #93215).
let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> {
// ZSTs cannot contain pointers, we can avoid the interning walk.
if mplace.layout.is_zst() {
return Ok(false);
}
// Now, check whether this allocation could contain references.
//
// Note, this check may sometimes not be cheap, so we only do it when the walk we'd like
// 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 {
// 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);
};
// 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 !alloc.has_provenance() {
return Ok(false);
}
} else {
// We're encountering a ZST here, and can avoid the walk as well.
return Ok(false);
}
}
// In the general case, we do the walk.
Ok(true)
};
// If this allocation contains no references to intern, we avoid the potentially costly
// walk.
//
// We can do this before the checks for interior mutability below, because only references
// are relevant in that situation, and we're checking if there are any here.
if !is_walk_needed(mplace)? {
return Ok(());
}
if let Some(def) = mplace.layout.ty.ty_adt_def() {
if def.is_unsafe_cell() {
// We are crossing over an `UnsafeCell`, we can mutate again. This means that
// References we encounter inside here are interned as pointing to mutable
// allocations.
// Remember the `old` value to handle nested `UnsafeCell`.
let old = std::mem::replace(&mut self.inside_unsafe_cell, true);
let walked = self.walk_aggregate(mplace, fields);
self.inside_unsafe_cell = old;
return walked;
}
}
self.walk_aggregate(mplace, fields)
}
fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
// Handle Reference types, as these are the only types with provenance supported by const eval.
// Raw pointers (and boxes) are handled by the `leftover_allocations` logic.
@ -315,7 +246,63 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
}
Ok(())
} else {
// Not a reference -- proceed recursively.
// Not a reference. Check if we want to recurse.
let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> {
// ZSTs cannot contain pointers, we can avoid the interning walk.
if mplace.layout.is_zst() {
return Ok(false);
}
// Now, check whether this allocation could contain references.
//
// Note, this check may sometimes not be cheap, so we only do it when the walk we'd like
// 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 {
// 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);
};
// 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 !alloc.has_provenance() {
return Ok(false);
}
} else {
// We're encountering a ZST here, and can avoid the walk as well.
return Ok(false);
}
}
// In the general case, we do the walk.
Ok(true)
};
// If this allocation contains no references to intern, we avoid the potentially costly
// walk.
//
// We can do this before the checks for interior mutability below, because only references
// are relevant in that situation, and we're checking if there are any here.
if !is_walk_needed(mplace)? {
return Ok(());
}
if let Some(def) = mplace.layout.ty.ty_adt_def() {
if def.is_unsafe_cell() {
// We are crossing over an `UnsafeCell`, we can mutate again. This means that
// References we encounter inside here are interned as pointing to mutable
// allocations.
// Remember the `old` value to handle nested `UnsafeCell`.
let old = std::mem::replace(&mut self.inside_unsafe_cell, true);
let walked = self.walk_value(mplace);
self.inside_unsafe_cell = old;
return walked;
}
}
self.walk_value(mplace)
}
}

View file

@ -226,8 +226,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
sym::discriminant_value => {
let place = self.deref_operand(&args[0])?;
let discr_val = self.read_discriminant(&place.into())?.0;
self.write_scalar(discr_val, dest)?;
let variant = self.read_discriminant(&place.into())?;
let discr = self.discriminant_for_variant(place.layout, variant)?;
self.write_scalar(discr, dest)?;
}
sym::exact_div => {
let l = self.read_immediate(&args[0])?;
@ -425,11 +426,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
);
for i in 0..dest_len {
let place = self.mplace_index(&dest, i)?;
let place = self.project_index(&dest, i)?;
let value = if i == index {
elem.clone()
} else {
self.mplace_index(&input, i)?.into()
self.project_index(&input, i)?.into()
};
self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?;
}
@ -444,7 +445,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
input_len
);
self.copy_op(
&self.mplace_index(&input, index)?.into(),
&self.project_index(&input, index)?.into(),
dest,
/*allow_transmute*/ false,
)?;

View file

@ -101,11 +101,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
// Initialize fields.
self.write_immediate(file.to_ref(self), &self.mplace_field(&location, 0).unwrap().into())
self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap().into())
.expect("writing to memory we just allocated cannot fail");
self.write_scalar(line, &self.mplace_field(&location, 1).unwrap().into())
self.write_scalar(line, &self.project_field(&location, 1).unwrap().into())
.expect("writing to memory we just allocated cannot fail");
self.write_scalar(col, &self.mplace_field(&location, 2).unwrap().into())
self.write_scalar(col, &self.project_field(&location, 2).unwrap().into())
.expect("writing to memory we just allocated cannot fail");
location

View file

@ -26,9 +26,10 @@ 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, Operand};
pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
pub use self::projection::Projectable;
pub use self::terminator::FnArg;
pub use self::validity::{CtfeValidationMode, RefTracking};
pub use self::visitor::{MutValueVisitor, Value, ValueVisitor};
pub use self::visitor::ValueVisitor;
pub(crate) use self::intrinsics::eval_nullary_intrinsic;
use eval_context::{from_known_layout, mir_assign_valid_types};

View file

@ -1,6 +1,8 @@
//! Functions concerning immediate values and operands, and reading from operands.
//! All high-level functions to read from memory work on operands as sources.
use std::assert_matches::assert_matches;
use either::{Either, Left, Right};
use rustc_hir::def::Namespace;
@ -13,8 +15,8 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
use super::{
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer,
Provenance, Scalar,
InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer,
Projectable, Provenance, Scalar,
};
/// An `Immediate` represents a single immediate self-contained Rust value.
@ -199,6 +201,20 @@ impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, 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 }
}
}
impl<'tcx, Prov: Provenance> From<&'_ mut ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
#[inline(always)]
fn from(val: &mut ImmTy<'tcx, Prov>) -> Self {
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
}
}
impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
#[inline]
pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
@ -240,43 +256,128 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
let int = self.to_scalar().assert_int();
ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral())
}
/// Compute the "sub-immediate" that is located within the `base` at the given offset with the
/// given layout.
// Not called `offset` to avoid confusion with the trait method.
fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
// 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) {
// if the entire value is uninit, then so is the field (can happen in ConstProp)
(Immediate::Uninit, _) => Immediate::Uninit,
// the field contains no information, can be left uninit
_ if layout.is_zst() => Immediate::Uninit,
// some fieldless enum variants can have non-zero size but still `Aggregate` ABI... try
// to detect those here and also give them no data
_ if matches!(layout.abi, Abi::Aggregate { .. })
&& matches!(&layout.fields, abi::FieldsShape::Arbitrary { offsets, .. } if offsets.len() == 0) =>
{
Immediate::Uninit
}
// the field covers the entire type
_ if layout.size == self.layout.size => {
assert_eq!(offset.bytes(), 0);
assert!(
match (self.layout.abi, layout.abi) {
(Abi::Scalar(..), Abi::Scalar(..)) => true,
(Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
_ => false,
},
"cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}",
self.layout.ty,
layout.ty,
self.layout.abi,
layout.abi,
);
**self
}
// extract fields from types with `ScalarPair` ABI
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
assert!(matches!(layout.abi, Abi::Scalar(..)));
Immediate::from(if offset.bytes() == 0 {
debug_assert_eq!(layout.size, a.size(cx));
a_val
} else {
debug_assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi));
debug_assert_eq!(layout.size, b.size(cx));
b_val
})
}
// everything else is a bug
_ => bug!("invalid field access on immediate {}, layout {:#?}", self, self.layout),
};
ImmTy::from_immediate(inner_val, layout)
}
}
impl<'mir, 'tcx: 'mir, Prov: Provenance> Projectable<'mir, 'tcx, Prov> for ImmTy<'tcx, Prov> {
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
assert!(self.layout.is_sized()); // unsized ImmTy can only exist temporarily and should never reach this here
Ok(MemPlaceMeta::None)
}
fn offset_with_meta(
&self,
offset: Size,
meta: MemPlaceMeta<Prov>,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway
Ok(self.offset_(offset, layout, cx))
}
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.into())
}
}
impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
if self.layout.is_unsized() {
if matches!(self.op, Operand::Immediate(Immediate::Uninit)) {
// Uninit unsized places shouldn't occur. In the interpreter we have them
// temporarily for unsized arguments before their value is put in; in ConstProp they
// remain uninit and this code can actually be reached.
throw_inval!(UninitUnsizedLocal);
// Provided as inherent method since it doesn't need the `ecx` of `Projectable::meta`.
pub fn meta(&self) -> InterpResult<'tcx, MemPlaceMeta<Prov>> {
Ok(if self.layout.is_unsized() {
if matches!(self.op, Operand::Immediate(_)) {
// Unsized immediate OpTy cannot occur. We create a MemPlace for all unsized locals during argument passing.
// However, ConstProp doesn't do that, so we can run into this nonsense situation.
throw_inval!(ConstPropNonsense);
}
// There are no unsized immediates.
self.assert_mem_place().len(cx)
self.assert_mem_place().meta
} else {
match self.layout.fields {
abi::FieldsShape::Array { count, .. } => Ok(count),
_ => bug!("len not supported on sized type {:?}", self.layout.ty),
}
}
MemPlaceMeta::None
})
}
}
impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
for OpTy<'tcx, Prov>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
/// Replace the layout of this operand. There's basically no sanity check that this makes sense,
/// you better know what you are doing! If this is an immediate, applying the wrong layout can
/// not just lead to invalid data, it can actually *shift the data around* since the offsets of
/// a ScalarPair are entirely determined by the layout, not the data.
pub fn transmute(&self, layout: TyAndLayout<'tcx>) -> Self {
assert_eq!(
self.layout.size, layout.size,
"transmuting with a size change, that doesn't seem right"
);
OpTy { layout, ..*self }
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
self.meta()
}
/// Offset the operand in memory (if possible) and change its metadata.
///
/// This can go wrong very easily if you give the wrong layout for the new place!
pub(super) fn offset_with_meta(
fn offset_with_meta(
&self,
offset: Size,
meta: MemPlaceMeta<Prov>,
@ -286,28 +387,18 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
match self.as_mplace_or_imm() {
Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()),
Right(imm) => {
assert!(
matches!(*imm, Immediate::Uninit),
"Scalar/ScalarPair cannot be offset into"
);
assert!(!meta.has_meta()); // no place to store metadata here
// Every part of an uninit is uninit.
Ok(ImmTy::uninit(layout).into())
Ok(imm.offset(offset, layout, cx)?.into())
}
}
}
/// Offset the operand in memory (if possible).
///
/// This can go wrong very easily if you give the wrong layout for the new place!
pub fn offset(
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
offset: Size,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
assert!(layout.is_sized());
self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.clone())
}
}
@ -497,18 +588,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Every place can be read from, so we can turn them into an operand.
/// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this
/// will never actually read from memory.
#[inline(always)]
pub fn place_to_op(
&self,
place: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
let op = match **place {
Place::Ptr(mplace) => Operand::Indirect(mplace),
Place::Local { frame, local } => {
*self.local_to_op(&self.stack()[frame], local, None)?
match place.as_mplace_or_local() {
Left(mplace) => Ok(mplace.into()),
Right((frame, local, offset)) => {
let base = self.local_to_op(&self.stack()[frame], local, None)?;
let mut field = if let Some(offset) = offset {
// This got offset. We can be sure that the field is sized.
base.offset(offset, place.layout, self)?
} else {
assert_eq!(place.layout, base.layout);
// Unsized cases are possible here since an unsized local will be a
// `Place::Local` until the first projection calls `place_to_op` to extract the
// underlying mplace.
base
};
field.align = Some(place.align);
Ok(field)
}
};
Ok(OpTy { op, layout: place.layout, align: Some(place.align) })
}
}
/// Evaluate a place with the goal of reading from it. This lets us sometimes
@ -525,7 +626,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?;
// Using `try_fold` turned out to be bad for performance, hence the loop.
for elem in mir_place.projection.iter() {
op = self.operand_projection(&op, elem)?
op = self.project(&op, elem)?
}
trace!("eval_place_to_op: got {:?}", *op);

View file

@ -38,9 +38,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to
// do a component-wise write here. This code path is slower than the above because
// `place_field` will have to `force_allocate` locals here.
let val_field = self.place_field(&dest, 0)?;
let val_field = self.project_field(dest, 0)?;
self.write_scalar(val, &val_field)?;
let overflowed_field = self.place_field(&dest, 1)?;
let overflowed_field = self.project_field(dest, 1)?;
self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?;
}
Ok(())

View file

@ -2,11 +2,14 @@
//! into a place.
//! All high-level functions to write to memory work on places as destinations.
use std::assert_matches::assert_matches;
use either::{Either, Left, Right};
use rustc_ast::Mutability;
use rustc_index::IndexSlice;
use rustc_middle::mir;
use rustc_middle::mir::interpret::PointerArithmetic;
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::Ty;
@ -15,7 +18,7 @@ use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_V
use super::{
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand,
Pointer, Provenance, Scalar,
Pointer, Projectable, Provenance, Scalar,
};
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@ -44,6 +47,27 @@ impl<Prov: Provenance> MemPlaceMeta<Prov> {
Self::None => false,
}
}
pub(crate) fn len<'tcx>(
&self,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, u64> {
if layout.is_unsized() {
// We need to consult `meta` metadata
match layout.ty.kind() {
ty::Slice(..) | ty::Str => self.unwrap_meta().to_target_usize(cx),
_ => bug!("len not supported on unsized type {:?}", layout.ty),
}
} else {
// Go through the layout. There are lots of types that support a length,
// e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
match layout.fields {
abi::FieldsShape::Array { count, .. } => Ok(count),
_ => bug!("len not supported on sized type {:?}", layout.ty),
}
}
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@ -73,9 +97,13 @@ pub enum Place<Prov: Provenance = AllocId> {
/// A place referring to a value allocated in the `Memory` system.
Ptr(MemPlace<Prov>),
/// To support alloc-free locals, we are able to write directly to a local.
/// To support alloc-free locals, we are able to write directly to a local. The offset indicates
/// where in the local this place is located; if it is `None`, no projection has been applied.
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
/// (Without that optimization, we'd just always be a `MemPlace`.)
Local { frame: usize, local: mir::Local },
/// Note that this only stores the frame index, not the thread this frame belongs to -- that is
/// implicit. This means a `Place` must never be moved across interpreter thread boundaries!
Local { frame: usize, local: mir::Local, offset: Option<Size> },
}
#[derive(Clone, Debug)]
@ -132,6 +160,11 @@ impl<Prov: Provenance> MemPlace<Prov> {
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 }
@ -150,7 +183,8 @@ impl<Prov: Provenance> MemPlace<Prov> {
}
#[inline]
pub(super) fn offset_with_meta<'tcx>(
// Not called `offset_with_meta` to avoid confusion with the trait method.
fn offset_with_meta_<'tcx>(
self,
offset: Size,
meta: MemPlaceMeta<Prov>,
@ -164,19 +198,6 @@ impl<Prov: Provenance> MemPlace<Prov> {
}
}
impl<Prov: Provenance> Place<Prov> {
/// Asserts that this points to some local variable.
/// Returns the frame idx and the variable idx.
#[inline]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_local(&self) -> (usize, mir::Local) {
match self {
Place::Local { frame, local } => (*frame, *local),
_ => bug!("assert_local: expected Place::Local, got {:?}", self),
}
}
}
impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
/// Produces a MemPlace that works for ZST but nothing else.
/// Conceptually this is a new allocation, but it doesn't actually create an allocation so you
@ -189,37 +210,6 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
}
/// Offset the place in memory and change its metadata.
///
/// This can go wrong very easily if you give the wrong layout for the new place!
#[inline]
pub(crate) fn offset_with_meta(
&self,
offset: Size,
meta: MemPlaceMeta<Prov>,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
Ok(MPlaceTy {
mplace: self.mplace.offset_with_meta(offset, meta, cx)?,
align: self.align.restrict_for_offset(offset),
layout,
})
}
/// Offset the place in memory.
///
/// This can go wrong very easily if you give the wrong layout for the new place!
pub fn offset(
&self,
offset: Size,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
assert!(layout.is_sized());
self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
}
#[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 }
@ -231,28 +221,48 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
layout: TyAndLayout<'tcx>,
meta: MemPlaceMeta<Prov>,
) -> Self {
let mut mplace = MemPlace::from_ptr(ptr);
mplace.meta = meta;
MPlaceTy {
mplace: MemPlace::from_ptr_with_meta(ptr, meta),
layout,
align: layout.align.abi,
}
}
}
MPlaceTy { mplace, layout, align: layout.align.abi }
impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
for MPlaceTy<'tcx, Prov>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline]
pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
if self.layout.is_unsized() {
// We need to consult `meta` metadata
match self.layout.ty.kind() {
ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_target_usize(cx),
_ => bug!("len not supported on unsized type {:?}", self.layout.ty),
}
} else {
// Go through the layout. There are lots of types that support a length,
// e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
match self.layout.fields {
abi::FieldsShape::Array { count, .. } => Ok(count),
_ => bug!("len not supported on sized type {:?}", self.layout.ty),
}
}
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
Ok(self.meta)
}
fn offset_with_meta(
&self,
offset: Size,
meta: MemPlaceMeta<Prov>,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
Ok(MPlaceTy {
mplace: self.mplace.offset_with_meta_(offset, meta, cx)?,
align: self.align.restrict_for_offset(offset),
layout,
})
}
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.into())
}
}
@ -280,13 +290,15 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
}
}
impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
impl<'tcx, Prov: Provenance + 'static> PlaceTy<'tcx, Prov> {
/// A place is either an mplace or some local.
#[inline]
pub fn as_mplace_or_local(&self) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> {
pub fn as_mplace_or_local(
&self,
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
match **self {
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }),
Place::Local { frame, local } => Right((frame, local)),
Place::Local { frame, local, offset } => Right((frame, local, offset)),
}
}
@ -302,12 +314,76 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
}
}
impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
for PlaceTy<'tcx, Prov>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
ecx.place_meta(self)
}
fn offset_with_meta(
&self,
offset: Size,
meta: MemPlaceMeta<Prov>,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
Ok(match self.as_mplace_or_local() {
Left(mplace) => mplace.offset_with_meta(offset, meta, layout, cx)?.into(),
Right((frame, local, old_offset)) => {
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
let new_offset = cx
.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,
}
}
})
}
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
ecx.place_to_op(self)
}
}
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
where
Prov: Provenance + 'static,
M: Machine<'mir, 'tcx, Provenance = Prov>,
{
/// Get the metadata of the given place.
pub(super) fn place_meta(
&self,
place: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
if place.layout.is_unsized() {
// For `Place::Local`, the metadata is stored with the local, not the place. So we have
// to look that up first.
self.place_to_op(place)?.meta()
} else {
Ok(MemPlaceMeta::None)
}
}
/// 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 `MemPlace::to_ref()`.
///
@ -327,11 +403,9 @@ where
Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
};
let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta };
// `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.
let align = layout.align.abi;
Ok(MPlaceTy { mplace, layout, align })
Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta))
}
/// Take an operand, representing a pointer, and dereference it to a place.
@ -422,7 +496,7 @@ where
local: mir::Local,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
let layout = self.layout_of_local(&self.stack()[frame], local, None)?;
let place = Place::Local { frame, local };
let place = Place::Local { frame, local, offset: None };
Ok(PlaceTy { place, layout, align: layout.align.abi })
}
@ -430,13 +504,13 @@ where
/// place; for reading, a more efficient alternative is `eval_place_to_op`.
#[instrument(skip(self), level = "debug")]
pub fn eval_place(
&mut self,
&self,
mir_place: mir::Place<'tcx>,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?;
// Using `try_fold` turned out to be bad for performance, hence the loop.
for elem in mir_place.projection.iter() {
place = self.place_projection(&place, elem)?
place = self.project(&place, elem)?
}
trace!("{:?}", self.dump_place(place.place));
@ -503,22 +577,54 @@ where
src: Immediate<M::Provenance>,
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
assert!(dest.layout.is_sized(), "Cannot write unsized data");
assert!(dest.layout.is_sized(), "Cannot write unsized immediate data");
trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
// 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.place {
Place::Local { frame, local } => {
match M::access_local_mut(self, frame, local)? {
Operand::Immediate(local) => {
// Local can be updated in-place.
*local = src;
return Ok(());
}
Operand::Indirect(mplace) => {
// The local is in memory, go on below.
*mplace
Place::Local { frame, local, offset } => {
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
// just fall back to the indirect path.
*self.force_allocation(dest)?
} else {
match M::access_local_mut(self, frame, local)? {
Operand::Immediate(local_val) => {
// Local can be updated in-place.
*local_val = src;
// Double-check that the value we are storing and the local fit to each other.
// (*After* doing the update for borrow checker reasons.)
if cfg!(debug_assertions) {
let local_layout =
self.layout_of_local(&self.stack()[frame], local, None)?;
match (src, local_layout.abi) {
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
assert_eq!(scalar.size(), s.size(self))
}
(
Immediate::ScalarPair(a_val, b_val),
Abi::ScalarPair(a, b),
) => {
assert_eq!(a_val.size(), a.size(self));
assert_eq!(b_val.size(), b.size(self));
}
(Immediate::Uninit, _) => {}
(src, abi) => {
bug!(
"value {src:?} cannot be written into local with type {} (ABI {abi:?})",
local_layout.ty
)
}
};
}
return Ok(());
}
Operand::Indirect(mplace) => {
// The local is in memory, go on below.
*mplace
}
}
}
}
@ -593,15 +699,23 @@ where
pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
let mplace = match dest.as_mplace_or_local() {
Left(mplace) => mplace,
Right((frame, local)) => {
match M::access_local_mut(self, frame, local)? {
Operand::Immediate(local) => {
*local = Immediate::Uninit;
return Ok(());
}
Operand::Indirect(mplace) => {
// The local is in memory, go on below.
MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align }
Right((frame, local, offset)) => {
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
// just fall back to the indirect path.
// FIXME: share the logic with `write_immediate_no_validate`.
self.force_allocation(dest)?
} else {
match M::access_local_mut(self, frame, local)? {
Operand::Immediate(local) => {
*local = Immediate::Uninit;
return Ok(());
}
Operand::Indirect(mplace) => {
// The local is in memory, go on below.
MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align }
}
}
}
}
@ -728,8 +842,8 @@ where
place: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
let mplace = match place.place {
Place::Local { frame, local } => {
match M::access_local_mut(self, frame, local)? {
Place::Local { frame, local, offset } => {
let whole_local = match M::access_local_mut(self, frame, local)? {
&mut Operand::Immediate(local_val) => {
// We need to make an allocation.
@ -742,10 +856,11 @@ where
throw_unsup_format!("unsized locals are not supported");
}
let mplace = *self.allocate(local_layout, MemoryKind::Stack)?;
// Preserve old value. (As an optimization, we can skip this if it was uninit.)
if !matches!(local_val, Immediate::Uninit) {
// Preserve old value. (As an optimization, we can skip this if it was uninit.)
// We don't have to validate as we can assume the local
// was already valid for its type.
// We don't have to validate as we can assume the local was already
// valid for its type. We must not use any part of `place` here, that
// could be a projection to a part of the local!
self.write_immediate_to_mplace_no_validate(
local_val,
local_layout,
@ -753,18 +868,25 @@ where
mplace,
)?;
}
// Now we can call `access_mut` again, asserting it goes well,
// and actually overwrite things.
// Now we can call `access_mut` again, asserting it goes well, and actually
// overwrite things. This points to the entire allocation, not just the part
// the place refers to, i.e. we do this before we apply `offset`.
*M::access_local_mut(self, frame, local).unwrap() =
Operand::Indirect(mplace);
mplace
}
&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)?
} else {
// Preserve wide place metadata, do not call `offset`.
whole_local
}
}
Place::Ptr(mplace) => mplace,
};
// Return with the original layout, so that the caller can go on
// Return with the original layout and align, so that the caller can go on
Ok(MPlaceTy { mplace, layout: place.layout, align: place.align })
}
@ -809,7 +931,7 @@ where
self.write_uninit(&dest)?;
let (variant_index, variant_dest, active_field_index) = match *kind {
mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
let variant_dest = self.place_downcast(&dest, variant_index)?;
let variant_dest = self.project_downcast(dest, variant_index)?;
(variant_index, variant_dest, active_field_index)
}
_ => (FIRST_VARIANT, dest.clone(), None),
@ -819,7 +941,7 @@ where
}
for (field_index, operand) in operands.iter_enumerated() {
let field_index = active_field_index.unwrap_or(field_index);
let field_dest = self.place_field(&variant_dest, field_index.as_usize())?;
let field_dest = self.project_field(&variant_dest, field_index.as_usize())?;
let op = self.eval_operand(operand, Some(field_dest.layout))?;
self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
}
@ -859,22 +981,24 @@ where
Ok((mplace, vtable))
}
/// Turn an operand with a `dyn* Trait` type into an operand with the actual dynamic type.
/// Aso returns the vtable.
pub(super) fn unpack_dyn_star(
/// Turn a `dyn* Trait` type into an value with the actual dynamic type.
/// Also returns the vtable.
pub(super) fn unpack_dyn_star<P: Projectable<'mir, 'tcx, M::Provenance>>(
&self,
op: &OpTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, (OpTy<'tcx, M::Provenance>, Pointer<Option<M::Provenance>>)> {
val: &P,
) -> InterpResult<'tcx, (P, Pointer<Option<M::Provenance>>)> {
assert!(
matches!(op.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
"`unpack_dyn_star` only makes sense on `dyn*` types"
);
let data = self.operand_field(&op, 0)?;
let vtable = self.operand_field(&op, 1)?;
let vtable = self.read_pointer(&vtable)?;
let data = self.project_field(val, 0)?;
let vtable = self.project_field(val, 1)?;
let vtable = self.read_pointer(&vtable.to_op(self)?)?;
let (ty, _) = self.get_ptr_vtable(vtable)?;
let layout = self.layout_of(ty)?;
let data = data.transmute(layout);
// `data` is already the right thing but has the wrong type. So we transmute it, by
// projecting with offset 0.
let data = data.transmute(layout, self)?;
Ok((data, vtable))
}
}

View file

@ -7,18 +7,71 @@
//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually
//! implement the logic on OpTy, and MPlaceTy calls that.
use either::{Left, Right};
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::Ty;
use rustc_target::abi::{self, Abi, VariantIdx};
use rustc_middle::ty::TyCtxt;
use rustc_target::abi::HasDataLayout;
use rustc_target::abi::Size;
use rustc_target::abi::{self, VariantIdx};
use super::{
ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy,
Provenance, Scalar,
};
use super::MPlaceTy;
use super::{InterpCx, InterpResult, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
/// A thing that we can project into, and that has a layout.
pub trait Projectable<'mir, 'tcx: 'mir, Prov: Provenance>: Sized {
/// Get the layout.
fn layout(&self) -> TyAndLayout<'tcx>;
/// Get the metadata of a wide value.
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>>;
fn len<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, u64> {
self.meta(ecx)?.len(self.layout(), ecx)
}
/// Offset the value by the given amount, replacing the layout and metadata.
fn offset_with_meta(
&self,
offset: Size,
meta: MemPlaceMeta<Prov>,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self>;
fn offset(
&self,
offset: Size,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
assert!(layout.is_sized());
self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
}
fn transmute(
&self,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
assert_eq!(self.layout().size, layout.size);
self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, cx)
}
/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
/// reading from this thing.
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
}
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
@ -26,167 +79,83 @@ where
Prov: Provenance + 'static,
M: Machine<'mir, 'tcx, Provenance = Prov>,
{
//# Field access
/// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
/// always possible without allocating, so it can take `&self`. Also return the field's layout.
/// This supports both struct and array fields.
/// This supports both struct and array fields, but not slices!
///
/// This also works for arrays, but then the `usize` index type is restricting.
/// For indexing into arrays, use `mplace_index`.
pub fn mplace_field(
pub fn project_field<P: Projectable<'mir, 'tcx, M::Provenance>>(
&self,
base: &MPlaceTy<'tcx, M::Provenance>,
base: &P,
field: usize,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
let offset = base.layout.fields.offset(field);
let field_layout = base.layout.field(self, field);
) -> InterpResult<'tcx, P> {
// Slices nominally have length 0, so they will panic somewhere in `fields.offset`.
debug_assert!(
!matches!(base.layout().ty.kind(), ty::Slice(..)),
"`field` projection called on a slice -- call `index` projection instead"
);
let offset = base.layout().fields.offset(field);
let field_layout = base.layout().field(self, field);
// Offset may need adjustment for unsized fields.
let (meta, offset) = if field_layout.is_unsized() {
if base.layout().is_sized() {
// An unsized field of a sized type? Sure...
// But const-prop actually feeds us such nonsense MIR!
throw_inval!(ConstPropNonsense);
}
let base_meta = base.meta(self)?;
// Re-use parent metadata to determine dynamic field layout.
// With custom DSTS, this *will* execute user-defined code, but the same
// happens at run-time so that's okay.
match self.size_and_align_of(&base.meta, &field_layout)? {
Some((_, align)) => (base.meta, offset.align_to(align)),
match self.size_and_align_of(&base_meta, &field_layout)? {
Some((_, align)) => (base_meta, offset.align_to(align)),
None => {
// For unsized types with an extern type tail we perform no adjustments.
// NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
assert!(matches!(base.meta, MemPlaceMeta::None));
(base.meta, offset)
assert!(matches!(base_meta, MemPlaceMeta::None));
(base_meta, offset)
}
}
} else {
// base.meta could be present; we might be accessing a sized field of an unsized
// base_meta could be present; we might be accessing a sized field of an unsized
// struct.
(MemPlaceMeta::None, offset)
};
// We do not look at `base.layout.align` nor `field_layout.align`, unlike
// codegen -- mostly to see if we can get away with that
base.offset_with_meta(offset, meta, field_layout, self)
}
/// Gets the place of a field inside the place, and also the field's type.
/// Just a convenience function, but used quite a bit.
/// This is the only projection that might have a side-effect: We cannot project
/// into the field of a local `ScalarPair`, we have to first allocate it.
pub fn place_field(
&mut self,
base: &PlaceTy<'tcx, M::Provenance>,
field: usize,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
// FIXME: We could try to be smarter and avoid allocation for fields that span the
// entire place.
let base = self.force_allocation(base)?;
Ok(self.mplace_field(&base, field)?.into())
}
pub fn operand_field(
/// Downcasting to an enum variant.
pub fn project_downcast<P: Projectable<'mir, 'tcx, M::Provenance>>(
&self,
base: &OpTy<'tcx, M::Provenance>,
field: usize,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
let base = match base.as_mplace_or_imm() {
Left(ref mplace) => {
// We can reuse the mplace field computation logic for indirect operands.
let field = self.mplace_field(mplace, field)?;
return Ok(field.into());
}
Right(value) => value,
};
let field_layout = base.layout.field(self, field);
let offset = base.layout.fields.offset(field);
// 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 field_val: Immediate<_> = match (*base, base.layout.abi) {
// if the entire value is uninit, then so is the field (can happen in ConstProp)
(Immediate::Uninit, _) => Immediate::Uninit,
// the field contains no information, can be left uninit
_ if field_layout.is_zst() => Immediate::Uninit,
// the field covers the entire type
_ if field_layout.size == base.layout.size => {
assert!(match (base.layout.abi, field_layout.abi) {
(Abi::Scalar(..), Abi::Scalar(..)) => true,
(Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
_ => false,
});
assert!(offset.bytes() == 0);
*base
}
// extract fields from types with `ScalarPair` ABI
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
assert!(matches!(field_layout.abi, Abi::Scalar(..)));
Immediate::from(if offset.bytes() == 0 {
debug_assert_eq!(field_layout.size, a.size(self));
a_val
} else {
debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
debug_assert_eq!(field_layout.size, b.size(self));
b_val
})
}
// everything else is a bug
_ => span_bug!(
self.cur_span(),
"invalid field access on immediate {}, layout {:#?}",
base,
base.layout
),
};
Ok(ImmTy::from_immediate(field_val, field_layout).into())
}
//# Downcasting
pub fn mplace_downcast(
&self,
base: &MPlaceTy<'tcx, M::Provenance>,
base: &P,
variant: VariantIdx,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
) -> InterpResult<'tcx, P> {
assert!(!base.meta(self)?.has_meta());
// Downcasts only change the layout.
// (In particular, no check about whether this is even the active variant -- that's by design,
// see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
assert!(!base.meta.has_meta());
let mut base = *base;
base.layout = base.layout.for_variant(self, variant);
Ok(base)
// So we just "offset" by 0.
let layout = base.layout().for_variant(self, variant);
if layout.abi.is_uninhabited() {
// `read_discriminant` should have excluded uninhabited variants... but ConstProp calls
// us on dead code.
throw_inval!(ConstPropNonsense)
}
// This cannot be `transmute` as variants *can* have a smaller size than the entire enum.
base.offset(Size::ZERO, layout, self)
}
pub fn place_downcast(
/// Compute the offset and field layout for accessing the given index.
pub fn project_index<P: Projectable<'mir, 'tcx, M::Provenance>>(
&self,
base: &PlaceTy<'tcx, M::Provenance>,
variant: VariantIdx,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
// Downcast just changes the layout
let mut base = base.clone();
base.layout = base.layout.for_variant(self, variant);
Ok(base)
}
pub fn operand_downcast(
&self,
base: &OpTy<'tcx, M::Provenance>,
variant: VariantIdx,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
// Downcast just changes the layout
let mut base = base.clone();
base.layout = base.layout.for_variant(self, variant);
Ok(base)
}
//# Slice indexing
#[inline(always)]
pub fn operand_index(
&self,
base: &OpTy<'tcx, M::Provenance>,
base: &P,
index: u64,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
) -> InterpResult<'tcx, P> {
// Not using the layout method because we want to compute on u64
match base.layout.fields {
let (offset, field_layout) = match base.layout().fields {
abi::FieldsShape::Array { stride, count: _ } => {
// `count` is nonsense for slices, use the dynamic length instead.
let len = base.len(self)?;
@ -196,63 +165,26 @@ where
}
let offset = stride * index; // `Size` multiplication
// All fields have the same layout.
let field_layout = base.layout.field(self, 0);
base.offset(offset, field_layout, self)
let field_layout = base.layout().field(self, 0);
(offset, field_layout)
}
_ => span_bug!(
self.cur_span(),
"`mplace_index` called on non-array type {:?}",
base.layout.ty
base.layout().ty
),
}
}
/// Iterates over all fields of an array. Much more efficient than doing the
/// same by repeatedly calling `operand_index`.
pub fn operand_array_fields<'a>(
&self,
base: &'a OpTy<'tcx, Prov>,
) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Prov>>> + 'a> {
let len = base.len(self)?; // also asserts that we have a type where this makes sense
let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
};
let field_layout = base.layout.field(self, 0);
let dl = &self.tcx.data_layout;
// `Size` multiplication
Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl)))
base.offset(offset, field_layout, self)
}
/// Index into an array.
pub fn mplace_index(
fn project_constant_index<P: Projectable<'mir, 'tcx, M::Provenance>>(
&self,
base: &MPlaceTy<'tcx, M::Provenance>,
index: u64,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
Ok(self.operand_index(&base.into(), index)?.assert_mem_place())
}
pub fn place_index(
&mut self,
base: &PlaceTy<'tcx, M::Provenance>,
index: u64,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
// There's not a lot we can do here, since we cannot have a place to a part of a local. If
// we are accessing the only element of a 1-element array, it's still the entire local...
// that doesn't seem worth it.
let base = self.force_allocation(base)?;
Ok(self.mplace_index(&base, index)?.into())
}
//# ConstantIndex support
fn operand_constant_index(
&self,
base: &OpTy<'tcx, M::Provenance>,
base: &P,
offset: u64,
min_length: u64,
from_end: bool,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
) -> InterpResult<'tcx, P> {
let n = base.len(self)?;
if n < min_length {
// This can only be reached in ConstProp and non-rustc-MIR.
@ -267,32 +199,38 @@ where
offset
};
self.operand_index(base, index)
self.project_index(base, index)
}
fn place_constant_index(
&mut self,
base: &PlaceTy<'tcx, M::Provenance>,
offset: u64,
min_length: u64,
from_end: bool,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
let base = self.force_allocation(base)?;
Ok(self
.operand_constant_index(&base.into(), offset, min_length, from_end)?
.assert_mem_place()
.into())
}
//# Subslicing
fn operand_subslice(
/// Iterates over all fields of an array. Much more efficient than doing the
/// same by repeatedly calling `operand_index`.
pub fn project_array_fields<'a, P: Projectable<'mir, 'tcx, M::Provenance>>(
&self,
base: &OpTy<'tcx, M::Provenance>,
base: &'a P,
) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, P>> + 'a>
where
'tcx: 'a,
{
let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
};
let len = base.len(self)?;
let field_layout = base.layout().field(self, 0);
let tcx: TyCtxt<'tcx> = *self.tcx;
// `Size` multiplication
Ok((0..len).map(move |i| {
base.offset_with_meta(stride * i, MemPlaceMeta::None, field_layout, &tcx)
}))
}
/// Subslicing
fn project_subslice<P: Projectable<'mir, 'tcx, M::Provenance>>(
&self,
base: &P,
from: u64,
to: u64,
from_end: bool,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
) -> InterpResult<'tcx, P> {
let len = base.len(self)?; // also asserts that we have a type where this makes sense
let actual_to = if from_end {
if from.checked_add(to).map_or(true, |to| to > len) {
@ -306,16 +244,20 @@ where
// Not using layout method because that works with usize, and does not work with slices
// (that have count 0 in their layout).
let from_offset = match base.layout.fields {
let from_offset = match base.layout().fields {
abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
_ => {
span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout)
span_bug!(
self.cur_span(),
"unexpected layout of index access: {:#?}",
base.layout()
)
}
};
// Compute meta and new layout
let inner_len = actual_to.checked_sub(from).unwrap();
let (meta, ty) = match base.layout.ty.kind() {
let (meta, ty) = match base.layout().ty.kind() {
// It is not nice to match on the type, but that seems to be the only way to
// implement this.
ty::Array(inner, _) => {
@ -323,85 +265,45 @@ where
}
ty::Slice(..) => {
let len = Scalar::from_target_usize(inner_len, self);
(MemPlaceMeta::Meta(len), base.layout.ty)
(MemPlaceMeta::Meta(len), base.layout().ty)
}
_ => {
span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty)
span_bug!(
self.cur_span(),
"cannot subslice non-array type: `{:?}`",
base.layout().ty
)
}
};
let layout = self.layout_of(ty)?;
base.offset_with_meta(from_offset, meta, layout, self)
}
pub fn place_subslice(
&mut self,
base: &PlaceTy<'tcx, M::Provenance>,
from: u64,
to: u64,
from_end: bool,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
let base = self.force_allocation(base)?;
Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into())
}
//# Applying a general projection
/// Projects into a place.
/// Applying a general projection
#[instrument(skip(self), level = "trace")]
pub fn place_projection(
&mut self,
base: &PlaceTy<'tcx, M::Provenance>,
proj_elem: mir::PlaceElem<'tcx>,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
pub fn project<P>(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P>
where
P: Projectable<'mir, 'tcx, M::Provenance>
+ From<MPlaceTy<'tcx, M::Provenance>>
+ std::fmt::Debug,
{
use rustc_middle::mir::ProjectionElem::*;
Ok(match proj_elem {
OpaqueCast(ty) => {
let mut place = base.clone();
place.layout = self.layout_of(ty)?;
place
}
Field(field, _) => self.place_field(base, field.index())?,
Downcast(_, variant) => self.place_downcast(base, variant)?,
Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
OpaqueCast(ty) => base.transmute(self.layout_of(ty)?, self)?,
Field(field, _) => self.project_field(base, field.index())?,
Downcast(_, variant) => self.project_downcast(base, variant)?,
Deref => self.deref_operand(&base.to_op(self)?)?.into(),
Index(local) => {
let layout = self.layout_of(self.tcx.types.usize)?;
let n = self.local_to_op(self.frame(), local, Some(layout))?;
let n = self.read_target_usize(&n)?;
self.place_index(base, n)?
self.project_index(base, n)?
}
ConstantIndex { offset, min_length, from_end } => {
self.place_constant_index(base, offset, min_length, from_end)?
self.project_constant_index(base, offset, min_length, from_end)?
}
Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?,
})
}
#[instrument(skip(self), level = "trace")]
pub fn operand_projection(
&self,
base: &OpTy<'tcx, M::Provenance>,
proj_elem: mir::PlaceElem<'tcx>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
use rustc_middle::mir::ProjectionElem::*;
Ok(match proj_elem {
OpaqueCast(ty) => {
let mut op = base.clone();
op.layout = self.layout_of(ty)?;
op
}
Field(field, _) => self.operand_field(base, field.index())?,
Downcast(_, variant) => self.operand_downcast(base, variant)?,
Deref => self.deref_operand(base)?.into(),
Index(local) => {
let layout = self.layout_of(self.tcx.types.usize)?;
let n = self.local_to_op(self.frame(), local, Some(layout))?;
let n = self.read_target_usize(&n)?;
self.operand_index(base, n)?
}
ConstantIndex { offset, min_length, from_end } => {
self.operand_constant_index(base, offset, min_length, from_end)?
}
Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?,
Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?,
})
}
}

View file

@ -8,7 +8,7 @@ use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::LayoutOf;
use super::{ImmTy, InterpCx, Machine};
use super::{ImmTy, InterpCx, Machine, Projectable};
use crate::util;
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
@ -197,7 +197,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.get_place_alloc_mut(&dest)?;
} else {
// Write the src to the first element.
let first = self.mplace_field(&dest, 0)?;
let first = self.project_index(&dest, 0)?;
self.copy_op(&src, &first.into(), /*allow_transmute*/ false)?;
// This is performance-sensitive code for big static/const arrays! So we
@ -302,8 +302,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Discriminant(place) => {
let op = self.eval_place_to_op(place, None)?;
let discr_val = self.read_discriminant(&op)?.0;
self.write_scalar(discr_val, &dest)?;
let variant = self.read_discriminant(&op)?;
let discr = self.discriminant_for_variant(op.layout, variant)?;
self.write_scalar(discr, &dest)?;
}
}

View file

@ -60,13 +60,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
pub fn fn_arg_field(
&mut self,
&self,
arg: &FnArg<'tcx, M::Provenance>,
field: usize,
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
Ok(match arg {
FnArg::Copy(op) => FnArg::Copy(self.operand_field(op, field)?),
FnArg::InPlace(place) => FnArg::InPlace(self.place_field(place, field)?),
FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?),
FnArg::InPlace(place) => FnArg::InPlace(self.project_field(place, field)?),
})
}
@ -239,7 +239,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Evaluate the arguments of a function call
pub(super) fn eval_fn_call_arguments(
&mut self,
&self,
ops: &[mir::Operand<'tcx>],
) -> InterpResult<'tcx, Vec<FnArg<'tcx, M::Provenance>>> {
ops.iter()
@ -382,12 +382,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// This all has to be in memory, there are no immediate unsized values.
let src = caller_arg_copy.assert_mem_place();
// The destination cannot be one of these "spread args".
let (dest_frame, dest_local) = callee_arg.assert_local();
let (dest_frame, dest_local, dest_offset) = callee_arg
.as_mplace_or_local()
.right()
.expect("callee fn arguments must be locals");
// We are just initializing things, so there can't be anything here yet.
assert!(matches!(
*self.local_to_op(&self.stack()[dest_frame], dest_local, None)?,
Operand::Immediate(Immediate::Uninit)
));
assert_eq!(dest_offset, None);
// Allocate enough memory to hold `src`.
let Some((size, align)) = self.size_and_align_of_mplace(&src)? else {
span_bug!(
@ -595,7 +599,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if Some(local) == body.spread_arg {
// Must be a tuple
for i in 0..dest.layout.fields.count() {
let dest = self.place_field(&dest, i)?;
let dest = self.project_field(&dest, i)?;
let callee_abi = callee_args_abis.next().unwrap();
self.pass_argument(&mut caller_args, callee_abi, &dest)?;
}
@ -677,7 +681,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Not there yet, search for the only non-ZST field.
let mut non_zst_field = None;
for i in 0..receiver.layout.fields.count() {
let field = self.operand_field(&receiver, i)?;
let field = self.project_field(&receiver, i)?;
let zst =
field.layout.is_zst() && field.layout.align.abi.bytes() == 1;
if !zst {
@ -703,12 +707,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) =
receiver_place.layout.ty.kind()
{
let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?;
let (recv, vptr) = self.unpack_dyn_star(&receiver_place)?;
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() {
throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch);
}
let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one
(vptr, dyn_ty, recv.ptr)
} else {
@ -836,7 +839,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
ty::Dynamic(_, _, ty::DynStar) => {
// Dropping a `dyn*`. Need to find actual drop fn.
self.unpack_dyn_star(&place.into())?.0.assert_mem_place()
self.unpack_dyn_star(&place)?.0
}
_ => {
debug_assert_eq!(

View file

@ -29,7 +29,7 @@ use std::hash::Hash;
use super::UndefinedBehaviorInfo::*;
use super::{
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor,
Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor,
};
macro_rules! throw_validation_failure {
@ -462,6 +462,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
/// Check if this is a value of primitive type, and if yes check the validity of the value
/// at that type. Return `true` if the type is indeed primitive.
///
/// Note that not all of these have `FieldsShape::Primitive`, e.g. wide references.
fn try_visit_primitive(
&mut self,
value: &OpTy<'tcx, M::Provenance>,
@ -660,10 +662,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
InvalidTag(val) => InvalidEnumTag {
value: format!("{val:x}"),
},
UninhabitedEnumVariantRead(_) => UninhabitedEnumTag,
InvalidUninitBytes(None) => UninitEnumTag,
)
.1)
))
})
}
@ -733,60 +734,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
}
}
// Recursively walk the value at its type.
self.walk_value(op)?;
// *After* all of this, check the ABI. We need to check the ABI to handle
// types like `NonNull` where the `Scalar` info is more restrictive than what
// the fields say (`rustc_layout_scalar_valid_range_start`).
// But in most cases, this will just propagate what the fields say,
// and then we want the error to point at the field -- so, first recurse,
// then check ABI.
//
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
// scalars, we do the same check on every "level" (e.g., first we check
// MyNewtype and then the scalar in there).
match op.layout.abi {
Abi::Uninhabited => {
let ty = op.layout.ty;
throw_validation_failure!(self.path, UninhabitedVal { ty });
}
Abi::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// There is something to check here.
let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
self.visit_scalar(scalar, scalar_layout)?;
}
}
Abi::ScalarPair(a_layout, b_layout) => {
// We can only proceed if *both* scalars need to be initialized.
// FIXME: find a way to also check ScalarPair when one side can be uninit but
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
let (a, b) =
self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
self.visit_scalar(a, a_layout)?;
self.visit_scalar(b, b_layout)?;
}
}
Abi::Vector { .. } => {
// No checks here, we assume layout computation gets this right.
// (This is harder to check since Miri does not represent these as `Immediate`. We
// also cannot use field projections since this might be a newtype around a vector.)
}
Abi::Aggregate { .. } => {
// Nothing to do.
}
}
Ok(())
}
fn visit_aggregate(
&mut self,
op: &OpTy<'tcx, M::Provenance>,
fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
) -> InterpResult<'tcx> {
// Recursively walk the value at its type. Apply optimizations for some large types.
match op.layout.ty.kind() {
ty::Str => {
let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
@ -874,12 +822,58 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// ZST type, so either validation fails for all elements or none.
ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => {
// Validate just the first element (if any).
self.walk_aggregate(op, fields.take(1))?
if op.len(self.ecx)? > 0 {
self.visit_field(op, 0, &self.ecx.project_index(op, 0)?)?;
}
}
_ => {
self.walk_aggregate(op, fields)? // default handler
self.walk_value(op)?; // default handler
}
}
// *After* all of this, check the ABI. We need to check the ABI to handle
// types like `NonNull` where the `Scalar` info is more restrictive than what
// the fields say (`rustc_layout_scalar_valid_range_start`).
// But in most cases, this will just propagate what the fields say,
// and then we want the error to point at the field -- so, first recurse,
// then check ABI.
//
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
// scalars, we do the same check on every "level" (e.g., first we check
// MyNewtype and then the scalar in there).
match op.layout.abi {
Abi::Uninhabited => {
let ty = op.layout.ty;
throw_validation_failure!(self.path, UninhabitedVal { ty });
}
Abi::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// There is something to check here.
let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
self.visit_scalar(scalar, scalar_layout)?;
}
}
Abi::ScalarPair(a_layout, b_layout) => {
// We can only proceed if *both* scalars need to be initialized.
// FIXME: find a way to also check ScalarPair when one side can be uninit but
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
let (a, b) =
self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
self.visit_scalar(a, a_layout)?;
self.visit_scalar(b, b_layout)?;
}
}
Abi::Vector { .. } => {
// No checks here, we assume layout computation gets this right.
// (This is harder to check since Miri does not represent these as `Immediate`. We
// also cannot use field projections since this might be a newtype around a vector.)
}
Abi::Aggregate { .. } => {
// Nothing to do.
}
}
Ok(())
}
}

View file

@ -1,544 +1,204 @@
//! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound
//! types until we arrive at the leaves, with custom handling for primitive types.
use rustc_index::IndexVec;
use rustc_middle::mir::interpret::InterpResult;
use rustc_middle::ty;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_target::abi::FieldIdx;
use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
use std::num::NonZeroUsize;
use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy};
use super::{InterpCx, MPlaceTy, Machine, Projectable};
/// A thing that we can project into, and that has a layout.
/// This wouldn't have to depend on `Machine` but with the current type inference,
/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
pub trait Value<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
/// Gets this value's layout.
fn layout(&self) -> TyAndLayout<'tcx>;
/// How to traverse a value and what to do when we are at the leaves.
pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
type V: Projectable<'mir, 'tcx, M::Provenance>
+ From<MPlaceTy<'tcx, M::Provenance>>
+ std::fmt::Debug;
/// Makes this into an `OpTy`, in a cheap way that is good for reading.
fn to_op_for_read(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
/// The visitor must have an `InterpCx` in it.
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M>;
/// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
fn to_op_for_proj(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
self.to_op_for_read(ecx)
/// `read_discriminant` can be hooked for better error messages.
#[inline(always)]
fn read_discriminant(&mut self, v: &Self::V) -> InterpResult<'tcx, VariantIdx> {
Ok(self.ecx().read_discriminant(&v.to_op(self.ecx())?)?)
}
/// Creates this from an `OpTy`.
/// This function provides the chance to reorder the order in which fields are visited for
/// `FieldsShape::Aggregate`: The order of fields will be
/// `(0..num_fields).map(aggregate_field_order)`.
///
/// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
/// Projects to the given enum variant.
fn project_downcast(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self>;
/// Projects to the n-th field.
fn project_field(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self>;
}
/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout.
/// This wouldn't have to depend on `Machine` but with the current type inference,
/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
pub trait ValueMut<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
/// Gets this value's layout.
fn layout(&self) -> TyAndLayout<'tcx>;
/// Makes this into an `OpTy`, in a cheap way that is good for reading.
fn to_op_for_read(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
/// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
fn to_op_for_proj(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
/// Creates this from an `OpTy`.
///
/// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
/// Projects to the given enum variant.
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self>;
/// Projects to the n-th field.
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self>;
}
// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we
// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.)
// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this
// double-impl, that would barely make the code shorter, if at all.)
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> {
/// The default means we iterate in source declaration order; alternative this can do an inverse
/// lookup in `memory_index` to use memory field order instead.
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
fn aggregate_field_order(_memory_index: &IndexVec<FieldIdx, u32>, idx: usize) -> usize {
idx
}
// Recursive actions, ready to be overloaded.
/// Visits the given value, dispatching as appropriate to more specialized visitors.
#[inline(always)]
fn to_op_for_read(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.clone())
fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx> {
self.walk_value(v)
}
/// Visits the given value as a union. No automatic recursion can happen here.
#[inline(always)]
fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> {
Ok(())
}
/// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
/// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
/// pointee type is the actual `T`.
#[inline(always)]
fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> {
Ok(())
}
/// Called each time we recurse down to a field of a "product-like" aggregate
/// (structs, tuples, arrays and the like, but not enums), passing in old (outer)
/// and new (inner) value.
/// This gives the visitor the chance to track the stack of nested fields that
/// we are descending through.
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
op.clone()
fn visit_field(
&mut self,
_old_val: &Self::V,
_field: usize,
new_val: &Self::V,
) -> InterpResult<'tcx> {
self.visit_value(new_val)
}
/// Called when recursing into an enum variant.
/// This gives the visitor the chance to track the stack of nested fields that
/// we are descending through.
#[inline(always)]
fn visit_variant(
&mut self,
_old_val: &Self::V,
_variant: VariantIdx,
new_val: &Self::V,
) -> InterpResult<'tcx> {
self.visit_value(new_val)
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.operand_downcast(self, variant)
}
fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> {
let ty = v.layout().ty;
trace!("walk_value: type: {ty}");
#[inline(always)]
fn project_field(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.operand_field(self, field)
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
for OpTy<'tcx, M::Provenance>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_op_for_read(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.clone())
}
#[inline(always)]
fn to_op_for_proj(
&self,
_ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.clone())
}
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
op.clone()
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.operand_downcast(self, variant)
}
#[inline(always)]
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.operand_field(self, field)
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
for MPlaceTy<'tcx, M::Provenance>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_op_for_read(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.into())
}
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
// assert is justified because our `to_op_for_read` only ever produces `Indirect` operands.
op.assert_mem_place()
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.mplace_downcast(self, variant)
}
#[inline(always)]
fn project_field(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.mplace_field(self, field)
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
for MPlaceTy<'tcx, M::Provenance>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_op_for_read(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.into())
}
#[inline(always)]
fn to_op_for_proj(
&self,
_ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.into())
}
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
// assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands.
op.assert_mem_place()
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.mplace_downcast(self, variant)
}
#[inline(always)]
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.mplace_field(self, field)
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
for PlaceTy<'tcx, M::Provenance>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_op_for_read(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
// No need for `force_allocation` since we are just going to read from this.
ecx.place_to_op(self)
}
#[inline(always)]
fn to_op_for_proj(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
// We `force_allocation` here so that `from_op` below can work.
Ok(ecx.force_allocation(self)?.into())
}
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
// assert is justified because our `to_op` only ever produces `Indirect` operands.
op.assert_mem_place().into()
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.place_downcast(self, variant)
}
#[inline(always)]
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.place_field(self, field)
}
}
macro_rules! make_value_visitor {
($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => {
/// How to traverse a value and what to do when we are at the leaves.
pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
type V: $value_trait<'mir, 'tcx, M>;
/// The visitor must have an `InterpCx` in it.
fn ecx(&$($mutability)? self)
-> &$($mutability)? InterpCx<'mir, 'tcx, M>;
/// `read_discriminant` can be hooked for better error messages.
#[inline(always)]
fn read_discriminant(
&mut self,
op: &OpTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, VariantIdx> {
Ok(self.ecx().read_discriminant(op)?.1)
// Special treatment for special types, where the (static) layout is not sufficient.
match *ty.kind() {
// If it is a trait object, switch to the real type that was used to create it.
ty::Dynamic(_, _, ty::Dyn) => {
// Dyn types. This is unsized, and the actual dynamic type of the data is given by the
// vtable stored in the place metadata.
// unsized values are never immediate, so we can assert_mem_place
let op = v.to_op(self.ecx())?;
let dest = op.assert_mem_place();
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0;
trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
// recurse with the inner type
return self.visit_field(&v, 0, &inner_mplace.into());
}
ty::Dynamic(_, _, ty::DynStar) => {
// DynStar types. Very different from a dyn type (but strangely part of the
// same variant in `TyKind`): These are pairs where the 2nd component is the
// vtable, and the first component is the data (which must be ptr-sized).
let data = self.ecx().unpack_dyn_star(v)?.0;
return self.visit_field(&v, 0, &data);
}
// Slices do not need special handling here: they have `Array` field
// placement with length 0, so we enter the `Array` case below which
// indirectly uses the metadata to determine the actual length.
// Recursive actions, ready to be overloaded.
/// Visits the given value, dispatching as appropriate to more specialized visitors.
#[inline(always)]
fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
{
self.walk_value(v)
}
/// Visits the given value as a union. No automatic recursion can happen here.
#[inline(always)]
fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx>
{
Ok(())
}
/// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
/// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
/// pointee type is the actual `T`.
#[inline(always)]
fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx>
{
Ok(())
}
/// Visits this value as an aggregate, you are getting an iterator yielding
/// all the fields (still in an `InterpResult`, you have to do error handling yourself).
/// Recurses into the fields.
#[inline(always)]
fn visit_aggregate(
&mut self,
v: &Self::V,
fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
) -> InterpResult<'tcx> {
self.walk_aggregate(v, fields)
}
// However, `Box`... let's talk about `Box`.
ty::Adt(def, ..) if def.is_box() => {
// `Box` is a hybrid primitive-library-defined type that one the one hand is
// a dereferenceable pointer, on the other hand has *basically arbitrary
// user-defined layout* since the user controls the 'allocator' field. So it
// cannot be treated like a normal pointer, since it does not fit into an
// `Immediate`. Yeah, it is quite terrible. But many visitors want to do
// something with "all boxed pointers", so we handle this mess for them.
//
// When we hit a `Box`, we do not do the usual field recursion; instead,
// we (a) call `visit_box` on the pointer value, and (b) recurse on the
// allocator field. We also assert tons of things to ensure we do not miss
// any other fields.
/// Called each time we recurse down to a field of a "product-like" aggregate
/// (structs, tuples, arrays and the like, but not enums), passing in old (outer)
/// and new (inner) value.
/// This gives the visitor the chance to track the stack of nested fields that
/// we are descending through.
#[inline(always)]
fn visit_field(
&mut self,
_old_val: &Self::V,
_field: usize,
new_val: &Self::V,
) -> InterpResult<'tcx> {
self.visit_value(new_val)
}
/// Called when recursing into an enum variant.
/// This gives the visitor the chance to track the stack of nested fields that
/// we are descending through.
#[inline(always)]
fn visit_variant(
&mut self,
_old_val: &Self::V,
_variant: VariantIdx,
new_val: &Self::V,
) -> InterpResult<'tcx> {
self.visit_value(new_val)
}
// `Box` has two fields: the pointer we care about, and the allocator.
assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
let (unique_ptr, alloc) =
(self.ecx().project_field(v, 0)?, self.ecx().project_field(v, 1)?);
// Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
// (which means another 2 fields, the second of which is a `PhantomData`)
assert_eq!(unique_ptr.layout().fields.count(), 2);
let (nonnull_ptr, phantom) = (
self.ecx().project_field(&unique_ptr, 0)?,
self.ecx().project_field(&unique_ptr, 1)?,
);
assert!(
phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
"2nd field of `Unique` should be PhantomData but is {:?}",
phantom.layout().ty,
);
// ... that contains a `NonNull`... (gladly, only a single field here)
assert_eq!(nonnull_ptr.layout().fields.count(), 1);
let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr
// ... whose only field finally is a raw ptr we can dereference.
self.visit_box(&raw_ptr)?;
// Default recursors. Not meant to be overloaded.
fn walk_aggregate(
&mut self,
v: &Self::V,
fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
) -> InterpResult<'tcx> {
// Now iterate over it.
for (idx, field_val) in fields.enumerate() {
self.visit_field(v, idx, &field_val?)?;
// The second `Box` field is the allocator, which we recursively check for validity
// like in regular structs.
self.visit_field(v, 1, &alloc)?;
// We visited all parts of this one.
return Ok(());
}
_ => {}
};
// Visit the fields of this value.
match &v.layout().fields {
FieldsShape::Primitive => {}
&FieldsShape::Union(fields) => {
self.visit_union(v, fields)?;
}
FieldsShape::Arbitrary { offsets, memory_index } => {
for idx in 0..offsets.len() {
let idx = Self::aggregate_field_order(memory_index, idx);
let field = self.ecx().project_field(v, idx)?;
self.visit_field(v, idx, &field)?;
}
Ok(())
}
fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
{
let ty = v.layout().ty;
trace!("walk_value: type: {ty}");
// Special treatment for special types, where the (static) layout is not sufficient.
match *ty.kind() {
// If it is a trait object, switch to the real type that was used to create it.
ty::Dynamic(_, _, ty::Dyn) => {
// Dyn types. This is unsized, and the actual dynamic type of the data is given by the
// vtable stored in the place metadata.
// unsized values are never immediate, so we can assert_mem_place
let op = v.to_op_for_read(self.ecx())?;
let dest = op.assert_mem_place();
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0;
trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
// recurse with the inner type
return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));
},
ty::Dynamic(_, _, ty::DynStar) => {
// DynStar types. Very different from a dyn type (but strangely part of the
// same variant in `TyKind`): These are pairs where the 2nd component is the
// vtable, and the first component is the data (which must be ptr-sized).
let op = v.to_op_for_proj(self.ecx())?;
let data = self.ecx().unpack_dyn_star(&op)?.0;
return self.visit_field(&v, 0, &$value_trait::from_op(&data));
}
// Slices do not need special handling here: they have `Array` field
// placement with length 0, so we enter the `Array` case below which
// indirectly uses the metadata to determine the actual length.
// However, `Box`... let's talk about `Box`.
ty::Adt(def, ..) if def.is_box() => {
// `Box` is a hybrid primitive-library-defined type that one the one hand is
// a dereferenceable pointer, on the other hand has *basically arbitrary
// user-defined layout* since the user controls the 'allocator' field. So it
// cannot be treated like a normal pointer, since it does not fit into an
// `Immediate`. Yeah, it is quite terrible. But many visitors want to do
// something with "all boxed pointers", so we handle this mess for them.
//
// When we hit a `Box`, we do not do the usual `visit_aggregate`; instead,
// we (a) call `visit_box` on the pointer value, and (b) recurse on the
// allocator field. We also assert tons of things to ensure we do not miss
// any other fields.
// `Box` has two fields: the pointer we care about, and the allocator.
assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
let (unique_ptr, alloc) =
(v.project_field(self.ecx(), 0)?, v.project_field(self.ecx(), 1)?);
// Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
// (which means another 2 fields, the second of which is a `PhantomData`)
assert_eq!(unique_ptr.layout().fields.count(), 2);
let (nonnull_ptr, phantom) = (
unique_ptr.project_field(self.ecx(), 0)?,
unique_ptr.project_field(self.ecx(), 1)?,
);
assert!(
phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
"2nd field of `Unique` should be PhantomData but is {:?}",
phantom.layout().ty,
);
// ... that contains a `NonNull`... (gladly, only a single field here)
assert_eq!(nonnull_ptr.layout().fields.count(), 1);
let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr
// ... whose only field finally is a raw ptr we can dereference.
self.visit_box(&raw_ptr)?;
// The second `Box` field is the allocator, which we recursively check for validity
// like in regular structs.
self.visit_field(v, 1, &alloc)?;
// We visited all parts of this one.
return Ok(());
}
_ => {},
};
// Visit the fields of this value.
match &v.layout().fields {
FieldsShape::Primitive => {}
&FieldsShape::Union(fields) => {
self.visit_union(v, fields)?;
}
FieldsShape::Arbitrary { offsets, .. } => {
// FIXME: We collect in a vec because otherwise there are lifetime
// errors: Projecting to a field needs access to `ecx`.
let fields: Vec<InterpResult<'tcx, Self::V>> =
(0..offsets.len()).map(|i| {
v.project_field(self.ecx(), i)
})
.collect();
self.visit_aggregate(v, fields.into_iter())?;
}
FieldsShape::Array { .. } => {
// Let's get an mplace (or immediate) first.
// This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway.
let op = v.to_op_for_proj(self.ecx())?;
// Now we can go over all the fields.
// This uses the *run-time length*, i.e., if we are a slice,
// the dynamic info from the metadata is used.
let iter = self.ecx().operand_array_fields(&op)?
.map(|f| f.and_then(|f| {
Ok($value_trait::from_op(&f))
}));
self.visit_aggregate(v, iter)?;
}
}
match v.layout().variants {
// If this is a multi-variant layout, find the right variant and proceed
// with *its* fields.
Variants::Multiple { .. } => {
let op = v.to_op_for_read(self.ecx())?;
let idx = self.read_discriminant(&op)?;
let inner = v.project_downcast(self.ecx(), idx)?;
trace!("walk_value: variant layout: {:#?}", inner.layout());
// recurse with the inner type
self.visit_variant(v, idx, &inner)
}
// For single-variant layouts, we already did anything there is to do.
Variants::Single { .. } => Ok(())
FieldsShape::Array { .. } => {
for (idx, field) in self.ecx().project_array_fields(v)?.enumerate() {
self.visit_field(v, idx, &field?)?;
}
}
}
match v.layout().variants {
// If this is a multi-variant layout, find the right variant and proceed
// with *its* fields.
Variants::Multiple { .. } => {
let idx = self.read_discriminant(v)?;
// There are 3 cases where downcasts can turn a Scalar/ScalarPair into a different ABI which
// could be a problem for `ImmTy` (see layout_sanity_check):
// - variant.size == Size::ZERO: works fine because `ImmTy::offset` has a special case for
// zero-sized layouts.
// - variant.fields.count() == 0: works fine because `ImmTy::offset` has a special case for
// zero-field aggregates.
// - variant.abi.is_uninhabited(): triggers UB in `read_discriminant` so we never get here.
let inner = self.ecx().project_downcast(v, idx)?;
trace!("walk_value: variant layout: {:#?}", inner.layout());
// recurse with the inner type
self.visit_variant(v, idx, &inner)?;
}
// For single-variant layouts, we already did anything there is to do.
Variants::Single { .. } => {}
}
Ok(())
}
}
make_value_visitor!(ValueVisitor, Value,);
make_value_visitor!(MutValueVisitor, ValueMut, mut);

View file

@ -8,6 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt;
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::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgs};
use rustc_middle::ty::{TraitRef, TypeVisitableExt};
@ -766,7 +767,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
};
match implsrc {
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
Ok(Some(ImplSource::Param(ty::BoundConstness::ConstIfConst, _))) => {
debug!(
"const_trait_impl: provided {:?} via where-clause in {:?}",
trait_ref, param_env
@ -774,7 +775,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
return;
}
// Closure: Fn{Once|Mut}
Ok(Some(ImplSource::Builtin(_)))
Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _)))
if trait_ref.self_ty().is_closure()
&& tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
{

View file

@ -7,6 +7,7 @@ use rustc_hir::LangItem;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir;
use rustc_middle::mir::*;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
use rustc_trait_selection::traits::{
self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
@ -172,7 +173,8 @@ impl Qualif for NeedsNonConstDrop {
if !matches!(
impl_src,
ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
ImplSource::Builtin(BuiltinImplSource::Misc, _)
| ImplSource::Param(ty::BoundConstness::ConstIfConst, _)
) {
// If our const destruct candidate is not ConstDestruct or implied by the param env,
// then it's bad

View file

@ -56,8 +56,13 @@ pub fn is_subtype<'tcx>(
// With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing`
// we would get unification errors because we're unable to look into opaque types,
// even if they're constrained in our current function.
//
// It seems very unlikely that this hides any bugs.
let _ = infcx.take_opaque_types();
for (key, ty) in infcx.take_opaque_types() {
span_bug!(
ty.hidden_type.span,
"{}, {}",
tcx.type_of(key.def_id).instantiate(tcx, key.args),
ty.hidden_type.ty
);
}
errors.is_empty()
}

View file

@ -268,11 +268,7 @@ impl<K: Eq + Hash, V> SsoHashMap<K, V> {
pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> {
match self {
SsoHashMap::Array(array) => {
if let Some(index) = array.iter().position(|(k, _v)| k == key) {
Some(array.swap_remove(index))
} else {
None
}
array.iter().position(|(k, _v)| k == key).map(|index| array.swap_remove(index))
}
SsoHashMap::Map(map) => map.remove_entry(key),
}

View file

@ -533,6 +533,14 @@ impl MultiSpan {
pub fn has_span_labels(&self) -> bool {
self.span_labels.iter().any(|(sp, _)| !sp.is_dummy())
}
/// Clone this `MultiSpan` without keeping any of the span labels - sometimes a `MultiSpan` is
/// to be re-used in another diagnostic, but includes `span_labels` which have translated
/// messages. These translated messages would fail to translate without their diagnostic
/// arguments which are unlikely to be cloned alongside the `Span`.
pub fn clone_ignoring_labels(&self) -> Self {
Self { primary_spans: self.primary_spans.clone(), ..MultiSpan::new() }
}
}
impl From<Span> for MultiSpan {

View file

@ -420,13 +420,13 @@ impl Diagnostic {
let expected_label = if expected_label.is_empty() {
"expected".to_string()
} else {
format!("expected {}", expected_label)
format!("expected {expected_label}")
};
let found_label = found_label.to_string();
let found_label = if found_label.is_empty() {
"found".to_string()
} else {
format!("found {}", found_label)
format!("found {found_label}")
};
let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
(expected_label.len() - found_label.len(), 0)
@ -439,13 +439,13 @@ impl Diagnostic {
StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
}));
msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
msg.push((format!("`{expected_extra}\n"), Style::NoStyle));
msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
msg.extend(found.0.iter().map(|x| match *x {
StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
}));
msg.push((format!("`{}", found_extra), Style::NoStyle));
msg.push((format!("`{found_extra}"), Style::NoStyle));
// For now, just attach these as notes.
self.highlighted_note(msg);
@ -454,7 +454,7 @@ impl Diagnostic {
pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
self.highlighted_note(vec![
(format!("`{}` from trait: `", name), Style::NoStyle),
(format!("`{name}` from trait: `"), Style::NoStyle),
(signature, Style::Highlight),
("`".to_string(), Style::NoStyle),
]);

View file

@ -102,7 +102,7 @@ impl IntoDiagnosticArg for bool {
impl IntoDiagnosticArg for char {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self)))
DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
}
}
@ -164,6 +164,12 @@ impl IntoDiagnosticArg for hir::ConstContext {
}
}
impl IntoDiagnosticArg for ast::Expr {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(pprust::expr_to_string(&self)))
}
}
impl IntoDiagnosticArg for ast::Path {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))

View file

@ -279,12 +279,12 @@ pub trait Emitter: Translate {
let msg = if substitution.is_empty() || sugg.style.hide_inline() {
// This substitution is only removal OR we explicitly don't want to show the
// code inline (`hide_inline`). Therefore, we don't show the substitution.
format!("help: {}", &msg)
format!("help: {msg}")
} else {
// Show the default suggestion text with the substitution
format!(
"help: {}{}: `{}`",
&msg,
msg,
if self.source_map().is_some_and(|sm| is_case_difference(
sm,
substitution,

View file

@ -1485,7 +1485,7 @@ impl HandlerInner {
let _ = self.fatal(errors);
}
(_, _) => {
let _ = self.fatal(format!("{}; {}", &errors, &warnings));
let _ = self.fatal(format!("{errors}; {warnings}"));
}
}

View file

@ -3013,8 +3013,7 @@ pub struct FieldDef<'hir> {
impl FieldDef<'_> {
// Still necessary in couple of places
pub fn is_positional(&self) -> bool {
let first = self.ident.as_str().as_bytes()[0];
(b'0'..=b'9').contains(&first)
self.ident.as_str().as_bytes()[0].is_ascii_digit()
}
}

View file

@ -19,11 +19,13 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::GenericArgKind;
use rustc_middle::ty::{
self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
self, AdtDef, ParamEnv, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt,
};
use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
use rustc_span::symbol::sym;
@ -34,6 +36,7 @@ use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplem
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _};
use rustc_type_ir::fold::TypeFoldable;
use std::ops::ControlFlow;
@ -437,7 +440,7 @@ fn check_opaque_meets_bounds<'tcx>(
// hidden type is well formed even without those bounds.
let predicate =
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(hidden_ty.into())));
ocx.register_obligation(Obligation::new(tcx, misc_cause, param_env, predicate));
ocx.register_obligation(Obligation::new(tcx, misc_cause.clone(), param_env, predicate));
// Check that all obligations are satisfied by the implementation's
// version.
@ -464,11 +467,179 @@ fn check_opaque_meets_bounds<'tcx>(
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
}
}
// Clean up after ourselves
let _ = infcx.take_opaque_types();
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
for (key, mut ty) in infcx.take_opaque_types() {
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?;
}
Ok(())
}
fn sanity_check_found_hidden_type<'tcx>(
tcx: TyCtxt<'tcx>,
key: ty::OpaqueTypeKey<'tcx>,
mut ty: ty::OpaqueHiddenType<'tcx>,
defining_use_anchor: LocalDefId,
origin: &hir::OpaqueTyOrigin,
) -> Result<(), ErrorGuaranteed> {
if ty.ty.is_ty_var() {
// Nothing was actually constrained.
return Ok(());
}
if let ty::Alias(ty::Opaque, alias) = ty.ty.kind() {
if alias.def_id == key.def_id.to_def_id() && alias.args == key.args {
// Nothing was actually constrained, this is an opaque usage that was
// only discovered to be opaque after inference vars resolved.
return Ok(());
}
}
// Closures frequently end up containing erased lifetimes in their final representation.
// These correspond to lifetime variables that never got resolved, so we patch this up here.
ty.ty = ty.ty.fold_with(&mut BottomUpFolder {
tcx,
ty_op: |t| t,
ct_op: |c| c,
lt_op: |l| match l.kind() {
RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
_ => l,
},
});
// Get the hidden type.
let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
if hidden_ty != ty.ty {
hidden_ty = find_and_apply_rpit_args(
tcx,
hidden_ty,
defining_use_anchor.to_def_id(),
key.def_id.to_def_id(),
)?;
}
}
// If the hidden types differ, emit a type mismatch diagnostic.
if hidden_ty == ty.ty {
Ok(())
} else {
let span = tcx.def_span(key.def_id);
let other = ty::OpaqueHiddenType { ty: hidden_ty, span };
Err(ty.report_mismatch(&other, key.def_id, tcx).emit())
}
}
/// In case it is in a nested opaque type, find that opaque type's
/// usage in the function signature and use the generic arguments from the usage site.
/// We need to do because RPITs ignore the lifetimes of the function,
/// as they have their own copies of all the lifetimes they capture.
/// So the only way to get the lifetimes represented in terms of the function,
/// is to look how they are used in the function signature (or do some other fancy
/// recording of this mapping at ast -> hir lowering time).
///
/// As an example:
/// ```text
/// trait Id {
/// type Assoc;
/// }
/// impl<'a> Id for &'a () {
/// type Assoc = &'a ();
/// }
/// fn func<'a>(x: &'a ()) -> impl Id<Assoc = impl Sized + 'a> { x }
/// // desugared to
/// fn func<'a>(x: &'a () -> Outer<'a> where <Outer<'a> as Id>::Assoc = Inner<'a> {
/// // Note that in contrast to other nested items, RPIT type aliases can
/// // access their parents' generics.
///
/// // hidden type is `&'aDupOuter ()`
/// // During wfcheck the hidden type of `Inner<'aDupOuter>` is `&'a ()`, but
/// // `typeof(Inner<'aDupOuter>) = &'aDupOuter ()`.
/// // So we walk the signature of `func` to find the use of `Inner<'a>`
/// // and then use that to replace the lifetimes in the hidden type, obtaining
/// // `&'a ()`.
/// type Outer<'aDupOuter> = impl Id<Assoc = Inner<'aDupOuter>>;
///
/// // hidden type is `&'aDupInner ()`
/// type Inner<'aDupInner> = impl Sized + 'aDupInner;
///
/// x
/// }
/// ```
fn find_and_apply_rpit_args<'tcx>(
tcx: TyCtxt<'tcx>,
mut hidden_ty: Ty<'tcx>,
function: DefId,
opaque: DefId,
) -> Result<Ty<'tcx>, ErrorGuaranteed> {
// Find use of the RPIT in the function signature and thus find the right args to
// convert it into the parameter space of the function signature. This is needed,
// because that's what `type_of` returns, against which we compare later.
let ret = tcx.fn_sig(function).instantiate_identity().output();
struct Visitor<'tcx> {
tcx: TyCtxt<'tcx>,
opaque: DefId,
function: DefId,
seen: FxHashSet<DefId>,
}
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> {
type BreakTy = GenericArgsRef<'tcx>;
#[instrument(level = "trace", skip(self), ret)]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
trace!("{:#?}", t.kind());
match t.kind() {
ty::Alias(ty::Opaque, alias) => {
trace!(?alias.def_id);
if alias.def_id == self.opaque {
return ControlFlow::Break(alias.args);
} else if self.seen.insert(alias.def_id) {
for clause in self
.tcx
.explicit_item_bounds(alias.def_id)
.iter_instantiated_copied(self.tcx, alias.args)
{
trace!(?clause);
clause.visit_with(self)?;
}
}
}
ty::Alias(ty::Projection, alias) => {
if self.tcx.is_impl_trait_in_trait(alias.def_id)
&& self.tcx.impl_trait_in_trait_parent_fn(alias.def_id) == self.function
{
// If we're lowering to associated item, install the opaque type which is just
// the `type_of` of the trait's associated item. If we're using the old lowering
// strategy, then just reinterpret the associated type like an opaque :^)
self.tcx
.type_of(alias.def_id)
.instantiate(self.tcx, alias.args)
.visit_with(self)?;
}
}
ty::Alias(ty::Weak, alias) => {
self.tcx
.type_of(alias.def_id)
.instantiate(self.tcx, alias.args)
.visit_with(self)?;
}
_ => (),
}
t.super_visit_with(self)
}
}
if let ControlFlow::Break(args) =
ret.visit_with(&mut Visitor { tcx, function, opaque, seen: Default::default() })
{
trace!(?args);
trace!("expected: {hidden_ty:#?}");
hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args);
trace!("expected: {hidden_ty:#?}");
} else {
tcx.sess
.delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}"));
}
Ok(hidden_ty)
}
fn is_enum_of_nonnullable_ptr<'tcx>(
tcx: TyCtxt<'tcx>,
adt_def: AdtDef<'tcx>,

View file

@ -352,7 +352,7 @@ fn emit_orphan_check_error<'tcx>(
let this = |name: &str| {
if !trait_ref.def_id.is_local() && !is_target_ty {
msg("this", &format!(" because this is a foreign trait"))
msg("this", " because this is a foreign trait")
} else {
msg("this", &format!(" because {name} are always foreign"))
}

View file

@ -581,6 +581,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_ty: Ty<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
) -> ErrorGuaranteed {
// Callee probe fails when APIT references errors, so suppress those
// errors here.
if let Some((_, _, args)) = self.extract_callable_info(callee_ty)
&& let Err(err) = args.error_reported()
{
return err;
}
let mut unit_variant = None;
if let hir::ExprKind::Path(qpath) = &callee_expr.kind
&& let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _)

View file

@ -705,10 +705,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
)
}),
|lint| {
lint.help(format!(
lint.help(
"cast can be replaced by coercion; this might \
require a temporary variable"
))
require a temporary variable",
)
},
);
}

View file

@ -46,6 +46,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
use rustc_infer::traits::{Obligation, PredicateObligation};
use rustc_middle::lint::in_external_macro;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
};
@ -636,22 +637,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))
if traits.contains(&trait_pred.def_id()) =>
{
let trait_pred = self.resolve_vars_if_possible(trait_pred);
if unsize_did == trait_pred.def_id() {
let self_ty = trait_pred.self_ty();
let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();
if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) =
(self_ty.kind(), unsize_ty.kind())
&& data_a.principal_def_id() != data_b.principal_def_id()
{
debug!("coerce_unsized: found trait upcasting coercion");
has_trait_upcasting_coercion = Some((self_ty, unsize_ty));
}
if let ty::Tuple(..) = unsize_ty.kind() {
debug!("coerce_unsized: found unsized tuple coercion");
has_unsized_tuple_coercion = true;
}
}
trait_pred
}
_ => {
@ -659,6 +644,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
continue;
}
};
let trait_pred = self.resolve_vars_if_possible(trait_pred);
match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) {
// Uncertain or unimplemented.
Ok(None) => {
@ -701,20 +687,28 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// be silent, as it causes a type mismatch later.
}
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
Ok(Some(impl_source)) => {
// Some builtin coercions are still unstable so we detect
// these here and emit a feature error if coercion doesn't fail
// due to another reason.
match impl_source {
traits::ImplSource::Builtin(
BuiltinImplSource::TraitUpcasting { .. },
_,
) => {
has_trait_upcasting_coercion =
Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1)));
}
traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
has_unsized_tuple_coercion = true;
}
_ => {}
}
queue.extend(impl_source.nested_obligations())
}
}
}
if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
feature_err(
&self.tcx.sess.parse_sess,
sym::unsized_tuple_coercion,
self.cause.span,
"unsized tuple coercion is not stable enough for use and is subject to change",
)
.emit();
}
if let Some((sub, sup)) = has_trait_upcasting_coercion
&& !self.tcx().features().trait_upcasting
{
@ -730,6 +724,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
err.emit();
}
if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
feature_err(
&self.tcx.sess.parse_sess,
sym::unsized_tuple_coercion,
self.cause.span,
"unsized tuple coercion is not stable enough for use and is subject to change",
)
.emit();
}
Ok(coercion)
}

View file

@ -53,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_no_capture_closure(err, expected, expr_ty)
|| self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty)
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|| self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected, expected_ty_expr)
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected)
@ -621,7 +621,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// is in a different line, so we point at both.
err.span_label(secondary_span, "expected due to the type of this binding");
err.span_label(primary_span, format!("expected due to this{post_message}"));
} else if post_message == "" {
} else if post_message.is_empty() {
// We are pointing at either the assignment lhs or the binding def pattern.
err.span_label(primary_span, "expected due to the type of this binding");
} else {

View file

@ -1069,7 +1069,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if let ExprKind::MethodCall(..) = rcvr.kind {
err.span_note(
sp,
modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.",
modifies_rcvr_note + ", it is not meant to be used in method chains.",
);
} else {
err.span_note(sp, modifies_rcvr_note);

View file

@ -1085,12 +1085,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
pub(crate) fn suggest_copied_or_cloned(
pub(crate) fn suggest_copied_cloned_or_as_ref(
&self,
diag: &mut Diagnostic,
expr: &hir::Expr<'_>,
expr_ty: Ty<'tcx>,
expected_ty: Ty<'tcx>,
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
) -> bool {
let ty::Adt(adt_def, args) = expr_ty.kind() else {
return false;
@ -1102,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return false;
}
let mut suggest_copied_or_cloned = || {
let mut suggest_copied_cloned_or_as_ref = || {
let expr_inner_ty = args.type_at(0);
let expected_inner_ty = expected_args.type_at(0);
if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
@ -1119,6 +1120,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
return true;
} else if let Some(expected_ty_expr) = expected_ty_expr {
diag.span_suggestion_verbose(
expected_ty_expr.span.shrink_to_hi(),
format!(
"use `{def_path}::as_ref()` to convert `{expected_ty}` to `{expr_ty}`"
),
".as_ref()",
Applicability::MachineApplicable,
);
return true;
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
self,
@ -1146,11 +1157,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Check that the error types are equal
&& self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
{
return suggest_copied_or_cloned();
return suggest_copied_cloned_or_as_ref();
} else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
&& adt_def.did() == option_did
{
return suggest_copied_or_cloned();
return suggest_copied_cloned_or_as_ref();
}
false
@ -1512,9 +1523,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
found_ty: Ty<'tcx>,
expr: &hir::Expr<'_>,
) {
// When `expr` is `x` in something like `let x = foo.clone(); x`, need to recurse up to get
// `foo` and `clone`.
let expr = self.note_type_is_not_clone_inner_expr(expr);
// If we've recursed to an `expr` of `foo.clone()`, get `foo` and `clone`.
let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
return;
};
let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
return;
};
@ -1567,6 +1584,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// Given a type mismatch error caused by `&T` being cloned instead of `T`, and
/// the `expr` as the source of this type mismatch, try to find the method call
/// as the source of this error and return that instead. Otherwise, return the
/// original expression.
fn note_type_is_not_clone_inner_expr<'b>(
&'b self,
expr: &'b hir::Expr<'b>,
) -> &'b hir::Expr<'b> {
match expr.peel_blocks().kind {
hir::ExprKind::Path(hir::QPath::Resolved(
None,
hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
)) => {
let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding)
else {
return expr;
};
let Some(parent) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id)) else {
return expr;
};
match parent {
// foo.clone()
hir::Node::Local(hir::Local { init: Some(init), .. }) => {
self.note_type_is_not_clone_inner_expr(init)
}
// When `expr` is more complex like a tuple
hir::Node::Pat(hir::Pat {
hir_id: pat_hir_id,
kind: hir::PatKind::Tuple(pats, ..),
..
}) => {
let Some(hir::Node::Local(hir::Local { init: Some(init), .. })) =
self.tcx.hir().find(self.tcx.hir().parent_id(*pat_hir_id)) else {
return expr;
};
match init.peel_blocks().kind {
ExprKind::Tup(init_tup) => {
if let Some(init) = pats
.iter()
.enumerate()
.filter(|x| x.1.hir_id == *hir_id)
.map(|(i, _)| init_tup.get(i).unwrap())
.next()
{
self.note_type_is_not_clone_inner_expr(init)
} else {
expr
}
}
_ => expr,
}
}
_ => expr,
}
}
// If we're calling into a closure that may not be typed recurse into that call. no need
// to worry if it's a call to a typed function or closure as this would ne handled
// previously.
hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) = call_expr_kind
&& let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path
&& let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding)
&& let Some(closure) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id))
&& let hir::Node::Local(hir::Local { init: Some(init), .. }) = closure
&& let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), ..} = init
{
let hir::Body { value: body_expr, .. } = self.tcx.hir().body(*body_id);
self.note_type_is_not_clone_inner_expr(body_expr)
} else {
expr
}
}
_ => expr,
}
}
/// A common error is to add an extra semicolon:
///
/// ```compile_fail,E0308

View file

@ -932,7 +932,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
var_hir_id: hir::HirId,
closure_clause: hir::CaptureBy,
) -> Option<FxIndexMap<UpvarMigrationInfo, UnordSet<&'static str>>> {
let auto_traits_def_id = vec![
let auto_traits_def_id = [
self.tcx.lang_items().clone_trait(),
self.tcx.lang_items().sync_trait(),
self.tcx.get_diagnostic_item(sym::Send),

View file

@ -264,11 +264,11 @@ fn msg_span_from_named_region<'tcx>(
ty::RePlaceholder(ty::PlaceholderRegion {
bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(Some(span)), .. },
..
}) => (format!("the anonymous lifetime defined here"), Some(span)),
}) => ("the anonymous lifetime defined here".to_owned(), Some(span)),
ty::RePlaceholder(ty::PlaceholderRegion {
bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(None), .. },
..
}) => (format!("an anonymous lifetime"), None),
}) => ("an anonymous lifetime".to_owned(), None),
_ => bug!("{:?}", region),
}
}
@ -2354,7 +2354,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span)
&& snip.starts_with(' ')
{
format!("{new_lt}")
new_lt.to_string()
} else {
format!("{new_lt} ")
}

View file

@ -333,11 +333,7 @@ pub fn suggest_new_region_bound(
} else {
None
};
let name = if let Some(name) = &existing_lt_name {
format!("{}", name)
} else {
format!("'a")
};
let name = if let Some(name) = &existing_lt_name { name } else { "'a" };
// if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
// introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
if let Some(id) = scope_def_id
@ -350,7 +346,7 @@ pub fn suggest_new_region_bound(
if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_)
(p.span.shrink_to_hi(),format!("{name} "))
} else { // Underscore (elided with '_)
(p.span, format!("{name}"))
(p.span, name.to_string())
}
)
.collect::<Vec<_>>()

View file

@ -425,9 +425,11 @@ impl<'tcx> MiniGraph<'tcx> {
}
}
} else {
for (constraint, _origin) in &region_constraints.data().constraints {
each_constraint(constraint)
}
region_constraints
.data()
.constraints
.keys()
.for_each(|constraint| each_constraint(constraint));
}
}

View file

@ -740,6 +740,7 @@ fn test_unstable_options_tracking_hash() {
untracked!(unstable_options, true);
untracked!(validate_mir, true);
untracked!(verbose, true);
untracked!(write_long_types_to_disk, false);
// tidy-alphabetical-end
macro_rules! tracked {

View file

@ -24,6 +24,10 @@ impl<'a> Cursor<'a> {
}
}
pub fn as_str(&self) -> &'a str {
self.chars.as_str()
}
/// Returns the last eaten symbol (or `'\0'` in release builds).
/// (For debug assertions only.)
pub(crate) fn prev(&self) -> char {

View file

@ -367,6 +367,13 @@ impl Cursor<'_> {
Some(|terminated| Byte { terminated }),
),
// c-string literal, raw c-string literal or identifier.
'c' => self.c_or_byte_string(
|terminated| CStr { terminated },
|n_hashes| RawCStr { n_hashes },
None,
),
// Identifier (this should be checked after other variant that can
// start as identifier).
c if is_id_start(c) => self.ident_or_unknown_prefix(),

View file

@ -372,7 +372,7 @@ where
callback(start..end, EscapeError::MultipleSkippedLinesWarning);
}
let tail = &tail[first_non_space..];
if let Some(c) = tail.chars().nth(0) {
if let Some(c) = tail.chars().next() {
if c.is_whitespace() {
// For error reporting, we would like the span to contain the character that was not
// skipped. The +1 is necessary to account for the leading \ that started the escape.

View file

@ -966,12 +966,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
args: GenericArgsRef<'tcx>,
) -> FfiResult<'tcx> {
let field_ty = field.ty(self.cx.tcx, args);
if field_ty.has_opaque_types() {
self.check_type_for_ffi(cache, field_ty)
} else {
let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty);
self.check_type_for_ffi(cache, field_ty)
}
let field_ty = self
.cx
.tcx
.try_normalize_erasing_regions(self.cx.param_env, field_ty)
.unwrap_or(field_ty);
self.check_type_for_ffi(cache, field_ty)
}
/// Checks if the given `VariantDef`'s field types are "ffi-safe".
@ -1320,7 +1320,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if let Some(ty) = self
.cx
.tcx
.normalize_erasing_regions(self.cx.param_env, ty)
.try_normalize_erasing_regions(self.cx.param_env, ty)
.unwrap_or(ty)
.visit_with(&mut ProhibitOpaqueTypes)
.break_value()
{
@ -1338,16 +1339,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
is_static: bool,
is_return_type: bool,
) {
// We have to check for opaque types before `normalize_erasing_regions`,
// which will replace opaque types with their underlying concrete type.
if self.check_for_opaque_ty(sp, ty) {
// We've already emitted an error due to an opaque type.
return;
}
// it is only OK to use this function because extern fns cannot have
// any generic types right now:
let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty);
let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty);
// C doesn't really support passing arrays by value - the only way to pass an array by value
// is through a struct. So, first test that the top level isn't an array, and then

View file

@ -104,8 +104,8 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
};
// Load metadata back to memory: codegen may need to include it in object files.
let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir)
.unwrap_or_else(|err| {
let metadata =
EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| {
tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err });
});

View file

@ -2244,13 +2244,12 @@ pub fn provide(providers: &mut Providers) {
tcx.resolutions(())
.doc_link_resolutions
.get(&def_id)
.expect("no resolutions for a doc link")
.unwrap_or_else(|| span_bug!(tcx.def_span(def_id), "no resolutions for a doc link"))
},
doc_link_traits_in_scope: |tcx, def_id| {
tcx.resolutions(())
.doc_link_traits_in_scope
.get(&def_id)
.expect("no traits in scope for a doc link")
tcx.resolutions(()).doc_link_traits_in_scope.get(&def_id).unwrap_or_else(|| {
span_bug!(tcx.def_span(def_id), "no traits in scope for a doc link")
})
},
traits: |tcx, LocalCrate| {
let mut traits = Vec::new();

View file

@ -43,7 +43,7 @@ macro_rules! span_bug {
#[macro_export]
macro_rules! CloneLiftImpls {
($($ty:ty,)+) => {
($($ty:ty),+ $(,)?) => {
$(
impl<'tcx> $crate::ty::Lift<'tcx> for $ty {
type Lifted = Self;
@ -59,7 +59,7 @@ macro_rules! CloneLiftImpls {
/// allocated data** (i.e., don't need to be folded).
#[macro_export]
macro_rules! TrivialTypeTraversalImpls {
($($ty:ty,)+) => {
($($ty:ty),+ $(,)?) => {
$(
impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty {
fn try_fold_with<F: $crate::ty::fold::FallibleTypeFolder<$crate::ty::TyCtxt<'tcx>>>(

View file

@ -178,9 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
}
}
TrivialTypeTraversalAndLiftImpls! {
Cache,
}
TrivialTypeTraversalAndLiftImpls! { Cache }
impl<S: Encoder> Encodable<S> for Cache {
#[inline]

View file

@ -571,7 +571,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
assert!(self.mutability == Mutability::Mut);
// `to_bits_or_ptr_internal` is the right method because we just want to store this data
// as-is into memory.
// as-is into memory. This also double-checks that `val.size()` matches `range.size`.
let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? {
Right(ptr) => {
let (provenance, offset) = ptr.into_parts();

View file

@ -12,7 +12,8 @@ use rustc_errors::{
use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace;
use rustc_span::def_id::DefId;
use rustc_target::abi::{call, Align, Size, WrappingRange};
use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange};
use std::borrow::Cow;
use std::{any::Any, backtrace::Backtrace, fmt};
@ -66,9 +67,7 @@ impl Into<ErrorGuaranteed> for ReportedErrorInfo {
}
}
TrivialTypeTraversalAndLiftImpls! {
ErrorHandled,
}
TrivialTypeTraversalAndLiftImpls! { ErrorHandled }
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
@ -191,9 +190,8 @@ pub enum InvalidProgramInfo<'tcx> {
FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
/// SizeOf of unsized type was requested.
SizeOfUnsizedType(Ty<'tcx>),
/// An unsized local was accessed without having been initialized.
/// This is not meaningful as we can't even have backing memory for such locals.
UninitUnsizedLocal,
/// We are runnning into a nonsense situation due to ConstProp violating our invariants.
ConstPropNonsense,
}
/// Details of why a pointer had to be in-bounds.
@ -324,7 +322,9 @@ pub enum UndefinedBehaviorInfo<'a> {
/// Data size is not equal to target size.
ScalarSizeMismatch(ScalarSizeMismatch),
/// A discriminant of an uninhabited enum variant is written.
UninhabitedEnumVariantWritten,
UninhabitedEnumVariantWritten(VariantIdx),
/// An uninhabited enum variant is projected.
UninhabitedEnumVariantRead(VariantIdx),
/// Validation error.
Validation(ValidationErrorInfo<'a>),
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
@ -394,6 +394,7 @@ pub enum ValidationErrorKind<'tcx> {
UnsafeCell,
UninhabitedVal { ty: Ty<'tcx> },
InvalidEnumTag { value: String },
UninhabitedEnumTag,
UninitEnumTag,
UninitStr,
Uninit { expected: ExpectedKind },

View file

@ -320,6 +320,14 @@ impl<Prov> Scalar<Prov> {
}
})
}
#[inline]
pub fn size(self) -> Size {
match self {
Scalar::Int(int) => int.size(),
Scalar::Ptr(_ptr, sz) => Size::from_bytes(sz),
}
}
}
impl<'tcx, Prov: Provenance> Scalar<Prov> {

View file

@ -706,9 +706,7 @@ pub enum BindingForm<'tcx> {
RefForGuard,
}
TrivialTypeTraversalAndLiftImpls! {
BindingForm<'tcx>,
}
TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> }
mod binding_form_impl {
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};

View file

@ -2064,9 +2064,9 @@ rustc_queries! {
}
}
query is_impossible_method(key: (DefId, DefId)) -> bool {
query is_impossible_associated_item(key: (DefId, DefId)) -> bool {
desc { |tcx|
"checking if `{}` is impossible to call within `{}`",
"checking if `{}` is impossible to reference within `{}`",
tcx.def_path_str(key.1),
tcx.def_path_str(key.0),
}

View file

@ -649,43 +649,31 @@ pub enum ImplSource<'tcx, N> {
/// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if
/// any).
Param(Vec<N>, ty::BoundConstness),
Param(ty::BoundConstness, Vec<N>),
/// Virtual calls through an object.
Object(ImplSourceObjectData<N>),
/// Successful resolution for a builtin trait.
Builtin(Vec<N>),
/// ImplSource for trait upcasting coercion
TraitUpcasting(ImplSourceTraitUpcastingData<N>),
/// Successful resolution for a builtin impl.
Builtin(BuiltinImplSource, Vec<N>),
}
impl<'tcx, N> ImplSource<'tcx, N> {
pub fn nested_obligations(self) -> Vec<N> {
match self {
ImplSource::UserDefined(i) => i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) => n,
ImplSource::Object(d) => d.nested,
ImplSource::TraitUpcasting(d) => d.nested,
ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
}
}
pub fn borrow_nested_obligations(&self) -> &[N] {
match self {
ImplSource::UserDefined(i) => &i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) => &n,
ImplSource::Object(d) => &d.nested,
ImplSource::TraitUpcasting(d) => &d.nested,
ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n,
}
}
pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] {
match self {
ImplSource::UserDefined(i) => &mut i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) => n,
ImplSource::Object(d) => &mut d.nested,
ImplSource::TraitUpcasting(d) => &mut d.nested,
ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
}
}
@ -699,17 +687,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
args: i.args,
nested: i.nested.into_iter().map(f).collect(),
}),
ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct),
ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()),
ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData {
vtable_base: o.vtable_base,
nested: o.nested.into_iter().map(f).collect(),
}),
ImplSource::TraitUpcasting(d) => {
ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData {
vtable_vptr_slot: d.vtable_vptr_slot,
nested: d.nested.into_iter().map(f).collect(),
})
ImplSource::Param(ct, n) => ImplSource::Param(ct, n.into_iter().map(f).collect()),
ImplSource::Builtin(source, n) => {
ImplSource::Builtin(source, n.into_iter().map(f).collect())
}
}
}
@ -733,30 +713,32 @@ pub struct ImplSourceUserDefinedData<'tcx, N> {
pub nested: Vec<N>,
}
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ImplSourceTraitUpcastingData<N> {
/// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits, pointers to supertrait vtable will
/// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable
/// within that vtable.
pub vtable_vptr_slot: Option<usize>,
pub nested: Vec<N>,
}
#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ImplSourceObjectData<N> {
#[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum BuiltinImplSource {
/// Some builtin impl we don't need to differentiate. This should be used
/// unless more specific information is necessary.
Misc,
/// A builtin impl for trait objects.
///
/// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits, pointers to supertrait vtable will
/// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
/// in that vtable.
pub vtable_base: usize,
pub nested: Vec<N>,
Object { vtable_base: usize },
/// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits, pointers to supertrait vtable will
/// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable
/// within that vtable.
TraitUpcasting { vtable_vptr_slot: Option<usize> },
/// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`.
///
/// This needs to be a separate variant as it is still unstable and we need to emit
/// a feature error when using it on stable.
TupleUnsizing,
}
TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource }
#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)]
pub enum ObjectSafetyViolation {
/// `Self: Sized` declared on the trait.

View file

@ -304,9 +304,7 @@ impl From<ErrorGuaranteed> for OverflowError {
}
}
TrivialTypeTraversalAndLiftImpls! {
OverflowError,
}
TrivialTypeTraversalAndLiftImpls! { OverflowError }
impl<'tcx> From<OverflowError> for SelectionError<'tcx> {
fn from(overflow_error: OverflowError) -> SelectionError<'tcx> {

View file

@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> {
pub enum CandidateKind<'tcx> {
/// Probe entered when normalizing the self ty during candidate assembly
NormalizedSelfTyAssembly,
DynUpcastingAssembly,
/// A normal candidate for proving a goal
Candidate { name: String, result: QueryResult<'tcx> },
Candidate {
name: String,
result: QueryResult<'tcx>,
},
}
impl Debug for GoalCandidate<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View file

@ -15,11 +15,11 @@ struct Indentor<'a, 'b> {
impl Write for Indentor<'_, '_> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
for line in s.split_inclusive("\n") {
for line in s.split_inclusive('\n') {
if self.on_newline {
self.f.write_str(" ")?;
}
self.on_newline = line.ends_with("\n");
self.on_newline = line.ends_with('\n');
self.f.write_str(line)?;
}
@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
CandidateKind::NormalizedSelfTyAssembly => {
writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
}
CandidateKind::DynUpcastingAssembly => {
writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:")
}
CandidateKind::Candidate { name, result } => {
writeln!(self.f, "CANDIDATE {}: {:?}", name, result)
}

View file

@ -6,18 +6,16 @@ use std::fmt;
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v),
match self {
super::ImplSource::UserDefined(v) => write!(f, "{:?}", v),
super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),
super::ImplSource::Object(ref d) => write!(f, "{:?}", d),
super::ImplSource::Param(ref n, ct) => {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
super::ImplSource::Builtin(source, d) => {
write!(f, "Builtin({source:?}, {d:?})")
}
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
super::ImplSource::Param(ct, n) => {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
}
}
}
}
@ -31,23 +29,3 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx,
)
}
}
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ImplSourceTraitUpcastingData(vtable_vptr_slot={:?}, nested={:?})",
self.vtable_vptr_slot, self.nested
)
}
}
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ImplSourceObjectData(vtable_base={}, nested={:?})",
self.vtable_base, self.nested
)
}
}

View file

@ -27,9 +27,7 @@ impl From<ErrorGuaranteed> for NotConstEvaluatable {
}
}
TrivialTypeTraversalAndLiftImpls! {
NotConstEvaluatable,
}
TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable }
pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;

View file

@ -6,7 +6,7 @@ pub enum BindingMode {
BindByValue(Mutability),
}
TrivialTypeTraversalAndLiftImpls! { BindingMode, }
TrivialTypeTraversalAndLiftImpls! { BindingMode }
impl BindingMode {
pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode {

View file

@ -339,12 +339,17 @@ impl<'tcx> TyCtxt<'tcx> {
}
pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
let width = self.sess.diagnostic_width();
let length_limit = width.saturating_sub(30);
let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
.pretty_print_type(ty)
.expect("could not write to `String`")
.into_buffer();
if !self.sess.opts.unstable_opts.write_long_types_to_disk {
return (regular, None);
}
let width = self.sess.diagnostic_width();
let length_limit = width.saturating_sub(30);
if regular.len() <= width {
return (regular, None);
}

View file

@ -741,9 +741,9 @@ where
let fields = match this.ty.kind() {
ty::Adt(def, _) if def.variants().is_empty() =>
bug!("for_variant called on zero-variant enum"),
bug!("for_variant called on zero-variant enum {}", this.ty),
ty::Adt(def, _) => def.variant(variant_index).fields.len(),
_ => bug!(),
_ => bug!("`ty_and_layout_for_variant` on unexpected type {}", this.ty),
};
tcx.mk_layout(LayoutS {
variants: Variants::Single { index: variant_index },

View file

@ -2670,11 +2670,6 @@ impl<'tcx> Ty<'tcx> {
variant_index: VariantIdx,
) -> Option<Discr<'tcx>> {
match self.kind() {
TyKind::Adt(adt, _) if adt.variants().is_empty() => {
// This can actually happen during CTFE, see
// https://github.com/rust-lang/rust/issues/89765.
None
}
TyKind::Adt(adt, _) if adt.is_enum() => {
Some(adt.discriminant_for_variant(tcx, variant_index))
}

View file

@ -1041,10 +1041,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<
}
elem(curr, curr_count);
let mut s = "[".to_string();
s.push_str(&v.join(", "));
s.push_str("]");
s
format!("[{}]", v.join(", "))
}
};
@ -1229,12 +1226,13 @@ fn dump_mono_items_stats<'tcx>(
// Gather instantiated mono items grouped by def_id
let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default();
for cgu in codegen_units {
for (&mono_item, _) in cgu.items() {
cgu.items()
.keys()
// Avoid variable-sized compiler-generated shims
if mono_item.is_user_defined() {
.filter(|mono_item| mono_item.is_user_defined())
.for_each(|mono_item| {
items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item);
}
}
});
}
#[derive(serde::Serialize)]
@ -1287,7 +1285,7 @@ fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet {
let mut result = items.clone();
for cgu in cgus {
for (item, _) in cgu.items() {
for item in cgu.items().keys() {
if let MonoItem::Fn(ref instance) = item {
let did = instance.def_id();
if !visited.insert(did) {

View file

@ -9,8 +9,8 @@ use rustc_ast::tokenstream::TokenStream;
use rustc_ast::util::unicode::contains_text_flow_control_chars;
use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
use rustc_lexer::unescape::{self, EscapeError, Mode};
use rustc_lexer::Cursor;
use rustc_lexer::{Base, DocStyle, RawStrError};
use rustc_lexer::{Cursor, LiteralKind};
use rustc_session::lint::builtin::{
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
};
@ -118,6 +118,7 @@ impl<'a> StringReader<'a> {
let mut swallow_next_invalid = 0;
// Skip trivial (whitespace & comments) tokens
loop {
let str_before = self.cursor.as_str();
let token = self.cursor.advance_token();
let start = self.pos;
self.pos = self.pos + BytePos(token.len);
@ -165,10 +166,7 @@ impl<'a> StringReader<'a> {
continue;
}
rustc_lexer::TokenKind::Ident => {
let sym = nfc_normalize(self.str_from(start));
let span = self.mk_sp(start, self.pos);
self.sess.symbol_gallery.insert(sym, span);
token::Ident(sym, false)
self.ident(start)
}
rustc_lexer::TokenKind::RawIdent => {
let sym = nfc_normalize(self.str_from(start + BytePos(2)));
@ -182,10 +180,7 @@ impl<'a> StringReader<'a> {
}
rustc_lexer::TokenKind::UnknownPrefix => {
self.report_unknown_prefix(start);
let sym = nfc_normalize(self.str_from(start));
let span = self.mk_sp(start, self.pos);
self.sess.symbol_gallery.insert(sym, span);
token::Ident(sym, false)
self.ident(start)
}
rustc_lexer::TokenKind::InvalidIdent
// Do not recover an identifier with emoji if the codepoint is a confusable
@ -203,6 +198,27 @@ impl<'a> StringReader<'a> {
.push(span);
token::Ident(sym, false)
}
// split up (raw) c string literals to an ident and a string literal when edition < 2021.
rustc_lexer::TokenKind::Literal {
kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }),
suffix_start: _,
} if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => {
let prefix_len = match kind {
LiteralKind::CStr { .. } => 1,
LiteralKind::RawCStr { .. } => 2,
_ => unreachable!(),
};
// reset the state so that only the prefix ("c" or "cr")
// was consumed.
let lit_start = start + BytePos(prefix_len);
self.pos = lit_start;
self.cursor = Cursor::new(&str_before[prefix_len as usize..]);
self.report_unknown_prefix(start);
let prefix_span = self.mk_sp(start, lit_start);
return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);
}
rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
let suffix_start = start + BytePos(suffix_start);
let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
@ -317,6 +333,13 @@ impl<'a> StringReader<'a> {
}
}
fn ident(&self, start: BytePos) -> TokenKind {
let sym = nfc_normalize(self.str_from(start));
let span = self.mk_sp(start, self.pos);
self.sess.symbol_gallery.insert(sym, span);
token::Ident(sym, false)
}
fn struct_fatal_span_char(
&self,
from_pos: BytePos,

View file

@ -27,7 +27,7 @@ pub(crate) fn emit_unescape_error(
lit, span_with_quotes, mode, range, error
);
let last_char = || {
let c = lit[range.clone()].chars().rev().next().unwrap();
let c = lit[range.clone()].chars().next_back().unwrap();
let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
(c, span)
};

View file

@ -238,7 +238,7 @@ impl<'a> Parser<'a> {
_ => unreachable!(),
}
.into();
let invalid = format!("{}=", &sugg);
let invalid = format!("{sugg}=");
self.sess.emit_err(errors::InvalidComparisonOperator {
span: sp,
invalid: invalid.clone(),

View file

@ -49,7 +49,7 @@ impl<'a> Parser<'a> {
&& self.check_ident()
// `Const` followed by IDENT
{
return Ok(self.recover_const_param_with_mistyped_const(preceding_attrs, ident)?);
return self.recover_const_param_with_mistyped_const(preceding_attrs, ident);
}
// Parse optional colon and param bounds.

View file

@ -157,15 +157,15 @@ fn emit_malformed_attribute(
matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench)
};
let error_msg = format!("malformed `{}` attribute input", name);
let error_msg = format!("malformed `{name}` attribute input");
let mut msg = "attribute must be of the form ".to_owned();
let mut suggestions = vec![];
let mut first = true;
let inner = if style == ast::AttrStyle::Inner { "!" } else { "" };
if template.word {
first = false;
let code = format!("#{}[{}]", inner, name);
msg.push_str(&format!("`{}`", &code));
let code = format!("#{inner}[{name}]");
msg.push_str(&format!("`{code}`"));
suggestions.push(code);
}
if let Some(descr) = template.list {
@ -173,16 +173,16 @@ fn emit_malformed_attribute(
msg.push_str(" or ");
}
first = false;
let code = format!("#{}[{}({})]", inner, name, descr);
msg.push_str(&format!("`{}`", &code));
let code = format!("#{inner}[{name}({descr})]");
msg.push_str(&format!("`{code}`"));
suggestions.push(code);
}
if let Some(descr) = template.name_value_str {
if !first {
msg.push_str(" or ");
}
let code = format!("#{}[{} = \"{}\"]", inner, name, descr);
msg.push_str(&format!("`{}`", &code));
let code = format!("#{inner}[{name} = \"{descr}\"]");
msg.push_str(&format!("`{code}`"));
suggestions.push(code);
}
if should_warn(name) {

View file

@ -211,6 +211,17 @@ passes_doc_keyword_not_mod =
passes_doc_keyword_only_impl =
`#[doc(keyword = "...")]` should be used on impl blocks
passes_doc_masked_not_extern_crate_self =
this attribute cannot be applied to an `extern crate self` item
.label = not applicable on `extern crate self` items
.extern_crate_self_label = `extern crate self` defined here
passes_doc_masked_only_extern_crate =
this attribute can only be applied to an `extern crate` item
.label = only applicable on `extern crate` items
.not_an_extern_crate_label = not an `extern crate` item
.note = read <https://doc.rust-lang.org/unstable-book/language-features/doc-masked.html> for more information
passes_doc_test_literal = `#![doc(test(...)]` does not take a literal
passes_doc_test_takes_list =

View file

@ -878,6 +878,44 @@ impl CheckAttrVisitor<'_> {
}
}
fn check_doc_masked(
&self,
attr: &Attribute,
meta: &NestedMetaItem,
hir_id: HirId,
target: Target,
) -> bool {
if target != Target::ExternCrate {
self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span(),
errors::DocMaskedOnlyExternCrate {
attr_span: meta.span(),
item_span: (attr.style == AttrStyle::Outer)
.then(|| self.tcx.hir().span(hir_id)),
},
);
return false;
}
if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() {
self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span(),
errors::DocMaskedNotExternCrateSelf {
attr_span: meta.span(),
item_span: (attr.style == AttrStyle::Outer)
.then(|| self.tcx.hir().span(hir_id)),
},
);
return false;
}
true
}
/// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
fn check_attr_not_crate_level(
&self,
@ -1048,6 +1086,17 @@ impl CheckAttrVisitor<'_> {
is_valid = false;
}
sym::masked
if !self.check_doc_masked(
attr,
meta,
hir_id,
target,
) =>
{
is_valid = false;
}
// no_default_passes: deprecated
// passes: deprecated
// plugins: removed, but rustdoc warns about it itself

View file

@ -267,6 +267,25 @@ pub struct DocInlineOnlyUse {
pub item_span: Option<Span>,
}
#[derive(LintDiagnostic)]
#[diag(passes_doc_masked_only_extern_crate)]
#[note]
pub struct DocMaskedOnlyExternCrate {
#[label]
pub attr_span: Span,
#[label(passes_not_an_extern_crate_label)]
pub item_span: Option<Span>,
}
#[derive(LintDiagnostic)]
#[diag(passes_doc_masked_not_extern_crate_self)]
pub struct DocMaskedNotExternCrateSelf {
#[label]
pub attr_span: Span,
#[label(passes_extern_crate_self_label)]
pub item_span: Option<Span>,
}
#[derive(Diagnostic)]
#[diag(passes_doc_attr_not_crate_level)]
pub struct DocAttrNotCrateLevel<'a> {

View file

@ -183,7 +183,7 @@ pub(super) fn encode_all_query_results<'tcx>(
encoder: &mut CacheEncoder<'_, 'tcx>,
query_result_index: &mut EncodedDepNodeIndex,
) {
for encode in super::ENCODE_QUERY_RESULTS.iter().copied().filter_map(|e| e) {
for encode in super::ENCODE_QUERY_RESULTS.iter().copied().flatten() {
encode(tcx, encoder, query_result_index);
}
}

View file

@ -1395,7 +1395,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let head_span = source_map.guess_head_span(span);
err.subdiagnostic(ConsiderAddingADerive {
span: head_span.shrink_to_lo(),
suggestion: format!("#[derive(Default)]\n")
suggestion: "#[derive(Default)]\n".to_string(),
});
}
for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
@ -1718,7 +1718,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if next_binding.is_none() && let Some(span) = non_exhaustive {
note_span.push_span_label(
span,
format!("cannot be constructed because it is `#[non_exhaustive]`"),
"cannot be constructed because it is `#[non_exhaustive]`",
);
}
err.span_note(note_span, msg);

View file

@ -989,14 +989,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
initial_binding.res()
});
let res = binding.res();
if res == Res::Err || !this.ambiguity_errors.is_empty() {
this.tcx
.sess
.delay_span_bug(import.span, "some error happened for an import");
return;
}
if let Ok(initial_res) = initial_res {
if res != initial_res && this.ambiguity_errors.is_empty() {
if res != initial_res {
span_bug!(import.span, "inconsistent resolution for an import");
}
} else if res != Res::Err
&& this.ambiguity_errors.is_empty()
&& this.privacy_errors.is_empty()
{
} else if this.privacy_errors.is_empty() {
this.tcx
.sess
.create_err(CannotDetermineImportResolution { span: import.span })

View file

@ -549,6 +549,7 @@ enum MaybeExported<'a> {
Ok(NodeId),
Impl(Option<DefId>),
ImplItem(Result<DefId, &'a Visibility>),
NestedUse(&'a Visibility),
}
impl MaybeExported<'_> {
@ -559,7 +560,9 @@ impl MaybeExported<'_> {
trait_def_id.as_local()
}
MaybeExported::Impl(None) => return true,
MaybeExported::ImplItem(Err(vis)) => return vis.kind.is_pub(),
MaybeExported::ImplItem(Err(vis)) | MaybeExported::NestedUse(vis) => {
return vis.kind.is_pub();
}
};
def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id))
}
@ -2284,7 +2287,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
fn resolve_item(&mut self, item: &'ast Item) {
let mod_inner_docs =
matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs);
if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..)) {
if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..) | ItemKind::Use(..)) {
self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
}
@ -2428,6 +2431,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}
ItemKind::Use(ref use_tree) => {
let maybe_exported = match use_tree.kind {
UseTreeKind::Simple(_) | UseTreeKind::Glob => MaybeExported::Ok(item.id),
UseTreeKind::Nested(_) => MaybeExported::NestedUse(&item.vis),
};
self.resolve_doc_links(&item.attrs, maybe_exported);
self.future_proof_import(use_tree);
}

View file

@ -45,6 +45,7 @@ session_int_literal_too_large = integer literal is too large
.note = value exceeds limit of `{$limit}`
session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}`
session_invalid_character_in_create_name_help = you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name
session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal
.label = invalid suffix `{$suffix}`

View file

@ -227,10 +227,8 @@ impl CodeStats {
}
pub fn print_vtable_sizes(&self, crate_name: &str) {
let mut infos = std::mem::take(&mut *self.vtable_sizes.lock())
.into_iter()
.map(|(_did, stats)| stats)
.collect::<Vec<_>>();
let mut infos =
std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::<Vec<_>>();
// Primary sort: cost % in reverse order (from largest to smallest)
// Secondary sort: trait_name

View file

@ -279,11 +279,11 @@ impl LinkSelfContained {
// set of all values like `y` or `n` used to be. Therefore, if this flag had previously been
// set in bulk with its historical values, then manually setting a component clears that
// `explicitly_set` state.
if let Some(component_to_enable) = component.strip_prefix("+") {
if let Some(component_to_enable) = component.strip_prefix('+') {
self.explicitly_set = None;
self.components.insert(component_to_enable.parse()?);
Ok(())
} else if let Some(component_to_disable) = component.strip_prefix("-") {
} else if let Some(component_to_disable) = component.strip_prefix('-') {
self.explicitly_set = None;
self.components.remove(component_to_disable.parse()?);
Ok(())

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