Merge from rustc
This commit is contained in:
commit
85ed3fe55d
1467 changed files with 22132 additions and 12274 deletions
|
|
@ -23,3 +23,5 @@ b2d2184edea578109a48ec3d8decbee5948e8f35
|
|||
# test directives migration
|
||||
6e48b96692d63a79a14563f27fe5185f122434f8
|
||||
ec2cc761bc7067712ecc7734502f703fe3b024c8
|
||||
# format use declarations
|
||||
84ac80f1921afc243d71fd0caaa4f2838c294102
|
||||
|
|
|
|||
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -33,7 +33,7 @@
|
|||
[submodule "src/llvm-project"]
|
||||
path = src/llvm-project
|
||||
url = https://github.com/rust-lang/llvm-project.git
|
||||
branch = rustc/18.1-2024-05-19
|
||||
branch = rustc/19.1-2024-07-30
|
||||
shallow = true
|
||||
[submodule "src/doc/embedded-book"]
|
||||
path = src/doc/embedded-book
|
||||
|
|
|
|||
|
|
@ -516,7 +516,7 @@ impl Size {
|
|||
/// Truncates `value` to `self` bits and then sign-extends it to 128 bits
|
||||
/// (i.e., if it is negative, fill with 1's on the left).
|
||||
#[inline]
|
||||
pub fn sign_extend(self, value: u128) -> u128 {
|
||||
pub fn sign_extend(self, value: u128) -> i128 {
|
||||
let size = self.bits();
|
||||
if size == 0 {
|
||||
// Truncated until nothing is left.
|
||||
|
|
@ -526,7 +526,7 @@ impl Size {
|
|||
let shift = 128 - size;
|
||||
// Shift the unsigned value to the left, then shift back to the right as signed
|
||||
// (essentially fills with sign bit on the left).
|
||||
(((value << shift) as i128) >> shift) as u128
|
||||
((value << shift) as i128) >> shift
|
||||
}
|
||||
|
||||
/// Truncates `value` to `self` bits.
|
||||
|
|
@ -544,7 +544,7 @@ impl Size {
|
|||
|
||||
#[inline]
|
||||
pub fn signed_int_min(&self) -> i128 {
|
||||
self.sign_extend(1_u128 << (self.bits() - 1)) as i128
|
||||
self.sign_extend(1_u128 << (self.bits() - 1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
//! HIR ty lowering.
|
||||
//!
|
||||
//! Similarly generics, predicates and header are set to the "default" values.
|
||||
//! In case of discrepancy with callee function the `NotSupportedDelegation` error will
|
||||
//! In case of discrepancy with callee function the `UnsupportedDelegation` error will
|
||||
//! also be emitted during HIR ty lowering.
|
||||
|
||||
use std::iter;
|
||||
|
|
|
|||
|
|
@ -563,11 +563,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
} = move_spans
|
||||
&& can_suggest_clone
|
||||
{
|
||||
self.suggest_cloning(err, ty, expr, None, Some(move_spans));
|
||||
self.suggest_cloning(err, ty, expr, Some(move_spans));
|
||||
} else if self.suggest_hoisting_call_outside_loop(err, expr) && can_suggest_clone {
|
||||
// The place where the type moves would be misleading to suggest clone.
|
||||
// #121466
|
||||
self.suggest_cloning(err, ty, expr, None, Some(move_spans));
|
||||
self.suggest_cloning(err, ty, expr, Some(move_spans));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1229,8 +1229,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
mut expr: &'tcx hir::Expr<'tcx>,
|
||||
mut other_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
use_spans: Option<UseSpans<'tcx>>,
|
||||
) {
|
||||
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
|
||||
|
|
@ -1242,66 +1241,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(some_other_expr) = other_expr
|
||||
&& let Some(parent_binop) =
|
||||
self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| {
|
||||
if let (hir_id, hir::Node::Expr(e)) = n
|
||||
&& let hir::ExprKind::AssignOp(_binop, target, _arg) = e.kind
|
||||
&& target.hir_id == expr.hir_id
|
||||
{
|
||||
Some(hir_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
&& let Some(other_parent_binop) =
|
||||
self.infcx.tcx.hir().parent_iter(some_other_expr.hir_id).find_map(|n| {
|
||||
if let (hir_id, hir::Node::Expr(expr)) = n
|
||||
&& let hir::ExprKind::AssignOp(..) = expr.kind
|
||||
{
|
||||
Some(hir_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
&& parent_binop == other_parent_binop
|
||||
{
|
||||
// Explicitly look for `expr += other_expr;` and avoid suggesting
|
||||
// `expr.clone() += other_expr;`, instead suggesting `expr += other_expr.clone();`.
|
||||
other_expr = Some(expr);
|
||||
expr = some_other_expr;
|
||||
}
|
||||
'outer: {
|
||||
if let ty::Ref(..) = ty.kind() {
|
||||
// We check for either `let binding = foo(expr, other_expr);` or
|
||||
// `foo(expr, other_expr);` and if so we don't suggest an incorrect
|
||||
// `foo(expr, other_expr).clone()`
|
||||
if let Some(other_expr) = other_expr
|
||||
&& let Some(parent_let) =
|
||||
self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| {
|
||||
if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n {
|
||||
Some(hir_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
&& let Some(other_parent_let) =
|
||||
self.infcx.tcx.hir().parent_iter(other_expr.hir_id).find_map(|n| {
|
||||
if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n {
|
||||
Some(hir_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
&& parent_let == other_parent_let
|
||||
{
|
||||
// Explicitly check that we don't have `foo(&*expr, other_expr)`, as cloning the
|
||||
// result of `foo(...)` won't help.
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
let ty = ty.peel_refs();
|
||||
if self.implements_clone(ty) {
|
||||
self.suggest_cloning_inner(err, ty, expr);
|
||||
} else if let ty::Adt(def, args) = ty.kind()
|
||||
|
|
@ -1573,10 +1512,27 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
);
|
||||
self.suggest_copy_for_type_in_cloned_ref(&mut err, place);
|
||||
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
if let Some(expr) = self.find_expr(borrow_span)
|
||||
&& let Some(ty) = typeck_results.node_type_opt(expr.hir_id)
|
||||
{
|
||||
self.suggest_cloning(&mut err, ty, expr, self.find_expr(span), Some(move_spans));
|
||||
if let Some(expr) = self.find_expr(borrow_span) {
|
||||
// This is a borrow span, so we want to suggest cloning the referent.
|
||||
if let hir::ExprKind::AddrOf(_, _, borrowed_expr) = expr.kind
|
||||
&& let Some(ty) = typeck_results.expr_ty_opt(borrowed_expr)
|
||||
{
|
||||
self.suggest_cloning(&mut err, ty, borrowed_expr, Some(move_spans));
|
||||
} else if typeck_results.expr_adjustments(expr).first().is_some_and(|adj| {
|
||||
matches!(
|
||||
adj.kind,
|
||||
ty::adjustment::Adjust::Borrow(ty::adjustment::AutoBorrow::Ref(
|
||||
_,
|
||||
ty::adjustment::AutoBorrowMutability::Not
|
||||
| ty::adjustment::AutoBorrowMutability::Mut {
|
||||
allow_two_phase_borrow: ty::adjustment::AllowTwoPhase::No
|
||||
}
|
||||
))
|
||||
)
|
||||
}) && let Some(ty) = typeck_results.expr_ty_opt(expr)
|
||||
{
|
||||
self.suggest_cloning(&mut err, ty, expr, Some(move_spans));
|
||||
}
|
||||
}
|
||||
self.buffer_error(err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -564,9 +564,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
|
||||
fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
|
||||
match error {
|
||||
GroupedMoveError::MovesFromPlace {
|
||||
mut binds_to, move_from, span: other_span, ..
|
||||
} => {
|
||||
GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
|
||||
self.add_borrow_suggestions(err, span);
|
||||
if binds_to.is_empty() {
|
||||
let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
|
||||
|
|
@ -576,7 +574,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(expr) = self.find_expr(span) {
|
||||
self.suggest_cloning(err, place_ty, expr, self.find_expr(other_span), None);
|
||||
self.suggest_cloning(err, place_ty, expr, None);
|
||||
}
|
||||
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
|
|
@ -608,13 +606,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(expr) = self.find_expr(use_span) {
|
||||
self.suggest_cloning(
|
||||
err,
|
||||
place_ty,
|
||||
expr,
|
||||
self.find_expr(span),
|
||||
Some(use_spans),
|
||||
);
|
||||
self.suggest_cloning(err, place_ty, expr, Some(use_spans));
|
||||
}
|
||||
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
|
|
@ -739,7 +731,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
|
||||
|
||||
if let Some(expr) = self.find_expr(binding_span) {
|
||||
self.suggest_cloning(err, bind_to.ty, expr, None, None);
|
||||
self.suggest_cloning(err, bind_to.ty, expr, None);
|
||||
}
|
||||
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
|
|
|
|||
|
|
@ -115,9 +115,6 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a
|
|||
builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values
|
||||
.suggestion = remove the value
|
||||
|
||||
builtin_macros_derive_unsafe_path = traits in `#[derive(...)]` don't accept `unsafe(...)`
|
||||
.suggestion = remove the `unsafe(...)`
|
||||
|
||||
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_expr})` instead
|
||||
.custom = use `std::env::var({$var_expr})` to read the variable at run time
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use rustc_ast::token;
|
|||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
|
||||
use rustc_parse::parser::attr::AllowLeadingUnsafe;
|
||||
use rustc_span::Span;
|
||||
use {rustc_ast as ast, rustc_attr as attr};
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a,
|
|||
return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span }));
|
||||
}
|
||||
|
||||
let cfg = p.parse_meta_item()?;
|
||||
let cfg = p.parse_meta_item(AllowLeadingUnsafe::Yes)?;
|
||||
|
||||
let _ = p.eat(&token::Comma);
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,13 @@ impl MultiItemModifier for Expander {
|
|||
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
||||
let template = AttributeTemplate { list: Some("path"), ..Default::default() };
|
||||
validate_attr::check_builtin_meta_item(
|
||||
&ecx.ecfg.features,
|
||||
&ecx.sess.psess,
|
||||
meta_item,
|
||||
ast::AttrStyle::Outer,
|
||||
sym::cfg_accessible,
|
||||
template,
|
||||
true,
|
||||
);
|
||||
|
||||
let Some(path) = validate_input(ecx, meta_item) else {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_ast as ast;
|
||||
use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, Safety, StmtKind};
|
||||
use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
|
||||
use rustc_expand::base::{
|
||||
Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
|
||||
};
|
||||
|
|
@ -38,11 +38,13 @@ impl MultiItemModifier for Expander {
|
|||
let template =
|
||||
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
|
||||
validate_attr::check_builtin_meta_item(
|
||||
features,
|
||||
&sess.psess,
|
||||
meta_item,
|
||||
ast::AttrStyle::Outer,
|
||||
sym::derive,
|
||||
template,
|
||||
true,
|
||||
);
|
||||
|
||||
let mut resolutions = match &meta_item.kind {
|
||||
|
|
@ -60,7 +62,6 @@ impl MultiItemModifier for Expander {
|
|||
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
|
||||
// paths.
|
||||
report_path_args(sess, meta);
|
||||
report_unsafe_args(sess, meta);
|
||||
meta.path.clone()
|
||||
})
|
||||
.map(|path| DeriveResolution {
|
||||
|
|
@ -160,13 +161,3 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) {
|
||||
match meta.unsafety {
|
||||
Safety::Unsafe(span) => {
|
||||
sess.dcx().emit_err(errors::DeriveUnsafePath { span });
|
||||
}
|
||||
Safety::Default => {}
|
||||
Safety::Safe(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use std::mem::swap;
|
||||
|
||||
use ast::ptr::P;
|
||||
use ast::HasAttrs;
|
||||
use rustc_ast::mut_visit::MutVisitor;
|
||||
use rustc_ast::visit::BoundKind;
|
||||
use rustc_ast::{
|
||||
self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
|
||||
TraitBoundModifiers, VariantData,
|
||||
TraitBoundModifiers, VariantData, WherePredicate,
|
||||
};
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use thin_vec::{thin_vec, ThinVec};
|
||||
|
||||
|
|
@ -141,33 +143,237 @@ pub fn expand_deriving_smart_ptr(
|
|||
alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone());
|
||||
let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params));
|
||||
|
||||
// # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location
|
||||
//
|
||||
// Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
|
||||
let mut impl_generics = generics.clone();
|
||||
let pointee_ty_ident = generics.params[pointee_param_idx].ident;
|
||||
let mut self_bounds;
|
||||
{
|
||||
let p = &mut impl_generics.params[pointee_param_idx];
|
||||
let pointee = &mut impl_generics.params[pointee_param_idx];
|
||||
self_bounds = pointee.bounds.clone();
|
||||
if !contains_maybe_sized_bound(&self_bounds)
|
||||
&& !contains_maybe_sized_bound_on_pointee(
|
||||
&generics.where_clause.predicates,
|
||||
pointee_ty_ident.name,
|
||||
)
|
||||
{
|
||||
cx.dcx()
|
||||
.struct_span_err(
|
||||
pointee_ty_ident.span,
|
||||
format!(
|
||||
"`derive(SmartPointer)` requires {} to be marked `?Sized`",
|
||||
pointee_ty_ident.name
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
}
|
||||
let arg = GenericArg::Type(s_ty.clone());
|
||||
let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
|
||||
p.bounds.push(cx.trait_bound(unsize, false));
|
||||
let mut attrs = thin_vec![];
|
||||
swap(&mut p.attrs, &mut attrs);
|
||||
p.attrs = attrs.into_iter().filter(|attr| !attr.has_name(sym::pointee)).collect();
|
||||
pointee.bounds.push(cx.trait_bound(unsize, false));
|
||||
// Drop `#[pointee]` attribute since it should not be recognized outside `derive(SmartPointer)`
|
||||
pointee.attrs.retain(|attr| !attr.has_name(sym::pointee));
|
||||
}
|
||||
|
||||
// Add the `__S: ?Sized` extra parameter to the impl block.
|
||||
let sized = cx.path_global(span, path!(span, core::marker::Sized));
|
||||
let bound = GenericBound::Trait(
|
||||
cx.poly_trait_ref(span, sized),
|
||||
TraitBoundModifiers {
|
||||
polarity: ast::BoundPolarity::Maybe(span),
|
||||
constness: ast::BoundConstness::Never,
|
||||
asyncness: ast::BoundAsyncness::Normal,
|
||||
},
|
||||
);
|
||||
let extra_param = cx.typaram(span, Ident::new(sym::__S, span), vec![bound], None);
|
||||
impl_generics.params.push(extra_param);
|
||||
// # Rewrite generic parameter bounds
|
||||
// For each bound `U: ..` in `struct<U: ..>`, make a new bound with `__S` in place of `#[pointee]`
|
||||
// Example:
|
||||
// ```
|
||||
// struct<
|
||||
// U: Trait<T>,
|
||||
// #[pointee] T: Trait<T> + ?Sized,
|
||||
// V: Trait<T>> ...
|
||||
// ```
|
||||
// ... generates this `impl` generic parameters
|
||||
// ```
|
||||
// impl<
|
||||
// U: Trait<T> + Trait<__S>,
|
||||
// T: Trait<T> + ?Sized + Unsize<__S>, // (**)
|
||||
// __S: Trait<__S> + ?Sized, // (*)
|
||||
// V: Trait<T> + Trait<__S>> ...
|
||||
// ```
|
||||
// The new bound marked with (*) has to be done separately.
|
||||
// See next section
|
||||
for (idx, (params, orig_params)) in
|
||||
impl_generics.params.iter_mut().zip(&generics.params).enumerate()
|
||||
{
|
||||
// Default type parameters are rejected for `impl` block.
|
||||
// We should drop them now.
|
||||
match &mut params.kind {
|
||||
ast::GenericParamKind::Const { default, .. } => *default = None,
|
||||
ast::GenericParamKind::Type { default } => *default = None,
|
||||
ast::GenericParamKind::Lifetime => {}
|
||||
}
|
||||
// We CANNOT rewrite `#[pointee]` type parameter bounds.
|
||||
// This has been set in stone. (**)
|
||||
// So we skip over it.
|
||||
// Otherwise, we push extra bounds involving `__S`.
|
||||
if idx != pointee_param_idx {
|
||||
for bound in &orig_params.bounds {
|
||||
let mut bound = bound.clone();
|
||||
let mut substitution = TypeSubstitution {
|
||||
from_name: pointee_ty_ident.name,
|
||||
to_ty: &s_ty,
|
||||
rewritten: false,
|
||||
};
|
||||
substitution.visit_param_bound(&mut bound, BoundKind::Bound);
|
||||
if substitution.rewritten {
|
||||
// We found use of `#[pointee]` somewhere,
|
||||
// so we make a new bound using `__S` in place of `#[pointee]`
|
||||
params.bounds.push(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// # Insert `__S` type parameter
|
||||
//
|
||||
// We now insert `__S` with the missing bounds marked with (*) above.
|
||||
// We should also write the bounds from `#[pointee]` to `__S` as required by `Unsize<__S>`.
|
||||
{
|
||||
let mut substitution =
|
||||
TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false };
|
||||
for bound in &mut self_bounds {
|
||||
substitution.visit_param_bound(bound, BoundKind::Bound);
|
||||
}
|
||||
}
|
||||
|
||||
// # Rewrite `where` clauses
|
||||
//
|
||||
// Move on to `where` clauses.
|
||||
// Example:
|
||||
// ```
|
||||
// struct MyPointer<#[pointee] T, ..>
|
||||
// where
|
||||
// U: Trait<V> + Trait<T>,
|
||||
// Companion<T>: Trait<T>,
|
||||
// T: Trait<T> + ?Sized,
|
||||
// { .. }
|
||||
// ```
|
||||
// ... will have a impl prelude like so
|
||||
// ```
|
||||
// impl<..> ..
|
||||
// where
|
||||
// U: Trait<V> + Trait<T>,
|
||||
// U: Trait<__S>,
|
||||
// Companion<T>: Trait<T>,
|
||||
// Companion<__S>: Trait<__S>,
|
||||
// T: Trait<T> + ?Sized,
|
||||
// __S: Trait<__S> + ?Sized,
|
||||
// ```
|
||||
//
|
||||
// We should also write a few new `where` bounds from `#[pointee] T` to `__S`
|
||||
// as well as any bound that indirectly involves the `#[pointee] T` type.
|
||||
for bound in &generics.where_clause.predicates {
|
||||
if let ast::WherePredicate::BoundPredicate(bound) = bound {
|
||||
let mut substitution = TypeSubstitution {
|
||||
from_name: pointee_ty_ident.name,
|
||||
to_ty: &s_ty,
|
||||
rewritten: false,
|
||||
};
|
||||
let mut predicate = ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
|
||||
span: bound.span,
|
||||
bound_generic_params: bound.bound_generic_params.clone(),
|
||||
bounded_ty: bound.bounded_ty.clone(),
|
||||
bounds: bound.bounds.clone(),
|
||||
});
|
||||
substitution.visit_where_predicate(&mut predicate);
|
||||
if substitution.rewritten {
|
||||
impl_generics.where_clause.predicates.push(predicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let extra_param = cx.typaram(span, Ident::new(sym::__S, span), self_bounds, None);
|
||||
impl_generics.params.insert(pointee_param_idx + 1, extra_param);
|
||||
|
||||
// Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`.
|
||||
let gen_args = vec![GenericArg::Type(alt_self_type.clone())];
|
||||
add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
|
||||
add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args.clone());
|
||||
}
|
||||
|
||||
fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: Symbol) -> bool {
|
||||
for bound in predicates {
|
||||
if let ast::WherePredicate::BoundPredicate(bound) = bound
|
||||
&& bound.bounded_ty.kind.is_simple_path().is_some_and(|name| name == pointee)
|
||||
{
|
||||
for bound in &bound.bounds {
|
||||
if is_maybe_sized_bound(bound) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_maybe_sized_bound(bound: &GenericBound) -> bool {
|
||||
if let GenericBound::Trait(
|
||||
trait_ref,
|
||||
TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. },
|
||||
) = bound
|
||||
{
|
||||
is_sized_marker(&trait_ref.trait_ref.path)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool {
|
||||
bounds.iter().any(is_maybe_sized_bound)
|
||||
}
|
||||
|
||||
fn path_segment_is_exact_match(path_segments: &[ast::PathSegment], syms: &[Symbol]) -> bool {
|
||||
path_segments.iter().zip(syms).all(|(segment, &symbol)| segment.ident.name == symbol)
|
||||
}
|
||||
|
||||
fn is_sized_marker(path: &ast::Path) -> bool {
|
||||
const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized];
|
||||
const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized];
|
||||
if path.segments.len() == 4 && path.is_global() {
|
||||
path_segment_is_exact_match(&path.segments[1..], &CORE_UNSIZE)
|
||||
|| path_segment_is_exact_match(&path.segments[1..], &STD_UNSIZE)
|
||||
} else if path.segments.len() == 3 {
|
||||
path_segment_is_exact_match(&path.segments, &CORE_UNSIZE)
|
||||
|| path_segment_is_exact_match(&path.segments, &STD_UNSIZE)
|
||||
} else {
|
||||
*path == sym::Sized
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeSubstitution<'a> {
|
||||
from_name: Symbol,
|
||||
to_ty: &'a ast::Ty,
|
||||
rewritten: bool,
|
||||
}
|
||||
|
||||
impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
|
||||
fn visit_ty(&mut self, ty: &mut P<ast::Ty>) {
|
||||
if let Some(name) = ty.kind.is_simple_path()
|
||||
&& name == self.from_name
|
||||
{
|
||||
**ty = self.to_ty.clone();
|
||||
self.rewritten = true;
|
||||
} else {
|
||||
ast::mut_visit::walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) {
|
||||
match where_predicate {
|
||||
rustc_ast::WherePredicate::BoundPredicate(bound) => {
|
||||
bound
|
||||
.bound_generic_params
|
||||
.flat_map_in_place(|param| self.flat_map_generic_param(param));
|
||||
self.visit_ty(&mut bound.bounded_ty);
|
||||
for bound in &mut bound.bounds {
|
||||
self.visit_param_bound(bound, BoundKind::Bound)
|
||||
}
|
||||
}
|
||||
rustc_ast::WherePredicate::RegionPredicate(_)
|
||||
| rustc_ast::WherePredicate::EqPredicate(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -297,13 +297,6 @@ pub(crate) struct DerivePathArgsValue {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_derive_unsafe_path)]
|
||||
pub(crate) struct DeriveUnsafePath {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_no_default_variant)]
|
||||
#[help]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
|
|||
let mut parser = cx.new_parser_from_tts(stream);
|
||||
|
||||
let ty = parser.parse_ty()?;
|
||||
parser.eat_keyword(sym::is);
|
||||
parser.expect_keyword(sym::is)?;
|
||||
let pat = parser.parse_pat_no_top_alt(None, None)?;
|
||||
|
||||
Ok((ty, pat))
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@ pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaI
|
|||
// All the built-in macro attributes are "words" at the moment.
|
||||
let template = AttributeTemplate { word: true, ..Default::default() };
|
||||
validate_attr::check_builtin_meta_item(
|
||||
&ecx.ecfg.features,
|
||||
&ecx.sess.psess,
|
||||
meta_item,
|
||||
AttrStyle::Outer,
|
||||
name,
|
||||
template,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_codegen_ssa::traits::*;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::config::{FunctionReturn, OptLevel};
|
||||
use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
|
||||
use smallvec::SmallVec;
|
||||
|
|
@ -405,8 +405,33 @@ pub fn from_fn_attrs<'ll, 'tcx>(
|
|||
// And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules.
|
||||
// Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768
|
||||
to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
|
||||
// Need this for AArch64.
|
||||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
|
||||
if llvm_util::get_version() < (19, 0, 0) {
|
||||
// Prior to LLVM 19, branch-target-enforcement was disabled by setting the attribute to
|
||||
// the string "false". Now it is disabled by absence of the attribute.
|
||||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
|
||||
}
|
||||
} else if llvm_util::get_version() >= (19, 0, 0) {
|
||||
// For non-naked functions, set branch protection attributes on aarch64.
|
||||
if let Some(BranchProtection { bti, pac_ret }) =
|
||||
cx.sess().opts.unstable_opts.branch_protection
|
||||
{
|
||||
assert!(cx.sess().target.arch == "aarch64");
|
||||
if bti {
|
||||
to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
|
||||
}
|
||||
if let Some(PacRet { leaf, key }) = pac_ret {
|
||||
to_add.push(llvm::CreateAttrStringValue(
|
||||
cx.llcx,
|
||||
"sign-return-address",
|
||||
if leaf { "all" } else { "non-leaf" },
|
||||
));
|
||||
to_add.push(llvm::CreateAttrStringValue(
|
||||
cx.llcx,
|
||||
"sign-return-address-key",
|
||||
if key == PAuthKey::A { "a_key" } else { "b_key" },
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
|
||||
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
|
||||
|
|
|
|||
|
|
@ -97,7 +97,9 @@ impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> {
|
|||
fn build(mut self: Box<Self>, output: &Path) -> bool {
|
||||
match self.build_with_llvm(output) {
|
||||
Ok(any_members) => any_members,
|
||||
Err(e) => self.sess.dcx().emit_fatal(ArchiveBuildFailure { error: e }),
|
||||
Err(error) => {
|
||||
self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1355,6 +1355,16 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_unpredictable(&mut self, inst: &'ll Value) {
|
||||
unsafe {
|
||||
llvm::LLVMSetMetadata(
|
||||
inst,
|
||||
llvm::MD_unpredictable as c_uint,
|
||||
llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
|
||||
unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}
|
|||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||
use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization};
|
||||
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
|
||||
use rustc_codegen_ssa::mir::place::PlaceRef;
|
||||
use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::BinOp;
|
||||
|
|
@ -203,6 +203,35 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
}
|
||||
sym::unlikely => self
|
||||
.call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(false)]),
|
||||
sym::select_unpredictable => {
|
||||
let cond = args[0].immediate();
|
||||
assert_eq!(args[1].layout, args[2].layout);
|
||||
let select = |bx: &mut Self, true_val, false_val| {
|
||||
let result = bx.select(cond, true_val, false_val);
|
||||
bx.set_unpredictable(&result);
|
||||
result
|
||||
};
|
||||
match (args[1].val, args[2].val) {
|
||||
(OperandValue::Ref(true_val), OperandValue::Ref(false_val)) => {
|
||||
assert!(true_val.llextra.is_none());
|
||||
assert!(false_val.llextra.is_none());
|
||||
assert_eq!(true_val.align, false_val.align);
|
||||
let ptr = select(self, true_val.llval, false_val.llval);
|
||||
let selected =
|
||||
OperandValue::Ref(PlaceValue::new_sized(ptr, true_val.align));
|
||||
selected.store(self, result);
|
||||
return Ok(());
|
||||
}
|
||||
(OperandValue::Immediate(_), OperandValue::Immediate(_))
|
||||
| (OperandValue::Pair(_, _), OperandValue::Pair(_, _)) => {
|
||||
let true_val = args[1].immediate_or_packed_pair(self);
|
||||
let false_val = args[2].immediate_or_packed_pair(self);
|
||||
select(self, true_val, false_val)
|
||||
}
|
||||
(OperandValue::ZeroSized, OperandValue::ZeroSized) => return Ok(()),
|
||||
_ => span_bug!(span, "Incompatible OperandValue for select_unpredictable"),
|
||||
}
|
||||
}
|
||||
sym::catch_unwind => {
|
||||
catch_unwind_intrinsic(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ pub enum MetadataType {
|
|||
MD_nontemporal = 9,
|
||||
MD_mem_parallel_loop_access = 10,
|
||||
MD_nonnull = 11,
|
||||
MD_unpredictable = 15,
|
||||
MD_align = 17,
|
||||
MD_type = 19,
|
||||
MD_vcall_visibility = 28,
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ codegen_ssa_add_native_library = failed to add native library {$library_path}: {
|
|||
|
||||
codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {$error}
|
||||
|
||||
codegen_ssa_archive_build_failure =
|
||||
failed to build archive: {$error}
|
||||
codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error}
|
||||
|
||||
codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering
|
||||
|
||||
|
|
@ -198,7 +197,7 @@ codegen_ssa_read_file = failed to read file: {$message}
|
|||
|
||||
codegen_ssa_repair_vs_build_tools = the Visual Studio build tools may need to be repaired using the Visual Studio installer
|
||||
|
||||
codegen_ssa_rlib_archive_build_failure = failed to build archive from rlib: {$error}
|
||||
codegen_ssa_rlib_archive_build_failure = failed to build archive from rlib at `{$path}`: {$error}
|
||||
|
||||
codegen_ssa_rlib_incompatible_dependency_formats = `{$ty1}` and `{$ty2}` do not have equivalent dependency formats (`{$list1}` vs `{$list2}`)
|
||||
|
||||
|
|
|
|||
|
|
@ -207,7 +207,9 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
|
|||
let sess = self.sess;
|
||||
match self.build_inner(output) {
|
||||
Ok(any_members) => any_members,
|
||||
Err(e) => sess.dcx().emit_fatal(ArchiveBuildFailure { error: e }),
|
||||
Err(error) => {
|
||||
sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,11 +220,7 @@ impl<'a> ArArchiveBuilder<'a> {
|
|||
"gnu" => ArchiveKind::Gnu,
|
||||
"bsd" => ArchiveKind::Bsd,
|
||||
"darwin" => ArchiveKind::Darwin,
|
||||
"coff" => {
|
||||
// FIXME: ar_archive_writer doesn't support COFF archives yet.
|
||||
// https://github.com/rust-lang/ar_archive_writer/issues/9
|
||||
ArchiveKind::Gnu
|
||||
}
|
||||
"coff" => ArchiveKind::Coff,
|
||||
"aix_big" => ArchiveKind::AixBig,
|
||||
kind => {
|
||||
self.sess.dcx().emit_fatal(UnknownArchiveKind { kind });
|
||||
|
|
|
|||
|
|
@ -2911,7 +2911,8 @@ fn add_static_crate(
|
|||
false
|
||||
}),
|
||||
) {
|
||||
sess.dcx().emit_fatal(errors::RlibArchiveBuildFailure { error });
|
||||
sess.dcx()
|
||||
.emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error });
|
||||
}
|
||||
if archive.build(&dst) {
|
||||
link_upstream(&dst);
|
||||
|
|
|
|||
|
|
@ -500,6 +500,7 @@ pub struct UnableToWriteDebuggerVisualizer {
|
|||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_rlib_archive_build_failure)]
|
||||
pub struct RlibArchiveBuildFailure {
|
||||
pub path: PathBuf,
|
||||
pub error: Error,
|
||||
}
|
||||
|
||||
|
|
@ -557,6 +558,7 @@ pub struct UnsupportedLinkSelfContained;
|
|||
#[diag(codegen_ssa_archive_build_failure)]
|
||||
// Public for rustc_codegen_llvm::back::archive
|
||||
pub struct ArchiveBuildFailure {
|
||||
pub path: PathBuf,
|
||||
pub error: std::io::Error,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,10 +88,18 @@ const_eval_exact_div_has_remainder =
|
|||
exact_div: {$a} cannot be divided by {$b} without remainder
|
||||
|
||||
const_eval_expected_inbounds_pointer =
|
||||
expected {$inbounds_size ->
|
||||
[0] a pointer to some allocation
|
||||
[1] a pointer to 1 byte of memory
|
||||
*[x] a pointer to {$inbounds_size} bytes of memory
|
||||
expected a pointer to {$inbounds_size_abs ->
|
||||
[0] some allocation
|
||||
*[x] {$inbounds_size_is_neg ->
|
||||
[false] {$inbounds_size_abs ->
|
||||
[1] 1 byte of memory
|
||||
*[x] {$inbounds_size_abs} bytes of memory
|
||||
}
|
||||
*[true] the end of {$inbounds_size_abs ->
|
||||
[1] 1 byte of memory
|
||||
*[x] {$inbounds_size_abs} bytes of memory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const_eval_extern_static =
|
||||
|
|
@ -243,7 +251,7 @@ const_eval_offset_from_different_allocations =
|
|||
const_eval_offset_from_overflow =
|
||||
`{$name}` called when first pointer is too far ahead of second
|
||||
const_eval_offset_from_test =
|
||||
out-of-bounds `offset_from`
|
||||
out-of-bounds `offset_from` origin
|
||||
const_eval_offset_from_underflow =
|
||||
`{$name}` called when first pointer is too far before second
|
||||
const_eval_offset_from_unsigned_overflow =
|
||||
|
|
@ -269,17 +277,24 @@ const_eval_partial_pointer_copy =
|
|||
const_eval_partial_pointer_overwrite =
|
||||
unable to overwrite parts of a pointer in memory at {$ptr}
|
||||
const_eval_pointer_arithmetic_overflow =
|
||||
overflowing in-bounds pointer arithmetic
|
||||
overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
|
||||
const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic
|
||||
const_eval_pointer_out_of_bounds =
|
||||
{$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} {$ptr_offset_is_neg ->
|
||||
[true] which points to before the beginning of the allocation
|
||||
*[false] {$alloc_size_minus_ptr_offset ->
|
||||
[0] which is at or beyond the end of the allocation of size {$alloc_size ->
|
||||
[1] 1 byte
|
||||
*[x] {$alloc_size} bytes
|
||||
*[false] {$inbounds_size_is_neg ->
|
||||
[true] {$ptr_offset_abs ->
|
||||
[0] which is at the beginning of the allocation
|
||||
*[other] which does not have enough space to the beginning of the allocation
|
||||
}
|
||||
*[false] {$alloc_size_minus_ptr_offset ->
|
||||
[0] which is at or beyond the end of the allocation of size {$alloc_size ->
|
||||
[1] 1 byte
|
||||
*[x] {$alloc_size} bytes
|
||||
}
|
||||
[1] which is only 1 byte from the end of the allocation
|
||||
*[x] which is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
|
||||
}
|
||||
*[x] and there are only {$alloc_size_minus_ptr_offset} bytes starting at that pointer
|
||||
}
|
||||
}
|
||||
const_eval_pointer_use_after_free =
|
||||
|
|
|
|||
|
|
@ -634,10 +634,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
trace!(
|
||||
"visit_projection_elem: place_ref={:?} elem={:?} \
|
||||
context={:?} location={:?}",
|
||||
place_ref,
|
||||
elem,
|
||||
context,
|
||||
location,
|
||||
place_ref, elem, context, location,
|
||||
);
|
||||
|
||||
self.super_projection_elem(place_ref, elem, context, location);
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
match self.ptr_try_get_alloc_id(ptr) {
|
||||
match self.ptr_try_get_alloc_id(ptr, 0) {
|
||||
Ok((alloc_id, offset, _extra)) => {
|
||||
let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
|
||||
|
||||
|
|
@ -510,7 +510,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
|
||||
// If an allocation is created in an another const,
|
||||
// we don't deallocate it.
|
||||
let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?;
|
||||
let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr, 0)?;
|
||||
let is_allocated_in_another_const = matches!(
|
||||
ecx.tcx.try_get_global_alloc(alloc_id),
|
||||
Some(interpret::GlobalAlloc::Memory(_))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
|
||||
use either::Either;
|
||||
use rustc_errors::codes::*;
|
||||
|
|
@ -15,7 +16,7 @@ use rustc_middle::mir::interpret::{
|
|||
use rustc_middle::ty::{self, Mutability, Ty};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::call::AdjustForForeignAbiError;
|
||||
use rustc_target::abi::{Size, WrappingRange};
|
||||
use rustc_target::abi::WrappingRange;
|
||||
|
||||
use crate::interpret::InternKind;
|
||||
|
||||
|
|
@ -575,18 +576,21 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
|
||||
}
|
||||
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => {
|
||||
diag.arg("alloc_size", alloc_size.bytes())
|
||||
.arg("inbounds_size", inbounds_size.bytes())
|
||||
.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
|
||||
diag.arg(
|
||||
"pointer",
|
||||
Pointer::new(
|
||||
Some(CtfeProvenance::from(alloc_id)),
|
||||
Size::from_bytes(ptr_offset as u64),
|
||||
)
|
||||
.to_string(),
|
||||
);
|
||||
diag.arg("alloc_size", alloc_size.bytes());
|
||||
diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
|
||||
diag.arg("pointer", {
|
||||
let mut out = format!("{:?}", alloc_id);
|
||||
if ptr_offset > 0 {
|
||||
write!(out, "+{:#x}", ptr_offset).unwrap();
|
||||
} else if ptr_offset < 0 {
|
||||
write!(out, "-{:#x}", ptr_offset.unsigned_abs()).unwrap();
|
||||
}
|
||||
out
|
||||
});
|
||||
diag.arg("inbounds_size_is_neg", inbounds_size < 0);
|
||||
diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs());
|
||||
diag.arg("ptr_offset_is_neg", ptr_offset < 0);
|
||||
diag.arg("ptr_offset_abs", ptr_offset.unsigned_abs());
|
||||
diag.arg(
|
||||
"alloc_size_minus_ptr_offset",
|
||||
alloc_size.bytes().saturating_sub(ptr_offset as u64),
|
||||
|
|
@ -600,7 +604,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
diag.arg("inbounds_size", inbounds_size.bytes());
|
||||
diag.arg("inbounds_size_is_neg", inbounds_size < 0);
|
||||
diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs());
|
||||
diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
|
||||
}
|
||||
AlignmentCheckFailed(Misalignment { required, has }, msg) => {
|
||||
|
|
|
|||
|
|
@ -560,17 +560,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.frame().body
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sign_extend(&self, value: u128, ty: TyAndLayout<'_>) -> u128 {
|
||||
assert!(ty.abi.is_signed());
|
||||
ty.size.sign_extend(value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn truncate(&self, value: u128, ty: TyAndLayout<'_>) -> u128 {
|
||||
ty.size.truncate(value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_freeze(*self.tcx, self.param_env)
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
} else {
|
||||
(val_bits >> shift_bits) | (val_bits << inv_shift_bits)
|
||||
};
|
||||
let truncated_bits = self.truncate(result_bits, layout_val);
|
||||
let truncated_bits = layout_val.size.truncate(result_bits);
|
||||
let result = Scalar::from_uint(truncated_bits, layout_val.size);
|
||||
self.write_scalar(result, dest)?;
|
||||
}
|
||||
|
|
@ -243,7 +243,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let (a_offset, b_offset, is_addr) = if M::Provenance::OFFSET_IS_ADDR {
|
||||
(a.addr().bytes(), b.addr().bytes(), /*is_addr*/ true)
|
||||
} else {
|
||||
match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) {
|
||||
match (self.ptr_try_get_alloc_id(a, 0), self.ptr_try_get_alloc_id(b, 0)) {
|
||||
(Err(a), Err(b)) => {
|
||||
// Neither pointer points to an allocation, so they are both absolute.
|
||||
(a, b, /*is_addr*/ true)
|
||||
|
|
@ -312,7 +312,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
|
||||
// Check that the memory between them is dereferenceable at all, starting from the
|
||||
// base pointer: `dist` is `a - b`, so it is based on `b`.
|
||||
// origin pointer: `dist` is `a - b`, so it is based on `b`.
|
||||
self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)?;
|
||||
// Then check that this is also dereferenceable from `a`. This ensures that they are
|
||||
// derived from the same allocation.
|
||||
|
|
@ -580,13 +580,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ptr: Pointer<Option<M::Provenance>>,
|
||||
offset_bytes: i64,
|
||||
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
|
||||
// We first compute the pointer with overflow checks, to get a specific error for when it
|
||||
// overflows (though technically this is redundant with the following inbounds check).
|
||||
let result = ptr.signed_offset(offset_bytes, self)?;
|
||||
// The offset must be in bounds starting from `ptr`.
|
||||
self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?;
|
||||
// Done.
|
||||
Ok(result)
|
||||
// This also implies that there is no overflow, so we are done.
|
||||
Ok(ptr.wrapping_signed_offset(offset_bytes, self))
|
||||
}
|
||||
|
||||
/// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
|
||||
|
|
|
|||
|
|
@ -321,15 +321,21 @@ pub trait Machine<'tcx>: Sized {
|
|||
ptr: Pointer<Self::Provenance>,
|
||||
) -> InterpResult<'tcx>;
|
||||
|
||||
/// Convert a pointer with provenance into an allocation-offset pair
|
||||
/// and extra provenance info.
|
||||
/// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.
|
||||
/// `size` says how many bytes of memory are expected at that pointer. The *sign* of `size` can
|
||||
/// be used to disambiguate situations where a wildcard pointer sits right in between two
|
||||
/// allocations.
|
||||
///
|
||||
/// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`.
|
||||
/// If `ptr.provenance.get_alloc_id()` is `Some(p)`, the returned `AllocId` must be `p`.
|
||||
/// The resulting `AllocId` will just be used for that one step and the forgotten again
|
||||
/// (i.e., we'll never turn the data returned here back into a `Pointer` that might be
|
||||
/// stored in machine state).
|
||||
///
|
||||
/// When this fails, that means the pointer does not point to a live allocation.
|
||||
fn ptr_get_alloc(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
ptr: Pointer<Self::Provenance>,
|
||||
size: i64,
|
||||
) -> Option<(AllocId, Size, Self::ProvenanceExtra)>;
|
||||
|
||||
/// Called to adjust global allocations to the Provenance and AllocExtra of this machine.
|
||||
|
|
@ -658,6 +664,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
|
|||
fn ptr_get_alloc(
|
||||
_ecx: &InterpCx<$tcx, Self>,
|
||||
ptr: Pointer<CtfeProvenance>,
|
||||
_size: i64,
|
||||
) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
|
||||
// We know `offset` is relative to the allocation, so we can use `into_parts`.
|
||||
let (prov, offset) = ptr.into_parts();
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
new_align: Align,
|
||||
kind: MemoryKind<M::MemoryKind>,
|
||||
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
|
||||
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
|
||||
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
if offset.bytes() != 0 {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_realloc_or_alloc_with_offset,
|
||||
|
|
@ -291,7 +291,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
old_size_and_align: Option<(Size, Align)>,
|
||||
kind: MemoryKind<M::MemoryKind>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr)?;
|
||||
let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
trace!("deallocating: {alloc_id:?}");
|
||||
|
||||
if offset.bytes() != 0 {
|
||||
|
|
@ -383,6 +383,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> {
|
||||
let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
|
||||
self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
|
|
@ -404,6 +405,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
size: Size,
|
||||
msg: CheckInAllocMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
|
||||
self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
|
|
@ -420,19 +422,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
size: i64,
|
||||
msg: CheckInAllocMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
if let Ok(size) = u64::try_from(size) {
|
||||
self.check_ptr_access(ptr, Size::from_bytes(size), msg)
|
||||
} else {
|
||||
// Compute the pointer at the beginning of the range, and do the standard
|
||||
// dereferenceability check from there.
|
||||
let begin_ptr = ptr.wrapping_signed_offset(size, self);
|
||||
self.check_ptr_access(begin_ptr, Size::from_bytes(size.unsigned_abs()), msg)
|
||||
}
|
||||
self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
|
||||
/// to the allocation it points to. Supports both shared and mutable references, as the actual
|
||||
/// checking is offloaded to a helper closure.
|
||||
/// checking is offloaded to a helper closure. Supports signed sizes for checks "to the left" of
|
||||
/// a pointer.
|
||||
///
|
||||
/// `alloc_size` will only get called for non-zero-sized accesses.
|
||||
///
|
||||
|
|
@ -440,7 +440,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
fn check_and_deref_ptr<T>(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
size: i64,
|
||||
msg: CheckInAllocMsg,
|
||||
alloc_size: impl FnOnce(
|
||||
AllocId,
|
||||
|
|
@ -449,24 +449,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
) -> InterpResult<'tcx, (Size, Align, T)>,
|
||||
) -> InterpResult<'tcx, Option<T>> {
|
||||
// Everything is okay with size 0.
|
||||
if size.bytes() == 0 {
|
||||
if size == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(match self.ptr_try_get_alloc_id(ptr) {
|
||||
Ok(match self.ptr_try_get_alloc_id(ptr, size) {
|
||||
Err(addr) => {
|
||||
// We couldn't get a proper allocation.
|
||||
throw_ub!(DanglingIntPointer { addr, inbounds_size: size, msg });
|
||||
}
|
||||
Ok((alloc_id, offset, prov)) => {
|
||||
let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
|
||||
// Test bounds.
|
||||
// It is sufficient to check this for the end pointer. Also check for overflow!
|
||||
if offset.checked_add(size, &self.tcx).is_none_or(|end| end > alloc_size) {
|
||||
let offset = offset.bytes();
|
||||
// Compute absolute begin and end of the range.
|
||||
let (begin, end) = if size >= 0 {
|
||||
(Some(offset), offset.checked_add(size as u64))
|
||||
} else {
|
||||
(offset.checked_sub(size.unsigned_abs()), Some(offset))
|
||||
};
|
||||
// Ensure both are within bounds.
|
||||
let in_bounds = begin.is_some() && end.is_some_and(|e| e <= alloc_size.bytes());
|
||||
if !in_bounds {
|
||||
throw_ub!(PointerOutOfBounds {
|
||||
alloc_id,
|
||||
alloc_size,
|
||||
ptr_offset: self.target_usize_to_isize(offset.bytes()),
|
||||
ptr_offset: self.sign_extend_to_target_isize(offset),
|
||||
inbounds_size: size,
|
||||
msg,
|
||||
})
|
||||
|
|
@ -498,7 +505,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn offset_misalignment(offset: u64, align: Align) -> Option<Misalignment> {
|
||||
fn is_offset_misaligned(offset: u64, align: Align) -> Option<Misalignment> {
|
||||
if offset % align.bytes() == 0 {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -508,8 +515,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
match self.ptr_try_get_alloc_id(ptr) {
|
||||
Err(addr) => offset_misalignment(addr, align),
|
||||
match self.ptr_try_get_alloc_id(ptr, 0) {
|
||||
Err(addr) => is_offset_misaligned(addr, align),
|
||||
Ok((alloc_id, offset, _prov)) => {
|
||||
let (_size, alloc_align, kind) = self.get_alloc_info(alloc_id);
|
||||
if let Some(misalign) =
|
||||
|
|
@ -517,14 +524,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
{
|
||||
Some(misalign)
|
||||
} else if M::Provenance::OFFSET_IS_ADDR {
|
||||
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
||||
offset_misalignment(ptr.addr().bytes(), align)
|
||||
is_offset_misaligned(ptr.addr().bytes(), align)
|
||||
} else {
|
||||
// Check allocation alignment and offset alignment.
|
||||
if alloc_align.bytes() < align.bytes() {
|
||||
Some(Misalignment { has: alloc_align, required: align })
|
||||
} else {
|
||||
offset_misalignment(offset.bytes(), align)
|
||||
is_offset_misaligned(offset.bytes(), align)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -660,9 +666,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
size: Size,
|
||||
) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
|
||||
{
|
||||
let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
|
||||
let ptr_and_alloc = self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
size_i64,
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let alloc = self.get_alloc_raw(alloc_id)?;
|
||||
|
|
@ -673,7 +680,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// accesses. That means we cannot rely on the closure above or the `Some` branch below. We
|
||||
// do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
|
||||
if !self.memory.validation_in_progress.get() {
|
||||
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) {
|
||||
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) {
|
||||
M::before_alloc_read(self, alloc_id)?;
|
||||
}
|
||||
}
|
||||
|
|
@ -894,7 +901,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
|
||||
trace!("get_ptr_fn({:?})", ptr);
|
||||
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
|
||||
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
if offset.bytes() != 0 {
|
||||
throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))
|
||||
}
|
||||
|
|
@ -910,7 +917,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
expected_trait: Option<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>,
|
||||
) -> InterpResult<'tcx, Ty<'tcx>> {
|
||||
trace!("get_ptr_vtable({:?})", ptr);
|
||||
let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
|
||||
let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
if offset.bytes() != 0 {
|
||||
throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
|
||||
}
|
||||
|
|
@ -1391,7 +1398,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Err(_) => {
|
||||
// Can only happen during CTFE.
|
||||
let ptr = scalar.to_pointer(self)?;
|
||||
match self.ptr_try_get_alloc_id(ptr) {
|
||||
match self.ptr_try_get_alloc_id(ptr, 0) {
|
||||
Ok((alloc_id, offset, _)) => {
|
||||
let (size, _align, _kind) = self.get_alloc_info(alloc_id);
|
||||
// If the pointer is out-of-bounds, it may be null.
|
||||
|
|
@ -1407,6 +1414,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// Turning a "maybe pointer" into a proper pointer (and some information
|
||||
/// about where it points), or an absolute address.
|
||||
///
|
||||
/// `size` says how many bytes of memory are expected at that pointer. This is largely only used
|
||||
/// for error messages; however, the *sign* of `size` can be used to disambiguate situations
|
||||
/// where a wildcard pointer sits right in between two allocations.
|
||||
/// It is almost always okay to just set the size to 0; this will be treated like a positive size
|
||||
/// for handling wildcard pointers.
|
||||
///
|
||||
/// The result must be used immediately; it is not allowed to convert
|
||||
/// the returned data back into a `Pointer` and store that in machine state.
|
||||
/// (In fact that's not even possible since `M::ProvenanceExtra` is generic and
|
||||
|
|
@ -1414,9 +1427,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
pub fn ptr_try_get_alloc_id(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: i64,
|
||||
) -> Result<(AllocId, Size, M::ProvenanceExtra), u64> {
|
||||
match ptr.into_pointer_or_addr() {
|
||||
Ok(ptr) => match M::ptr_get_alloc(self, ptr) {
|
||||
Ok(ptr) => match M::ptr_get_alloc(self, ptr, size) {
|
||||
Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)),
|
||||
None => {
|
||||
assert!(M::Provenance::OFFSET_IS_ADDR);
|
||||
|
|
@ -1430,6 +1444,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
/// Turning a "maybe pointer" into a proper pointer (and some information about where it points).
|
||||
///
|
||||
/// `size` says how many bytes of memory are expected at that pointer. This is largely only used
|
||||
/// for error messages; however, the *sign* of `size` can be used to disambiguate situations
|
||||
/// where a wildcard pointer sits right in between two allocations.
|
||||
/// It is almost always okay to just set the size to 0; this will be treated like a positive size
|
||||
/// for handling wildcard pointers.
|
||||
///
|
||||
/// The result must be used immediately; it is not allowed to convert
|
||||
/// the returned data back into a `Pointer` and store that in machine state.
|
||||
/// (In fact that's not even possible since `M::ProvenanceExtra` is generic and
|
||||
|
|
@ -1438,12 +1458,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
pub fn ptr_get_alloc_id(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: i64,
|
||||
) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> {
|
||||
self.ptr_try_get_alloc_id(ptr).map_err(|offset| {
|
||||
self.ptr_try_get_alloc_id(ptr, size).map_err(|offset| {
|
||||
err_ub!(DanglingIntPointer {
|
||||
addr: offset,
|
||||
// We don't know the actually required size.
|
||||
inbounds_size: Size::ZERO,
|
||||
inbounds_size: size,
|
||||
msg: CheckInAllocMsg::InboundsTest
|
||||
})
|
||||
.into()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_middle::{bug, mir, span_bug};
|
|||
use rustc_span::symbol::sym;
|
||||
use tracing::trace;
|
||||
|
||||
use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
|
||||
use super::{throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
|
||||
|
|
@ -298,17 +298,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Pointer ops that are always supported.
|
||||
Offset => {
|
||||
let ptr = left.to_scalar().to_pointer(self)?;
|
||||
let offset_count = right.to_scalar().to_target_isize(self)?;
|
||||
let pointee_ty = left.layout.ty.builtin_deref(true).unwrap();
|
||||
let pointee_layout = self.layout_of(pointee_ty)?;
|
||||
assert!(pointee_layout.abi.is_sized());
|
||||
|
||||
// We cannot overflow i64 as a type's size must be <= isize::MAX.
|
||||
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
|
||||
// The computed offset, in bytes, must not overflow an isize.
|
||||
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
|
||||
// the difference to be noticeable.
|
||||
let offset_bytes =
|
||||
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
|
||||
let pointee_size = i64::try_from(pointee_layout.size.bytes()).unwrap();
|
||||
let pointee_size = ImmTy::from_int(pointee_size, right.layout);
|
||||
// Multiply element size and element count.
|
||||
let (val, overflowed) = self
|
||||
.binary_op(mir::BinOp::MulWithOverflow, right, &pointee_size)?
|
||||
.to_scalar_pair();
|
||||
// This must not overflow.
|
||||
if overflowed.to_bool()? {
|
||||
throw_ub!(PointerArithOverflow)
|
||||
}
|
||||
|
||||
let offset_bytes = val.to_target_isize(self)?;
|
||||
let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
|
||||
Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout))
|
||||
}
|
||||
|
|
@ -329,11 +335,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
trace!(
|
||||
"Running binary op {:?}: {:?} ({}), {:?} ({})",
|
||||
bin_op,
|
||||
*left,
|
||||
left.layout.ty,
|
||||
*right,
|
||||
right.layout.ty
|
||||
bin_op, *left, left.layout.ty, *right, right.layout.ty
|
||||
);
|
||||
|
||||
match left.layout.ty.kind() {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size};
|
|||
use tracing::{instrument, trace};
|
||||
|
||||
use super::{
|
||||
alloc_range, mir_assign_valid_types, throw_ub, AllocRef, AllocRefMut, CheckAlignMsg,
|
||||
CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment,
|
||||
OffsetMode, OpTy, Operand, Pointer, PointerArithmetic, Projectable, Provenance, Readable,
|
||||
Scalar,
|
||||
alloc_range, mir_assign_valid_types, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance,
|
||||
ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy,
|
||||
Operand, Pointer, Projectable, Provenance, Readable, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
|
|
@ -85,9 +84,6 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
|||
!meta.has_meta() || self.meta.has_meta(),
|
||||
"cannot use `offset_with_meta` to add metadata to a place"
|
||||
);
|
||||
if offset > ecx.data_layout().max_size_of_val() {
|
||||
throw_ub!(PointerArithOverflow);
|
||||
}
|
||||
let ptr = match mode {
|
||||
OffsetMode::Inbounds => {
|
||||
ecx.ptr_offset_inbounds(self.ptr, offset.bytes().try_into().unwrap())?
|
||||
|
|
@ -289,10 +285,8 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
|||
// projections are type-checked and bounds-checked.
|
||||
assert!(offset + layout.size <= self.layout.size);
|
||||
|
||||
let new_offset = Size::from_bytes(
|
||||
ecx.data_layout()
|
||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?,
|
||||
);
|
||||
// Size `+`, ensures no overflow.
|
||||
let new_offset = old_offset.unwrap_or(Size::ZERO) + offset;
|
||||
|
||||
PlaceTy {
|
||||
place: Place::Local { local, offset: Some(new_offset), locals_addr },
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// of the first element.
|
||||
let elem_size = first.layout.size;
|
||||
let first_ptr = first.ptr();
|
||||
let rest_ptr = first_ptr.offset(elem_size, self)?;
|
||||
let rest_ptr = first_ptr.wrapping_offset(elem_size, self);
|
||||
// No alignment requirement since `copy_op` above already checked it.
|
||||
self.mem_copy_repeatedly(
|
||||
first_ptr,
|
||||
|
|
|
|||
|
|
@ -431,8 +431,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
} else {
|
||||
trace!(
|
||||
"check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}",
|
||||
caller_abi,
|
||||
callee_abi
|
||||
caller_abi, callee_abi
|
||||
);
|
||||
return Ok(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
};
|
||||
// Proceed recursively even for ZST, no reason to skip them!
|
||||
// `!` is a ZST and we want to validate it.
|
||||
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
|
||||
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr(), 0) {
|
||||
let mut skip_recursive_check = false;
|
||||
if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::default_hash_types)]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
#![cfg_attr(not(parallel_compiler), feature(cell_leak))]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
|
|
|
|||
|
|
@ -909,6 +909,15 @@ pub fn version_at_macro_invocation(
|
|||
) {
|
||||
let verbose = matches.opt_present("verbose");
|
||||
|
||||
let mut version = version;
|
||||
let mut release = release;
|
||||
let tmp;
|
||||
if let Ok(force_version) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING") {
|
||||
tmp = force_version;
|
||||
version = &tmp;
|
||||
release = &tmp;
|
||||
}
|
||||
|
||||
safe_println!("{binary} {version}");
|
||||
|
||||
if verbose {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ use rustc_ast::tokenstream::{
|
|||
use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId};
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_feature::{Features, ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES};
|
||||
use rustc_feature::{
|
||||
AttributeSafety, Features, ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES,
|
||||
};
|
||||
use rustc_lint_defs::BuiltinLintDiag;
|
||||
use rustc_parse::validate_attr;
|
||||
use rustc_session::parse::feature_err;
|
||||
|
|
@ -263,6 +265,13 @@ impl<'a> StripUnconfigured<'a> {
|
|||
/// is in the original source file. Gives a compiler error if the syntax of
|
||||
/// the attribute is incorrect.
|
||||
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
|
||||
validate_attr::check_attribute_safety(
|
||||
self.features.unwrap_or(&Features::default()),
|
||||
&self.sess.psess,
|
||||
AttributeSafety::Normal,
|
||||
&cfg_attr,
|
||||
);
|
||||
|
||||
let Some((cfg_predicate, expanded_attrs)) =
|
||||
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
|
||||
else {
|
||||
|
|
@ -385,6 +394,13 @@ impl<'a> StripUnconfigured<'a> {
|
|||
return (true, None);
|
||||
}
|
||||
};
|
||||
|
||||
validate_attr::deny_builtin_meta_unsafety(
|
||||
self.features.unwrap_or(&Features::default()),
|
||||
&self.sess.psess,
|
||||
&meta_item,
|
||||
);
|
||||
|
||||
(
|
||||
parse_cfg(&meta_item, self.sess).map_or(true, |meta_item| {
|
||||
attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ declare_features! (
|
|||
/// Allows `c"foo"` literals.
|
||||
(accepted, c_str_literals, "1.77.0", Some(105723)),
|
||||
/// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries and treat `extern "C" fn` as nounwind.
|
||||
(accepted, c_unwind, "CURRENT_RUSTC_VERSION", Some(74990)),
|
||||
(accepted, c_unwind, "1.81.0", Some(74990)),
|
||||
/// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`.
|
||||
(accepted, cfg_attr_multi, "1.33.0", Some(54881)),
|
||||
/// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests.
|
||||
|
|
@ -238,7 +238,7 @@ declare_features! (
|
|||
/// Allows `let...else` statements.
|
||||
(accepted, let_else, "1.65.0", Some(87335)),
|
||||
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
|
||||
(accepted, lint_reasons, "CURRENT_RUSTC_VERSION", Some(54503)),
|
||||
(accepted, lint_reasons, "1.81.0", Some(54503)),
|
||||
/// Allows `break {expr}` with a value inside `loop`s.
|
||||
(accepted, loop_break_value, "1.19.0", Some(37339)),
|
||||
/// Allows use of `?` as the Kleene "at most one" operator in macros.
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ declare_features! (
|
|||
(removed, unwind_attributes, "1.56.0", Some(58760), Some("use the C-unwind ABI instead")),
|
||||
(removed, visible_private_types, "1.0.0", None, None),
|
||||
/// Allows `extern "wasm" fn`
|
||||
(removed, wasm_abi, "CURRENT_RUSTC_VERSION", Some(83788),
|
||||
(removed, wasm_abi, "1.81.0", Some(83788),
|
||||
Some("non-standard wasm ABI is no longer supported")),
|
||||
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
|
||||
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
|
||||
|
|
|
|||
|
|
@ -518,7 +518,7 @@ declare_features! (
|
|||
/// Give access to additional metadata about declarative macro meta-variables.
|
||||
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
|
||||
/// Provides a way to concatenate identifiers using metavariable expressions.
|
||||
(unstable, macro_metavar_expr_concat, "CURRENT_RUSTC_VERSION", Some(124225)),
|
||||
(unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)),
|
||||
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
|
||||
(unstable, marker_trait_attr, "1.30.0", Some(29864)),
|
||||
/// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are
|
||||
|
|
@ -561,11 +561,11 @@ declare_features! (
|
|||
/// Allows using enums in offset_of!
|
||||
(unstable, offset_of_enum, "1.75.0", Some(120141)),
|
||||
/// Allows using fields with slice type in offset_of!
|
||||
(unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)),
|
||||
(unstable, offset_of_slice, "1.81.0", Some(126151)),
|
||||
/// Allows using `#[optimize(X)]`.
|
||||
(unstable, optimize_attribute, "1.34.0", Some(54882)),
|
||||
/// Allows specifying nop padding on functions for dynamic patching.
|
||||
(unstable, patchable_function_entry, "CURRENT_RUSTC_VERSION", Some(123115)),
|
||||
(unstable, patchable_function_entry, "1.81.0", Some(123115)),
|
||||
/// Allows postfix match `expr.match { ... }`
|
||||
(unstable, postfix_match, "1.79.0", Some(121618)),
|
||||
/// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.
|
||||
|
|
@ -577,7 +577,7 @@ declare_features! (
|
|||
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
|
||||
(incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
|
||||
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
|
||||
(incomplete, ref_pat_eat_one_layer_2024_structural, "CURRENT_RUSTC_VERSION", Some(123076)),
|
||||
(incomplete, ref_pat_eat_one_layer_2024_structural, "1.81.0", Some(123076)),
|
||||
/// Allows using the `#[register_tool]` attribute.
|
||||
(unstable, register_tool, "1.41.0", Some(66079)),
|
||||
/// Allows the `#[repr(i128)]` attribute for enums.
|
||||
|
|
@ -643,9 +643,9 @@ declare_features! (
|
|||
/// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
|
||||
(unstable, used_with_arg, "1.60.0", Some(93798)),
|
||||
/// Allows use of x86 `AMX` target-feature attributes and intrinsics
|
||||
(unstable, x86_amx_intrinsics, "CURRENT_RUSTC_VERSION", Some(126622)),
|
||||
(unstable, x86_amx_intrinsics, "1.81.0", Some(126622)),
|
||||
/// Allows use of the `xop` target-feature
|
||||
(unstable, xop_target_feature, "CURRENT_RUSTC_VERSION", Some(127208)),
|
||||
(unstable, xop_target_feature, "1.81.0", Some(127208)),
|
||||
/// Allows `do yeet` expressions
|
||||
(unstable, yeet_expr, "1.62.0", Some(96373)),
|
||||
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
|
||||
|
|
|
|||
|
|
@ -341,8 +341,7 @@ hir_analysis_must_implement_not_function_span_note = required by this annotation
|
|||
|
||||
hir_analysis_must_implement_one_of_attribute = the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
|
||||
|
||||
hir_analysis_not_supported_delegation =
|
||||
{$descr} is not supported yet
|
||||
hir_analysis_not_supported_delegation = {$descr}
|
||||
.label = callee defined here
|
||||
|
||||
hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
|
|||
| sym::type_id
|
||||
| sym::likely
|
||||
| sym::unlikely
|
||||
| sym::select_unpredictable
|
||||
| sym::ptr_guaranteed_cmp
|
||||
| sym::minnumf16
|
||||
| sym::minnumf32
|
||||
|
|
@ -488,6 +489,7 @@ pub fn check_intrinsic_type(
|
|||
sym::assume => (0, 0, vec![tcx.types.bool], tcx.types.unit),
|
||||
sym::likely => (0, 0, vec![tcx.types.bool], tcx.types.bool),
|
||||
sym::unlikely => (0, 0, vec![tcx.types.bool], tcx.types.bool),
|
||||
sym::select_unpredictable => (1, 0, vec![tcx.types.bool, param(0), param(0)], param(0)),
|
||||
|
||||
sym::read_via_copy => (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
|
||||
sym::write_via_move => {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use rustc_session::lint;
|
|||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::delegation::inherit_generics_for_delegation_item;
|
||||
use crate::middle::resolve_bound_vars as rbv;
|
||||
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
|
|
@ -53,6 +54,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
};
|
||||
}
|
||||
|
||||
// For a delegation item inherit generics from callee.
|
||||
if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id)
|
||||
&& let Some(generics) = inherit_generics_for_delegation_item(tcx, def_id, sig_id)
|
||||
{
|
||||
return generics;
|
||||
}
|
||||
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
|
||||
let node = tcx.hir_node(hir_id);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_span::{Span, DUMMY_SP};
|
|||
use crate::bounds::Bounds;
|
||||
use crate::collect::ItemCtxt;
|
||||
use crate::constrained_generic_params as cgp;
|
||||
use crate::delegation::inherit_predicates_for_delegation_item;
|
||||
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason};
|
||||
|
||||
/// Returns a list of all type predicates (explicit and implicit) for the definition with
|
||||
|
|
@ -114,6 +115,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
None => {}
|
||||
}
|
||||
|
||||
// For a delegation item inherit predicates from callee.
|
||||
if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id)
|
||||
&& let Some(predicates) = inherit_predicates_for_delegation_item(tcx, def_id, sig_id)
|
||||
{
|
||||
return predicates;
|
||||
}
|
||||
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let node = tcx.hir_node(hir_id);
|
||||
|
||||
|
|
|
|||
259
compiler/rustc_hir_analysis/src/delegation.rs
Normal file
259
compiler/rustc_hir_analysis/src/delegation.rs
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use rustc_type_ir::visit::TypeVisitableExt;
|
||||
|
||||
type RemapTable = FxHashMap<u32, u32>;
|
||||
|
||||
struct ParamIndexRemapper<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
remap_table: RemapTable,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamIndexRemapper<'tcx> {
|
||||
fn cx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if !ty.has_param() {
|
||||
return ty;
|
||||
}
|
||||
|
||||
if let ty::Param(param) = ty.kind()
|
||||
&& let Some(index) = self.remap_table.get(¶m.index)
|
||||
{
|
||||
return Ty::new_param(self.tcx, *index, param.name);
|
||||
}
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
if let ty::ReEarlyParam(param) = r.kind()
|
||||
&& let Some(index) = self.remap_table.get(¶m.index).copied()
|
||||
{
|
||||
return ty::Region::new_early_param(
|
||||
self.tcx,
|
||||
ty::EarlyParamRegion { index, name: param.name },
|
||||
);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
if let ty::ConstKind::Param(param) = ct.kind()
|
||||
&& let Some(idx) = self.remap_table.get(¶m.index)
|
||||
{
|
||||
let param = ty::ParamConst::new(*idx, param.name);
|
||||
return ty::Const::new_param(self.tcx, param);
|
||||
}
|
||||
ct.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum FnKind {
|
||||
Free,
|
||||
AssocInherentImpl,
|
||||
AssocTrait,
|
||||
AssocTraitImpl,
|
||||
}
|
||||
|
||||
fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind {
|
||||
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn));
|
||||
|
||||
let parent = tcx.parent(def_id);
|
||||
match tcx.def_kind(parent) {
|
||||
DefKind::Trait => FnKind::AssocTrait,
|
||||
DefKind::Impl { of_trait: true } => FnKind::AssocTraitImpl,
|
||||
DefKind::Impl { of_trait: false } => FnKind::AssocInherentImpl,
|
||||
_ => FnKind::Free,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_generic_args<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
sig_id: DefId,
|
||||
) -> ty::GenericArgsRef<'tcx> {
|
||||
let caller_generics = tcx.generics_of(def_id);
|
||||
let callee_generics = tcx.generics_of(sig_id);
|
||||
|
||||
let caller_kind = fn_kind(tcx, def_id.into());
|
||||
let callee_kind = fn_kind(tcx, sig_id);
|
||||
// FIXME(fn_delegation): Support generics on associated delegation items.
|
||||
// Error will be reported in `check_constraints`.
|
||||
match (caller_kind, callee_kind) {
|
||||
(FnKind::Free, _) => {
|
||||
// Lifetime parameters must be declared before type and const parameters.
|
||||
// Therefore, When delegating from a free function to a associated function,
|
||||
// generic parameters need to be reordered:
|
||||
//
|
||||
// trait Trait<'a, A> {
|
||||
// fn foo<'b, B>(...) {...}
|
||||
// }
|
||||
//
|
||||
// reuse Trait::foo;
|
||||
// desugaring:
|
||||
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
|
||||
// Trait::foo(...)
|
||||
// }
|
||||
let mut remap_table = RemapTable::default();
|
||||
for caller_param in &caller_generics.own_params {
|
||||
let callee_index =
|
||||
callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap();
|
||||
remap_table.insert(callee_index, caller_param.index);
|
||||
}
|
||||
let mut folder = ParamIndexRemapper { tcx, remap_table };
|
||||
ty::GenericArgs::identity_for_item(tcx, sig_id).fold_with(&mut folder)
|
||||
}
|
||||
// FIXME(fn_delegation): Only `Self` param supported here.
|
||||
(FnKind::AssocTraitImpl, FnKind::AssocTrait)
|
||||
| (FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
|
||||
let parent = tcx.parent(def_id.into());
|
||||
let self_ty = tcx.type_of(parent).instantiate_identity();
|
||||
let generic_self_ty = ty::GenericArg::from(self_ty);
|
||||
tcx.mk_args_from_iter(std::iter::once(generic_self_ty))
|
||||
}
|
||||
_ => ty::GenericArgs::identity_for_item(tcx, sig_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
sig_id: DefId,
|
||||
) -> Option<ty::Generics> {
|
||||
// FIXME(fn_delegation): Support generics on associated delegation items.
|
||||
// Error will be reported in `check_constraints`.
|
||||
if fn_kind(tcx, def_id.into()) != FnKind::Free {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut own_params = vec![];
|
||||
|
||||
let callee_generics = tcx.generics_of(sig_id);
|
||||
if let Some(parent_sig_id) = callee_generics.parent {
|
||||
let parent_sig_generics = tcx.generics_of(parent_sig_id);
|
||||
own_params.append(&mut parent_sig_generics.own_params.clone());
|
||||
}
|
||||
own_params.append(&mut callee_generics.own_params.clone());
|
||||
|
||||
// Lifetimes go first.
|
||||
own_params.sort_by_key(|key| key.kind.is_ty_or_const());
|
||||
|
||||
for (idx, param) in own_params.iter_mut().enumerate() {
|
||||
param.index = idx as u32;
|
||||
// Default parameters are not inherited: they are not allowed
|
||||
// in fn's.
|
||||
if let ty::GenericParamDefKind::Type { has_default, .. }
|
||||
| ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
|
||||
{
|
||||
*has_default = false;
|
||||
}
|
||||
}
|
||||
|
||||
let param_def_id_to_index =
|
||||
own_params.iter().map(|param| (param.def_id, param.index)).collect();
|
||||
|
||||
Some(ty::Generics {
|
||||
parent: None,
|
||||
parent_count: 0,
|
||||
own_params,
|
||||
param_def_id_to_index,
|
||||
has_self: false,
|
||||
has_late_bound_regions: callee_generics.has_late_bound_regions,
|
||||
host_effect_index: callee_generics.host_effect_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
sig_id: DefId,
|
||||
) -> Option<ty::GenericPredicates<'tcx>> {
|
||||
// FIXME(fn_delegation): Support generics on associated delegation items.
|
||||
// Error will be reported in `check_constraints`.
|
||||
if fn_kind(tcx, def_id.into()) != FnKind::Free {
|
||||
return None;
|
||||
}
|
||||
|
||||
let callee_predicates = tcx.predicates_of(sig_id);
|
||||
let args = create_generic_args(tcx, def_id, sig_id);
|
||||
|
||||
let mut preds = vec![];
|
||||
if let Some(parent_id) = callee_predicates.parent {
|
||||
preds.extend(tcx.predicates_of(parent_id).instantiate_own(tcx, args));
|
||||
}
|
||||
preds.extend(callee_predicates.instantiate_own(tcx, args));
|
||||
|
||||
Some(ty::GenericPredicates {
|
||||
parent: None,
|
||||
predicates: tcx.arena.alloc_from_iter(preds),
|
||||
effects_min_tys: ty::List::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn check_constraints<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
sig_id: DefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let mut ret = Ok(());
|
||||
|
||||
let mut emit = |descr| {
|
||||
ret = Err(tcx.dcx().emit_err(crate::errors::UnsupportedDelegation {
|
||||
span: tcx.def_span(def_id),
|
||||
descr,
|
||||
callee_span: tcx.def_span(sig_id),
|
||||
}));
|
||||
};
|
||||
|
||||
if tcx.has_host_param(sig_id) {
|
||||
emit("delegation to a function with effect parameter is not supported yet");
|
||||
}
|
||||
|
||||
if let Some(local_sig_id) = sig_id.as_local()
|
||||
&& tcx.hir().opt_delegation_sig_id(local_sig_id).is_some()
|
||||
{
|
||||
emit("recursive delegation is not supported yet");
|
||||
}
|
||||
|
||||
if fn_kind(tcx, def_id.into()) != FnKind::Free {
|
||||
let sig_generics = tcx.generics_of(sig_id);
|
||||
let parent = tcx.parent(def_id.into());
|
||||
let parent_generics = tcx.generics_of(parent);
|
||||
|
||||
let parent_has_self = parent_generics.has_self as usize;
|
||||
let sig_has_self = sig_generics.has_self as usize;
|
||||
|
||||
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_has_self {
|
||||
emit("early bound generics are not supported for associated delegation items");
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) fn inherit_sig_for_delegation_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) -> &'tcx [Ty<'tcx>] {
|
||||
let sig_id = tcx.hir().delegation_sig_id(def_id);
|
||||
let caller_sig = tcx.fn_sig(sig_id);
|
||||
if let Err(err) = check_constraints(tcx, def_id, sig_id) {
|
||||
let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1;
|
||||
let err_type = Ty::new_error(tcx, err);
|
||||
return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type));
|
||||
}
|
||||
let args = create_generic_args(tcx, def_id, sig_id);
|
||||
|
||||
// Bound vars are also inherited from `sig_id`.
|
||||
// They will be rebound later in `lower_fn_ty`.
|
||||
let sig = caller_sig.instantiate(tcx, args).skip_binder();
|
||||
let sig_iter = sig.inputs().iter().cloned().chain(std::iter::once(sig.output()));
|
||||
tcx.arena.alloc_from_iter(sig_iter)
|
||||
}
|
||||
|
|
@ -1575,7 +1575,7 @@ pub struct RefOfMutStatic<'a> {
|
|||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_not_supported_delegation)]
|
||||
pub struct NotSupportedDelegation<'a> {
|
||||
pub struct UnsupportedDelegation<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub descr: &'a str,
|
||||
|
|
|
|||
|
|
@ -2007,93 +2007,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
self.lower_ty_common(hir_ty, false, true)
|
||||
}
|
||||
|
||||
fn check_delegation_constraints(&self, sig_id: DefId, span: Span, emit: bool) -> bool {
|
||||
let mut error_occured = false;
|
||||
let sig_span = self.tcx().def_span(sig_id);
|
||||
let mut try_emit = |descr| {
|
||||
if emit {
|
||||
self.dcx().emit_err(crate::errors::NotSupportedDelegation {
|
||||
span,
|
||||
descr,
|
||||
callee_span: sig_span,
|
||||
});
|
||||
}
|
||||
error_occured = true;
|
||||
};
|
||||
|
||||
if let Some(node) = self.tcx().hir().get_if_local(sig_id)
|
||||
&& let Some(decl) = node.fn_decl()
|
||||
&& let hir::FnRetTy::Return(ty) = decl.output
|
||||
&& let hir::TyKind::InferDelegation(_, _) = ty.kind
|
||||
{
|
||||
try_emit("recursive delegation");
|
||||
}
|
||||
|
||||
let sig_generics = self.tcx().generics_of(sig_id);
|
||||
let parent = self.tcx().local_parent(self.item_def_id());
|
||||
let parent_generics = self.tcx().generics_of(parent);
|
||||
|
||||
let parent_is_trait = (self.tcx().def_kind(parent) == DefKind::Trait) as usize;
|
||||
let sig_has_self = sig_generics.has_self as usize;
|
||||
|
||||
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_is_trait {
|
||||
try_emit("delegation with early bound generics");
|
||||
}
|
||||
|
||||
// There is no way to instantiate `Self` param for caller if
|
||||
// 1. callee is a trait method
|
||||
// 2. delegation item isn't an associative item
|
||||
if let DefKind::AssocFn = self.tcx().def_kind(sig_id)
|
||||
&& let DefKind::Fn = self.tcx().def_kind(self.item_def_id())
|
||||
&& self.tcx().associated_item(sig_id).container
|
||||
== ty::AssocItemContainer::TraitContainer
|
||||
{
|
||||
try_emit("delegation to a trait method from a free function");
|
||||
}
|
||||
|
||||
error_occured
|
||||
}
|
||||
|
||||
fn lower_delegation_ty(
|
||||
&self,
|
||||
sig_id: DefId,
|
||||
idx: hir::InferDelegationKind,
|
||||
span: Span,
|
||||
) -> Ty<'tcx> {
|
||||
if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output)
|
||||
{
|
||||
let e = self.dcx().span_delayed_bug(span, "not supported delegation case");
|
||||
return Ty::new_error(self.tcx(), e);
|
||||
};
|
||||
let sig = self.tcx().fn_sig(sig_id);
|
||||
let sig_generics = self.tcx().generics_of(sig_id);
|
||||
|
||||
let parent = self.tcx().local_parent(self.item_def_id());
|
||||
let parent_def_kind = self.tcx().def_kind(parent);
|
||||
|
||||
let sig = if let DefKind::Impl { .. } = parent_def_kind
|
||||
&& sig_generics.has_self
|
||||
{
|
||||
// Generic params can't be here except the trait self type.
|
||||
// They are not supported yet.
|
||||
assert_eq!(sig_generics.count(), 1);
|
||||
assert_eq!(self.tcx().generics_of(parent).count(), 0);
|
||||
|
||||
let self_ty = self.tcx().type_of(parent).instantiate_identity();
|
||||
let generic_self_ty = ty::GenericArg::from(self_ty);
|
||||
let args = self.tcx().mk_args_from_iter(std::iter::once(generic_self_ty));
|
||||
sig.instantiate(self.tcx(), args)
|
||||
} else {
|
||||
sig.instantiate_identity()
|
||||
};
|
||||
|
||||
// Bound vars are also inherited from `sig_id`.
|
||||
// They will be rebound later in `lower_fn_ty`.
|
||||
let sig = sig.skip_binder();
|
||||
|
||||
fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> {
|
||||
let delegation_sig = self.tcx().inherit_sig_for_delegation_item(self.item_def_id());
|
||||
match idx {
|
||||
hir::InferDelegationKind::Input(id) => sig.inputs()[id],
|
||||
hir::InferDelegationKind::Output => sig.output(),
|
||||
hir::InferDelegationKind::Input(idx) => delegation_sig[idx],
|
||||
hir::InferDelegationKind::Output => *delegation_sig.last().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2110,9 +2028,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let tcx = self.tcx();
|
||||
|
||||
let result_ty = match &hir_ty.kind {
|
||||
hir::TyKind::InferDelegation(sig_id, idx) => {
|
||||
self.lower_delegation_ty(*sig_id, *idx, hir_ty.span)
|
||||
}
|
||||
hir::TyKind::InferDelegation(_, idx) => self.lower_delegation_ty(*idx),
|
||||
hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.lower_ty(ty)),
|
||||
hir::TyKind::Ptr(mt) => Ty::new_ptr(tcx, self.lower_ty(mt.ty), mt.mutbl),
|
||||
hir::TyKind::Ref(region, mt) => {
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ pub mod autoderef;
|
|||
mod bounds;
|
||||
mod check_unused;
|
||||
mod coherence;
|
||||
mod delegation;
|
||||
pub mod hir_ty_lowering;
|
||||
// FIXME: This module shouldn't be public.
|
||||
pub mod collect;
|
||||
|
|
@ -146,6 +147,10 @@ pub fn provide(providers: &mut Providers) {
|
|||
variance::provide(providers);
|
||||
outlives::provide(providers);
|
||||
hir_wf_check::provide(providers);
|
||||
*providers = Providers {
|
||||
inherit_sig_for_delegation_item: delegation::inherit_sig_for_delegation_item,
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
|
||||
pub fn check_crate(tcx: TyCtxt<'_>) {
|
||||
|
|
|
|||
|
|
@ -1306,6 +1306,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// No way to know whether it's diverging because
|
||||
// of a `break` or an outer `break` or `return`.
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
} else {
|
||||
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
|
||||
}
|
||||
|
||||
// If we permit break with a value, then result type is
|
||||
|
|
|
|||
|
|
@ -48,30 +48,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// Produces warning on the given node, if the current point in the
|
||||
/// function is unreachable, and there hasn't been another warning.
|
||||
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
|
||||
// FIXME: Combine these two 'if' expressions into one once
|
||||
// let chains are implemented
|
||||
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
|
||||
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
|
||||
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
|
||||
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
|
||||
if !span.is_desugaring(DesugaringKind::CondTemporary)
|
||||
&& !span.is_desugaring(DesugaringKind::Async)
|
||||
&& !orig_span.is_desugaring(DesugaringKind::Await)
|
||||
{
|
||||
self.diverges.set(Diverges::WarnedAlways);
|
||||
|
||||
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
|
||||
|
||||
let msg = format!("unreachable {kind}");
|
||||
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
|
||||
lint.primary_message(msg.clone());
|
||||
lint.span_label(span, msg).span_label(
|
||||
orig_span,
|
||||
custom_note.unwrap_or("any code following this expression is unreachable"),
|
||||
);
|
||||
})
|
||||
}
|
||||
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
|
||||
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
|
||||
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
|
||||
if span.is_desugaring(DesugaringKind::CondTemporary) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't lint if the result of an async block or async function is `!`.
|
||||
// This does not affect the unreachable lints *within* the body.
|
||||
if span.is_desugaring(DesugaringKind::Async) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't lint *within* the `.await` operator, since that's all just desugaring junk.
|
||||
// We only want to lint if there is a subsequent expression after the `.await`.
|
||||
if span.is_desugaring(DesugaringKind::Await) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Don't warn twice.
|
||||
self.diverges.set(Diverges::WarnedAlways);
|
||||
|
||||
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
|
||||
|
||||
let msg = format!("unreachable {kind}");
|
||||
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
|
||||
lint.primary_message(msg.clone());
|
||||
lint.span_label(span, msg).span_label(
|
||||
orig_span,
|
||||
custom_note.unwrap_or("any code following this expression is unreachable"),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use region_constraints::{
|
|||
pub use relate::combine::{CombineFields, PredicateEmittingRelation};
|
||||
pub use relate::StructurallyRelateAliases;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::undo_log::Rollback;
|
||||
use rustc_data_structures::unify as ut;
|
||||
|
|
@ -1318,38 +1318,36 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
return inner;
|
||||
}
|
||||
|
||||
struct ToFreshVars<'a, 'tcx> {
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
span: Span,
|
||||
lbrct: BoundRegionConversionTime,
|
||||
map: FxHashMap<ty::BoundVar, ty::GenericArg<'tcx>>,
|
||||
let bound_vars = value.bound_vars();
|
||||
let mut args = Vec::with_capacity(bound_vars.len());
|
||||
|
||||
for bound_var_kind in bound_vars {
|
||||
let arg: ty::GenericArg<'_> = match bound_var_kind {
|
||||
ty::BoundVariableKind::Ty(_) => self.next_ty_var(span).into(),
|
||||
ty::BoundVariableKind::Region(br) => {
|
||||
self.next_region_var(BoundRegion(span, br, lbrct)).into()
|
||||
}
|
||||
ty::BoundVariableKind::Const => self.next_const_var(span).into(),
|
||||
};
|
||||
args.push(arg);
|
||||
}
|
||||
|
||||
impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'_, 'tcx> {
|
||||
struct ToFreshVars<'tcx> {
|
||||
args: Vec<ty::GenericArg<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'tcx> {
|
||||
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
|
||||
self.map
|
||||
.entry(br.var)
|
||||
.or_insert_with(|| {
|
||||
self.infcx
|
||||
.next_region_var(BoundRegion(self.span, br.kind, self.lbrct))
|
||||
.into()
|
||||
})
|
||||
.expect_region()
|
||||
self.args[br.var.index()].expect_region()
|
||||
}
|
||||
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
|
||||
self.map
|
||||
.entry(bt.var)
|
||||
.or_insert_with(|| self.infcx.next_ty_var(self.span).into())
|
||||
.expect_ty()
|
||||
self.args[bt.var.index()].expect_ty()
|
||||
}
|
||||
fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx> {
|
||||
self.map
|
||||
.entry(bv)
|
||||
.or_insert_with(|| self.infcx.next_const_var(self.span).into())
|
||||
.expect_const()
|
||||
self.args[bv.index()].expect_const()
|
||||
}
|
||||
}
|
||||
let delegate = ToFreshVars { infcx: self, span, lbrct, map: Default::default() };
|
||||
let delegate = ToFreshVars { args };
|
||||
self.tcx.replace_bound_vars_uncached(value, delegate)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use rustc_middle::ty;
|
|||
use rustc_middle::ty::CurrentGcx;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_parse::new_parser_from_source_str;
|
||||
use rustc_parse::parser::attr::AllowLeadingUnsafe;
|
||||
use rustc_query_impl::QueryCtxt;
|
||||
use rustc_query_system::query::print_query_stack;
|
||||
use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
|
||||
|
|
@ -67,7 +68,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
|
|||
}
|
||||
|
||||
match new_parser_from_source_str(&psess, filename, s.to_string()) {
|
||||
Ok(mut parser) => match parser.parse_meta_item() {
|
||||
Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) {
|
||||
Ok(meta_item) if parser.token == token::Eof => {
|
||||
if meta_item.path.segments.len() != 1 {
|
||||
error!("argument key must be an identifier");
|
||||
|
|
@ -173,7 +174,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
|
|||
}
|
||||
};
|
||||
|
||||
let meta_item = match parser.parse_meta_item() {
|
||||
let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::Yes) {
|
||||
Ok(meta_item) if parser.token == token::Eof => meta_item,
|
||||
Ok(..) => expected_error(),
|
||||
Err(err) => {
|
||||
|
|
|
|||
|
|
@ -544,7 +544,13 @@ fn resolver_for_lowering_raw<'tcx>(
|
|||
let arenas = Resolver::arenas();
|
||||
let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`.
|
||||
let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal();
|
||||
let mut resolver = Resolver::new(tcx, &pre_configured_attrs, krate.spans.inner_span, &arenas);
|
||||
let mut resolver = Resolver::new(
|
||||
tcx,
|
||||
&pre_configured_attrs,
|
||||
krate.spans.inner_span,
|
||||
krate.spans.inject_use_span,
|
||||
&arenas,
|
||||
);
|
||||
let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver);
|
||||
|
||||
// Make sure we don't mutate the cstore from here on.
|
||||
|
|
|
|||
|
|
@ -700,10 +700,10 @@ lint_reason_must_be_string_literal = reason must be a string literal
|
|||
lint_reason_must_come_last = reason in lint attribute must come last
|
||||
|
||||
lint_redundant_import = the item `{$ident}` is imported redundantly
|
||||
.label_imported_here = the item `{ident}` is already imported here
|
||||
.label_defined_here = the item `{ident}` is already defined here
|
||||
.label_imported_prelude = the item `{ident}` is already imported by the extern prelude
|
||||
.label_defined_prelude = the item `{ident}` is already defined by the extern prelude
|
||||
.label_imported_here = the item `{$ident}` is already imported here
|
||||
.label_defined_here = the item `{$ident}` is already defined here
|
||||
.label_imported_prelude = the item `{$ident}` is already imported by the extern prelude
|
||||
.label_defined_prelude = the item `{$ident}` is already defined by the extern prelude
|
||||
|
||||
lint_redundant_import_visibility = glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough
|
||||
.note = the most public imported item is `{$max_vis}`
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ declare_lint! {
|
|||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// # #![deny(ambiguous_negative_literals)]
|
||||
/// # #![allow(unused)]
|
||||
/// -1i32.abs(); // equals -1, while `(-1i32).abs()` equals 1
|
||||
/// ```
|
||||
|
|
@ -27,7 +28,7 @@ declare_lint! {
|
|||
/// Method calls take precedence over unary precedence. Setting the
|
||||
/// precedence explicitly makes the code clearer and avoid potential bugs.
|
||||
pub AMBIGUOUS_NEGATIVE_LITERALS,
|
||||
Deny,
|
||||
Allow,
|
||||
"ambiguous negative literals operations",
|
||||
report_in_external_macro
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,11 +309,7 @@ fn report_bin_hex_error(
|
|||
) {
|
||||
let (t, actually) = match ty {
|
||||
attr::IntType::SignedInt(t) => {
|
||||
let actually = if negative {
|
||||
-(size.sign_extend(val) as i128)
|
||||
} else {
|
||||
size.sign_extend(val) as i128
|
||||
};
|
||||
let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) };
|
||||
(t.name_str(), actually.to_string())
|
||||
}
|
||||
attr::IntType::UnsignedInt(t) => {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ declare_lint_pass! {
|
|||
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
|
||||
PTR_CAST_ADD_AUTO_TO_OBJECT,
|
||||
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
||||
REDUNDANT_IMPORTS,
|
||||
REDUNDANT_LIFETIMES,
|
||||
REFINING_IMPL_TRAIT_INTERNAL,
|
||||
REFINING_IMPL_TRAIT_REACHABLE,
|
||||
|
|
@ -426,6 +427,31 @@ declare_lint! {
|
|||
"imports that are never used"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `redundant_imports` lint detects imports that are redundant due to being
|
||||
/// imported already; either through a previous import, or being present in
|
||||
/// the prelude.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(redundant_imports)]
|
||||
/// use std::option::Option::None;
|
||||
/// fn foo() -> Option<i32> { None }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Redundant imports are unnecessary and can be removed to simplify code.
|
||||
/// If you intended to re-export the item to make it available outside of the
|
||||
/// module, add a visibility modifier like `pub`.
|
||||
pub REDUNDANT_IMPORTS,
|
||||
Allow,
|
||||
"imports that are redundant due to being imported already"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `must_not_suspend` lint guards against values that shouldn't be held across suspend points
|
||||
/// (`.await`)
|
||||
|
|
@ -617,8 +643,6 @@ declare_lint! {
|
|||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
///
|
||||
/// #[expect(unused_variables)]
|
||||
/// let x = 10;
|
||||
/// println!("{}", x);
|
||||
|
|
@ -1836,8 +1860,7 @@ declare_lint! {
|
|||
/// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
|
||||
pub ELIDED_LIFETIMES_IN_PATHS,
|
||||
Allow,
|
||||
"hidden lifetime parameters in types are deprecated",
|
||||
crate_level_only
|
||||
"hidden lifetime parameters in types are deprecated"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
|
|
|
|||
|
|
@ -1555,7 +1555,7 @@ LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) {
|
|||
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) {
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
|
||||
return wrap(llvm::Intrinsic::getDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update));
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -746,6 +746,21 @@ impl<'hir> Map<'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn opt_delegation_sig_id(self, def_id: LocalDefId) -> Option<DefId> {
|
||||
if let Some(ret) = self.get_fn_output(def_id)
|
||||
&& let FnRetTy::Return(ty) = ret
|
||||
&& let TyKind::InferDelegation(sig_id, _) = ty.kind
|
||||
{
|
||||
return Some(sig_id);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn delegation_sig_id(self, def_id: LocalDefId) -> DefId {
|
||||
self.opt_delegation_sig_id(def_id).unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn opt_ident(self, id: HirId) -> Option<Ident> {
|
||||
match self.tcx.hir_node(id) {
|
||||
|
|
|
|||
|
|
@ -334,14 +334,15 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
alloc_size: Size,
|
||||
ptr_offset: i64,
|
||||
/// The size of the memory range that was expected to be in-bounds.
|
||||
inbounds_size: Size,
|
||||
inbounds_size: i64,
|
||||
msg: CheckInAllocMsg,
|
||||
},
|
||||
/// Using an integer as a pointer in the wrong way.
|
||||
DanglingIntPointer {
|
||||
addr: u64,
|
||||
/// The size of the memory range that was expected to be in-bounds (or 0 if we don't know).
|
||||
inbounds_size: Size,
|
||||
/// The size of the memory range that was expected to be in-bounds (or 0 if we need an
|
||||
/// allocation but not any actual memory there, e.g. for function pointers).
|
||||
inbounds_size: i64,
|
||||
msg: CheckInAllocMsg,
|
||||
},
|
||||
/// Used a pointer with bad alignment.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_data_structures::static_assert_size;
|
|||
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
|
||||
use rustc_target::abi::{HasDataLayout, Size};
|
||||
|
||||
use super::{AllocId, InterpResult};
|
||||
use super::AllocId;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Pointer arithmetic
|
||||
|
|
@ -40,62 +40,13 @@ pub trait PointerArithmetic: HasDataLayout {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn target_usize_to_isize(&self, val: u64) -> i64 {
|
||||
let val = val as i64;
|
||||
// Now wrap-around into the machine_isize range.
|
||||
if val > self.target_isize_max() {
|
||||
// This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into
|
||||
// i64.
|
||||
debug_assert!(self.pointer_size().bits() < 64);
|
||||
let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
|
||||
val - i64::try_from(max_usize_plus_1).unwrap()
|
||||
} else {
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function: truncate given value-"overflowed flag" pair to pointer size and
|
||||
/// update "overflowed flag" if there was an overflow.
|
||||
/// This should be called by all the other methods before returning!
|
||||
#[inline]
|
||||
fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) {
|
||||
let val = u128::from(val);
|
||||
let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
|
||||
(u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1)
|
||||
fn truncate_to_target_usize(&self, val: u64) -> u64 {
|
||||
self.pointer_size().truncate(val.into()).try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
|
||||
// We do not need to check if i fits in a machine usize. If it doesn't,
|
||||
// either the wrapping_add will wrap or res will not fit in a pointer.
|
||||
let res = val.overflowing_add(i);
|
||||
self.truncate_to_ptr(res)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) {
|
||||
// We need to make sure that i fits in a machine isize.
|
||||
let n = i.unsigned_abs();
|
||||
if i >= 0 {
|
||||
let (val, over) = self.overflowing_offset(val, n);
|
||||
(val, over || i > self.target_isize_max())
|
||||
} else {
|
||||
let res = val.overflowing_sub(n);
|
||||
let (val, over) = self.truncate_to_ptr(res);
|
||||
(val, over || i < self.target_isize_min())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> {
|
||||
let (res, over) = self.overflowing_offset(val, i);
|
||||
if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> {
|
||||
let (res, over) = self.overflowing_signed_offset(val, i);
|
||||
if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
|
||||
fn sign_extend_to_target_isize(&self, val: u64) -> i64 {
|
||||
self.pointer_size().sign_extend(val.into()).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,12 +132,9 @@ impl Provenance for CtfeProvenance {
|
|||
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Print AllocId.
|
||||
fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag
|
||||
// Print offset only if it is non-zero. Print it signed.
|
||||
let signed_offset = ptr.offset.bytes() as i64;
|
||||
if signed_offset > 0 {
|
||||
write!(f, "+{:#x}", signed_offset)?;
|
||||
} else if signed_offset < 0 {
|
||||
write!(f, "-{:#x}", signed_offset.unsigned_abs())?;
|
||||
// Print offset only if it is non-zero.
|
||||
if ptr.offset.bytes() > 0 {
|
||||
write!(f, "+{:#x}", ptr.offset.bytes())?;
|
||||
}
|
||||
// Print immutable status.
|
||||
if ptr.provenance.immutable() {
|
||||
|
|
@ -334,7 +282,7 @@ impl<Prov> Pointer<Option<Prov>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov> Pointer<Prov> {
|
||||
impl<Prov> Pointer<Prov> {
|
||||
#[inline(always)]
|
||||
pub fn new(provenance: Prov, offset: Size) -> Self {
|
||||
Pointer { provenance, offset }
|
||||
|
|
@ -352,43 +300,16 @@ impl<'tcx, Prov> Pointer<Prov> {
|
|||
Pointer { provenance: f(self.provenance), ..self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
|
||||
Ok(Pointer {
|
||||
offset: Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
|
||||
..self
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
|
||||
let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
|
||||
let ptr = Pointer { offset: Size::from_bytes(res), ..self };
|
||||
(ptr, over)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
|
||||
self.overflowing_offset(i, cx).0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
|
||||
Ok(Pointer {
|
||||
offset: Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
|
||||
..self
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn overflowing_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> (Self, bool) {
|
||||
let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
|
||||
let ptr = Pointer { offset: Size::from_bytes(res), ..self };
|
||||
(ptr, over)
|
||||
let res =
|
||||
cx.data_layout().truncate_to_target_usize(self.offset.bytes().wrapping_add(i.bytes()));
|
||||
Pointer { offset: Size::from_bytes(res), ..self }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
|
||||
self.overflowing_signed_offset(i, cx).0
|
||||
// It's wrapping anyway, so we can just cast to `u64`.
|
||||
self.wrapping_offset(Size::from_bytes(i as u64), cx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
|
|||
#[inline]
|
||||
pub fn to_int(self, size: Size) -> InterpResult<'tcx, i128> {
|
||||
let b = self.to_bits(size)?;
|
||||
Ok(size.sign_extend(b) as i128)
|
||||
Ok(size.sign_extend(b))
|
||||
}
|
||||
|
||||
/// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer.
|
||||
|
|
|
|||
|
|
@ -1722,6 +1722,10 @@ rustc_queries! {
|
|||
desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
query inherit_sig_for_delegation_item(def_id: LocalDefId) -> &'tcx [Ty<'tcx>] {
|
||||
desc { "inheriting delegation signature" }
|
||||
}
|
||||
|
||||
/// Does lifetime resolution on items. Importantly, we can't resolve
|
||||
/// lifetimes directly on things like trait methods, because of trait params.
|
||||
/// See `rustc_resolve::late::lifetimes` for details.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use rustc_middle::ty::{
|
|||
TyCtxt, UpvarArgs,
|
||||
};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, ErrorGuaranteed, Span, Symbol, DUMMY_SP};
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx};
|
||||
use rustc_target::asm::InlineAsmRegOrRegClass;
|
||||
use tracing::instrument;
|
||||
|
|
@ -597,10 +597,6 @@ pub struct Pat<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> Pat<'tcx> {
|
||||
pub fn wildcard_from_ty(ty: Ty<'tcx>) -> Self {
|
||||
Pat { ty, span: DUMMY_SP, kind: PatKind::Wild }
|
||||
}
|
||||
|
||||
pub fn simple_ident(&self) -> Option<Symbol> {
|
||||
match self.kind {
|
||||
PatKind::Binding {
|
||||
|
|
@ -1073,186 +1069,6 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Pat<'tcx> {
|
||||
/// Prints a [`Pat`] to an owned string, for user-facing diagnostics.
|
||||
///
|
||||
/// If we ever switch over to storing subpatterns as `PatId`, this will also
|
||||
/// need to take a context that can resolve IDs to subpatterns.
|
||||
pub fn to_string(&self) -> String {
|
||||
format!("{}", self.display())
|
||||
}
|
||||
|
||||
/// Used internally by [`fmt::Display`] for [`PatDisplay`].
|
||||
fn display(&self) -> PatDisplay<'_, 'tcx> {
|
||||
PatDisplay { pat: self }
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around [`&Pat<'tcx>`][`Pat`] that implements [`fmt::Display`].
|
||||
///
|
||||
/// If we ever switch over to storing subpatterns as `PatId`, this will also
|
||||
/// need to hold a context that can resolve IDs to subpatterns.
|
||||
struct PatDisplay<'pat, 'tcx> {
|
||||
pat: &'pat Pat<'tcx>,
|
||||
}
|
||||
|
||||
impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let &Self { pat } = self;
|
||||
|
||||
// Printing lists is a chore.
|
||||
let mut first = true;
|
||||
let mut start_or_continue = |s| {
|
||||
if first {
|
||||
first = false;
|
||||
""
|
||||
} else {
|
||||
s
|
||||
}
|
||||
};
|
||||
let mut start_or_comma = || start_or_continue(", ");
|
||||
|
||||
match pat.kind {
|
||||
PatKind::Wild => write!(f, "_"),
|
||||
PatKind::Never => write!(f, "!"),
|
||||
PatKind::AscribeUserType { ref subpattern, .. } => {
|
||||
write!(f, "{}: _", subpattern.display())
|
||||
}
|
||||
PatKind::Binding { name, mode, ref subpattern, .. } => {
|
||||
f.write_str(mode.prefix_str())?;
|
||||
write!(f, "{name}")?;
|
||||
if let Some(ref subpattern) = *subpattern {
|
||||
write!(f, " @ {}", subpattern.display())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => {
|
||||
let variant_and_name = match pat.kind {
|
||||
PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| {
|
||||
let variant = adt_def.variant(variant_index);
|
||||
let adt_did = adt_def.did();
|
||||
let name = if tcx.get_diagnostic_item(sym::Option) == Some(adt_did)
|
||||
|| tcx.get_diagnostic_item(sym::Result) == Some(adt_did)
|
||||
{
|
||||
variant.name.to_string()
|
||||
} else {
|
||||
format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name)
|
||||
};
|
||||
Some((variant, name))
|
||||
}),
|
||||
_ => pat.ty.ty_adt_def().and_then(|adt_def| {
|
||||
if !adt_def.is_enum() {
|
||||
ty::tls::with(|tcx| {
|
||||
Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
if let Some((variant, name)) = &variant_and_name {
|
||||
write!(f, "{name}")?;
|
||||
|
||||
// Only for Adt we can have `S {...}`,
|
||||
// which we handle separately here.
|
||||
if variant.ctor.is_none() {
|
||||
write!(f, " {{ ")?;
|
||||
|
||||
let mut printed = 0;
|
||||
for p in subpatterns {
|
||||
if let PatKind::Wild = p.pattern.kind {
|
||||
continue;
|
||||
}
|
||||
let name = variant.fields[p.field].name;
|
||||
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern.display())?;
|
||||
printed += 1;
|
||||
}
|
||||
|
||||
let is_union = pat.ty.ty_adt_def().is_some_and(|adt| adt.is_union());
|
||||
if printed < variant.fields.len() && (!is_union || printed == 0) {
|
||||
write!(f, "{}..", start_or_comma())?;
|
||||
}
|
||||
|
||||
return write!(f, " }}");
|
||||
}
|
||||
}
|
||||
|
||||
let num_fields =
|
||||
variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len());
|
||||
if num_fields != 0 || variant_and_name.is_none() {
|
||||
write!(f, "(")?;
|
||||
for i in 0..num_fields {
|
||||
write!(f, "{}", start_or_comma())?;
|
||||
|
||||
// Common case: the field is where we expect it.
|
||||
if let Some(p) = subpatterns.get(i) {
|
||||
if p.field.index() == i {
|
||||
write!(f, "{}", p.pattern.display())?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we have to go looking for it.
|
||||
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
|
||||
write!(f, "{}", p.pattern.display())?;
|
||||
} else {
|
||||
write!(f, "_")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
PatKind::Deref { ref subpattern } => {
|
||||
match pat.ty.kind() {
|
||||
ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
|
||||
ty::Ref(_, _, mutbl) => {
|
||||
write!(f, "&{}", mutbl.prefix_str())?;
|
||||
}
|
||||
_ => bug!("{} is a bad Deref pattern type", pat.ty),
|
||||
}
|
||||
write!(f, "{}", subpattern.display())
|
||||
}
|
||||
PatKind::DerefPattern { ref subpattern, .. } => {
|
||||
write!(f, "deref!({})", subpattern.display())
|
||||
}
|
||||
PatKind::Constant { value } => write!(f, "{value}"),
|
||||
PatKind::InlineConstant { def: _, ref subpattern } => {
|
||||
write!(f, "{} (from inline const)", subpattern.display())
|
||||
}
|
||||
PatKind::Range(ref range) => write!(f, "{range}"),
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix }
|
||||
| PatKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
write!(f, "[")?;
|
||||
for p in prefix.iter() {
|
||||
write!(f, "{}{}", start_or_comma(), p.display())?;
|
||||
}
|
||||
if let Some(ref slice) = *slice {
|
||||
write!(f, "{}", start_or_comma())?;
|
||||
match slice.kind {
|
||||
PatKind::Wild => {}
|
||||
_ => write!(f, "{}", slice.display())?,
|
||||
}
|
||||
write!(f, "..")?;
|
||||
}
|
||||
for p in suffix.iter() {
|
||||
write!(f, "{}{}", start_or_comma(), p.display())?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
PatKind::Or { ref pats } => {
|
||||
for pat in pats.iter() {
|
||||
write!(f, "{}{}", start_or_continue(" | "), pat.display())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
PatKind::Error(_) => write!(f, "<error>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
mod size_asserts {
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ impl ScalarInt {
|
|||
let data = i.into();
|
||||
// `into` performed sign extension, we have to truncate
|
||||
let r = Self::raw(size.truncate(data as u128), size);
|
||||
(r, size.sign_extend(r.data) as i128 != data)
|
||||
(r, size.sign_extend(r.data) != data)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -335,7 +335,7 @@ impl ScalarInt {
|
|||
#[inline]
|
||||
pub fn to_int(self, size: Size) -> i128 {
|
||||
let b = self.to_bits(size);
|
||||
size.sign_extend(b) as i128
|
||||
size.sign_extend(b)
|
||||
}
|
||||
|
||||
/// Converts the `ScalarInt` to i8.
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl<'tcx> Discr<'tcx> {
|
|||
let (val, oflo) = if signed {
|
||||
let min = size.signed_int_min();
|
||||
let max = size.signed_int_max();
|
||||
let val = size.sign_extend(self.val) as i128;
|
||||
let val = size.sign_extend(self.val);
|
||||
assert!(n < (i128::MAX as u128));
|
||||
let n = n as i128;
|
||||
let oflo = val > max - n;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ use rustc_span::symbol::Symbol;
|
|||
use rustc_span::{BytePos, Pos, Span};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
use tracing::{debug, instrument};
|
||||
use util::visit_bindings;
|
||||
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::scope::DropKind;
|
||||
|
|
@ -366,28 +365,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let scrutinee_place =
|
||||
unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span));
|
||||
|
||||
let mut arm_candidates = self.create_match_candidates(&scrutinee_place, arms);
|
||||
|
||||
let match_has_guard = arm_candidates.iter().any(|(_, candidate)| candidate.has_guard);
|
||||
let mut candidates =
|
||||
arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::<Vec<_>>();
|
||||
|
||||
let arms = arms.iter().map(|arm| &self.thir[*arm]);
|
||||
let match_start_span = span.shrink_to_lo().to(scrutinee_span);
|
||||
|
||||
// The set of places that we are creating fake borrows of. If there are no match guards then
|
||||
// we don't need any fake borrows, so don't track them.
|
||||
let fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
|
||||
util::collect_fake_borrows(self, &candidates, scrutinee_span, scrutinee_place.base())
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
self.lower_match_tree(
|
||||
let patterns = arms
|
||||
.clone()
|
||||
.map(|arm| {
|
||||
let has_match_guard =
|
||||
if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No };
|
||||
(&*arm.pattern, has_match_guard)
|
||||
})
|
||||
.collect();
|
||||
let built_tree = self.lower_match_tree(
|
||||
block,
|
||||
scrutinee_span,
|
||||
&scrutinee_place,
|
||||
match_start_span,
|
||||
&mut candidates,
|
||||
patterns,
|
||||
false,
|
||||
);
|
||||
|
||||
|
|
@ -395,9 +388,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
destination,
|
||||
scrutinee_place,
|
||||
scrutinee_span,
|
||||
arm_candidates,
|
||||
arms,
|
||||
built_tree,
|
||||
self.source_info(span),
|
||||
fake_borrow_temps,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -417,51 +410,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
block.and(scrutinee_place_builder)
|
||||
}
|
||||
|
||||
/// Create the initial `Candidate`s for a `match` expression.
|
||||
fn create_match_candidates<'pat>(
|
||||
&mut self,
|
||||
scrutinee: &PlaceBuilder<'tcx>,
|
||||
arms: &'pat [ArmId],
|
||||
) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)>
|
||||
where
|
||||
'a: 'pat,
|
||||
{
|
||||
// Assemble the initial list of candidates. These top-level candidates
|
||||
// are 1:1 with the original match arms, but other parts of match
|
||||
// lowering also introduce subcandidates (for subpatterns), and will
|
||||
// also flatten candidates in some cases. So in general a list of
|
||||
// candidates does _not_ necessarily correspond to a list of arms.
|
||||
arms.iter()
|
||||
.copied()
|
||||
.map(|arm| {
|
||||
let arm = &self.thir[arm];
|
||||
let arm_has_guard = arm.guard.is_some();
|
||||
let arm_candidate =
|
||||
Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard, self);
|
||||
(arm, arm_candidate)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Lower the bindings, guards and arm bodies of a `match` expression.
|
||||
///
|
||||
/// The decision tree should have already been created
|
||||
/// (by [Builder::lower_match_tree]).
|
||||
///
|
||||
/// `outer_source_info` is the SourceInfo for the whole match.
|
||||
fn lower_match_arms(
|
||||
fn lower_match_arms<'pat>(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
scrutinee_place_builder: PlaceBuilder<'tcx>,
|
||||
scrutinee_span: Span,
|
||||
arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
|
||||
arms: impl IntoIterator<Item = &'pat Arm<'tcx>>,
|
||||
built_match_tree: BuiltMatchTree<'tcx>,
|
||||
outer_source_info: SourceInfo,
|
||||
fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
|
||||
) -> BlockAnd<()> {
|
||||
let arm_end_blocks: Vec<BasicBlock> = arm_candidates
|
||||
) -> BlockAnd<()>
|
||||
where
|
||||
'tcx: 'pat,
|
||||
{
|
||||
let arm_end_blocks: Vec<BasicBlock> = arms
|
||||
.into_iter()
|
||||
.map(|(arm, candidate)| {
|
||||
debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate);
|
||||
.zip(built_match_tree.branches)
|
||||
.map(|(arm, branch)| {
|
||||
debug!("lowering arm {:?}\ncorresponding branch = {:?}", arm, branch);
|
||||
|
||||
let arm_source_info = self.source_info(arm.span);
|
||||
let arm_scope = (arm.scope, arm_source_info);
|
||||
|
|
@ -494,8 +465,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
let arm_block = this.bind_pattern(
|
||||
outer_source_info,
|
||||
candidate,
|
||||
&fake_borrow_temps,
|
||||
branch,
|
||||
&built_match_tree.fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
Some((arm, match_scope)),
|
||||
EmitStorageLive::Yes,
|
||||
|
|
@ -548,18 +519,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn bind_pattern(
|
||||
&mut self,
|
||||
outer_source_info: SourceInfo,
|
||||
candidate: Candidate<'_, 'tcx>,
|
||||
branch: MatchTreeBranch<'tcx>,
|
||||
fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
|
||||
scrutinee_span: Span,
|
||||
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
|
||||
emit_storage_live: EmitStorageLive,
|
||||
) -> BasicBlock {
|
||||
if candidate.subcandidates.is_empty() {
|
||||
// Avoid generating another `BasicBlock` when we only have one
|
||||
// candidate.
|
||||
if branch.sub_branches.len() == 1 {
|
||||
let [sub_branch] = branch.sub_branches.try_into().unwrap();
|
||||
// Avoid generating another `BasicBlock` when we only have one sub branch.
|
||||
self.bind_and_guard_matched_candidate(
|
||||
candidate,
|
||||
&[],
|
||||
sub_branch,
|
||||
fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
arm_match_scope,
|
||||
|
|
@ -587,35 +557,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// We keep a stack of all of the bindings and type ascriptions
|
||||
// from the parent candidates that we visit, that also need to
|
||||
// be bound for each candidate.
|
||||
traverse_candidate(
|
||||
candidate,
|
||||
&mut Vec::new(),
|
||||
&mut |leaf_candidate, parent_data| {
|
||||
if let Some(arm) = arm {
|
||||
self.clear_top_scope(arm.scope);
|
||||
}
|
||||
let binding_end = self.bind_and_guard_matched_candidate(
|
||||
leaf_candidate,
|
||||
parent_data,
|
||||
fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
arm_match_scope,
|
||||
schedule_drops,
|
||||
emit_storage_live,
|
||||
);
|
||||
if arm.is_none() {
|
||||
schedule_drops = ScheduleDrops::No;
|
||||
}
|
||||
self.cfg.goto(binding_end, outer_source_info, target_block);
|
||||
},
|
||||
|inner_candidate, parent_data| {
|
||||
parent_data.push(inner_candidate.extra_data);
|
||||
inner_candidate.subcandidates.into_iter()
|
||||
},
|
||||
|parent_data| {
|
||||
parent_data.pop();
|
||||
},
|
||||
);
|
||||
for sub_branch in branch.sub_branches {
|
||||
if let Some(arm) = arm {
|
||||
self.clear_top_scope(arm.scope);
|
||||
}
|
||||
let binding_end = self.bind_and_guard_matched_candidate(
|
||||
sub_branch,
|
||||
fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
arm_match_scope,
|
||||
schedule_drops,
|
||||
emit_storage_live,
|
||||
);
|
||||
if arm.is_none() {
|
||||
schedule_drops = ScheduleDrops::No;
|
||||
}
|
||||
self.cfg.goto(binding_end, outer_source_info, target_block);
|
||||
}
|
||||
|
||||
target_block
|
||||
}
|
||||
|
|
@ -725,7 +683,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
initializer: PlaceBuilder<'tcx>,
|
||||
set_match_place: bool,
|
||||
) -> BlockAnd<()> {
|
||||
let mut candidate = Candidate::new(initializer.clone(), irrefutable_pat, false, self);
|
||||
let built_tree = self.lower_match_tree(
|
||||
block,
|
||||
irrefutable_pat.span,
|
||||
&initializer,
|
||||
irrefutable_pat.span,
|
||||
vec![(irrefutable_pat, HasMatchGuard::No)],
|
||||
false,
|
||||
);
|
||||
let [branch] = built_tree.branches.try_into().unwrap();
|
||||
|
||||
// For matches and function arguments, the place that is being matched
|
||||
// can be set when creating the variables. But the place for
|
||||
|
|
@ -746,7 +712,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// };
|
||||
// ```
|
||||
if let Some(place) = initializer.try_to_place(self) {
|
||||
visit_bindings(&[&mut candidate], |binding: &Binding<'_>| {
|
||||
// Because or-alternatives bind the same variables, we only explore the first one.
|
||||
let first_sub_branch = branch.sub_branches.first().unwrap();
|
||||
for binding in &first_sub_branch.bindings {
|
||||
let local = self.var_local_id(binding.var_id, OutsideGuard);
|
||||
if let LocalInfo::User(BindingForm::Var(VarBindingForm {
|
||||
opt_match_place: Some((ref mut match_place, _)),
|
||||
|
|
@ -757,21 +725,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
} else {
|
||||
bug!("Let binding to non-user variable.")
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.lower_match_tree(
|
||||
block,
|
||||
irrefutable_pat.span,
|
||||
&initializer,
|
||||
irrefutable_pat.span,
|
||||
&mut [&mut candidate],
|
||||
false,
|
||||
);
|
||||
self.bind_pattern(
|
||||
self.source_info(irrefutable_pat.span),
|
||||
candidate,
|
||||
branch,
|
||||
&[],
|
||||
irrefutable_pat.span,
|
||||
None,
|
||||
|
|
@ -1152,20 +1112,21 @@ struct Candidate<'pat, 'tcx> {
|
|||
/// The earliest block that has only candidates >= this one as descendents. Used for false
|
||||
/// edges, see the doc for [`Builder::match_expr`].
|
||||
false_edge_start_block: Option<BasicBlock>,
|
||||
/// The `false_edge_start_block` of the next candidate.
|
||||
next_candidate_start_block: Option<BasicBlock>,
|
||||
}
|
||||
|
||||
impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
||||
fn new(
|
||||
place: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
has_guard: bool,
|
||||
has_guard: HasMatchGuard,
|
||||
cx: &mut Builder<'_, 'tcx>,
|
||||
) -> Self {
|
||||
// Use `FlatPat` to build simplified match pairs, then immediately
|
||||
// incorporate them into a new candidate.
|
||||
Self::from_flat_pat(FlatPat::new(place, pattern, cx), has_guard)
|
||||
Self::from_flat_pat(
|
||||
FlatPat::new(place, pattern, cx),
|
||||
matches!(has_guard, HasMatchGuard::Yes),
|
||||
)
|
||||
}
|
||||
|
||||
/// Incorporates an already-simplified [`FlatPat`] into a new candidate.
|
||||
|
|
@ -1179,7 +1140,6 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
|||
otherwise_block: None,
|
||||
pre_binding_block: None,
|
||||
false_edge_start_block: None,
|
||||
next_candidate_start_block: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1199,6 +1159,17 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
|||
|_| {},
|
||||
);
|
||||
}
|
||||
|
||||
/// Visit the leaf candidates in reverse order.
|
||||
fn visit_leaves_rev<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) {
|
||||
traverse_candidate(
|
||||
self,
|
||||
&mut (),
|
||||
&mut move |c, _| visit_leaf(c),
|
||||
move |c, _| c.subcandidates.iter_mut().rev(),
|
||||
|_| {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A depth-first traversal of the `Candidate` and all of its recursive
|
||||
|
|
@ -1409,12 +1380,114 @@ pub(crate) struct ArmHasGuard(pub(crate) bool);
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// Main matching algorithm
|
||||
|
||||
/// A sub-branch in the output of match lowering. Match lowering has generated MIR code that will
|
||||
/// branch to `success_block` when the matched value matches the corresponding pattern. If there is
|
||||
/// a guard, its failure must continue to `otherwise_block`, which will resume testing patterns.
|
||||
#[derive(Debug)]
|
||||
struct MatchTreeSubBranch<'tcx> {
|
||||
span: Span,
|
||||
/// The block that is branched to if the corresponding subpattern matches.
|
||||
success_block: BasicBlock,
|
||||
/// The block to branch to if this arm had a guard and the guard fails.
|
||||
otherwise_block: BasicBlock,
|
||||
/// The bindings to set up in this sub-branch.
|
||||
bindings: Vec<Binding<'tcx>>,
|
||||
/// The ascriptions to set up in this sub-branch.
|
||||
ascriptions: Vec<Ascription<'tcx>>,
|
||||
/// Whether the sub-branch corresponds to a never pattern.
|
||||
is_never: bool,
|
||||
}
|
||||
|
||||
/// A branch in the output of match lowering.
|
||||
#[derive(Debug)]
|
||||
struct MatchTreeBranch<'tcx> {
|
||||
sub_branches: Vec<MatchTreeSubBranch<'tcx>>,
|
||||
}
|
||||
|
||||
/// The result of generating MIR for a pattern-matching expression. Each input branch/arm/pattern
|
||||
/// gives rise to an output `MatchTreeBranch`. If one of the patterns matches, we branch to the
|
||||
/// corresponding `success_block`. If none of the patterns matches, we branch to `otherwise_block`.
|
||||
///
|
||||
/// Each branch is made of one of more sub-branches, corresponding to or-patterns. E.g.
|
||||
/// ```ignore(illustrative)
|
||||
/// match foo {
|
||||
/// (x, false) | (false, x) => {}
|
||||
/// (true, true) => {}
|
||||
/// }
|
||||
/// ```
|
||||
/// Here the first arm gives the first `MatchTreeBranch`, which has two sub-branches, one for each
|
||||
/// alternative of the or-pattern. They are kept separate because each needs to bind `x` to a
|
||||
/// different place.
|
||||
#[derive(Debug)]
|
||||
struct BuiltMatchTree<'tcx> {
|
||||
branches: Vec<MatchTreeBranch<'tcx>>,
|
||||
otherwise_block: BasicBlock,
|
||||
/// If any of the branches had a guard, we collect here the places and locals to fakely borrow
|
||||
/// to ensure match guards can't modify the values as we match them. For more details, see
|
||||
/// [`util::collect_fake_borrows`].
|
||||
fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
|
||||
}
|
||||
|
||||
impl<'tcx> MatchTreeSubBranch<'tcx> {
|
||||
fn from_sub_candidate(
|
||||
candidate: Candidate<'_, 'tcx>,
|
||||
parent_data: &Vec<PatternExtraData<'tcx>>,
|
||||
) -> Self {
|
||||
debug_assert!(candidate.match_pairs.is_empty());
|
||||
MatchTreeSubBranch {
|
||||
span: candidate.extra_data.span,
|
||||
success_block: candidate.pre_binding_block.unwrap(),
|
||||
otherwise_block: candidate.otherwise_block.unwrap(),
|
||||
bindings: parent_data
|
||||
.iter()
|
||||
.flat_map(|d| &d.bindings)
|
||||
.chain(&candidate.extra_data.bindings)
|
||||
.cloned()
|
||||
.collect(),
|
||||
ascriptions: parent_data
|
||||
.iter()
|
||||
.flat_map(|d| &d.ascriptions)
|
||||
.cloned()
|
||||
.chain(candidate.extra_data.ascriptions)
|
||||
.collect(),
|
||||
is_never: candidate.extra_data.is_never,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MatchTreeBranch<'tcx> {
|
||||
fn from_candidate(candidate: Candidate<'_, 'tcx>) -> Self {
|
||||
let mut sub_branches = Vec::new();
|
||||
traverse_candidate(
|
||||
candidate,
|
||||
&mut Vec::new(),
|
||||
&mut |candidate: Candidate<'_, '_>, parent_data: &mut Vec<PatternExtraData<'_>>| {
|
||||
sub_branches.push(MatchTreeSubBranch::from_sub_candidate(candidate, parent_data));
|
||||
},
|
||||
|inner_candidate, parent_data| {
|
||||
parent_data.push(inner_candidate.extra_data);
|
||||
inner_candidate.subcandidates.into_iter()
|
||||
},
|
||||
|parent_data| {
|
||||
parent_data.pop();
|
||||
},
|
||||
);
|
||||
MatchTreeBranch { sub_branches }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum HasMatchGuard {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// The entrypoint of the matching algorithm. Create the decision tree for the match expression,
|
||||
/// starting from `block`.
|
||||
///
|
||||
/// Modifies `candidates` to store the bindings and type ascriptions for
|
||||
/// that candidate.
|
||||
/// `patterns` is a list of patterns, one for each arm. The associated boolean indicates whether
|
||||
/// the arm has a guard.
|
||||
///
|
||||
/// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
|
||||
/// or not (for `let` and `match`). In the refutable case we return the block to which we branch
|
||||
|
|
@ -1425,31 +1498,76 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
scrutinee_span: Span,
|
||||
scrutinee_place_builder: &PlaceBuilder<'tcx>,
|
||||
match_start_span: Span,
|
||||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||
patterns: Vec<(&'pat Pat<'tcx>, HasMatchGuard)>,
|
||||
refutable: bool,
|
||||
) -> BasicBlock {
|
||||
// This will generate code to test scrutinee_place and branch to the appropriate arm block.
|
||||
// See the doc comment on `match_candidates` for why we have an otherwise block.
|
||||
let otherwise_block =
|
||||
self.match_candidates(match_start_span, scrutinee_span, block, candidates);
|
||||
) -> BuiltMatchTree<'tcx>
|
||||
where
|
||||
'tcx: 'pat,
|
||||
{
|
||||
// Assemble the initial list of candidates. These top-level candidates are 1:1 with the
|
||||
// input patterns, but other parts of match lowering also introduce subcandidates (for
|
||||
// sub-or-patterns). So inside the algorithm, the candidates list may not correspond to
|
||||
// match arms directly.
|
||||
let mut candidates: Vec<Candidate<'_, '_>> = patterns
|
||||
.into_iter()
|
||||
.map(|(pat, has_guard)| {
|
||||
Candidate::new(scrutinee_place_builder.clone(), pat, has_guard, self)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Link each leaf candidate to the `false_edge_start_block` of the next one.
|
||||
let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
|
||||
for candidate in candidates {
|
||||
candidate.visit_leaves(|leaf_candidate| {
|
||||
if let Some(ref mut prev) = previous_candidate {
|
||||
assert!(leaf_candidate.false_edge_start_block.is_some());
|
||||
prev.next_candidate_start_block = leaf_candidate.false_edge_start_block;
|
||||
let fake_borrow_temps = util::collect_fake_borrows(
|
||||
self,
|
||||
&candidates,
|
||||
scrutinee_span,
|
||||
scrutinee_place_builder.base(),
|
||||
);
|
||||
|
||||
// This will generate code to test scrutinee_place and branch to the appropriate arm block.
|
||||
// If none of the arms match, we branch to `otherwise_block`. When lowering a `match`
|
||||
// expression, exhaustiveness checking ensures that this block is unreachable.
|
||||
let mut candidate_refs = candidates.iter_mut().collect::<Vec<_>>();
|
||||
let otherwise_block =
|
||||
self.match_candidates(match_start_span, scrutinee_span, block, &mut candidate_refs);
|
||||
|
||||
// Set up false edges so that the borrow-checker cannot make use of the specific CFG we
|
||||
// generated. We falsely branch from each candidate to the one below it to make it as if we
|
||||
// were testing match branches one by one in order. In the refutable case we also want a
|
||||
// false edge to the final failure block.
|
||||
let mut next_candidate_start_block = if refutable { Some(otherwise_block) } else { None };
|
||||
for candidate in candidates.iter_mut().rev() {
|
||||
let has_guard = candidate.has_guard;
|
||||
candidate.visit_leaves_rev(|leaf_candidate| {
|
||||
if let Some(next_candidate_start_block) = next_candidate_start_block {
|
||||
let source_info = self.source_info(leaf_candidate.extra_data.span);
|
||||
// Falsely branch to `next_candidate_start_block` before reaching pre_binding.
|
||||
let old_pre_binding = leaf_candidate.pre_binding_block.unwrap();
|
||||
let new_pre_binding = self.cfg.start_new_block();
|
||||
self.false_edges(
|
||||
old_pre_binding,
|
||||
new_pre_binding,
|
||||
next_candidate_start_block,
|
||||
source_info,
|
||||
);
|
||||
leaf_candidate.pre_binding_block = Some(new_pre_binding);
|
||||
if has_guard {
|
||||
// Falsely branch to `next_candidate_start_block` also if the guard fails.
|
||||
let new_otherwise = self.cfg.start_new_block();
|
||||
let old_otherwise = leaf_candidate.otherwise_block.unwrap();
|
||||
self.false_edges(
|
||||
new_otherwise,
|
||||
old_otherwise,
|
||||
next_candidate_start_block,
|
||||
source_info,
|
||||
);
|
||||
leaf_candidate.otherwise_block = Some(new_otherwise);
|
||||
}
|
||||
}
|
||||
previous_candidate = Some(leaf_candidate);
|
||||
assert!(leaf_candidate.false_edge_start_block.is_some());
|
||||
next_candidate_start_block = leaf_candidate.false_edge_start_block;
|
||||
});
|
||||
}
|
||||
|
||||
if refutable {
|
||||
// In refutable cases there's always at least one candidate, and we want a false edge to
|
||||
// the failure block.
|
||||
previous_candidate.as_mut().unwrap().next_candidate_start_block = Some(otherwise_block)
|
||||
} else {
|
||||
if !refutable {
|
||||
// Match checking ensures `otherwise_block` is actually unreachable in irrefutable
|
||||
// cases.
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
|
|
@ -1479,7 +1597,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
|
||||
}
|
||||
|
||||
otherwise_block
|
||||
BuiltMatchTree {
|
||||
branches: candidates.into_iter().map(MatchTreeBranch::from_candidate).collect(),
|
||||
otherwise_block,
|
||||
fake_borrow_temps,
|
||||
}
|
||||
}
|
||||
|
||||
/// The main match algorithm. It begins with a set of candidates `candidates` and has the job of
|
||||
|
|
@ -2229,17 +2351,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
) -> BlockAnd<()> {
|
||||
let expr_span = self.thir[expr_id].span;
|
||||
let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
|
||||
let mut candidate = Candidate::new(scrutinee.clone(), pat, false, self);
|
||||
let otherwise_block = self.lower_match_tree(
|
||||
let built_tree = self.lower_match_tree(
|
||||
block,
|
||||
expr_span,
|
||||
&scrutinee,
|
||||
pat.span,
|
||||
&mut [&mut candidate],
|
||||
vec![(pat, HasMatchGuard::No)],
|
||||
true,
|
||||
);
|
||||
let [branch] = built_tree.branches.try_into().unwrap();
|
||||
|
||||
self.break_for_else(otherwise_block, self.source_info(expr_span));
|
||||
self.break_for_else(built_tree.otherwise_block, self.source_info(expr_span));
|
||||
|
||||
match declare_let_bindings {
|
||||
DeclareLetBindings::Yes => {
|
||||
|
|
@ -2261,7 +2383,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
let success = self.bind_pattern(
|
||||
self.source_info(pat.span),
|
||||
candidate,
|
||||
branch,
|
||||
&[],
|
||||
expr_span,
|
||||
None,
|
||||
|
|
@ -2269,7 +2391,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
);
|
||||
|
||||
// If branch coverage is enabled, record this branch.
|
||||
self.visit_coverage_conditional_let(pat, success, otherwise_block);
|
||||
self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block);
|
||||
|
||||
success.unit()
|
||||
}
|
||||
|
|
@ -2282,52 +2404,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// Note: we do not check earlier that if there is a guard,
|
||||
/// there cannot be move bindings. We avoid a use-after-move by only
|
||||
/// moving the binding once the guard has evaluated to true (see below).
|
||||
fn bind_and_guard_matched_candidate<'pat>(
|
||||
fn bind_and_guard_matched_candidate(
|
||||
&mut self,
|
||||
candidate: Candidate<'pat, 'tcx>,
|
||||
parent_data: &[PatternExtraData<'tcx>],
|
||||
sub_branch: MatchTreeSubBranch<'tcx>,
|
||||
fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
|
||||
scrutinee_span: Span,
|
||||
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
|
||||
schedule_drops: ScheduleDrops,
|
||||
emit_storage_live: EmitStorageLive,
|
||||
) -> BasicBlock {
|
||||
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
|
||||
debug!("bind_and_guard_matched_candidate(subbranch={:?})", sub_branch);
|
||||
|
||||
debug_assert!(candidate.match_pairs.is_empty());
|
||||
let block = sub_branch.success_block;
|
||||
|
||||
let candidate_source_info = self.source_info(candidate.extra_data.span);
|
||||
|
||||
let mut block = candidate.pre_binding_block.unwrap();
|
||||
|
||||
if candidate.next_candidate_start_block.is_some() {
|
||||
let fresh_block = self.cfg.start_new_block();
|
||||
self.false_edges(
|
||||
block,
|
||||
fresh_block,
|
||||
candidate.next_candidate_start_block,
|
||||
candidate_source_info,
|
||||
);
|
||||
block = fresh_block;
|
||||
}
|
||||
|
||||
if candidate.extra_data.is_never {
|
||||
if sub_branch.is_never {
|
||||
// This arm has a dummy body, we don't need to generate code for it. `block` is already
|
||||
// unreachable (except via false edge).
|
||||
let source_info = self.source_info(candidate.extra_data.span);
|
||||
let source_info = self.source_info(sub_branch.span);
|
||||
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
|
||||
return self.cfg.start_new_block();
|
||||
}
|
||||
|
||||
let ascriptions = parent_data
|
||||
.iter()
|
||||
.flat_map(|d| &d.ascriptions)
|
||||
.cloned()
|
||||
.chain(candidate.extra_data.ascriptions);
|
||||
let bindings =
|
||||
parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings);
|
||||
|
||||
self.ascribe_types(block, ascriptions);
|
||||
self.ascribe_types(block, sub_branch.ascriptions);
|
||||
|
||||
// Lower an instance of the arm guard (if present) for this candidate,
|
||||
// and then perform bindings for the arm body.
|
||||
|
|
@ -2338,9 +2436,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
// Bindings for guards require some extra handling to automatically
|
||||
// insert implicit references/dereferences.
|
||||
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
|
||||
self.bind_matched_candidate_for_guard(
|
||||
block,
|
||||
schedule_drops,
|
||||
sub_branch.bindings.iter(),
|
||||
);
|
||||
let guard_frame = GuardFrame {
|
||||
locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(),
|
||||
locals: sub_branch
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|b| GuardFrameLocal::new(b.var_id))
|
||||
.collect(),
|
||||
};
|
||||
debug!("entering guard building context: {:?}", guard_frame);
|
||||
self.guard_context.push(guard_frame);
|
||||
|
|
@ -2376,17 +2482,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
|
||||
}
|
||||
|
||||
let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| {
|
||||
let unreachable = self.cfg.start_new_block();
|
||||
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
|
||||
unreachable
|
||||
});
|
||||
self.false_edges(
|
||||
otherwise_post_guard_block,
|
||||
otherwise_block,
|
||||
candidate.next_candidate_start_block,
|
||||
source_info,
|
||||
);
|
||||
self.cfg.goto(otherwise_post_guard_block, source_info, sub_branch.otherwise_block);
|
||||
|
||||
// We want to ensure that the matched candidates are bound
|
||||
// after we have confirmed this candidate *and* any
|
||||
|
|
@ -2414,8 +2510,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// ```
|
||||
//
|
||||
// and that is clearly not correct.
|
||||
let by_value_bindings =
|
||||
bindings.filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
|
||||
let by_value_bindings = sub_branch
|
||||
.bindings
|
||||
.iter()
|
||||
.filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
|
||||
// Read all of the by reference bindings to ensure that the
|
||||
// place they refer to can't be modified by the guard.
|
||||
for binding in by_value_bindings.clone() {
|
||||
|
|
@ -2443,7 +2541,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.bind_matched_candidate_for_arm_body(
|
||||
block,
|
||||
schedule_drops,
|
||||
bindings,
|
||||
sub_branch.bindings.iter(),
|
||||
emit_storage_live,
|
||||
);
|
||||
block
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::Ty;
|
||||
|
|
@ -18,18 +16,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&mut self,
|
||||
from_block: BasicBlock,
|
||||
real_target: BasicBlock,
|
||||
imaginary_target: Option<BasicBlock>,
|
||||
imaginary_target: BasicBlock,
|
||||
source_info: SourceInfo,
|
||||
) {
|
||||
match imaginary_target {
|
||||
Some(target) if target != real_target => {
|
||||
self.cfg.terminate(
|
||||
from_block,
|
||||
source_info,
|
||||
TerminatorKind::FalseEdge { real_target, imaginary_target: target },
|
||||
);
|
||||
}
|
||||
_ => self.cfg.goto(from_block, source_info, real_target),
|
||||
if imaginary_target != real_target {
|
||||
self.cfg.terminate(
|
||||
from_block,
|
||||
source_info,
|
||||
TerminatorKind::FalseEdge { real_target, imaginary_target },
|
||||
);
|
||||
} else {
|
||||
self.cfg.goto(from_block, source_info, real_target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -71,10 +68,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// a MIR pass run after borrow checking.
|
||||
pub(super) fn collect_fake_borrows<'tcx>(
|
||||
cx: &mut Builder<'_, 'tcx>,
|
||||
candidates: &[&mut Candidate<'_, 'tcx>],
|
||||
candidates: &[Candidate<'_, 'tcx>],
|
||||
temp_span: Span,
|
||||
scrutinee_base: PlaceBase,
|
||||
) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
|
||||
if candidates.iter().all(|candidate| !candidate.has_guard) {
|
||||
// Fake borrows are only used when there is a guard.
|
||||
return Vec::new();
|
||||
}
|
||||
let mut collector =
|
||||
FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() };
|
||||
for candidate in candidates.iter() {
|
||||
|
|
@ -222,57 +223,6 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Visit all the bindings of these candidates. Because or-alternatives bind the same variables, we
|
||||
/// only explore the first one of each or-pattern.
|
||||
pub(super) fn visit_bindings<'tcx>(
|
||||
candidates: &[&mut Candidate<'_, 'tcx>],
|
||||
f: impl FnMut(&Binding<'tcx>),
|
||||
) {
|
||||
let mut visitor = BindingsVisitor { f, phantom: PhantomData };
|
||||
for candidate in candidates.iter() {
|
||||
visitor.visit_candidate(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct BindingsVisitor<'tcx, F> {
|
||||
f: F,
|
||||
phantom: PhantomData<&'tcx ()>,
|
||||
}
|
||||
|
||||
impl<'tcx, F> BindingsVisitor<'tcx, F>
|
||||
where
|
||||
F: FnMut(&Binding<'tcx>),
|
||||
{
|
||||
fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) {
|
||||
for binding in &candidate.extra_data.bindings {
|
||||
(self.f)(binding)
|
||||
}
|
||||
for match_pair in &candidate.match_pairs {
|
||||
self.visit_match_pair(match_pair);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) {
|
||||
for binding in &flat_pat.extra_data.bindings {
|
||||
(self.f)(binding)
|
||||
}
|
||||
for match_pair in &flat_pat.match_pairs {
|
||||
self.visit_match_pair(match_pair);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'_, 'tcx>) {
|
||||
if let TestCase::Or { pats, .. } = &match_pair.test_case {
|
||||
// All the or-alternatives should bind the same locals, so we only visit the first one.
|
||||
self.visit_flat_pat(&pats[0])
|
||||
} else {
|
||||
for subpair in &match_pair.subpairs {
|
||||
self.visit_match_pair(subpair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
|
||||
match ref_mutability {
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||
// Clone dominators as we need them while mutating the body.
|
||||
let dominators = body.basic_blocks.dominators().clone();
|
||||
|
||||
let mut state = VnState::new(tcx, param_env, &ssa, &dominators, &body.local_decls);
|
||||
let mut state = VnState::new(tcx, body, param_env, &ssa, &dominators, &body.local_decls);
|
||||
ssa.for_each_assignment_mut(
|
||||
body.basic_blocks.as_mut_preserves_cfg(),
|
||||
|local, value, location| {
|
||||
|
|
@ -204,6 +204,7 @@ enum Value<'tcx> {
|
|||
value: Const<'tcx>,
|
||||
/// Some constants do not have a deterministic value. To avoid merging two instances of the
|
||||
/// same `Const`, we assign them an additional integer index.
|
||||
// `disambiguator` is 0 iff the constant is deterministic.
|
||||
disambiguator: usize,
|
||||
},
|
||||
/// An aggregate value, either tuple/closure/struct/enum.
|
||||
|
|
@ -266,21 +267,29 @@ struct VnState<'body, 'tcx> {
|
|||
impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ssa: &'body SsaLocals,
|
||||
dominators: &'body Dominators<BasicBlock>,
|
||||
local_decls: &'body LocalDecls<'tcx>,
|
||||
) -> Self {
|
||||
// Compute a rough estimate of the number of values in the body from the number of
|
||||
// statements. This is meant to reduce the number of allocations, but it's all right if
|
||||
// we miss the exact amount. We estimate based on 2 values per statement (one in LHS and
|
||||
// one in RHS) and 4 values per terminator (for call operands).
|
||||
let num_values =
|
||||
2 * body.basic_blocks.iter().map(|bbdata| bbdata.statements.len()).sum::<usize>()
|
||||
+ 4 * body.basic_blocks.len();
|
||||
VnState {
|
||||
tcx,
|
||||
ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine),
|
||||
param_env,
|
||||
local_decls,
|
||||
locals: IndexVec::from_elem(None, local_decls),
|
||||
rev_locals: IndexVec::default(),
|
||||
values: FxIndexSet::default(),
|
||||
evaluated: IndexVec::new(),
|
||||
next_opaque: Some(0),
|
||||
rev_locals: IndexVec::with_capacity(num_values),
|
||||
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
|
||||
evaluated: IndexVec::with_capacity(num_values),
|
||||
next_opaque: Some(1),
|
||||
feature_unsized_locals: tcx.features().unsized_locals,
|
||||
ssa,
|
||||
dominators,
|
||||
|
|
@ -293,9 +302,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
let (index, new) = self.values.insert_full(value);
|
||||
let index = VnIndex::from_usize(index);
|
||||
if new {
|
||||
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
|
||||
let evaluated = self.eval_to_const(index);
|
||||
let _index = self.evaluated.push(evaluated);
|
||||
debug_assert_eq!(index, _index);
|
||||
// No need to push to `rev_locals` if we finished listing assignments.
|
||||
if self.next_opaque.is_some() {
|
||||
let _index = self.rev_locals.push(SmallVec::new());
|
||||
debug_assert_eq!(index, _index);
|
||||
}
|
||||
}
|
||||
index
|
||||
}
|
||||
|
|
@ -332,7 +347,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
let is_sized = !self.feature_unsized_locals
|
||||
|| self.local_decls[local].ty.is_sized(self.tcx, self.param_env);
|
||||
if is_sized {
|
||||
self.rev_locals.ensure_contains_elem(value, SmallVec::new).push(local);
|
||||
self.rev_locals[value].push(local);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -346,6 +361,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
let next_opaque = self.next_opaque.as_mut()?;
|
||||
let disambiguator = *next_opaque;
|
||||
*next_opaque += 1;
|
||||
// `disambiguator: 0` means deterministic.
|
||||
debug_assert_ne!(disambiguator, 0);
|
||||
disambiguator
|
||||
};
|
||||
Some(self.insert(Value::Constant { value, disambiguator }))
|
||||
|
|
@ -353,12 +370,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
|
||||
fn insert_bool(&mut self, flag: bool) -> VnIndex {
|
||||
// Booleans are deterministic.
|
||||
self.insert(Value::Constant { value: Const::from_bool(self.tcx, flag), disambiguator: 0 })
|
||||
let value = Const::from_bool(self.tcx, flag);
|
||||
debug_assert!(value.is_deterministic());
|
||||
self.insert(Value::Constant { value, disambiguator: 0 })
|
||||
}
|
||||
|
||||
fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
|
||||
self.insert_constant(Const::from_scalar(self.tcx, scalar, ty))
|
||||
.expect("scalars are deterministic")
|
||||
// Scalars are deterministic.
|
||||
let value = Const::from_scalar(self.tcx, scalar, ty);
|
||||
debug_assert!(value.is_deterministic());
|
||||
self.insert(Value::Constant { value, disambiguator: 0 })
|
||||
}
|
||||
|
||||
fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
|
||||
|
|
@ -671,7 +692,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
fn simplify_place_projection(&mut self, place: &mut Place<'tcx>, location: Location) {
|
||||
// If the projection is indirect, we treat the local as a value, so can replace it with
|
||||
// another local.
|
||||
if place.is_indirect()
|
||||
if place.is_indirect_first_projection()
|
||||
&& let Some(base) = self.locals[place.local]
|
||||
&& let Some(new_local) = self.try_as_local(base, location)
|
||||
&& place.local != new_local
|
||||
|
|
@ -773,10 +794,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
location: Location,
|
||||
) -> Option<VnIndex> {
|
||||
match *operand {
|
||||
Operand::Constant(ref mut constant) => {
|
||||
let const_ = constant.const_.normalize(self.tcx, self.param_env);
|
||||
self.insert_constant(const_)
|
||||
}
|
||||
Operand::Constant(ref constant) => self.insert_constant(constant.const_),
|
||||
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
|
||||
let value = self.simplify_place_value(place, location)?;
|
||||
if let Some(const_) = self.try_as_constant(value) {
|
||||
|
|
@ -1371,8 +1389,13 @@ fn op_to_prop_const<'tcx>(
|
|||
// If this constant has scalar ABI, return it as a `ConstValue::Scalar`.
|
||||
if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi
|
||||
&& let Ok(scalar) = ecx.read_scalar(op)
|
||||
&& scalar.try_to_scalar_int().is_ok()
|
||||
{
|
||||
if !scalar.try_to_scalar_int().is_ok() {
|
||||
// Check that we do not leak a pointer.
|
||||
// Those pointers may lose part of their identity in codegen.
|
||||
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
|
||||
return None;
|
||||
}
|
||||
return Some(ConstValue::Scalar(scalar));
|
||||
}
|
||||
|
||||
|
|
@ -1436,12 +1459,11 @@ impl<'tcx> VnState<'_, 'tcx> {
|
|||
|
||||
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
|
||||
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
|
||||
// This was already constant in MIR, do not change it.
|
||||
if let Value::Constant { value, disambiguator: _ } = *self.get(index)
|
||||
// If the constant is not deterministic, adding an additional mention of it in MIR will
|
||||
// not give the same value as the former mention.
|
||||
&& value.is_deterministic()
|
||||
{
|
||||
// This was already constant in MIR, do not change it. If the constant is not
|
||||
// deterministic, adding an additional mention of it in MIR will not give the same value as
|
||||
// the former mention.
|
||||
if let Value::Constant { value, disambiguator: 0 } = *self.get(index) {
|
||||
debug_assert!(value.is_deterministic());
|
||||
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -698,6 +698,18 @@ where
|
|||
if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? {
|
||||
Err(NoSolution)
|
||||
} else {
|
||||
// While the trait bound itself may be unknowable, we may be able to
|
||||
// prove that a super trait is not implemented. For this, we recursively
|
||||
// prove the super trait bounds of the current goal.
|
||||
//
|
||||
// We skip the goal itself as that one would cycle.
|
||||
let predicate: I::Predicate = trait_ref.upcast(cx);
|
||||
ecx.add_goals(
|
||||
GoalSource::Misc,
|
||||
elaborate::elaborate(cx, [predicate])
|
||||
.skip(1)
|
||||
.map(|predicate| goal.with(cx, predicate)),
|
||||
);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ parse_box_not_pat = expected pattern, found {$descr}
|
|||
.suggestion = escape `box` to use it as an identifier
|
||||
|
||||
parse_box_syntax_removed = `box_syntax` has been removed
|
||||
.suggestion = use `Box::new()` instead
|
||||
parse_box_syntax_removed_suggestion = use `Box::new()` instead
|
||||
|
||||
parse_cannot_be_raw_ident = `{$ident}` cannot be a raw identifier
|
||||
|
||||
|
|
@ -365,6 +365,7 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment
|
|||
.sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style
|
||||
|
||||
parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
|
||||
.label = this is not an unsafe attribute
|
||||
.suggestion = remove the `unsafe(...)`
|
||||
.note = extraneous unsafe is not allowed in attributes
|
||||
|
||||
|
|
|
|||
|
|
@ -555,12 +555,7 @@ pub(crate) enum MissingInInForLoopSub {
|
|||
code = "in"
|
||||
)]
|
||||
InNotOf(#[primary_span] Span),
|
||||
#[suggestion(
|
||||
parse_add_in,
|
||||
style = "verbose",
|
||||
applicability = "maybe-incorrect",
|
||||
code = " in "
|
||||
)]
|
||||
#[suggestion(parse_add_in, style = "verbose", applicability = "maybe-incorrect", code = " in ")]
|
||||
AddIn(#[primary_span] Span),
|
||||
}
|
||||
|
||||
|
|
@ -2730,15 +2725,24 @@ impl HelpUseLatestEdition {
|
|||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_box_syntax_removed)]
|
||||
pub struct BoxSyntaxRemoved<'a> {
|
||||
pub struct BoxSyntaxRemoved {
|
||||
#[primary_span]
|
||||
#[suggestion(
|
||||
code = "Box::new({code})",
|
||||
applicability = "machine-applicable",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub span: Span,
|
||||
pub code: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub sugg: AddBoxNew,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
parse_box_syntax_removed_suggestion,
|
||||
applicability = "machine-applicable",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub struct AddBoxNew {
|
||||
#[suggestion_part(code = "Box::new(")]
|
||||
pub box_kw_and_lo: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -3188,6 +3192,7 @@ pub(crate) struct DotDotRangeAttribute {
|
|||
#[note]
|
||||
pub struct InvalidAttrUnsafe {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub name: Path,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,16 +73,31 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
|
|||
fn eof_err(&mut self) -> PErr<'psess> {
|
||||
let msg = "this file contains an unclosed delimiter";
|
||||
let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg);
|
||||
for &(_, sp) in &self.diag_info.open_braces {
|
||||
err.span_label(sp, "unclosed delimiter");
|
||||
|
||||
let unclosed_delimiter_show_limit = 5;
|
||||
let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_braces.len());
|
||||
for &(_, span) in &self.diag_info.open_braces[..len] {
|
||||
err.span_label(span, "unclosed delimiter");
|
||||
self.diag_info.unmatched_delims.push(UnmatchedDelim {
|
||||
found_delim: None,
|
||||
found_span: self.token.span,
|
||||
unclosed_span: Some(sp),
|
||||
unclosed_span: Some(span),
|
||||
candidate_span: None,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some((_, span)) = self.diag_info.open_braces.get(unclosed_delimiter_show_limit)
|
||||
&& self.diag_info.open_braces.len() >= unclosed_delimiter_show_limit + 2
|
||||
{
|
||||
err.span_label(
|
||||
*span,
|
||||
format!(
|
||||
"another {} unclosed delimiters begin from here",
|
||||
self.diag_info.open_braces.len() - unclosed_delimiter_show_limit
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((delim, _)) = self.diag_info.open_braces.last() {
|
||||
report_suspicious_mismatch_block(
|
||||
&mut err,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ enum OuterAttributeType {
|
|||
Attribute,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AllowLeadingUnsafe {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
/// Parses attributes that appear before an item.
|
||||
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
|
||||
|
|
@ -332,7 +338,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
|
||||
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
|
||||
let cfg_predicate = self.parse_meta_item()?;
|
||||
let cfg_predicate = self.parse_meta_item(AllowLeadingUnsafe::No)?;
|
||||
self.expect(&token::Comma)?;
|
||||
|
||||
// Presumably, the majority of the time there will only be one attr.
|
||||
|
|
@ -368,7 +374,10 @@ impl<'a> Parser<'a> {
|
|||
/// MetaItem = SimplePath ( '=' UNSUFFIXED_LIT | '(' MetaSeq? ')' )? ;
|
||||
/// MetaSeq = MetaItemInner (',' MetaItemInner)* ','? ;
|
||||
/// ```
|
||||
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
|
||||
pub fn parse_meta_item(
|
||||
&mut self,
|
||||
unsafe_allowed: AllowLeadingUnsafe,
|
||||
) -> PResult<'a, ast::MetaItem> {
|
||||
// We can't use `maybe_whole` here because it would bump in the `None`
|
||||
// case, which we don't want.
|
||||
if let token::Interpolated(nt) = &self.token.kind
|
||||
|
|
@ -384,7 +393,11 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
let lo = self.token.span;
|
||||
let is_unsafe = self.eat_keyword(kw::Unsafe);
|
||||
let is_unsafe = if unsafe_allowed == AllowLeadingUnsafe::Yes {
|
||||
self.eat_keyword(kw::Unsafe)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let unsafety = if is_unsafe {
|
||||
let unsafe_span = self.prev_token.span;
|
||||
self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
|
||||
|
|
@ -427,7 +440,7 @@ impl<'a> Parser<'a> {
|
|||
Err(err) => err.cancel(), // we provide a better error below
|
||||
}
|
||||
|
||||
match self.parse_meta_item() {
|
||||
match self.parse_meta_item(AllowLeadingUnsafe::No) {
|
||||
Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
|
||||
Err(err) => err.cancel(), // we provide a better error below
|
||||
}
|
||||
|
|
|
|||
|
|
@ -618,10 +618,12 @@ impl<'a> Parser<'a> {
|
|||
/// Parse `box expr` - this syntax has been removed, but we still parse this
|
||||
/// for now to provide a more useful error
|
||||
fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {
|
||||
let (span, _) = self.parse_expr_prefix_common(box_kw)?;
|
||||
let inner_span = span.with_lo(box_kw.hi());
|
||||
let code = self.psess.source_map().span_to_snippet(inner_span).unwrap();
|
||||
let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() });
|
||||
let (span, expr) = self.parse_expr_prefix_common(box_kw)?;
|
||||
// Make a multipart suggestion instead of `span_to_snippet` in case source isn't available
|
||||
let box_kw_and_lo = box_kw.until(self.interpolated_or_expr_span(&expr));
|
||||
let hi = span.shrink_to_hi();
|
||||
let sugg = errors::AddBoxNew { box_kw_and_lo, hi };
|
||||
let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span, sugg });
|
||||
Ok((span, ExprKind::Err(guar)))
|
||||
}
|
||||
|
||||
|
|
@ -3153,7 +3155,8 @@ impl<'a> Parser<'a> {
|
|||
|
||||
if !require_comma {
|
||||
arm_body = Some(expr);
|
||||
this.eat(&token::Comma);
|
||||
// Eat a comma if it exists, though.
|
||||
let _ = this.eat(&token::Comma);
|
||||
Ok(Recovered::No)
|
||||
} else if let Some((span, guar)) =
|
||||
this.parse_arm_body_missing_braces(&expr, arrow_span)
|
||||
|
|
@ -3654,7 +3657,7 @@ impl<'a> Parser<'a> {
|
|||
fields.push(f);
|
||||
}
|
||||
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
|
||||
self.eat(&token::Comma);
|
||||
let _ = self.eat(&token::Comma);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,8 @@ impl<'a> Parser<'a> {
|
|||
span: this.prev_token.span,
|
||||
});
|
||||
|
||||
this.eat(&token::Comma);
|
||||
// Eat a trailing comma, if it exists.
|
||||
let _ = this.eat(&token::Comma);
|
||||
}
|
||||
|
||||
let param = if this.check_lifetime() {
|
||||
|
|
|
|||
|
|
@ -1192,13 +1192,14 @@ impl<'a> Parser<'a> {
|
|||
mut safety: Safety,
|
||||
) -> PResult<'a, ItemInfo> {
|
||||
let abi = self.parse_abi(); // ABI?
|
||||
// FIXME: This recovery should be tested better.
|
||||
if safety == Safety::Default
|
||||
&& self.token.is_keyword(kw::Unsafe)
|
||||
&& self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Brace))
|
||||
{
|
||||
self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err().emit();
|
||||
safety = Safety::Unsafe(self.token.span);
|
||||
self.eat_keyword(kw::Unsafe);
|
||||
let _ = self.eat_keyword(kw::Unsafe);
|
||||
}
|
||||
let module = ast::ForeignMod {
|
||||
safety,
|
||||
|
|
@ -1759,7 +1760,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
self.eat(&token::CloseDelim(Delimiter::Brace));
|
||||
self.expect(&token::CloseDelim(Delimiter::Brace))?;
|
||||
} else {
|
||||
let token_str = super::token_descr(&self.token);
|
||||
let where_str = if parsed_where { "" } else { "`where`, or " };
|
||||
|
|
@ -1902,7 +1903,7 @@ impl<'a> Parser<'a> {
|
|||
if let Some(_guar) = guar {
|
||||
// Handle a case like `Vec<u8>>,` where we can continue parsing fields
|
||||
// after the comma
|
||||
self.eat(&token::Comma);
|
||||
let _ = self.eat(&token::Comma);
|
||||
|
||||
// `check_trailing_angle_brackets` already emitted a nicer error, as
|
||||
// proven by the presence of `_guar`. We can continue parsing.
|
||||
|
|
|
|||
|
|
@ -547,6 +547,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn check_noexpect(&self, tok: &TokenKind) -> bool {
|
||||
self.token == *tok
|
||||
}
|
||||
|
|
@ -556,6 +557,7 @@ impl<'a> Parser<'a> {
|
|||
/// the main purpose of this function is to reduce the cluttering of the suggestions list
|
||||
/// which using the normal eat method could introduce in some cases.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn eat_noexpect(&mut self, tok: &TokenKind) -> bool {
|
||||
let is_present = self.check_noexpect(tok);
|
||||
if is_present {
|
||||
|
|
@ -566,6 +568,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Consumes a token 'tok' if it exists. Returns whether the given token was present.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn eat(&mut self, tok: &TokenKind) -> bool {
|
||||
let is_present = self.check(tok);
|
||||
if is_present {
|
||||
|
|
@ -577,12 +580,14 @@ impl<'a> Parser<'a> {
|
|||
/// If the next token is the given keyword, returns `true` without eating it.
|
||||
/// An expectation is also added for diagnostics purposes.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn check_keyword(&mut self, kw: Symbol) -> bool {
|
||||
self.expected_tokens.push(TokenType::Keyword(kw));
|
||||
self.token.is_keyword(kw)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool {
|
||||
if self.check_keyword(kw) {
|
||||
return true;
|
||||
|
|
@ -602,6 +607,7 @@ impl<'a> Parser<'a> {
|
|||
/// Otherwise, returns `false`. An expectation is also added for diagnostics purposes.
|
||||
// Public for rustc_builtin_macros and rustfmt usage.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn eat_keyword(&mut self, kw: Symbol) -> bool {
|
||||
if self.check_keyword(kw) {
|
||||
self.bump();
|
||||
|
|
@ -615,6 +621,7 @@ impl<'a> Parser<'a> {
|
|||
/// If the case differs (and is ignored) an error is issued.
|
||||
/// This is useful for recovery.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool {
|
||||
if self.eat_keyword(kw) {
|
||||
return true;
|
||||
|
|
@ -636,6 +643,7 @@ impl<'a> Parser<'a> {
|
|||
/// Otherwise, returns `false`. No expectation is added.
|
||||
// Public for rustc_builtin_macros usage.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool {
|
||||
if self.token.is_keyword(kw) {
|
||||
self.bump();
|
||||
|
|
@ -648,7 +656,7 @@ impl<'a> Parser<'a> {
|
|||
/// If the given word is not a keyword, signals an error.
|
||||
/// If the next token is not the given word, signals an error.
|
||||
/// Otherwise, eats it.
|
||||
fn expect_keyword(&mut self, kw: Symbol) -> PResult<'a, ()> {
|
||||
pub fn expect_keyword(&mut self, kw: Symbol) -> PResult<'a, ()> {
|
||||
if !self.eat_keyword(kw) { self.unexpected() } else { Ok(()) }
|
||||
}
|
||||
|
||||
|
|
@ -1025,8 +1033,11 @@ impl<'a> Parser<'a> {
|
|||
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||
) -> PResult<'a, (ThinVec<T>, Trailing)> {
|
||||
let (val, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?;
|
||||
if matches!(recovered, Recovered::No) {
|
||||
self.eat(ket);
|
||||
if matches!(recovered, Recovered::No) && !self.eat(ket) {
|
||||
self.dcx().span_delayed_bug(
|
||||
self.token.span,
|
||||
"recovered but `parse_seq_to_before_end` did not give us the ket token",
|
||||
);
|
||||
}
|
||||
Ok((val, trailing))
|
||||
}
|
||||
|
|
@ -1250,7 +1261,7 @@ impl<'a> Parser<'a> {
|
|||
if pat {
|
||||
self.psess.gated_spans.gate(sym::inline_const_pat, span);
|
||||
}
|
||||
self.eat_keyword(kw::Const);
|
||||
self.expect_keyword(kw::Const)?;
|
||||
let (attrs, blk) = self.parse_inner_attrs_and_block()?;
|
||||
let anon_const = AnonConst {
|
||||
id: DUMMY_NODE_ID,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
match kind {
|
||||
// `expr_2021` and earlier
|
||||
NonterminalKind::Expr(Expr2021 { .. }) => {
|
||||
token.can_begin_expr()
|
||||
// This exception is here for backwards compatibility.
|
||||
|
|
@ -46,8 +47,16 @@ impl<'a> Parser<'a> {
|
|||
// This exception is here for backwards compatibility.
|
||||
&& !token.is_keyword(kw::Const)
|
||||
}
|
||||
// Current edition expressions
|
||||
NonterminalKind::Expr(Expr) => {
|
||||
token.can_begin_expr()
|
||||
// In Edition 2024, `_` is considered an expression, so we
|
||||
// need to allow it here because `token.can_begin_expr()` does
|
||||
// not consider `_` to be an expression.
|
||||
//
|
||||
// Because `can_begin_expr` is used elsewhere, we need to reduce
|
||||
// the scope of where the `_` is considered an expression to
|
||||
// just macro parsing code.
|
||||
(token.can_begin_expr() || token.is_keyword(kw::Underscore))
|
||||
// This exception is here for backwards compatibility.
|
||||
&& !token.is_keyword(kw::Let)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -313,7 +313,8 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
// Generic arguments are found - `<`, `(`, `::<` or `::(`.
|
||||
self.eat(&token::PathSep);
|
||||
// First, eat `::` if it exists.
|
||||
let _ = self.eat(&token::PathSep);
|
||||
let lo = self.token.span;
|
||||
let args = if self.eat_lt() {
|
||||
// `<'a, T, A = U>`
|
||||
|
|
|
|||
|
|
@ -26,76 +26,35 @@ pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) {
|
|||
let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
|
||||
let attr_item = attr.get_normal_item();
|
||||
|
||||
let is_unsafe_attr = attr_info.is_some_and(|attr| attr.safety == AttributeSafety::Unsafe);
|
||||
|
||||
if features.unsafe_attributes {
|
||||
if is_unsafe_attr {
|
||||
if let ast::Safety::Default = attr_item.unsafety {
|
||||
let path_span = attr_item.path.span;
|
||||
|
||||
// If the `attr_item`'s span is not from a macro, then just suggest
|
||||
// wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
|
||||
// `unsafe(`, `)` right after and right before the opening and closing
|
||||
// square bracket respectively.
|
||||
let diag_span = if attr_item.span().can_be_used_for_suggestions() {
|
||||
attr_item.span()
|
||||
} else {
|
||||
attr.span
|
||||
.with_lo(attr.span.lo() + BytePos(2))
|
||||
.with_hi(attr.span.hi() - BytePos(1))
|
||||
};
|
||||
|
||||
if attr.span.at_least_rust_2024() {
|
||||
psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe {
|
||||
span: path_span,
|
||||
suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion {
|
||||
left: diag_span.shrink_to_lo(),
|
||||
right: diag_span.shrink_to_hi(),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
psess.buffer_lint(
|
||||
UNSAFE_ATTR_OUTSIDE_UNSAFE,
|
||||
path_span,
|
||||
ast::CRATE_NODE_ID,
|
||||
BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
|
||||
attribute_name_span: path_span,
|
||||
sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Safety::Unsafe(unsafe_span) = attr_item.unsafety {
|
||||
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
|
||||
span: unsafe_span,
|
||||
name: attr_item.path.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// All non-builtin attributes are considered safe
|
||||
let safety = attr_info.map(|x| x.safety).unwrap_or(AttributeSafety::Normal);
|
||||
check_attribute_safety(features, psess, safety, attr);
|
||||
|
||||
// Check input tokens for built-in and key-value attributes.
|
||||
match attr_info {
|
||||
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
|
||||
Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
|
||||
match parse_meta(psess, attr) {
|
||||
Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, *name, *template),
|
||||
// Don't check safety again, we just did that
|
||||
Ok(meta) => check_builtin_meta_item(
|
||||
features, psess, &meta, attr.style, *name, *template, false,
|
||||
),
|
||||
Err(err) => {
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ if let AttrArgs::Eq(..) = attr_item.args => {
|
||||
// All key-value attributes are restricted to meta-item syntax.
|
||||
match parse_meta(psess, attr) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
err.emit();
|
||||
_ => {
|
||||
if let AttrArgs::Eq(..) = attr_item.args {
|
||||
// All key-value attributes are restricted to meta-item syntax.
|
||||
match parse_meta(psess, attr) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -198,12 +157,85 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_attribute_safety(
|
||||
features: &Features,
|
||||
psess: &ParseSess,
|
||||
safety: AttributeSafety,
|
||||
attr: &Attribute,
|
||||
) {
|
||||
if !features.unsafe_attributes {
|
||||
return;
|
||||
}
|
||||
|
||||
let attr_item = attr.get_normal_item();
|
||||
|
||||
if safety == AttributeSafety::Unsafe {
|
||||
if let ast::Safety::Default = attr_item.unsafety {
|
||||
let path_span = attr_item.path.span;
|
||||
|
||||
// If the `attr_item`'s span is not from a macro, then just suggest
|
||||
// wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
|
||||
// `unsafe(`, `)` right after and right before the opening and closing
|
||||
// square bracket respectively.
|
||||
let diag_span = if attr_item.span().can_be_used_for_suggestions() {
|
||||
attr_item.span()
|
||||
} else {
|
||||
attr.span.with_lo(attr.span.lo() + BytePos(2)).with_hi(attr.span.hi() - BytePos(1))
|
||||
};
|
||||
|
||||
if attr.span.at_least_rust_2024() {
|
||||
psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe {
|
||||
span: path_span,
|
||||
suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion {
|
||||
left: diag_span.shrink_to_lo(),
|
||||
right: diag_span.shrink_to_hi(),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
psess.buffer_lint(
|
||||
UNSAFE_ATTR_OUTSIDE_UNSAFE,
|
||||
path_span,
|
||||
ast::CRATE_NODE_ID,
|
||||
BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
|
||||
attribute_name_span: path_span,
|
||||
sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Safety::Unsafe(unsafe_span) = attr_item.unsafety {
|
||||
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
|
||||
span: unsafe_span,
|
||||
name: attr_item.path.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called by `check_builtin_meta_item` and code that manually denies
|
||||
// `unsafe(...)` in `cfg`
|
||||
pub fn deny_builtin_meta_unsafety(features: &Features, psess: &ParseSess, meta: &MetaItem) {
|
||||
// This only supports denying unsafety right now - making builtin attributes
|
||||
// support unsafety will requite us to thread the actual `Attribute` through
|
||||
// for the nice diagnostics.
|
||||
if features.unsafe_attributes {
|
||||
if let Safety::Unsafe(unsafe_span) = meta.unsafety {
|
||||
psess
|
||||
.dcx()
|
||||
.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: meta.path.clone() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_builtin_meta_item(
|
||||
features: &Features,
|
||||
psess: &ParseSess,
|
||||
meta: &MetaItem,
|
||||
style: ast::AttrStyle,
|
||||
name: Symbol,
|
||||
template: AttributeTemplate,
|
||||
deny_unsafety: bool,
|
||||
) {
|
||||
// Some special attributes like `cfg` must be checked
|
||||
// before the generic check, so we skip them here.
|
||||
|
|
@ -212,6 +244,10 @@ pub fn check_builtin_meta_item(
|
|||
if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
|
||||
emit_malformed_attribute(psess, style, meta.span, name, template);
|
||||
}
|
||||
|
||||
if deny_unsafety {
|
||||
deny_builtin_meta_unsafety(features, psess, meta);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_malformed_attribute(
|
||||
|
|
|
|||
|
|
@ -542,6 +542,10 @@ passes_only_has_effect_on =
|
|||
*[unspecified] (unspecified--this is a compiler bug)
|
||||
}
|
||||
|
||||
passes_optimize_not_fn_or_closure =
|
||||
attribute should be applied to function or closure
|
||||
.label = not a function or closure
|
||||
|
||||
passes_outer_crate_level_attr =
|
||||
crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -68,6 +68,10 @@ pub struct CoverageNotFnOrClosure {
|
|||
pub defn_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_optimize_not_fn_or_closure)]
|
||||
pub struct OptimizeNotFnOrClosure;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_should_be_applied_to_fn)]
|
||||
pub struct AttrShouldBeAppliedToFn {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_hir::HirId;
|
|||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef,
|
||||
|
|
@ -26,6 +26,8 @@ use crate::pat_column::PatternColumn;
|
|||
use crate::usefulness::{compute_match_usefulness, PlaceValidity};
|
||||
use crate::{errors, Captures, PatCx, PrivateUninhabitedField};
|
||||
|
||||
mod print;
|
||||
|
||||
// Re-export rustc-specific versions of all these types.
|
||||
pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcPatCtxt<'p, 'tcx>>;
|
||||
pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet<RustcPatCtxt<'p, 'tcx>>;
|
||||
|
|
@ -773,8 +775,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert back to a `thir::Pat` for diagnostic purposes.
|
||||
fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> Pat<'tcx> {
|
||||
/// Convert to a [`print::Pat`] for diagnostic purposes.
|
||||
fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat<'tcx> {
|
||||
use print::{Pat, PatKind};
|
||||
use MaybeInfiniteInt::*;
|
||||
let cx = self;
|
||||
let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
|
||||
|
|
@ -808,19 +811,20 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
|
||||
};
|
||||
|
||||
Pat { ty: ty.inner(), span: DUMMY_SP, kind }
|
||||
Pat { ty: ty.inner(), kind }
|
||||
}
|
||||
|
||||
/// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes.
|
||||
pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
|
||||
// This works by converting the witness pattern back to a `thir::Pat`
|
||||
// This works by converting the witness pattern to a `print::Pat`
|
||||
// and then printing that, but callers don't need to know that.
|
||||
self.hoist_witness_pat(pat).to_string()
|
||||
}
|
||||
|
||||
/// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't
|
||||
/// Convert to a [`print::Pat`] for diagnostic purposes. This panics for patterns that don't
|
||||
/// appear in diagnostics, like float ranges.
|
||||
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> {
|
||||
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
|
||||
use print::{FieldPat, Pat, PatKind};
|
||||
let cx = self;
|
||||
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
|
||||
let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p)));
|
||||
|
|
@ -840,7 +844,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
// the pattern is a box pattern.
|
||||
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
|
||||
}
|
||||
ty::Adt(adt_def, args) => {
|
||||
ty::Adt(adt_def, _args) => {
|
||||
let variant_index = RustcPatCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
|
||||
let subpatterns = subpatterns
|
||||
.enumerate()
|
||||
|
|
@ -848,7 +852,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
.collect();
|
||||
|
||||
if adt_def.is_enum() {
|
||||
PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns }
|
||||
PatKind::Variant { adt_def: *adt_def, variant_index, subpatterns }
|
||||
} else {
|
||||
PatKind::Leaf { subpatterns }
|
||||
}
|
||||
|
|
@ -885,7 +889,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
}
|
||||
}
|
||||
let suffix: Box<[_]> = subpatterns.collect();
|
||||
let wild = Pat::wildcard_from_ty(pat.ty().inner());
|
||||
let wild = Pat { ty: pat.ty().inner(), kind: PatKind::Wild };
|
||||
PatKind::Slice {
|
||||
prefix: prefix.into_boxed_slice(),
|
||||
slice: Some(Box::new(wild)),
|
||||
|
|
@ -906,7 +910,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
}
|
||||
};
|
||||
|
||||
Pat { ty: pat.ty().inner(), span: DUMMY_SP, kind }
|
||||
Pat { ty: pat.ty().inner(), kind }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1003,12 +1007,11 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
|||
}
|
||||
// `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with
|
||||
// `gap+1`.
|
||||
let suggested_range: thir::Pat<'_> = {
|
||||
let suggested_range: String = {
|
||||
// Suggest `lo..=gap` instead.
|
||||
let mut suggested_range = thir_pat.clone();
|
||||
let thir::PatKind::Range(range) = &mut suggested_range.kind else { unreachable!() };
|
||||
range.end = rustc_hir::RangeEnd::Included;
|
||||
suggested_range
|
||||
let mut suggested_range = PatRange::clone(range);
|
||||
suggested_range.end = rustc_hir::RangeEnd::Included;
|
||||
suggested_range.to_string()
|
||||
};
|
||||
let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty());
|
||||
if gapped_with.is_empty() {
|
||||
|
|
@ -1023,7 +1026,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
|||
// That's the gap that isn't covered.
|
||||
max: gap_as_pat.to_string(),
|
||||
// Suggest `lo..=max` instead.
|
||||
suggestion: suggested_range.to_string(),
|
||||
suggestion: suggested_range,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
|
|
@ -1037,7 +1040,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
|||
// That's the gap that isn't covered.
|
||||
gap: gap_as_pat.to_string(),
|
||||
// Suggest `lo..=gap` instead.
|
||||
suggestion: suggested_range.to_string(),
|
||||
suggestion: suggested_range,
|
||||
// All these ranges skipped over `gap` which we think is probably a
|
||||
// mistake.
|
||||
gap_with: gapped_with
|
||||
|
|
@ -1045,7 +1048,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
|||
.map(|pat| errors::GappedRange {
|
||||
span: pat.data().span,
|
||||
gap: gap_as_pat.to_string(),
|
||||
first_range: thir_pat.to_string(),
|
||||
first_range: range.to_string(),
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
|
|
|
|||
193
compiler/rustc_pattern_analysis/src/rustc/print.rs
Normal file
193
compiler/rustc_pattern_analysis/src/rustc/print.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//! Pattern analysis sometimes wants to print patterns as part of a user-visible
|
||||
//! diagnostic.
|
||||
//!
|
||||
//! Historically it did so by creating a synthetic [`thir::Pat`](rustc_middle::thir::Pat)
|
||||
//! and printing that, but doing so was making it hard to modify the THIR pattern
|
||||
//! representation for other purposes.
|
||||
//!
|
||||
//! So this module contains a forked copy of `thir::Pat` that is used _only_
|
||||
//! for diagnostics, and has been partly simplified to remove things that aren't
|
||||
//! needed for printing.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use rustc_middle::thir::PatRange;
|
||||
use rustc_middle::ty::{self, AdtDef, Ty};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_span::sym;
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FieldPat<'tcx> {
|
||||
pub(crate) field: FieldIdx,
|
||||
pub(crate) pattern: Box<Pat<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Pat<'tcx> {
|
||||
pub(crate) ty: Ty<'tcx>,
|
||||
pub(crate) kind: PatKind<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum PatKind<'tcx> {
|
||||
Wild,
|
||||
|
||||
Variant {
|
||||
adt_def: AdtDef<'tcx>,
|
||||
variant_index: VariantIdx,
|
||||
subpatterns: Vec<FieldPat<'tcx>>,
|
||||
},
|
||||
|
||||
Leaf {
|
||||
subpatterns: Vec<FieldPat<'tcx>>,
|
||||
},
|
||||
|
||||
Deref {
|
||||
subpattern: Box<Pat<'tcx>>,
|
||||
},
|
||||
|
||||
Constant {
|
||||
value: mir::Const<'tcx>,
|
||||
},
|
||||
|
||||
Range(Box<PatRange<'tcx>>),
|
||||
|
||||
Slice {
|
||||
prefix: Box<[Box<Pat<'tcx>>]>,
|
||||
slice: Option<Box<Pat<'tcx>>>,
|
||||
suffix: Box<[Box<Pat<'tcx>>]>,
|
||||
},
|
||||
|
||||
Never,
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for Pat<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Printing lists is a chore.
|
||||
let mut first = true;
|
||||
let mut start_or_continue = |s| {
|
||||
if first {
|
||||
first = false;
|
||||
""
|
||||
} else {
|
||||
s
|
||||
}
|
||||
};
|
||||
let mut start_or_comma = || start_or_continue(", ");
|
||||
|
||||
match self.kind {
|
||||
PatKind::Wild => write!(f, "_"),
|
||||
PatKind::Never => write!(f, "!"),
|
||||
PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => {
|
||||
let variant_and_name = match self.kind {
|
||||
PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| {
|
||||
let variant = adt_def.variant(variant_index);
|
||||
let adt_did = adt_def.did();
|
||||
let name = if tcx.get_diagnostic_item(sym::Option) == Some(adt_did)
|
||||
|| tcx.get_diagnostic_item(sym::Result) == Some(adt_did)
|
||||
{
|
||||
variant.name.to_string()
|
||||
} else {
|
||||
format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name)
|
||||
};
|
||||
Some((variant, name))
|
||||
}),
|
||||
_ => self.ty.ty_adt_def().and_then(|adt_def| {
|
||||
if !adt_def.is_enum() {
|
||||
ty::tls::with(|tcx| {
|
||||
Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
if let Some((variant, name)) = &variant_and_name {
|
||||
write!(f, "{name}")?;
|
||||
|
||||
// Only for Adt we can have `S {...}`,
|
||||
// which we handle separately here.
|
||||
if variant.ctor.is_none() {
|
||||
write!(f, " {{ ")?;
|
||||
|
||||
let mut printed = 0;
|
||||
for p in subpatterns {
|
||||
if let PatKind::Wild = p.pattern.kind {
|
||||
continue;
|
||||
}
|
||||
let name = variant.fields[p.field].name;
|
||||
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
|
||||
printed += 1;
|
||||
}
|
||||
|
||||
let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union());
|
||||
if printed < variant.fields.len() && (!is_union || printed == 0) {
|
||||
write!(f, "{}..", start_or_comma())?;
|
||||
}
|
||||
|
||||
return write!(f, " }}");
|
||||
}
|
||||
}
|
||||
|
||||
let num_fields =
|
||||
variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len());
|
||||
if num_fields != 0 || variant_and_name.is_none() {
|
||||
write!(f, "(")?;
|
||||
for i in 0..num_fields {
|
||||
write!(f, "{}", start_or_comma())?;
|
||||
|
||||
// Common case: the field is where we expect it.
|
||||
if let Some(p) = subpatterns.get(i) {
|
||||
if p.field.index() == i {
|
||||
write!(f, "{}", p.pattern)?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we have to go looking for it.
|
||||
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
|
||||
write!(f, "{}", p.pattern)?;
|
||||
} else {
|
||||
write!(f, "_")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
PatKind::Deref { ref subpattern } => {
|
||||
match self.ty.kind() {
|
||||
ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
|
||||
ty::Ref(_, _, mutbl) => {
|
||||
write!(f, "&{}", mutbl.prefix_str())?;
|
||||
}
|
||||
_ => bug!("{} is a bad Deref pattern type", self.ty),
|
||||
}
|
||||
write!(f, "{subpattern}")
|
||||
}
|
||||
PatKind::Constant { value } => write!(f, "{value}"),
|
||||
PatKind::Range(ref range) => write!(f, "{range}"),
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
|
||||
write!(f, "[")?;
|
||||
for p in prefix.iter() {
|
||||
write!(f, "{}{}", start_or_comma(), p)?;
|
||||
}
|
||||
if let Some(ref slice) = *slice {
|
||||
write!(f, "{}", start_or_comma())?;
|
||||
match slice.kind {
|
||||
PatKind::Wild => {}
|
||||
_ => write!(f, "{slice}")?,
|
||||
}
|
||||
write!(f, "..")?;
|
||||
}
|
||||
for p in suffix.iter() {
|
||||
write!(f, "{}{}", start_or_comma(), p)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2026,14 +2026,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
Applicability::MaybeIncorrect,
|
||||
)),
|
||||
)
|
||||
} else if ident.name == kw::Underscore {
|
||||
(format!("`_` is not a valid crate or module name"), None)
|
||||
} else if self.tcx.sess.is_rust_2015() {
|
||||
(
|
||||
format!("you might be missing crate `{ident}`"),
|
||||
Some((
|
||||
vec![],
|
||||
format!(
|
||||
"consider adding `extern crate {ident}` to use the `{ident}` crate"
|
||||
),
|
||||
vec![(
|
||||
self.current_crate_outer_attr_insert_span,
|
||||
format!("extern crate {ident};\n"),
|
||||
)],
|
||||
format!("consider importing the `{ident}` crate"),
|
||||
Applicability::MaybeIncorrect,
|
||||
)),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use rustc_middle::metadata::{ModChild, Reexport};
|
|||
use rustc_middle::{span_bug, ty};
|
||||
use rustc_session::lint::builtin::{
|
||||
AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
||||
UNUSED_IMPORTS,
|
||||
REDUNDANT_IMPORTS, UNUSED_IMPORTS,
|
||||
};
|
||||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
|
|
@ -1387,14 +1387,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
let mut redundant_spans: Vec<_> = redundant_span.present_items().collect();
|
||||
redundant_spans.sort();
|
||||
redundant_spans.dedup();
|
||||
/* FIXME(unused_imports): Add this back as a new lint
|
||||
self.lint_buffer.buffer_lint_with_diagnostic(
|
||||
UNUSED_IMPORTS,
|
||||
self.lint_buffer.buffer_lint(
|
||||
REDUNDANT_IMPORTS,
|
||||
id,
|
||||
import.span,
|
||||
BuiltinLintDiag::RedundantImport(redundant_spans, source),
|
||||
);
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2671,17 +2671,17 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
// Store all seen lifetimes names from outer scopes.
|
||||
let mut seen_lifetimes = FxHashSet::default();
|
||||
|
||||
// We also can't shadow bindings from the parent item
|
||||
if let RibKind::AssocItem = kind {
|
||||
let mut add_bindings_for_ns = |ns| {
|
||||
let parent_rib = self.ribs[ns]
|
||||
.iter()
|
||||
.rfind(|r| matches!(r.kind, RibKind::Item(..)))
|
||||
.expect("associated item outside of an item");
|
||||
// We also can't shadow bindings from associated parent items.
|
||||
for ns in [ValueNS, TypeNS] {
|
||||
for parent_rib in self.ribs[ns].iter().rev() {
|
||||
seen_bindings.extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span)));
|
||||
};
|
||||
add_bindings_for_ns(ValueNS);
|
||||
add_bindings_for_ns(TypeNS);
|
||||
|
||||
// Break at mod level, to account for nested items which are
|
||||
// allowed to shadow generic param names.
|
||||
if matches!(parent_rib.kind, RibKind::Module(..)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forbid shadowing lifetime bindings
|
||||
|
|
|
|||
|
|
@ -178,8 +178,8 @@ enum ImplTraitContext {
|
|||
|
||||
/// Used for tracking import use types which will be used for redundant import checking.
|
||||
/// ### Used::Scope Example
|
||||
/// ```rust,ignore (redundant_imports)
|
||||
/// #![deny(unused_imports)]
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(redundant_imports)]
|
||||
/// use std::mem::drop;
|
||||
/// fn main() {
|
||||
/// let s = Box::new(32);
|
||||
|
|
@ -1180,6 +1180,10 @@ pub struct Resolver<'a, 'tcx> {
|
|||
/// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations.
|
||||
/// Needed because glob delegations exclude explicitly defined names.
|
||||
impl_binding_keys: FxHashMap<LocalDefId, FxHashSet<BindingKey>>,
|
||||
|
||||
/// This is the `Span` where an `extern crate foo;` suggestion would be inserted, if `foo`
|
||||
/// could be a crate that wasn't imported. For diagnostics use only.
|
||||
current_crate_outer_attr_insert_span: Span,
|
||||
}
|
||||
|
||||
/// Nothing really interesting here; it just provides memory for the rest of the crate.
|
||||
|
|
@ -1342,6 +1346,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
attrs: &[ast::Attribute],
|
||||
crate_span: Span,
|
||||
current_crate_outer_attr_insert_span: Span,
|
||||
arenas: &'a ResolverArenas<'a>,
|
||||
) -> Resolver<'a, 'tcx> {
|
||||
let root_def_id = CRATE_DEF_ID.to_def_id();
|
||||
|
|
@ -1525,6 +1530,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
glob_delegation_invoc_ids: Default::default(),
|
||||
impl_unexpanded_invocations: Default::default(),
|
||||
impl_binding_keys: Default::default(),
|
||||
current_crate_outer_attr_insert_span,
|
||||
};
|
||||
|
||||
let root_parent_scope = ParentScope::module(graph_root, &resolver);
|
||||
|
|
|
|||
|
|
@ -1701,6 +1701,7 @@ symbols! {
|
|||
saturating_add,
|
||||
saturating_div,
|
||||
saturating_sub,
|
||||
select_unpredictable,
|
||||
self_in_typedefs,
|
||||
self_struct_ctor,
|
||||
semitransparent,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::abi::call::{ArgAbi, FnAbi, Reg};
|
||||
use crate::abi::Abi;
|
||||
use crate::abi::{Abi, Float, Primitive};
|
||||
|
||||
// Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing
|
||||
|
||||
|
|
@ -18,8 +18,12 @@ pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
|
|||
// FIXME(eddyb) there should be a size cap here
|
||||
// (probably what clang calls "illegal vectors").
|
||||
}
|
||||
Abi::Scalar(_) => {
|
||||
if a.layout.size.bytes() > 8 {
|
||||
Abi::Scalar(scalar) => {
|
||||
// Match what LLVM does for `f128` so that `compiler-builtins` builtins match up
|
||||
// with what LLVM expects.
|
||||
if a.layout.size.bytes() > 8
|
||||
&& !matches!(scalar.primitive(), Primitive::Float(Float::F128))
|
||||
{
|
||||
a.make_indirect();
|
||||
} else {
|
||||
a.extend_integer_width_to(32);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ pub fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "loongarch64-unknown-linux-musl".into(),
|
||||
metadata: crate::spec::TargetMetadata {
|
||||
description: Some("LoongArch64 Linux (LP64D ABI) with musl 1.2.3".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: None, // ?
|
||||
description: Some("LoongArch64 Linux (LP64D ABI) with musl 1.2.5".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(true),
|
||||
std: Some(true),
|
||||
},
|
||||
pointer_width: 64,
|
||||
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "loongarch64-unknown-none".into(),
|
||||
metadata: crate::spec::TargetMetadata {
|
||||
description: None,
|
||||
description: Some("Freestanding/bare-metal LoongArch64".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(false),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "loongarch64-unknown-none".into(),
|
||||
metadata: crate::spec::TargetMetadata {
|
||||
description: None,
|
||||
description: Some("Freestanding/bare-metal LoongArch64 softfloat".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(false),
|
||||
|
|
|
|||
|
|
@ -1582,10 +1582,7 @@ pub enum TypeErrorAdditionalDiags {
|
|||
span: Span,
|
||||
code: String,
|
||||
},
|
||||
#[multipart_suggestion(
|
||||
trait_selection_meant_str_literal,
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
#[multipart_suggestion(trait_selection_meant_str_literal, applicability = "machine-applicable")]
|
||||
MeantStrLiteral {
|
||||
#[suggestion_part(code = "\"")]
|
||||
start: Span,
|
||||
|
|
|
|||
|
|
@ -42,8 +42,15 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
|
|||
| ty::Foreign(..)
|
||||
| ty::Error(_) => true,
|
||||
|
||||
// `T is PAT`, `[T; N]`, and `[T]` have same properties as T.
|
||||
ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty),
|
||||
// `T is PAT` and `[T]` have same properties as T.
|
||||
ty::Pat(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty),
|
||||
ty::Array(ty, size) => {
|
||||
// Empty array never has a dtor. See issue #110288.
|
||||
match size.try_to_target_usize(tcx) {
|
||||
Some(0) => true,
|
||||
_ => trivial_dropck_outlives(tcx, *ty),
|
||||
}
|
||||
}
|
||||
|
||||
// (T1..Tn) and closures have same properties as T1..Tn --
|
||||
// check if *all* of them are trivial.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue