mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-20 03:32:52 +00:00
commit
7a41eacf17
44
Cargo.lock
44
Cargo.lock
@ -1443,6 +1443,13 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generate-windows-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"windows-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
@ -1546,9 +1553,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash 0.7.4",
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1558,6 +1562,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
|
||||
dependencies = [
|
||||
"ahash 0.8.2",
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1938,9 +1945,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
version = "0.2.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024"
|
||||
dependencies = [
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
@ -3353,6 +3360,7 @@ dependencies = [
|
||||
"rustc_middle",
|
||||
"rustc_mir_build",
|
||||
"rustc_mir_dataflow",
|
||||
"rustc_mir_transform",
|
||||
"rustc_monomorphize",
|
||||
"rustc_parse",
|
||||
"rustc_passes",
|
||||
@ -3861,8 +3869,10 @@ dependencies = [
|
||||
"rustc_const_eval",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_mir_dataflow",
|
||||
"rustc_serialize",
|
||||
@ -4607,7 +4617,7 @@ dependencies = [
|
||||
"core",
|
||||
"dlmalloc",
|
||||
"fortanix-sgx-abi",
|
||||
"hashbrown 0.12.3",
|
||||
"hashbrown 0.13.1",
|
||||
"hermit-abi 0.3.0",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
@ -5506,6 +5516,22 @@ dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-bindgen"
|
||||
version = "0.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6935fb09b84ee57929ae92518b475f5dfdfbeb87c5334756acc28ee8e202b60"
|
||||
dependencies = [
|
||||
"windows-metadata",
|
||||
"windows-tokens",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-metadata"
|
||||
version = "0.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f5bca94a32bf1e6a376522b6601275a3b611ee885ec0f1b6a05f17e8cfd3385"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
@ -5569,6 +5595,12 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-tokens"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b34c9a3b28cb41db7385546f7f9a8179348dffc89923dde66857b1ba5312f6b4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
|
@ -39,6 +39,7 @@ members = [
|
||||
"src/tools/collect-license-metadata",
|
||||
"src/tools/generate-copyright",
|
||||
"src/tools/suggest-tests",
|
||||
"src/tools/generate-windows-sys",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
|
@ -120,6 +120,12 @@ impl Path {
|
||||
pub fn is_global(&self) -> bool {
|
||||
!self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot
|
||||
}
|
||||
|
||||
/// If this path is a single identifier with no arguments, does not ensure
|
||||
/// that the path resolves to a const param, the caller should check this.
|
||||
pub fn is_potential_trivial_const_arg(&self) -> bool {
|
||||
self.segments.len() == 1 && self.segments[0].args.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// A segment of a path: an identifier, an optional lifetime, and a set of types.
|
||||
@ -1154,7 +1160,9 @@ impl Expr {
|
||||
///
|
||||
/// If this is not the case, name resolution does not resolve `N` when using
|
||||
/// `min_const_generics` as more complex expressions are not supported.
|
||||
pub fn is_potential_trivial_const_param(&self) -> bool {
|
||||
///
|
||||
/// Does not ensure that the path resolves to a const param, the caller should check this.
|
||||
pub fn is_potential_trivial_const_arg(&self) -> bool {
|
||||
let this = if let ExprKind::Block(block, None) = &self.kind
|
||||
&& block.stmts.len() == 1
|
||||
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
|
||||
@ -1165,8 +1173,7 @@ impl Expr {
|
||||
};
|
||||
|
||||
if let ExprKind::Path(None, path) = &this.kind
|
||||
&& path.segments.len() == 1
|
||||
&& path.segments[0].args.is_none()
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
|
@ -188,6 +188,9 @@ pub trait Visitor<'ast>: Sized {
|
||||
fn visit_variant(&mut self, v: &'ast Variant) {
|
||||
walk_variant(self, v)
|
||||
}
|
||||
fn visit_variant_discr(&mut self, discr: &'ast AnonConst) {
|
||||
self.visit_anon_const(discr);
|
||||
}
|
||||
fn visit_label(&mut self, label: &'ast Label) {
|
||||
walk_label(self, label)
|
||||
}
|
||||
@ -380,7 +383,7 @@ where
|
||||
visitor.visit_ident(variant.ident);
|
||||
visitor.visit_vis(&variant.vis);
|
||||
visitor.visit_variant_data(&variant.data);
|
||||
walk_list!(visitor, visit_anon_const, &variant.disr_expr);
|
||||
walk_list!(visitor, visit_variant_discr, &variant.disr_expr);
|
||||
walk_list!(visitor, visit_attribute, &variant.attrs);
|
||||
}
|
||||
|
||||
|
@ -446,7 +446,30 @@ fn expand_format_args<'hir>(
|
||||
&& argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
|
||||
&& arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
|
||||
|
||||
let args = if use_simple_array {
|
||||
let args = if arguments.is_empty() {
|
||||
// Generate:
|
||||
// &<core::fmt::Argument>::none()
|
||||
//
|
||||
// Note:
|
||||
// `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
|
||||
//
|
||||
// This makes sure that this still fails to compile, even when the argument is inlined:
|
||||
//
|
||||
// ```
|
||||
// let f = format_args!("{}", "a");
|
||||
// println!("{f}"); // error E0716
|
||||
// ```
|
||||
//
|
||||
// Cases where keeping the object around is allowed, such as `format_args!("a")`,
|
||||
// are handled above by the `allow_const` case.
|
||||
let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||
macsp,
|
||||
hir::LangItem::FormatArgument,
|
||||
sym::none,
|
||||
));
|
||||
let none = ctx.expr_call(macsp, none_fn, &[]);
|
||||
ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
|
||||
} else if use_simple_array {
|
||||
// Generate:
|
||||
// &[
|
||||
// <core::fmt::Argument>::new_display(&arg0),
|
||||
|
@ -1190,13 +1190,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// parsing. We try to resolve that ambiguity by attempting resolution in both the
|
||||
// type and value namespaces. If we resolved the path in the value namespace, we
|
||||
// transform it into a generic const argument.
|
||||
TyKind::Path(qself, path) => {
|
||||
TyKind::Path(None, path) => {
|
||||
if let Some(res) = self
|
||||
.resolver
|
||||
.get_partial_res(ty.id)
|
||||
.and_then(|partial_res| partial_res.full_res())
|
||||
{
|
||||
if !res.matches_ns(Namespace::TypeNS) {
|
||||
if !res.matches_ns(Namespace::TypeNS)
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
{
|
||||
debug!(
|
||||
"lower_generic_arg: Lowering type argument as const argument: {:?}",
|
||||
ty,
|
||||
@ -1218,7 +1220,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
||||
let path_expr = Expr {
|
||||
id: ty.id,
|
||||
kind: ExprKind::Path(qself.clone(), path.clone()),
|
||||
kind: ExprKind::Path(None, path.clone()),
|
||||
span,
|
||||
attrs: AttrVec::new(),
|
||||
tokens: None,
|
||||
@ -1477,6 +1479,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
/// Given a function definition like:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::fmt::Debug;
|
||||
///
|
||||
/// fn test<'a, T: Debug>(x: &'a T) -> impl Debug + 'a {
|
||||
/// x
|
||||
/// }
|
||||
@ -1484,13 +1488,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
///
|
||||
/// we will create a TAIT definition in the HIR like
|
||||
///
|
||||
/// ```
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// type TestReturn<'a, T, 'x> = impl Debug + 'x
|
||||
/// ```
|
||||
///
|
||||
/// and return a type like `TestReturn<'static, T, 'a>`, so that the function looks like:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// fn test<'a, T: Debug>(x: &'a T) -> TestReturn<'static, T, 'a>
|
||||
/// ```
|
||||
///
|
||||
|
@ -603,6 +603,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
|
||||
gate_all!(yeet_expr, "`do yeet` expression is experimental");
|
||||
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
|
||||
gate_all!(const_closures, "const closures are experimental");
|
||||
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
|
||||
|
||||
if !visitor.features.negative_bounds {
|
||||
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
|
||||
|
@ -556,8 +556,7 @@ impl<'a> State<'a> {
|
||||
self.pclose();
|
||||
}
|
||||
ast::ExprKind::OffsetOf(container, fields) => {
|
||||
// FIXME: This should have its own syntax, distinct from a macro invocation.
|
||||
self.word("offset_of!");
|
||||
self.word("builtin # offset_of");
|
||||
self.popen();
|
||||
self.rbox(0, Inconsistent);
|
||||
self.print_type(container);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::Node;
|
||||
@ -478,179 +478,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
|
||||
match self.local_names[local] {
|
||||
Some(name) if !local_decl.from_compiler_desugaring() => {
|
||||
let label = match *local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
|
||||
let (span, suggestion) =
|
||||
suggest_ampmut_self(self.infcx.tcx, local_decl);
|
||||
Some((true, span, suggestion))
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
binding_mode: ty::BindingMode::BindByValue(_),
|
||||
opt_ty_info,
|
||||
..
|
||||
})) => {
|
||||
// check if the RHS is from desugaring
|
||||
let opt_assignment_rhs_span =
|
||||
self.body.find_assignments(local).first().map(|&location| {
|
||||
if let Some(mir::Statement {
|
||||
source_info: _,
|
||||
kind:
|
||||
mir::StatementKind::Assign(box (
|
||||
_,
|
||||
mir::Rvalue::Use(mir::Operand::Copy(place)),
|
||||
)),
|
||||
}) = self.body[location.block]
|
||||
.statements
|
||||
.get(location.statement_index)
|
||||
{
|
||||
self.body.local_decls[place.local].source_info.span
|
||||
} else {
|
||||
self.body.source_info(location).span
|
||||
}
|
||||
});
|
||||
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
|
||||
// on for loops, RHS points to the iterator part
|
||||
Some(DesugaringKind::ForLoop) => {
|
||||
self.suggest_similar_mut_method_for_for_loop(&mut err);
|
||||
err.span_label(opt_assignment_rhs_span.unwrap(), format!(
|
||||
"this iterator yields `{pointer_sigil}` {pointer_desc}s",
|
||||
));
|
||||
None
|
||||
}
|
||||
// don't create labels for compiler-generated spans
|
||||
Some(_) => None,
|
||||
None => {
|
||||
let label = if name != kw::SelfLower {
|
||||
suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
)
|
||||
} else {
|
||||
match local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::Var(
|
||||
mir::VarBindingForm {
|
||||
opt_ty_info: None, ..
|
||||
},
|
||||
)) => {
|
||||
let (span, sugg) = suggest_ampmut_self(
|
||||
self.infcx.tcx,
|
||||
local_decl,
|
||||
);
|
||||
(true, span, sugg)
|
||||
}
|
||||
// explicit self (eg `self: &'a Self`)
|
||||
_ => suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
),
|
||||
}
|
||||
};
|
||||
Some(label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
binding_mode: ty::BindingMode::BindByReference(_),
|
||||
..
|
||||
})) => {
|
||||
let pattern_span = local_decl.source_info.span;
|
||||
suggest_ref_mut(self.infcx.tcx, pattern_span)
|
||||
.map(|replacement| (true, pattern_span, replacement))
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match label {
|
||||
Some((true, err_help_span, suggested_code)) => {
|
||||
let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
|
||||
if !is_trait_sig {
|
||||
err.span_suggestion_verbose(
|
||||
err_help_span,
|
||||
format!(
|
||||
"consider changing this to be a mutable {pointer_desc}"
|
||||
),
|
||||
suggested_code,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if let Some(x) = local_trait {
|
||||
err.span_suggestion_verbose(
|
||||
x,
|
||||
format!(
|
||||
"consider changing that to be a mutable {pointer_desc}"
|
||||
),
|
||||
suggested_code,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
Some((false, err_label_span, message)) => {
|
||||
struct BindingFinder {
|
||||
span: Span,
|
||||
hir_id: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BindingFinder {
|
||||
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
|
||||
if let hir::StmtKind::Local(local) = s.kind {
|
||||
if local.pat.span == self.span {
|
||||
self.hir_id = Some(local.hir_id);
|
||||
}
|
||||
}
|
||||
hir::intravisit::walk_stmt(self, s);
|
||||
}
|
||||
}
|
||||
let hir_map = self.infcx.tcx.hir();
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
|
||||
let node = hir_map.find(hir_id);
|
||||
let hir_id = if let Some(hir::Node::Item(item)) = node
|
||||
&& let hir::ItemKind::Fn(.., body_id) = item.kind
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let mut v = BindingFinder {
|
||||
span: err_label_span,
|
||||
hir_id: None,
|
||||
};
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
|
||||
{
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => (
|
||||
"specifying",
|
||||
local.pat.span.shrink_to_hi(),
|
||||
format!(": {message}"),
|
||||
),
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("consider {changing} this binding's type"),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
err_label_span,
|
||||
format!(
|
||||
"consider changing this binding's type to be: `{message}`"
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
err.span_label(
|
||||
span,
|
||||
format!(
|
||||
@ -658,6 +485,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
so the data it refers to cannot be {acted_on}",
|
||||
),
|
||||
);
|
||||
|
||||
self.suggest_make_local_mut(&mut err, local, name);
|
||||
}
|
||||
_ => {
|
||||
err.span_label(
|
||||
@ -1131,6 +960,184 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_make_local_mut(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
|
||||
local: Local,
|
||||
name: Symbol,
|
||||
) {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
|
||||
let (pointer_sigil, pointer_desc) =
|
||||
if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
|
||||
|
||||
let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
|
||||
if is_trait_sig && local_trait.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let decl_span = match local_trait {
|
||||
Some(span) => span,
|
||||
None => local_decl.source_info.span,
|
||||
};
|
||||
|
||||
let label = match *local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
|
||||
let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
Some((true, decl_span, suggestion))
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
binding_mode: ty::BindingMode::BindByValue(_),
|
||||
opt_ty_info,
|
||||
..
|
||||
})) => {
|
||||
// check if the RHS is from desugaring
|
||||
let opt_assignment_rhs_span =
|
||||
self.body.find_assignments(local).first().map(|&location| {
|
||||
if let Some(mir::Statement {
|
||||
source_info: _,
|
||||
kind:
|
||||
mir::StatementKind::Assign(box (
|
||||
_,
|
||||
mir::Rvalue::Use(mir::Operand::Copy(place)),
|
||||
)),
|
||||
}) = self.body[location.block].statements.get(location.statement_index)
|
||||
{
|
||||
self.body.local_decls[place.local].source_info.span
|
||||
} else {
|
||||
self.body.source_info(location).span
|
||||
}
|
||||
});
|
||||
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
|
||||
// on for loops, RHS points to the iterator part
|
||||
Some(DesugaringKind::ForLoop) => {
|
||||
self.suggest_similar_mut_method_for_for_loop(err);
|
||||
err.span_label(
|
||||
opt_assignment_rhs_span.unwrap(),
|
||||
format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
|
||||
);
|
||||
None
|
||||
}
|
||||
// don't create labels for compiler-generated spans
|
||||
Some(_) => None,
|
||||
None => {
|
||||
let label = if name != kw::SelfLower {
|
||||
suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl.ty,
|
||||
decl_span,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
)
|
||||
} else {
|
||||
match local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
opt_ty_info: None,
|
||||
..
|
||||
})) => {
|
||||
let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
(true, decl_span, sugg)
|
||||
}
|
||||
// explicit self (eg `self: &'a Self`)
|
||||
_ => suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl.ty,
|
||||
decl_span,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
),
|
||||
}
|
||||
};
|
||||
Some(label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
binding_mode: ty::BindingMode::BindByReference(_),
|
||||
..
|
||||
})) => {
|
||||
let pattern_span: Span = local_decl.source_info.span;
|
||||
suggest_ref_mut(self.infcx.tcx, pattern_span)
|
||||
.map(|span| (true, span, "mut ".to_owned()))
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match label {
|
||||
Some((true, err_help_span, suggested_code)) => {
|
||||
err.span_suggestion_verbose(
|
||||
err_help_span,
|
||||
format!("consider changing this to be a mutable {pointer_desc}"),
|
||||
suggested_code,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
Some((false, err_label_span, message)) => {
|
||||
struct BindingFinder {
|
||||
span: Span,
|
||||
hir_id: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BindingFinder {
|
||||
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
|
||||
if let hir::StmtKind::Local(local) = s.kind {
|
||||
if local.pat.span == self.span {
|
||||
self.hir_id = Some(local.hir_id);
|
||||
}
|
||||
}
|
||||
hir::intravisit::walk_stmt(self, s);
|
||||
}
|
||||
}
|
||||
let hir_map = self.infcx.tcx.hir();
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
|
||||
let node = hir_map.find(hir_id);
|
||||
let hir_id = if let Some(hir::Node::Item(item)) = node
|
||||
&& let hir::ItemKind::Fn(.., body_id) = item.kind
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let mut v = BindingFinder {
|
||||
span: err_label_span,
|
||||
hir_id: None,
|
||||
};
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
|
||||
{
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => (
|
||||
"specifying",
|
||||
local.pat.span.shrink_to_hi(),
|
||||
format!(": {message}"),
|
||||
),
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("consider {changing} this binding's type"),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
err_label_span,
|
||||
format!(
|
||||
"consider changing this binding's type to be: `{message}`"
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
|
||||
@ -1160,25 +1167,18 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_ampmut_self<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decl: &mir::LocalDecl<'tcx>,
|
||||
) -> (Span, String) {
|
||||
let sp = local_decl.source_info.span;
|
||||
(
|
||||
sp,
|
||||
match tcx.sess.source_map().span_to_snippet(sp) {
|
||||
Ok(snippet) => {
|
||||
let lt_pos = snippet.find('\'');
|
||||
if let Some(lt_pos) = lt_pos {
|
||||
format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
|
||||
} else {
|
||||
"&mut self".to_string()
|
||||
}
|
||||
fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
|
||||
match tcx.sess.source_map().span_to_snippet(span) {
|
||||
Ok(snippet) => {
|
||||
let lt_pos = snippet.find('\'');
|
||||
if let Some(lt_pos) = lt_pos {
|
||||
format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
|
||||
} else {
|
||||
"&mut self".to_string()
|
||||
}
|
||||
_ => "&mut self".to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
_ => "&mut self".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
// When we want to suggest a user change a local variable to be a `&mut`, there
|
||||
@ -1198,72 +1198,89 @@ fn suggest_ampmut_self<'tcx>(
|
||||
// by trying (3.), then (2.) and finally falling back on (1.).
|
||||
fn suggest_ampmut<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decl: &mir::LocalDecl<'tcx>,
|
||||
decl_ty: Ty<'tcx>,
|
||||
decl_span: Span,
|
||||
opt_assignment_rhs_span: Option<Span>,
|
||||
opt_ty_info: Option<Span>,
|
||||
) -> (bool, Span, String) {
|
||||
// if there is a RHS and it starts with a `&` from it, then check if it is
|
||||
// mutable, and if not, put suggest putting `mut ` to make it mutable.
|
||||
// we don't have to worry about lifetime annotations here because they are
|
||||
// not valid when taking a reference. For example, the following is not valid Rust:
|
||||
//
|
||||
// let x: &i32 = &'a 5;
|
||||
// ^^ lifetime annotation not allowed
|
||||
//
|
||||
if let Some(assignment_rhs_span) = opt_assignment_rhs_span
|
||||
&& let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
|
||||
&& let Some(stripped) = src.strip_prefix('&')
|
||||
{
|
||||
let is_mutbl = |ty: &str| -> bool {
|
||||
if let Some(rest) = ty.strip_prefix("mut") {
|
||||
match rest.chars().next() {
|
||||
// e.g. `&mut x`
|
||||
Some(c) if c.is_whitespace() => true,
|
||||
// e.g. `&mut(x)`
|
||||
Some('(') => true,
|
||||
// e.g. `&mut{x}`
|
||||
Some('{') => true,
|
||||
// e.g. `&mutablevar`
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") {
|
||||
match rest.chars().next() {
|
||||
// e.g. `&mut x`
|
||||
Some(c) if c.is_whitespace() => true,
|
||||
// e.g. `&mut(x)`
|
||||
Some('(') => true,
|
||||
// e.g. `&mut{x}`
|
||||
Some('{') => true,
|
||||
// e.g. `&mutablevar`
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
|
||||
let lt_name = &src[1..ws_pos];
|
||||
let ty = src[ws_pos..].trim_start();
|
||||
if !is_mutbl(ty) {
|
||||
return (true, assignment_rhs_span, format!("&{lt_name} mut {ty}"));
|
||||
}
|
||||
} else if let Some(stripped) = src.strip_prefix('&') {
|
||||
let stripped = stripped.trim_start();
|
||||
if !is_mutbl(stripped) {
|
||||
return (true, assignment_rhs_span, format!("&mut {stripped}"));
|
||||
}
|
||||
// if the reference is already mutable then there is nothing we can do
|
||||
// here.
|
||||
if !is_mut {
|
||||
let span = assignment_rhs_span;
|
||||
// shrink the span to just after the `&` in `&variable`
|
||||
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
|
||||
|
||||
// FIXME(Ezrashaw): returning is bad because we still might want to
|
||||
// update the annotated type, see #106857.
|
||||
return (true, span, "mut ".to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
let (suggestibility, highlight_span) = match opt_ty_info {
|
||||
let (binding_exists, span) = match opt_ty_info {
|
||||
// if this is a variable binding with an explicit type,
|
||||
// try to highlight that for the suggestion.
|
||||
// then we will suggest changing it to be mutable.
|
||||
// this is `Applicability::MachineApplicable`.
|
||||
Some(ty_span) => (true, ty_span),
|
||||
|
||||
// otherwise, just highlight the span associated with
|
||||
// the (MIR) LocalDecl.
|
||||
None => (false, local_decl.source_info.span),
|
||||
// otherwise, we'll suggest *adding* an annotated type, we'll suggest
|
||||
// the RHS's type for that.
|
||||
// this is `Applicability::HasPlaceholders`.
|
||||
None => (false, decl_span),
|
||||
};
|
||||
|
||||
if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span)
|
||||
&& let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace))
|
||||
// if the binding already exists and is a reference with a explicit
|
||||
// lifetime, then we can suggest adding ` mut`. this is special-cased from
|
||||
// the path without a explicit lifetime.
|
||||
if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
|
||||
&& src.starts_with("&'")
|
||||
// note that `& 'a T` is invalid so this is correct.
|
||||
&& let Some(ws_pos) = src.find(char::is_whitespace)
|
||||
{
|
||||
let lt_name = &src[1..ws_pos];
|
||||
let ty = &src[ws_pos..];
|
||||
return (true, highlight_span, format!("&{lt_name} mut{ty}"));
|
||||
}
|
||||
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
|
||||
(true, span, " mut".to_owned())
|
||||
// if there is already a binding, we modify it to be `mut`
|
||||
} else if binding_exists {
|
||||
// shrink the span to just after the `&` in `&variable`
|
||||
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
|
||||
(true, span, "mut ".to_owned())
|
||||
} else {
|
||||
// otherwise, suggest that the user annotates the binding; we provide the
|
||||
// type of the local.
|
||||
let ty_mut = decl_ty.builtin_deref(true).unwrap();
|
||||
assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
|
||||
|
||||
let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
|
||||
assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
|
||||
(
|
||||
suggestibility,
|
||||
highlight_span,
|
||||
if local_decl.ty.is_ref() {
|
||||
format!("&mut {}", ty_mut.ty)
|
||||
} else {
|
||||
format!("*mut {}", ty_mut.ty)
|
||||
},
|
||||
)
|
||||
(
|
||||
false,
|
||||
span,
|
||||
format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_closure_or_generator(ty: Ty<'_>) -> bool {
|
||||
@ -1300,11 +1317,13 @@ fn get_mut_span_in_struct_field<'tcx>(
|
||||
}
|
||||
|
||||
/// If possible, suggest replacing `ref` with `ref mut`.
|
||||
fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
|
||||
let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
|
||||
if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
|
||||
let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
|
||||
Some(replacement)
|
||||
fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
|
||||
let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
|
||||
if pattern_str.starts_with("ref")
|
||||
&& pattern_str["ref".len()..].starts_with(rustc_lexer::is_whitespace)
|
||||
{
|
||||
let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
|
||||
Some(span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -845,7 +845,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some((alias_tys, alias_span)) = self
|
||||
let Some((alias_tys, alias_span, lt_addition_span)) = self
|
||||
.infcx
|
||||
.tcx
|
||||
.return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
|
||||
@ -858,10 +858,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
()
|
||||
}
|
||||
if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
|
||||
spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
|
||||
if lt.ident.name == kw::Empty {
|
||||
spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
|
||||
} else {
|
||||
spans_suggs.push((lt.ident.span, "'a".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
|
||||
|
||||
if let Some(lt_addition_span) = lt_addition_span {
|
||||
spans_suggs.push((lt_addition_span, "'a, ".to_string()));
|
||||
} else {
|
||||
spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
|
||||
}
|
||||
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!(
|
||||
"to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
|
||||
|
@ -150,10 +150,6 @@ builtin_macros_format_pos_mismatch = {$n} positional {$n ->
|
||||
*[more] arguments
|
||||
} in format string, but {$desc}
|
||||
|
||||
builtin_macros_offset_of_expected_field = expected field
|
||||
|
||||
builtin_macros_offset_of_expected_two_args = expected 2 arguments
|
||||
|
||||
builtin_macros_test_case_non_item = `#[test_case]` attribute is only allowed on items
|
||||
|
||||
builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
|
||||
|
@ -1038,7 +1038,7 @@ impl<'a> MethodDef<'a> {
|
||||
/// `&self.x` because that might cause an unaligned ref. So for any trait
|
||||
/// method that takes a reference, we use a local block to force a copy.
|
||||
/// This requires that the field impl `Copy`.
|
||||
/// ```
|
||||
/// ```rust,ignore (example)
|
||||
/// # struct A { x: u8, y: u8 }
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, other: &A) -> bool {
|
||||
|
@ -44,7 +44,6 @@ mod format;
|
||||
mod format_foreign;
|
||||
mod global_allocator;
|
||||
mod log_syntax;
|
||||
mod offset_of;
|
||||
mod source_util;
|
||||
mod test;
|
||||
mod trace_macros;
|
||||
@ -92,7 +91,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
||||
line: source_util::expand_line,
|
||||
log_syntax: log_syntax::expand_log_syntax,
|
||||
module_path: source_util::expand_mod,
|
||||
offset_of: offset_of::expand_offset_of,
|
||||
option_env: env::expand_option_env,
|
||||
core_panic: edition_panic::expand_panic,
|
||||
std_panic: edition_panic::expand_panic,
|
||||
|
@ -1,99 +0,0 @@
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{self, *};
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_span::{symbol::Ident, Span};
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_offset_of_expected_field)]
|
||||
struct ExpectedField {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_offset_of_expected_two_args)]
|
||||
struct ExpectedTwoArgs {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
}
|
||||
|
||||
fn parse_field<'a>(cx: &ExtCtxt<'a>, p: &mut Parser<'a>) -> PResult<'a, Ident> {
|
||||
let token = p.token.uninterpolate();
|
||||
let field = match token.kind {
|
||||
token::Ident(name, _) => Ident::new(name, token.span),
|
||||
token::Literal(token::Lit { kind: token::Integer, symbol, suffix: None }) => {
|
||||
Ident::new(symbol, token.span)
|
||||
}
|
||||
_ => return Err(cx.create_err(ExpectedField { span: p.token.span })),
|
||||
};
|
||||
|
||||
p.bump();
|
||||
|
||||
Ok(field)
|
||||
}
|
||||
|
||||
fn parse_args<'a>(
|
||||
cx: &mut ExtCtxt<'a>,
|
||||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> PResult<'a, (P<ast::Ty>, P<[Ident]>)> {
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
|
||||
let container = p.parse_ty()?;
|
||||
|
||||
p.expect(&token::Comma)?;
|
||||
|
||||
if p.eat(&token::Eof) {
|
||||
return Err(cx.create_err(ExpectedTwoArgs { span: sp }));
|
||||
}
|
||||
|
||||
let mut fields = Vec::new();
|
||||
|
||||
loop {
|
||||
let field = parse_field(cx, &mut p)?;
|
||||
fields.push(field);
|
||||
|
||||
if p.eat(&token::Dot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
p.eat(&token::Comma);
|
||||
|
||||
if !p.eat(&token::Eof) {
|
||||
return Err(cx.create_err(ExpectedTwoArgs { span: sp }));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Ok((container, fields.into()))
|
||||
}
|
||||
|
||||
pub fn expand_offset_of<'cx>(
|
||||
cx: &'cx mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> Box<dyn base::MacResult + 'cx> {
|
||||
match parse_args(cx, sp, tts) {
|
||||
Ok((container, fields)) => {
|
||||
let expr = P(ast::Expr {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
kind: ast::ExprKind::OffsetOf(container, fields),
|
||||
span: sp,
|
||||
attrs: ast::AttrVec::new(),
|
||||
tokens: None,
|
||||
});
|
||||
|
||||
MacEager::expr(expr)
|
||||
}
|
||||
Err(mut err) => {
|
||||
err.emit();
|
||||
DummyResult::any(sp)
|
||||
}
|
||||
}
|
||||
}
|
@ -361,7 +361,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
||||
self.instance.subst_mir_and_normalize_erasing_regions(
|
||||
self.tcx,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
value,
|
||||
ty::EarlyBinder(value),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1227,6 +1227,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
(value1, value2)
|
||||
}
|
||||
|
||||
fn filter_landing_pad(&mut self, pers_fn: RValue<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) {
|
||||
// TODO(antoyo): generate the correct landing pad
|
||||
self.cleanup_landing_pad(pers_fn)
|
||||
}
|
||||
|
||||
#[cfg(feature="master")]
|
||||
fn resume(&mut self, exn0: RValue<'gcc>, _exn1: RValue<'gcc>) {
|
||||
let exn_type = exn0.get_type();
|
||||
|
@ -236,7 +236,18 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
||||
InlineAsmArch::Nvptx64 => {}
|
||||
InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {}
|
||||
InlineAsmArch::Hexagon => {}
|
||||
InlineAsmArch::LoongArch64 => {}
|
||||
InlineAsmArch::LoongArch64 => {
|
||||
constraints.extend_from_slice(&[
|
||||
"~{$fcc0}".to_string(),
|
||||
"~{$fcc1}".to_string(),
|
||||
"~{$fcc2}".to_string(),
|
||||
"~{$fcc3}".to_string(),
|
||||
"~{$fcc4}".to_string(),
|
||||
"~{$fcc5}".to_string(),
|
||||
"~{$fcc6}".to_string(),
|
||||
"~{$fcc7}".to_string(),
|
||||
]);
|
||||
}
|
||||
InlineAsmArch::Mips | InlineAsmArch::Mips64 => {}
|
||||
InlineAsmArch::S390x => {
|
||||
constraints.push("~{cc}".to_string());
|
||||
|
@ -985,13 +985,20 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
|
||||
fn cleanup_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) {
|
||||
let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false);
|
||||
let landing_pad = self.landing_pad(ty, pers_fn, 1 /* FIXME should this be 0? */);
|
||||
let landing_pad = self.landing_pad(ty, pers_fn, 0);
|
||||
unsafe {
|
||||
llvm::LLVMSetCleanup(landing_pad, llvm::True);
|
||||
}
|
||||
(self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1))
|
||||
}
|
||||
|
||||
fn filter_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) {
|
||||
let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false);
|
||||
let landing_pad = self.landing_pad(ty, pers_fn, 1);
|
||||
self.add_clause(landing_pad, self.const_array(self.type_i8p(), &[]));
|
||||
(self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1))
|
||||
}
|
||||
|
||||
fn resume(&mut self, exn0: &'ll Value, exn1: &'ll Value) {
|
||||
let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false);
|
||||
let mut exn = self.const_poison(ty);
|
||||
|
@ -93,7 +93,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
let callee = cx.tcx.subst_and_normalize_erasing_regions(
|
||||
instance.substs,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
callee,
|
||||
ty::EarlyBinder(callee),
|
||||
);
|
||||
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
|
||||
cx.dbg_scope_fn(callee, callee_fn_abi, None)
|
||||
|
@ -529,7 +529,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
|
||||
instance.substs,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
cx.tcx.type_of(impl_def_id).skip_binder(),
|
||||
cx.tcx.type_of(impl_def_id),
|
||||
);
|
||||
|
||||
// Only "class" methods are generally understood by LLVM,
|
||||
|
@ -12,6 +12,7 @@ use object::{
|
||||
|
||||
use snap::write::FrameEncoder;
|
||||
|
||||
use object::elf::NT_GNU_PROPERTY_TYPE_0;
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::owned_slice::try_slice_owned;
|
||||
use rustc_data_structures::sync::MetadataRef;
|
||||
@ -93,6 +94,54 @@ pub(super) fn search_for_section<'a>(
|
||||
.map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
|
||||
}
|
||||
|
||||
fn add_gnu_property_note(
|
||||
file: &mut write::Object<'static>,
|
||||
architecture: Architecture,
|
||||
binary_format: BinaryFormat,
|
||||
endianness: Endianness,
|
||||
) {
|
||||
// check bti protection
|
||||
if binary_format != BinaryFormat::Elf
|
||||
|| !matches!(architecture, Architecture::X86_64 | Architecture::Aarch64)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let section = file.add_section(
|
||||
file.segment_name(StandardSegment::Data).to_vec(),
|
||||
b".note.gnu.property".to_vec(),
|
||||
SectionKind::Note,
|
||||
);
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
let n_namsz: u32 = 4; // Size of the n_name field
|
||||
let n_descsz: u32 = 16; // Size of the n_desc field
|
||||
let n_type: u32 = NT_GNU_PROPERTY_TYPE_0; // Type of note descriptor
|
||||
let header_values = [n_namsz, n_descsz, n_type];
|
||||
header_values.iter().for_each(|v| {
|
||||
data.extend_from_slice(&match endianness {
|
||||
Endianness::Little => v.to_le_bytes(),
|
||||
Endianness::Big => v.to_be_bytes(),
|
||||
})
|
||||
});
|
||||
data.extend_from_slice(b"GNU\0"); // Owner of the program property note
|
||||
let pr_type: u32 = match architecture {
|
||||
Architecture::X86_64 => 0xc0000002,
|
||||
Architecture::Aarch64 => 0xc0000000,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let pr_datasz: u32 = 4; //size of the pr_data field
|
||||
let pr_data: u32 = 3; //program property descriptor
|
||||
let pr_padding: u32 = 0;
|
||||
let property_values = [pr_type, pr_datasz, pr_data, pr_padding];
|
||||
property_values.iter().for_each(|v| {
|
||||
data.extend_from_slice(&match endianness {
|
||||
Endianness::Little => v.to_le_bytes(),
|
||||
Endianness::Big => v.to_be_bytes(),
|
||||
})
|
||||
});
|
||||
file.append_section_data(section, &data, 8);
|
||||
}
|
||||
|
||||
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
|
||||
let endianness = match sess.target.options.endian {
|
||||
Endian::Little => Endianness::Little,
|
||||
@ -205,6 +254,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
|
||||
_ => elf::ELFOSABI_NONE,
|
||||
};
|
||||
let abi_version = 0;
|
||||
add_gnu_property_note(&mut file, architecture, binary_format, endianness);
|
||||
file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
|
||||
Some(file)
|
||||
}
|
||||
|
@ -1600,7 +1600,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
bx = Bx::build(self.cx, llbb);
|
||||
|
||||
let llpersonality = self.cx.eh_personality();
|
||||
bx.cleanup_landing_pad(llpersonality);
|
||||
bx.filter_landing_pad(llpersonality);
|
||||
|
||||
funclet = None;
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
self.instance.subst_mir_and_normalize_erasing_regions(
|
||||
self.cx.tcx(),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
value,
|
||||
ty::EarlyBinder(value),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -274,6 +274,7 @@ pub trait BuilderMethods<'a, 'tcx>:
|
||||
|
||||
// These are used by everyone except msvc
|
||||
fn cleanup_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value);
|
||||
fn filter_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value);
|
||||
fn resume(&mut self, exn0: Self::Value, exn1: Self::Value);
|
||||
|
||||
// These are used only by msvc
|
||||
|
@ -495,7 +495,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
) -> Result<T, InterpError<'tcx>> {
|
||||
frame
|
||||
.instance
|
||||
.try_subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
|
||||
.try_subst_mir_and_normalize_erasing_regions(
|
||||
*self.tcx,
|
||||
self.param_env,
|
||||
ty::EarlyBinder(value),
|
||||
)
|
||||
.map_err(|_| err_inval!(TooGeneric))
|
||||
}
|
||||
|
||||
|
@ -109,9 +109,11 @@ impl Borrow<[u8]> for OwnedSlice {
|
||||
}
|
||||
|
||||
// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box<dyn Send + Sync>)`, which is `Send`
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe impl Send for OwnedSlice {}
|
||||
|
||||
// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box<dyn Send + Sync>)`, which is `Sync`
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe impl Sync for OwnedSlice {}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -101,7 +101,7 @@ use parking_lot::RwLock;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct EventFilter: u32 {
|
||||
struct EventFilter: u16 {
|
||||
const GENERIC_ACTIVITIES = 1 << 0;
|
||||
const QUERY_PROVIDERS = 1 << 1;
|
||||
const QUERY_CACHE_HITS = 1 << 2;
|
||||
|
@ -51,6 +51,7 @@ rustc_interface = { path = "../rustc_interface" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
|
||||
rustc_mir_transform = { path = "../rustc_mir_transform" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
@ -64,5 +65,8 @@ features = [
|
||||
[features]
|
||||
llvm = ['rustc_interface/llvm']
|
||||
max_level_info = ['rustc_log/max_level_info']
|
||||
rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler',
|
||||
'rustc_middle/rustc_use_parallel_compiler']
|
||||
rustc_use_parallel_compiler = [
|
||||
'rustc_data_structures/rustc_use_parallel_compiler',
|
||||
'rustc_interface/rustc_use_parallel_compiler',
|
||||
'rustc_middle/rustc_use_parallel_compiler'
|
||||
]
|
||||
|
@ -99,6 +99,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
|
||||
rustc_middle::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_mir_build::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_mir_transform::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_monomorphize::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_parse::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_passes::DEFAULT_LOCALE_RESOURCE,
|
||||
@ -745,6 +746,22 @@ fn print_crate_info(
|
||||
}
|
||||
}
|
||||
}
|
||||
DeploymentTarget => {
|
||||
use rustc_target::spec::current_apple_deployment_target;
|
||||
|
||||
if sess.target.is_like_osx {
|
||||
safe_println!(
|
||||
"deployment_target={}",
|
||||
current_apple_deployment_target(&sess.target)
|
||||
.expect("unknown Apple target OS")
|
||||
)
|
||||
} else {
|
||||
early_error(
|
||||
ErrorOutputType::default(),
|
||||
"only Apple targets currently support deployment version info",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Compilation::Stop
|
||||
|
@ -1,9 +1,11 @@
|
||||
#### Note: this error code is no longer emitted by the compiler
|
||||
|
||||
A non-`'static` lifetime was used in a const generic. This is currently not
|
||||
allowed.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0771
|
||||
```compile_fail,E0770
|
||||
#![feature(adt_const_params)]
|
||||
|
||||
fn function_with_str<'a, const STRING: &'a str>() {} // error!
|
||||
|
@ -571,6 +571,14 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
||||
Some((diagnostic, handler))
|
||||
}
|
||||
|
||||
/// Retrieves the [`Handler`] if available
|
||||
pub fn handler(&self) -> Option<&Handler> {
|
||||
match self.inner.state {
|
||||
DiagnosticBuilderState::Emittable(handler) => Some(handler),
|
||||
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffers the diagnostic for later emission,
|
||||
/// unless handler has disabled such buffering.
|
||||
pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
|
||||
|
@ -313,6 +313,8 @@ declare_features! (
|
||||
(active, async_closure, "1.37.0", Some(62290), None),
|
||||
/// Allows async functions to be declared, implemented, and used in traits.
|
||||
(active, async_fn_in_trait, "1.66.0", Some(91611), None),
|
||||
/// Allows builtin # foo() syntax
|
||||
(active, builtin_syntax, "CURRENT_RUSTC_VERSION", Some(110680), None),
|
||||
/// Allows `c"foo"` literals.
|
||||
(active, c_str_literals, "CURRENT_RUSTC_VERSION", Some(105723), None),
|
||||
/// Treat `extern "C"` function as nounwind.
|
||||
|
@ -167,7 +167,7 @@
|
||||
//! fn node_label(&self, n: &Nd) -> dot::LabelText<'_> {
|
||||
//! dot::LabelText::LabelStr(self.nodes[*n].into())
|
||||
//! }
|
||||
//! fn edge_label<'b>(&'b self, _: &Ed) -> dot::LabelText<'b> {
|
||||
//! fn edge_label(&self, _: &Ed<'_>) -> dot::LabelText<'_> {
|
||||
//! dot::LabelText::LabelStr("⊆".into())
|
||||
//! }
|
||||
//! }
|
||||
@ -177,8 +177,8 @@
|
||||
//! type Edge = Ed<'a>;
|
||||
//! fn nodes(&self) -> dot::Nodes<'a,Nd> { (0..self.nodes.len()).collect() }
|
||||
//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() }
|
||||
//! fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s }
|
||||
//! fn target(&self, e: &Ed) -> Nd { let & &(_,t) = e; t }
|
||||
//! fn source(&self, e: &Ed<'_>) -> Nd { let & &(s,_) = e; s }
|
||||
//! fn target(&self, e: &Ed<'_>) -> Nd { let & &(_,t) = e; t }
|
||||
//! }
|
||||
//!
|
||||
//! # pub fn main() { render_to(&mut Vec::new()) }
|
||||
@ -226,11 +226,11 @@
|
||||
//! fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> {
|
||||
//! dot::Id::new(format!("N{}", n.0)).unwrap()
|
||||
//! }
|
||||
//! fn node_label<'b>(&'b self, n: &Nd<'b>) -> dot::LabelText<'b> {
|
||||
//! fn node_label(&self, n: &Nd<'_>) -> dot::LabelText<'_> {
|
||||
//! let &(i, _) = n;
|
||||
//! dot::LabelText::LabelStr(self.nodes[i].into())
|
||||
//! }
|
||||
//! fn edge_label<'b>(&'b self, _: &Ed<'b>) -> dot::LabelText<'b> {
|
||||
//! fn edge_label(&self, _: &Ed<'_>) -> dot::LabelText<'_> {
|
||||
//! dot::LabelText::LabelStr("⊆".into())
|
||||
//! }
|
||||
//! }
|
||||
|
@ -279,6 +279,9 @@ hir_analysis_specialization_trait = implementing `rustc_specialization_trait` tr
|
||||
hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
|
||||
.label = `for<...>` is here
|
||||
|
||||
hir_analysis_empty_specialization = specialization impl does not specialize any associated items
|
||||
.note = impl is a specialization of this impl
|
||||
|
||||
hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
|
||||
|
||||
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
|
||||
|
@ -2419,6 +2419,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
//
|
||||
// Select applicable inherent associated type candidates modulo regions.
|
||||
//
|
||||
|
||||
// In contexts that have no inference context, just make a new one.
|
||||
// We do need a local variable to store it, though.
|
||||
let infcx_;
|
||||
@ -2431,7 +2435,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}
|
||||
};
|
||||
|
||||
let param_env = tcx.param_env(block.owner.to_def_id());
|
||||
// FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors
|
||||
// when inside of an ADT (#108491) or where clause.
|
||||
let param_env = tcx.param_env(block.owner);
|
||||
let cause = ObligationCause::misc(span, block.owner.def_id);
|
||||
|
||||
let mut fulfillment_errors = Vec::new();
|
||||
@ -2439,6 +2445,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
let universe = infcx.create_next_universe();
|
||||
|
||||
// Regions are not considered during selection.
|
||||
// FIXME(non_lifetime_binders): Here we are "truncating" or "flattening" the universes
|
||||
// of type and const binders. Is that correct in the selection phase? See also #109505.
|
||||
let self_ty = tcx.replace_escaping_bound_vars_uncached(
|
||||
self_ty,
|
||||
FnMutDelegate {
|
||||
@ -2454,41 +2462,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
|
||||
candidates
|
||||
.iter()
|
||||
.filter_map(|&(impl_, (assoc_item, def_scope))| {
|
||||
.copied()
|
||||
.filter(|&(impl_, _)| {
|
||||
infcx.probe(|_| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
|
||||
|
||||
let impl_ty = tcx.type_of(impl_);
|
||||
let impl_substs = infcx.fresh_item_substs(impl_);
|
||||
let impl_ty = impl_ty.subst(tcx, impl_substs);
|
||||
let impl_ty = tcx.type_of(impl_).subst(tcx, impl_substs);
|
||||
let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
|
||||
|
||||
// Check that the Self-types can be related.
|
||||
// FIXME(fmease): Should we use `eq` here?
|
||||
ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?;
|
||||
// Check that the self types can be related.
|
||||
// FIXME(inherent_associated_types): Should we use `eq` here? Method probing uses
|
||||
// `sup` for this situtation, too. What for? To constrain inference variables?
|
||||
if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether the impl imposes obligations we have to worry about.
|
||||
let impl_bounds = tcx.predicates_of(impl_);
|
||||
let impl_bounds = impl_bounds.instantiate(tcx, impl_substs);
|
||||
|
||||
let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_substs);
|
||||
let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds);
|
||||
|
||||
let impl_obligations = traits::predicates_for_generics(
|
||||
|_, _| cause.clone(),
|
||||
param_env,
|
||||
impl_bounds,
|
||||
);
|
||||
|
||||
ocx.register_obligations(impl_obligations);
|
||||
|
||||
let mut errors = ocx.select_where_possible();
|
||||
if !errors.is_empty() {
|
||||
fulfillment_errors.append(&mut errors);
|
||||
return None;
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot.
|
||||
Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs)))
|
||||
true
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
@ -2497,24 +2504,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
if applicable_candidates.len() > 1 {
|
||||
return Err(self.complain_about_ambiguous_inherent_assoc_type(
|
||||
name,
|
||||
applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(),
|
||||
applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
|
||||
if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() {
|
||||
if let Some((impl_, (assoc_item, def_scope))) = applicable_candidates.pop() {
|
||||
self.check_assoc_ty(assoc_item, name, def_scope, block, span);
|
||||
|
||||
// FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, we still
|
||||
// need to relate the Self-type with fresh item substs & register region obligations for
|
||||
// regionck to prove/disprove.
|
||||
// FIXME(fmease): Currently creating throwaway `parent_substs` to please
|
||||
// `create_substs_for_associated_item`. Modify the latter instead (or sth. similar) to
|
||||
// not require the parent substs logic.
|
||||
let parent_substs = InternalSubsts::identity_for_item(tcx, impl_);
|
||||
let substs =
|
||||
self.create_substs_for_associated_item(span, assoc_item, segment, parent_substs);
|
||||
let substs = tcx.mk_substs_from_iter(
|
||||
std::iter::once(ty::GenericArg::from(self_ty))
|
||||
.chain(substs.into_iter().skip(parent_substs.len())),
|
||||
);
|
||||
|
||||
let item_substs =
|
||||
self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs);
|
||||
|
||||
// FIXME(fmease, #106722): Check if the bounds on the parameters of the
|
||||
// associated type hold, if any.
|
||||
let ty = tcx.type_of(assoc_item).subst(tcx, item_substs);
|
||||
let ty = tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(assoc_item, substs));
|
||||
|
||||
return Ok(Some((ty, assoc_item)));
|
||||
}
|
||||
|
@ -60,19 +60,21 @@ pub(super) fn compare_impl_method<'tcx>(
|
||||
};
|
||||
}
|
||||
|
||||
/// This function is best explained by example. Consider a trait:
|
||||
/// This function is best explained by example. Consider a trait with it's implementation:
|
||||
///
|
||||
/// trait Trait<'t, T> {
|
||||
/// // `trait_m`
|
||||
/// fn method<'a, M>(t: &'t T, m: &'a M) -> Self;
|
||||
/// }
|
||||
/// ```rust
|
||||
/// trait Trait<'t, T> {
|
||||
/// // `trait_m`
|
||||
/// fn method<'a, M>(t: &'t T, m: &'a M) -> Self;
|
||||
/// }
|
||||
///
|
||||
/// And an impl:
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
|
||||
/// // `impl_m`
|
||||
/// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo;
|
||||
/// }
|
||||
/// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
|
||||
/// // `impl_m`
|
||||
/// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo { Foo }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// We wish to decide if those two method types are compatible.
|
||||
/// For this we have to show that, assuming the bounds of the impl hold, the
|
||||
@ -82,7 +84,9 @@ pub(super) fn compare_impl_method<'tcx>(
|
||||
/// type parameters to impl type parameters. This is taken from the
|
||||
/// impl trait reference:
|
||||
///
|
||||
/// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
|
||||
/// ```
|
||||
///
|
||||
/// We create a mapping `dummy_substs` that maps from the impl type
|
||||
/// parameters to fresh types and regions. For type parameters,
|
||||
@ -91,13 +95,17 @@ pub(super) fn compare_impl_method<'tcx>(
|
||||
/// regions (Note: but only early-bound regions, i.e., those
|
||||
/// declared on the impl or used in type parameter bounds).
|
||||
///
|
||||
/// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 }
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 }
|
||||
/// ```
|
||||
///
|
||||
/// Now we can apply `placeholder_substs` to the type of the impl method
|
||||
/// to yield a new function type in terms of our fresh, placeholder
|
||||
/// types:
|
||||
///
|
||||
/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
|
||||
/// ```
|
||||
///
|
||||
/// We now want to extract and substitute the type of the *trait*
|
||||
/// method and compare it. To do so, we must create a compound
|
||||
@ -106,11 +114,15 @@ pub(super) fn compare_impl_method<'tcx>(
|
||||
/// type parameters. We extend the mapping to also include
|
||||
/// the method parameters.
|
||||
///
|
||||
/// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 }
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 }
|
||||
/// ```
|
||||
///
|
||||
/// Applying this to the trait method type yields:
|
||||
///
|
||||
/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
|
||||
/// ```
|
||||
///
|
||||
/// This type is also the same but the name of the bound region (`'a`
|
||||
/// vs `'b`). However, the normal subtyping rules on fn types handle
|
||||
@ -1163,7 +1175,7 @@ fn compare_self_type<'tcx>(
|
||||
/// as the number of generics on the respective assoc item in the trait definition.
|
||||
///
|
||||
/// For example this code emits the errors in the following code:
|
||||
/// ```
|
||||
/// ```rust,compile_fail
|
||||
/// trait Trait {
|
||||
/// fn foo();
|
||||
/// type Assoc<T>;
|
||||
@ -1547,7 +1559,7 @@ fn compare_synthetic_generics<'tcx>(
|
||||
/// the same kind as the respective generic parameter in the trait def.
|
||||
///
|
||||
/// For example all 4 errors in the following code are emitted here:
|
||||
/// ```
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// trait Foo {
|
||||
/// fn foo<const N: u8>();
|
||||
/// type bar<const N: u8>;
|
||||
|
@ -210,6 +210,19 @@ fn do_orphan_check_impl<'tcx>(
|
||||
NonlocalImpl::DisallowOther,
|
||||
),
|
||||
|
||||
// ```
|
||||
// struct S<T>(T);
|
||||
// impl<T: ?Sized> S<T> {
|
||||
// type This = T;
|
||||
// }
|
||||
// impl<T: ?Sized> AutoTrait for S<T>::This {}
|
||||
// ```
|
||||
// FIXME(inherent_associated_types): The example code above currently leads to a cycle
|
||||
ty::Alias(AliasKind::Inherent, _) => (
|
||||
LocalImpl::Disallow { problematic_kind: "associated type" },
|
||||
NonlocalImpl::DisallowOther,
|
||||
),
|
||||
|
||||
// type Opaque = impl Trait;
|
||||
// impl AutoTrait for Opaque {}
|
||||
ty::Alias(AliasKind::Opaque, _) => (
|
||||
|
@ -51,7 +51,15 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
// of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
|
||||
None
|
||||
} else if tcx.lazy_normalization() {
|
||||
if let Some(param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id) {
|
||||
let parent_node = tcx.hir().get_parent(hir_id);
|
||||
if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node
|
||||
&& constant.hir_id == hir_id
|
||||
{
|
||||
// enum variant discriminants are not allowed to use any kind of generics
|
||||
None
|
||||
} else if let Some(param_id) =
|
||||
tcx.hir().opt_const_param_default_param_def_id(hir_id)
|
||||
{
|
||||
// If the def_id we are calling generics_of on is an anon ct default i.e:
|
||||
//
|
||||
// struct Foo<const N: usize = { .. }>;
|
||||
@ -94,15 +102,15 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
has_self: generics.has_self,
|
||||
has_late_bound_regions: generics.has_late_bound_regions,
|
||||
};
|
||||
} else {
|
||||
// HACK(eddyb) this provides the correct generics when
|
||||
// `feature(generic_const_expressions)` is enabled, so that const expressions
|
||||
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
|
||||
//
|
||||
// Note that we do not supply the parent generics when using
|
||||
// `min_const_generics`.
|
||||
Some(parent_def_id.to_def_id())
|
||||
}
|
||||
|
||||
// HACK(eddyb) this provides the correct generics when
|
||||
// `feature(generic_const_expressions)` is enabled, so that const expressions
|
||||
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
|
||||
//
|
||||
// Note that we do not supply the parent generics when using
|
||||
// `min_const_generics`.
|
||||
Some(parent_def_id.to_def_id())
|
||||
} else {
|
||||
let parent_node = tcx.hir().get_parent(hir_id);
|
||||
match parent_node {
|
||||
@ -115,11 +123,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
{
|
||||
Some(parent_def_id.to_def_id())
|
||||
}
|
||||
Node::Variant(Variant { disr_expr: Some(constant), .. })
|
||||
if constant.hir_id == hir_id =>
|
||||
{
|
||||
Some(parent_def_id.to_def_id())
|
||||
}
|
||||
Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => {
|
||||
Some(tcx.typeck_root_def_id(def_id.to_def_id()))
|
||||
}
|
||||
|
@ -657,14 +657,15 @@ pub(super) fn implied_predicates_with_filter(
|
||||
&*tcx.arena.alloc_from_iter(superbounds.predicates().chain(where_bounds_that_match));
|
||||
debug!(?implied_bounds);
|
||||
|
||||
// Now require that immediate supertraits are converted,
|
||||
// which will, in turn, reach indirect supertraits.
|
||||
// Now require that immediate supertraits are converted, which will, in
|
||||
// turn, reach indirect supertraits, so we detect cycles now instead of
|
||||
// overflowing during elaboration.
|
||||
if matches!(filter, PredicateFilter::SelfOnly) {
|
||||
// Now require that immediate supertraits are converted,
|
||||
// which will, in turn, reach indirect supertraits.
|
||||
for &(pred, span) in implied_bounds {
|
||||
debug!("superbound: {:?}", pred);
|
||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(bound)) = pred.kind().skip_binder() {
|
||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(bound)) = pred.kind().skip_binder()
|
||||
&& bound.polarity == ty::ImplPolarity::Positive
|
||||
{
|
||||
tcx.at(span).super_predicates_of(bound.def_id());
|
||||
}
|
||||
}
|
||||
|
@ -1923,7 +1923,7 @@ fn is_late_bound_map(
|
||||
/// handles cycle detection as we go through the query system.
|
||||
///
|
||||
/// This is necessary in the first place for the following case:
|
||||
/// ```
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// type Alias<'a, T> = <T as Trait<'a>>::Assoc;
|
||||
/// fn foo<'a>(_: Alias<'a, ()>) -> Alias<'a, ()> { ... }
|
||||
/// ```
|
||||
@ -1948,7 +1948,7 @@ fn is_late_bound_map(
|
||||
ty::Param(param_ty) => {
|
||||
self.arg_is_constrained[param_ty.index as usize] = true;
|
||||
}
|
||||
ty::Alias(ty::Projection, _) => return ControlFlow::Continue(()),
|
||||
ty::Alias(ty::Projection | ty::Inherent, _) => return ControlFlow::Continue(()),
|
||||
_ => (),
|
||||
}
|
||||
t.super_visit_with(self)
|
||||
|
@ -127,7 +127,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
|
||||
// the def_id that this query was called with. We filter to only type and const args here
|
||||
// as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
|
||||
// but it can't hurt to be safe ^^
|
||||
if let ty::Alias(ty::Projection, projection) = ty.kind() {
|
||||
if let ty::Alias(ty::Projection | ty::Inherent, projection) = ty.kind() {
|
||||
let generics = tcx.generics_of(projection.def_id);
|
||||
|
||||
let arg_index = segment
|
||||
|
@ -59,7 +59,7 @@ struct ParameterCollector {
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match *t.kind() {
|
||||
ty::Alias(ty::Projection, ..) if !self.include_nonconstraining => {
|
||||
ty::Alias(ty::Projection | ty::Inherent, ..) if !self.include_nonconstraining => {
|
||||
// projections are not injective
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
@ -814,6 +814,15 @@ pub(crate) struct ClosureImplicitHrtb {
|
||||
pub for_sp: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_empty_specialization)]
|
||||
pub(crate) struct EmptySpecialization {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[note]
|
||||
pub base_impl_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_const_specialize)]
|
||||
pub(crate) struct ConstSpecialize {
|
||||
|
@ -80,7 +80,7 @@ use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt};
|
||||
use rustc_trait_selection::traits::{self, translate_substs_with_cause, wf, ObligationCtxt};
|
||||
|
||||
pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
|
||||
if let Some(node) = parent_specialization_node(tcx, impl_def_id) {
|
||||
@ -100,12 +100,19 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti
|
||||
// Implementing a normal trait isn't a specialization.
|
||||
return None;
|
||||
}
|
||||
if trait_def.is_marker {
|
||||
// Overlapping marker implementations are not really specializations.
|
||||
return None;
|
||||
}
|
||||
Some(impl2_node)
|
||||
}
|
||||
|
||||
/// Check that `impl1` is a sound specialization
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) {
|
||||
let span = tcx.def_span(impl1_def_id);
|
||||
check_has_items(tcx, impl1_def_id, impl2_node, span);
|
||||
|
||||
if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
|
||||
let impl2_def_id = impl2_node.def_id();
|
||||
debug!(?impl2_def_id, ?impl2_substs);
|
||||
@ -116,7 +123,6 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
|
||||
unconstrained_parent_impl_substs(tcx, impl2_def_id, impl2_substs)
|
||||
};
|
||||
|
||||
let span = tcx.def_span(impl1_def_id);
|
||||
check_constness(tcx, impl1_def_id, impl2_node, span);
|
||||
check_static_lifetimes(tcx, &parent_substs, span);
|
||||
check_duplicate_params(tcx, impl1_substs, &parent_substs, span);
|
||||
@ -124,6 +130,13 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
|
||||
}
|
||||
}
|
||||
|
||||
fn check_has_items(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) {
|
||||
if let Node::Impl(impl2_id) = impl2_node && tcx.associated_item_def_ids(impl1_def_id).is_empty() {
|
||||
let base_impl_span = tcx.def_span(impl2_id);
|
||||
tcx.sess.emit_err(errors::EmptySpecialization { span, base_impl_span });
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the specializing impl `impl1` is at least as const as the base
|
||||
/// impl `impl2`
|
||||
fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) {
|
||||
@ -167,8 +180,21 @@ fn get_impl_substs(
|
||||
ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
|
||||
|
||||
let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id);
|
||||
let impl2_substs =
|
||||
translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node);
|
||||
let impl1_span = tcx.def_span(impl1_def_id);
|
||||
let impl2_substs = translate_substs_with_cause(
|
||||
infcx,
|
||||
param_env,
|
||||
impl1_def_id.to_def_id(),
|
||||
impl1_substs,
|
||||
impl2_node,
|
||||
|_, span| {
|
||||
traits::ObligationCause::new(
|
||||
impl1_span,
|
||||
impl1_def_id,
|
||||
traits::ObligationCauseCode::BindingObligation(impl2_node.def_id(), span),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
|
@ -210,6 +210,9 @@ fn insert_required_predicates_to_be_wf<'tcx>(
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(inherent_associated_types): Handle this case properly.
|
||||
ty::Alias(ty::Inherent, _) => {}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -75,3 +75,7 @@ hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns
|
||||
|
||||
hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where
|
||||
.note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new
|
||||
|
||||
hir_typeck_suggest_boxing_note = for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html
|
||||
|
||||
hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `Box::new`
|
||||
|
@ -51,7 +51,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
|
||||
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
|
||||
|| self.suggest_no_capture_closure(err, expected, expr_ty)
|
||||
|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
|
||||
|| self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty)
|
||||
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|
||||
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|
||||
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|
||||
@ -86,9 +86,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
|
||||
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
||||
self.note_internal_mutation_in_method(err, expr, Some(expected), expr_ty);
|
||||
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
|
||||
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
|
||||
self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
|
||||
self.suggest_method_call_on_range_literal(err, expr, expr_ty, expected);
|
||||
self.suggest_return_binding_for_missing_tail_expr(err, expr, expr_ty, expected);
|
||||
self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty);
|
||||
}
|
||||
|
||||
/// Requires that the two types unify, and prints an error message if
|
||||
@ -1087,7 +1087,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// ```ignore (illustrative)
|
||||
/// opt.map(|param| { takes_ref(param) });
|
||||
/// ```
|
||||
fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> {
|
||||
fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
|
||||
let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind else {
|
||||
return None;
|
||||
};
|
||||
@ -1133,12 +1133,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) {
|
||||
(true, Ok(src)) => {
|
||||
let suggestion = format!("as_ref().{}", src);
|
||||
Some((method_path.ident.span, "consider using `as_ref` instead", suggestion))
|
||||
}
|
||||
_ => None,
|
||||
if is_as_ref_able {
|
||||
Some((
|
||||
vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
|
||||
"consider using `as_ref` instead",
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -1217,14 +1218,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// In addition of this check, it also checks between references mutability state. If the
|
||||
/// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
|
||||
/// `&mut`!".
|
||||
pub fn check_ref(
|
||||
pub fn suggest_deref_or_ref(
|
||||
&self,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
checked_ty: Ty<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
) -> Option<(
|
||||
Span,
|
||||
String,
|
||||
Vec<(Span, String)>,
|
||||
String,
|
||||
Applicability,
|
||||
bool, /* verbose */
|
||||
@ -1254,30 +1254,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&& let Ok(src) = sm.span_to_snippet(sp)
|
||||
&& replace_prefix(&src, "b\"", "\"").is_some()
|
||||
{
|
||||
let pos = sp.lo() + BytePos(1);
|
||||
return Some((
|
||||
sp.with_hi(pos),
|
||||
"consider removing the leading `b`".to_string(),
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
}
|
||||
let pos = sp.lo() + BytePos(1);
|
||||
return Some((
|
||||
vec![(sp.with_hi(pos), String::new())],
|
||||
"consider removing the leading `b`".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
}
|
||||
(&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
|
||||
if let hir::ExprKind::Lit(_) = expr.kind
|
||||
&& let Ok(src) = sm.span_to_snippet(sp)
|
||||
&& replace_prefix(&src, "\"", "b\"").is_some()
|
||||
{
|
||||
return Some((
|
||||
sp.shrink_to_lo(),
|
||||
"consider adding a leading `b`".to_string(),
|
||||
"b".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
return Some((
|
||||
vec![(sp.shrink_to_lo(), "b".to_string())],
|
||||
"consider adding a leading `b`".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -1320,66 +1318,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind
|
||||
&& let Some(1) = self.deref_steps(expected, checked_ty) {
|
||||
&& let Some(1) = self.deref_steps(expected, checked_ty)
|
||||
{
|
||||
// We have `*&T`, check if what was expected was `&T`.
|
||||
// If so, we may want to suggest removing a `*`.
|
||||
sugg_sp = sugg_sp.with_hi(inner.span.lo());
|
||||
return Some((
|
||||
sugg_sp,
|
||||
vec![(sugg_sp, String::new())],
|
||||
"consider removing deref here".to_string(),
|
||||
"".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
if let Ok(src) = sm.span_to_snippet(sugg_sp) {
|
||||
let needs_parens = match expr.kind {
|
||||
// parenthesize if needed (Issue #46756)
|
||||
hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
|
||||
// parenthesize borrows of range literals (Issue #54505)
|
||||
_ if is_range_literal(expr) => true,
|
||||
_ => false,
|
||||
};
|
||||
let needs_parens = match expr.kind {
|
||||
// parenthesize if needed (Issue #46756)
|
||||
hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
|
||||
// parenthesize borrows of range literals (Issue #54505)
|
||||
_ if is_range_literal(expr) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if let Some(sugg) = self.can_use_as_ref(expr) {
|
||||
return Some((
|
||||
sugg.0,
|
||||
sugg.1.to_string(),
|
||||
sugg.2,
|
||||
Applicability::MachineApplicable,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
|
||||
Some(ident) => format!("{ident}: "),
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
if let Some(hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Assign(..),
|
||||
..
|
||||
})) = self.tcx.hir().find_parent(expr.hir_id)
|
||||
{
|
||||
if mutability.is_mut() {
|
||||
// Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let sugg_expr = if needs_parens { format!("({src})") } else { src };
|
||||
if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
|
||||
return Some((
|
||||
sp,
|
||||
format!("consider {}borrowing here", mutability.mutably_str()),
|
||||
format!("{prefix}{}{sugg_expr}", mutability.ref_prefix_str()),
|
||||
sugg,
|
||||
msg.to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
|
||||
Some(ident) => format!("{ident}: "),
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
if let Some(hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Assign(..),
|
||||
..
|
||||
})) = self.tcx.hir().find_parent(expr.hir_id)
|
||||
{
|
||||
if mutability.is_mut() {
|
||||
// Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let sugg = mutability.ref_prefix_str();
|
||||
let (sugg, verbose) = if needs_parens {
|
||||
(
|
||||
vec![
|
||||
(sp.shrink_to_lo(), format!("{prefix}{sugg}(")),
|
||||
(sp.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
(vec![(sp.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
|
||||
};
|
||||
return Some((
|
||||
sugg,
|
||||
format!("consider {}borrowing here", mutability.mutably_str()),
|
||||
Applicability::MachineApplicable,
|
||||
verbose,
|
||||
false,
|
||||
));
|
||||
}
|
||||
}
|
||||
(
|
||||
@ -1401,23 +1406,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&& sm.is_span_accessible(call_span)
|
||||
{
|
||||
return Some((
|
||||
sp.with_hi(call_span.lo()),
|
||||
vec![(sp.with_hi(call_span.lo()), String::new())],
|
||||
"consider removing the borrow".to_string(),
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
if sp.contains(expr.span)
|
||||
&& sm.is_span_accessible(expr.span)
|
||||
{
|
||||
if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
|
||||
return Some((
|
||||
sp.with_hi(expr.span.lo()),
|
||||
vec![(sp.with_hi(expr.span.lo()), String::new())],
|
||||
"consider removing the borrow".to_string(),
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
true,
|
||||
@ -1441,23 +1442,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
|
||||
// skip `&` or `&mut ` if both mutabilities are mutable
|
||||
let lo = sp.lo() + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
|
||||
let lo = sp.lo()
|
||||
+ BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
|
||||
// skip `&` or `&mut `
|
||||
let hi = sp.lo() + BytePos(old_prefix.len() as _);
|
||||
let sp = sp.with_lo(lo).with_hi(hi);
|
||||
|
||||
(
|
||||
sp,
|
||||
format!("{}{derefs}", if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }),
|
||||
if mutbl_b <= mutbl_a { Applicability::MachineApplicable } else { Applicability::MaybeIncorrect }
|
||||
format!(
|
||||
"{}{derefs}",
|
||||
if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
|
||||
),
|
||||
if mutbl_b <= mutbl_a {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
if let Some((span, src, applicability)) = suggestion {
|
||||
return Some((
|
||||
span,
|
||||
vec![(span, src)],
|
||||
"consider dereferencing".to_string(),
|
||||
src,
|
||||
applicability,
|
||||
true,
|
||||
false,
|
||||
@ -1486,9 +1494,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// If we've reached our target type with just removing `&`, then just print now.
|
||||
if steps == 0 && !remove.trim().is_empty() {
|
||||
return Some((
|
||||
prefix_span,
|
||||
vec![(prefix_span, String::new())],
|
||||
format!("consider removing the `{}`", remove.trim()),
|
||||
String::new(),
|
||||
// Do not remove `&&` to get to bool, because it might be something like
|
||||
// { a } && b, which we have a separate fixup suggestion that is more
|
||||
// likely correct...
|
||||
@ -1554,9 +1561,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
return Some((
|
||||
span,
|
||||
vec![(span, suggestion)],
|
||||
message,
|
||||
suggestion,
|
||||
Applicability::MachineApplicable,
|
||||
true,
|
||||
false,
|
||||
@ -1569,7 +1575,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_for_cast(
|
||||
pub fn suggest_cast(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
@ -1936,7 +1942,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
|
||||
pub fn check_for_range_as_method_call(
|
||||
pub fn suggest_method_call_on_range_literal(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
@ -2005,7 +2011,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
/// Identify when the type error is because `()` is found in a binding that was assigned a
|
||||
/// block without a tail expression.
|
||||
fn check_for_binding_assigned_block_without_tail_expression(
|
||||
fn suggest_return_binding_for_missing_tail_expr(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
@ -2047,7 +2053,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_wrong_return_type_due_to_generic_arg(
|
||||
fn note_wrong_return_ty_due_to_generic_arg(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
@ -267,3 +267,31 @@ pub struct ArgMismatchIndeterminate {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub enum SuggestBoxing {
|
||||
#[note(hir_typeck_suggest_boxing_note)]
|
||||
#[multipart_suggestion(
|
||||
hir_typeck_suggest_boxing_when_appropriate,
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
Unit {
|
||||
#[suggestion_part(code = "Box::new(())")]
|
||||
start: Span,
|
||||
#[suggestion_part(code = "")]
|
||||
end: Span,
|
||||
},
|
||||
#[note(hir_typeck_suggest_boxing_note)]
|
||||
AsyncBody,
|
||||
#[note(hir_typeck_suggest_boxing_note)]
|
||||
#[multipart_suggestion(
|
||||
hir_typeck_suggest_boxing_when_appropriate,
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
Other {
|
||||
#[suggestion_part(code = "Box::new(")]
|
||||
start: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
end: Span,
|
||||
},
|
||||
}
|
||||
|
@ -843,7 +843,7 @@ fn find_param_in_ty<'tcx>(
|
||||
return true;
|
||||
}
|
||||
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Alias(ty::Projection, ..) = ty.kind()
|
||||
&& let ty::Alias(ty::Projection | ty::Inherent, ..) = ty.kind()
|
||||
{
|
||||
// This logic may seem a bit strange, but typically when
|
||||
// we have a projection type in a function signature, the
|
||||
|
@ -1519,7 +1519,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// case we can ignore the tail expression (e.g., `'a: {
|
||||
// break 'a 22; }` would not force the type of the block
|
||||
// to be `()`).
|
||||
let tail_expr = blk.expr.as_ref();
|
||||
let coerce_to_ty = expected.coercion_target_type(self, blk.span);
|
||||
let coerce = if blk.targeted_by_break {
|
||||
CoerceMany::new(coerce_to_ty)
|
||||
@ -1537,13 +1536,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
// check the tail expression **without** holding the
|
||||
// `enclosing_breakables` lock below.
|
||||
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
|
||||
let tail_expr_ty =
|
||||
blk.expr.map(|expr| (expr, self.check_expr_with_expectation(expr, expected)));
|
||||
|
||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
|
||||
let coerce = ctxt.coerce.as_mut().unwrap();
|
||||
if let Some(tail_expr_ty) = tail_expr_ty {
|
||||
let tail_expr = tail_expr.unwrap();
|
||||
if let Some((tail_expr, tail_expr_ty)) = tail_expr_ty {
|
||||
let span = self.get_expr_coercion_span(tail_expr);
|
||||
let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
|
||||
let ty_for_diagnostic = coerce.merged_ty();
|
||||
@ -1596,6 +1595,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self.misc(sp),
|
||||
&mut |err| {
|
||||
if let Some(expected_ty) = expected.only_has_type(self) {
|
||||
if blk.stmts.is_empty() && blk.expr.is_none() {
|
||||
self.suggest_boxing_when_appropriate(
|
||||
err,
|
||||
blk.span,
|
||||
blk.hir_id,
|
||||
expected_ty,
|
||||
self.tcx.mk_unit(),
|
||||
);
|
||||
}
|
||||
if !self.consider_removing_semicolon(blk, expected_ty, err) {
|
||||
self.err_ctxt().consider_returning_binding(
|
||||
blk,
|
||||
@ -1608,7 +1616,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// silence this redundant error, as we already emit E0070.
|
||||
|
||||
// Our block must be a `assign desugar local; assignment`
|
||||
if let Some(hir::Node::Block(hir::Block {
|
||||
if let hir::Block {
|
||||
stmts:
|
||||
[
|
||||
hir::Stmt {
|
||||
@ -1630,7 +1638,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
},
|
||||
],
|
||||
..
|
||||
})) = self.tcx.hir().find(blk.hir_id)
|
||||
} = blk
|
||||
{
|
||||
self.comes_from_while_condition(blk.hir_id, |_| {
|
||||
err.downgrade_to_delayed_bug();
|
||||
|
@ -300,7 +300,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
|
||||
match ty.kind() {
|
||||
ty::Adt(adt_def, _) => Some(*adt_def),
|
||||
// FIXME(#104767): Should we handle bound regions here?
|
||||
ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => {
|
||||
ty::Alias(ty::Projection | ty::Inherent, _) if !ty.has_escaping_bound_vars() => {
|
||||
self.normalize(span, ty).ty_adt_def()
|
||||
}
|
||||
_ => None,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::FnCtxt;
|
||||
|
||||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
||||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
||||
@ -9,7 +9,8 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{
|
||||
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
|
||||
AsyncGeneratorKind, Expr, ExprKind, GeneratorKind, GenericBound, HirId, Node, Path, QPath,
|
||||
Stmt, StmtKind, TyKind, WherePredicate,
|
||||
};
|
||||
use rustc_hir_analysis::astconv::AstConv;
|
||||
use rustc_infer::traits::{self, StatementAsExpression};
|
||||
@ -274,13 +275,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
) -> bool {
|
||||
let expr = expr.peel_blocks();
|
||||
if let Some((sp, msg, suggestion, applicability, verbose, annotation)) =
|
||||
self.check_ref(expr, found, expected)
|
||||
if let Some((suggestion, msg, applicability, verbose, annotation)) =
|
||||
self.suggest_deref_or_ref(expr, found, expected)
|
||||
{
|
||||
if verbose {
|
||||
err.span_suggestion_verbose(sp, msg, suggestion, applicability);
|
||||
err.multipart_suggestion_verbose(msg, suggestion, applicability);
|
||||
} else {
|
||||
err.span_suggestion(sp, msg, suggestion, applicability);
|
||||
err.multipart_suggestion(msg, suggestion, applicability);
|
||||
}
|
||||
if annotation {
|
||||
let suggest_annotation = match expr.peel_drop_temps().kind {
|
||||
@ -342,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
err.span_label(sp, format!("{descr} `{name}` defined here"));
|
||||
}
|
||||
return true;
|
||||
} else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
|
||||
} else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
|
||||
return true;
|
||||
} else {
|
||||
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
|
||||
@ -438,33 +439,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(in super::super) fn suggest_boxing_when_appropriate(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) -> bool {
|
||||
if self.tcx.hir().is_inside_const_context(expr.hir_id) {
|
||||
// Do not suggest `Box::new` in const context.
|
||||
// Do not suggest `Box::new` in const context.
|
||||
if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
|
||||
return false;
|
||||
}
|
||||
if !expected.is_box() || found.is_box() {
|
||||
return false;
|
||||
}
|
||||
let boxed_found = self.tcx.mk_box(found);
|
||||
if self.can_coerce(boxed_found, expected) {
|
||||
err.multipart_suggestion(
|
||||
"store this in the heap by calling `Box::new`",
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
err.note(
|
||||
"for more on the distinction between the stack and the heap, read \
|
||||
https://doc.rust-lang.org/book/ch15-01-box.html, \
|
||||
https://doc.rust-lang.org/rust-by-example/std/box.html, and \
|
||||
https://doc.rust-lang.org/std/boxed/index.html",
|
||||
);
|
||||
if self.can_coerce(self.tcx.mk_box(found), expected) {
|
||||
let suggest_boxing = match found.kind() {
|
||||
ty::Tuple(tuple) if tuple.is_empty() => {
|
||||
SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
|
||||
}
|
||||
ty::Generator(def_id, ..)
|
||||
if matches!(
|
||||
self.tcx.generator_kind(def_id),
|
||||
Some(GeneratorKind::Async(AsyncGeneratorKind::Closure))
|
||||
) =>
|
||||
{
|
||||
SuggestBoxing::AsyncBody
|
||||
}
|
||||
_ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() },
|
||||
};
|
||||
err.subdiagnostic(suggest_boxing);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -1045,7 +1045,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
self.check_for_inner_self(&mut err, source, rcvr_ty, item_name);
|
||||
self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name);
|
||||
|
||||
bound_spans.sort();
|
||||
bound_spans.dedup();
|
||||
@ -1132,7 +1132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected);
|
||||
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
|
||||
return Some(err);
|
||||
}
|
||||
|
||||
@ -1805,7 +1805,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_inner_self(
|
||||
fn suggest_unwrapping_inner_self(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
source: SelfSource<'tcx>,
|
||||
@ -2175,7 +2175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_deref_method(
|
||||
fn note_derefed_ty_has_method(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
self_source: SelfSource<'tcx>,
|
||||
@ -2211,7 +2211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
| ty::Float(_)
|
||||
| ty::Adt(_, _)
|
||||
| ty::Str
|
||||
| ty::Alias(ty::Projection, _)
|
||||
| ty::Alias(ty::Projection | ty::Inherent, _)
|
||||
| ty::Param(_) => format!("{deref_ty}"),
|
||||
// we need to test something like <&[_]>::len or <(&[u32])>::len
|
||||
// and Vec::function();
|
||||
|
@ -127,7 +127,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
bug!()
|
||||
}
|
||||
|
||||
(_, ty::Alias(AliasKind::Projection, _)) | (ty::Alias(AliasKind::Projection, _), _)
|
||||
(_, ty::Alias(AliasKind::Projection | AliasKind::Inherent, _))
|
||||
| (ty::Alias(AliasKind::Projection | AliasKind::Inherent, _), _)
|
||||
if self.tcx.trait_solver_next() =>
|
||||
{
|
||||
relation.register_type_relate_obligation(a, b);
|
||||
|
@ -2354,7 +2354,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
let labeled_user_string = match bound_kind {
|
||||
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
|
||||
GenericKind::Alias(ref p) => match p.kind(self.tcx) {
|
||||
ty::AliasKind::Projection => format!("the associated type `{}`", p),
|
||||
ty::AliasKind::Projection | ty::AliasKind::Inherent => {
|
||||
format!("the associated type `{}`", p)
|
||||
}
|
||||
ty::AliasKind::Opaque => format!("the opaque type `{}`", p),
|
||||
},
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ pub enum TypeAnnotationNeeded {
|
||||
/// ```
|
||||
E0282,
|
||||
/// An implementation cannot be chosen unambiguously because of lack of information.
|
||||
/// ```compile_fail,E0283
|
||||
/// ```compile_fail,E0790
|
||||
/// let _ = Default::default();
|
||||
/// ```
|
||||
E0283,
|
||||
|
@ -21,7 +21,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
///
|
||||
/// Consider a case where we have
|
||||
///
|
||||
/// ```compile_fail,E0623
|
||||
/// ```compile_fail
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
|
||||
/// x.push(y);
|
||||
/// }
|
||||
|
@ -14,7 +14,7 @@ use rustc_middle::ty::{self, Region, TyCtxt};
|
||||
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
|
||||
///
|
||||
/// # Example
|
||||
/// ```compile_fail,E0623
|
||||
/// ```compile_fail
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8)
|
||||
/// { x.push(y); }
|
||||
/// ```
|
||||
|
@ -71,9 +71,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
#traits-as-parameters",
|
||||
);
|
||||
}
|
||||
(ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => {
|
||||
(ty::Alias(ty::Projection | ty::Inherent, _), ty::Alias(ty::Projection | ty::Inherent, _)) => {
|
||||
diag.note("an associated type was expected, but a different one was found");
|
||||
}
|
||||
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
|
||||
(ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
|
||||
if !tcx.is_impl_trait_in_trait(proj.def_id) =>
|
||||
{
|
||||
@ -222,7 +223,7 @@ impl<T> Trait<T> for X {
|
||||
diag.span_label(p_span, "this type parameter");
|
||||
}
|
||||
}
|
||||
(ty::Alias(ty::Projection, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
|
||||
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
|
||||
self.expected_projection(
|
||||
diag,
|
||||
proj_ty,
|
||||
@ -231,7 +232,7 @@ impl<T> Trait<T> for X {
|
||||
cause.code(),
|
||||
);
|
||||
}
|
||||
(_, ty::Alias(ty::Projection, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
|
||||
(_, ty::Alias(ty::Projection | ty::Inherent, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
|
||||
let msg = format!(
|
||||
"consider constraining the associated type `{}` to `{}`",
|
||||
values.found, values.expected,
|
||||
|
@ -549,6 +549,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
// We can't normalize associated types from `rustc_infer`,
|
||||
// but we can eagerly register inference variables for them.
|
||||
// FIXME(RPITIT): Don't replace RPITITs with inference vars.
|
||||
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
|
||||
ty::Alias(ty::Projection, projection_ty)
|
||||
if !projection_ty.has_escaping_bound_vars()
|
||||
&& !tcx.is_impl_trait_in_trait(projection_ty.def_id) =>
|
||||
@ -569,6 +570,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
hidden_ty
|
||||
}
|
||||
// FIXME(RPITIT): This can go away when we move to associated types
|
||||
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
|
||||
ty::Alias(
|
||||
ty::Projection,
|
||||
ty::AliasTy { def_id: def_id2, substs: substs2, .. },
|
||||
|
@ -13,9 +13,11 @@ use crate::infer::region_constraints::VerifyIfEq;
|
||||
|
||||
/// Given a "verify-if-eq" type test like:
|
||||
///
|
||||
/// exists<'a...> {
|
||||
/// verify_if_eq(some_type, bound_region)
|
||||
/// }
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// exists<'a...> {
|
||||
/// verify_if_eq(some_type, bound_region)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// and the type `test_ty` that the type test is being tested against,
|
||||
/// returns:
|
||||
|
@ -277,7 +277,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||
///
|
||||
/// It will not, however, work for higher-ranked bounds like:
|
||||
///
|
||||
/// ```compile_fail,E0311
|
||||
/// ```ignore(this does compile today, previously was marked as `compile_fail,E0311`)
|
||||
/// trait Foo<'a, 'b>
|
||||
/// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
|
||||
/// {
|
||||
|
@ -217,7 +217,7 @@ pub enum VerifyBound<'tcx> {
|
||||
/// and supplies a bound if it ended up being relevant. It's used in situations
|
||||
/// like this:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// fn foo<'a, 'b, T: SomeTrait<'a>>
|
||||
/// where
|
||||
/// <T as SomeTrait<'a>>::Item: 'b
|
||||
@ -232,7 +232,7 @@ pub enum VerifyBound<'tcx> {
|
||||
/// In the [`VerifyBound`], this struct is enclosed in `Binder` to account
|
||||
/// for cases like
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore (pseudo-Rust)
|
||||
/// where for<'a> <T as SomeTrait<'a>::Item: 'a
|
||||
/// ```
|
||||
///
|
||||
|
@ -62,6 +62,7 @@ use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
|
||||
use rustc_session::config::ExpectedValues;
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
|
||||
@ -1442,6 +1443,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
|
||||
// Bounds are respected for `type X = impl Trait`
|
||||
return;
|
||||
}
|
||||
if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() {
|
||||
// Bounds are respected for `type X = … Type::Inherent …`
|
||||
return;
|
||||
}
|
||||
// There must not be a where clause
|
||||
if type_alias_generics.predicates.is_empty() {
|
||||
return;
|
||||
@ -1561,7 +1566,6 @@ declare_lint_pass!(
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
||||
use rustc_middle::ty::visit::TypeVisitableExt;
|
||||
use rustc_middle::ty::Clause;
|
||||
use rustc_middle::ty::PredicateKind::*;
|
||||
|
||||
@ -2898,6 +2902,7 @@ impl ClashingExternDeclarations {
|
||||
| (Generator(..), Generator(..))
|
||||
| (GeneratorWitness(..), GeneratorWitness(..))
|
||||
| (Alias(ty::Projection, ..), Alias(ty::Projection, ..))
|
||||
| (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..))
|
||||
| (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false,
|
||||
|
||||
// These definitely should have been caught above.
|
||||
|
@ -1119,14 +1119,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
|
||||
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
|
||||
// so they are currently ignored for the purposes of this lint.
|
||||
ty::Param(..) | ty::Alias(ty::Projection, ..)
|
||||
ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
|
||||
if matches!(self.mode, CItemKind::Definition) =>
|
||||
{
|
||||
FfiSafe
|
||||
}
|
||||
|
||||
ty::Param(..)
|
||||
| ty::Alias(ty::Projection, ..)
|
||||
| ty::Alias(ty::Projection | ty::Inherent, ..)
|
||||
| ty::Infer(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Error(_)
|
||||
|
@ -333,6 +333,7 @@ declare_lint! {
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(unused_extern_crates)]
|
||||
/// #![deny(warnings)]
|
||||
/// extern crate proc_macro;
|
||||
/// ```
|
||||
///
|
||||
@ -1667,6 +1668,7 @@ declare_lint! {
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(elided_lifetimes_in_paths)]
|
||||
/// #![deny(warnings)]
|
||||
/// struct Foo<'a> {
|
||||
/// x: &'a u32
|
||||
/// }
|
||||
@ -2158,6 +2160,7 @@ declare_lint! {
|
||||
/// ```rust,compile_fail
|
||||
/// # #![allow(unused)]
|
||||
/// #![deny(explicit_outlives_requirements)]
|
||||
/// #![deny(warnings)]
|
||||
///
|
||||
/// struct SharedRef<'a, T>
|
||||
/// where
|
||||
|
@ -9,7 +9,7 @@ use crate::diagnostics::utils::{
|
||||
FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
|
||||
};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::Token;
|
||||
use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type};
|
||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||
@ -251,7 +251,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||
let diag = &self.parent.diag;
|
||||
|
||||
let field = binding_info.ast();
|
||||
let field_binding = &binding_info.binding;
|
||||
let mut field_binding = binding_info.binding.clone();
|
||||
field_binding.set_span(field.ty.span());
|
||||
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
|
||||
@ -284,9 +285,9 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||
name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_));
|
||||
let (binding, needs_destructure) = if needs_clone {
|
||||
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
|
||||
(quote! { #field_binding.clone() }, false)
|
||||
(quote_spanned! {inner_ty.span()=> #field_binding.clone() }, false)
|
||||
} else {
|
||||
(quote! { #field_binding }, true)
|
||||
(quote_spanned! {inner_ty.span()=> #field_binding }, true)
|
||||
};
|
||||
|
||||
let generated_code = self
|
||||
|
@ -140,7 +140,7 @@ pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
|
||||
/// ```fluent
|
||||
/// parser_expected_identifier = expected identifier
|
||||
///
|
||||
/// parser_expected_identifier-found = expected identifier, found {$found}
|
||||
/// parser_expected_identifier_found = expected identifier, found {$found}
|
||||
///
|
||||
/// parser_raw_identifier = escape `{$ident}` to use it as an identifier
|
||||
/// ```
|
||||
|
@ -4,17 +4,16 @@ use crate::diagnostics::error::{
|
||||
invalid_attr, span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError,
|
||||
};
|
||||
use crate::diagnostics::utils::{
|
||||
build_field_mapping, is_doc_comment, new_code_ident,
|
||||
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo,
|
||||
FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
|
||||
build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident,
|
||||
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
|
||||
should_generate_set_arg, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap,
|
||||
HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path};
|
||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||
|
||||
use super::utils::{build_suggestion_code, AllowMultipleAlternatives};
|
||||
|
||||
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
|
||||
pub(crate) struct SubdiagnosticDeriveBuilder {
|
||||
diag: syn::Ident,
|
||||
@ -210,19 +209,20 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||
}
|
||||
|
||||
/// Generates the code for a field with no attributes.
|
||||
fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
|
||||
let ast = binding.ast();
|
||||
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
|
||||
|
||||
fn generate_field_set_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
|
||||
let diag = &self.parent.diag;
|
||||
let ident = ast.ident.as_ref().unwrap();
|
||||
// strip `r#` prefix, if present
|
||||
let ident = format_ident!("{}", ident);
|
||||
|
||||
let field = binding_info.ast();
|
||||
let mut field_binding = binding_info.binding.clone();
|
||||
field_binding.set_span(field.ty.span());
|
||||
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
|
||||
|
||||
quote! {
|
||||
#diag.set_arg(
|
||||
stringify!(#ident),
|
||||
#binding
|
||||
#field_binding
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -399,7 +399,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||
clone_suggestion_code: bool,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let span = attr.span().unwrap();
|
||||
let ident = &list.path.segments.last().unwrap().ident;
|
||||
let mut ident = list.path.segments.last().unwrap().ident.clone();
|
||||
ident.set_span(info.ty.span());
|
||||
let name = ident.to_string();
|
||||
let name = name.as_str();
|
||||
|
||||
@ -498,7 +499,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||
.variant
|
||||
.bindings()
|
||||
.iter()
|
||||
.filter(|binding| !binding.ast().attrs.is_empty())
|
||||
.filter(|binding| !should_generate_set_arg(binding.ast()))
|
||||
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
|
||||
.collect();
|
||||
|
||||
@ -580,7 +581,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||
.variant
|
||||
.bindings()
|
||||
.iter()
|
||||
.filter(|binding| binding.ast().attrs.is_empty())
|
||||
.filter(|binding| should_generate_set_arg(binding.ast()))
|
||||
.map(|binding| self.generate_field_set_arg(binding))
|
||||
.collect();
|
||||
|
||||
|
@ -207,6 +207,12 @@ impl<'ty> FieldInnerTy<'ty> {
|
||||
FieldInnerTy::Plain(..) => quote! { #inner },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> proc_macro2::Span {
|
||||
match self {
|
||||
FieldInnerTy::Option(ty) | FieldInnerTy::Vec(ty) | FieldInnerTy::Plain(ty) => ty.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Field information passed to the builder. Deliberately omits attrs to discourage the
|
||||
@ -851,7 +857,8 @@ impl quote::IdentFragment for SubdiagnosticKind {
|
||||
/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
|
||||
/// call (like `span_label`).
|
||||
pub(super) fn should_generate_set_arg(field: &Field) -> bool {
|
||||
field.attrs.is_empty()
|
||||
// Perhaps this should be an exhaustive list...
|
||||
field.attrs.iter().all(|attr| is_doc_comment(attr))
|
||||
}
|
||||
|
||||
pub(super) fn is_doc_comment(attr: &Attribute) -> bool {
|
||||
|
@ -64,7 +64,7 @@ impl EffectiveVisibility {
|
||||
self.at_level(level).is_public()
|
||||
}
|
||||
|
||||
pub const fn from_vis(vis: Visibility) -> EffectiveVisibility {
|
||||
pub fn from_vis(vis: Visibility) -> EffectiveVisibility {
|
||||
EffectiveVisibility {
|
||||
direct: vis,
|
||||
reexported: vis,
|
||||
@ -72,18 +72,6 @@ impl EffectiveVisibility {
|
||||
reachable_through_impl_trait: vis,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn min(mut self, lhs: EffectiveVisibility, tcx: TyCtxt<'_>) -> Self {
|
||||
for l in Level::all_levels() {
|
||||
let rhs_vis = self.at_level_mut(l);
|
||||
let lhs_vis = *lhs.at_level(l);
|
||||
if rhs_vis.is_at_least(lhs_vis, tcx) {
|
||||
*rhs_vis = lhs_vis;
|
||||
};
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a map of effective visibilities for reachable HIR nodes.
|
||||
@ -149,6 +137,24 @@ impl EffectiveVisibilities {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_public_at_level(
|
||||
&mut self,
|
||||
id: LocalDefId,
|
||||
lazy_private_vis: impl FnOnce() -> Visibility,
|
||||
level: Level,
|
||||
) {
|
||||
let mut effective_vis = self
|
||||
.effective_vis(id)
|
||||
.copied()
|
||||
.unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
|
||||
for l in Level::all_levels() {
|
||||
if l <= level {
|
||||
*effective_vis.at_level_mut(l) = Visibility::Public;
|
||||
}
|
||||
}
|
||||
self.map.insert(id, effective_vis);
|
||||
}
|
||||
|
||||
pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
|
||||
if !cfg!(debug_assertions) {
|
||||
return;
|
||||
@ -213,7 +219,7 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
id: Id,
|
||||
nominal_vis: Option<Visibility>,
|
||||
nominal_vis: Visibility,
|
||||
lazy_private_vis: impl FnOnce() -> Visibility,
|
||||
inherited_effective_vis: EffectiveVisibility,
|
||||
level: Level,
|
||||
@ -237,11 +243,12 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
|
||||
if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
|
||||
&& level != l)
|
||||
{
|
||||
calculated_effective_vis = if let Some(nominal_vis) = nominal_vis && !nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
|
||||
nominal_vis
|
||||
} else {
|
||||
inherited_effective_vis_at_level
|
||||
}
|
||||
calculated_effective_vis =
|
||||
if nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
|
||||
inherited_effective_vis_at_level
|
||||
} else {
|
||||
nominal_vis
|
||||
};
|
||||
}
|
||||
// effective visibility can't be decreased at next update call for the
|
||||
// same id
|
||||
|
@ -1524,6 +1524,19 @@ impl<V, T> ProjectionElem<V, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the target of this projection always refers to the same memory region
|
||||
/// whatever the state of the program.
|
||||
pub fn is_stable_offset(&self) -> bool {
|
||||
match self {
|
||||
Self::Deref | Self::Index(_) => false,
|
||||
Self::Field(_, _)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::ConstantIndex { .. }
|
||||
| Self::Subslice { .. }
|
||||
| Self::Downcast(_, _) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
|
||||
pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
|
||||
matches!(*self, Self::Downcast(_, x) if x == v)
|
||||
@ -2728,8 +2741,6 @@ pub struct UserTypeProjection {
|
||||
pub projs: Vec<ProjectionKind>,
|
||||
}
|
||||
|
||||
impl Copy for ProjectionKind {}
|
||||
|
||||
impl UserTypeProjection {
|
||||
pub(crate) fn index(mut self) -> Self {
|
||||
self.projs.push(ProjectionElem::Index(()));
|
||||
|
@ -1821,6 +1821,16 @@ rustc_queries! {
|
||||
desc { "normalizing `{}`", goal.value.value }
|
||||
}
|
||||
|
||||
/// Do not call this query directly: invoke `normalize` instead.
|
||||
query normalize_inherent_projection_ty(
|
||||
goal: CanonicalProjectionGoal<'tcx>
|
||||
) -> Result<
|
||||
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
|
||||
NoSolution,
|
||||
> {
|
||||
desc { "normalizing `{}`", goal.value.value }
|
||||
}
|
||||
|
||||
/// Do not call this query directly: invoke `try_normalize_erasing_regions` instead.
|
||||
query try_normalize_generic_arg_after_erasing_regions(
|
||||
goal: ParamEnvAnd<'tcx, GenericArg<'tcx>>
|
||||
|
@ -26,7 +26,7 @@ use super::{Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, Var
|
||||
|
||||
bitflags! {
|
||||
#[derive(HashStable, TyEncodable, TyDecodable)]
|
||||
pub struct AdtFlags: u32 {
|
||||
pub struct AdtFlags: u16 {
|
||||
const NO_ADT_FLAGS = 0;
|
||||
/// Indicates whether the ADT is an enum.
|
||||
const IS_ENUM = 1 << 0;
|
||||
|
@ -1093,11 +1093,13 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
v.0
|
||||
}
|
||||
|
||||
/// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type and associated alias span when type alias is used
|
||||
/// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in
|
||||
/// its return type, and the associated alias span when type alias is used,
|
||||
/// along with a span for lifetime suggestion (if there are existing generics).
|
||||
pub fn return_type_impl_or_dyn_traits_with_type_alias(
|
||||
self,
|
||||
scope_def_id: LocalDefId,
|
||||
) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span)> {
|
||||
) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span, Option<Span>)> {
|
||||
let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id);
|
||||
let mut v = TraitObjectVisitor(vec![], self.hir());
|
||||
// when the return type is a type alias
|
||||
@ -1111,7 +1113,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
{
|
||||
v.visit_ty(alias_ty);
|
||||
if !v.0.is_empty() {
|
||||
return Some((v.0, alias_generics.span));
|
||||
return Some((v.0, alias_generics.span, alias_generics.span_for_lifetime_suggestion()));
|
||||
}
|
||||
}
|
||||
return None;
|
||||
@ -1846,7 +1848,17 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
let substs = substs.into_iter().map(Into::into);
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let n = self.generics_of(_def_id).count();
|
||||
let generics = self.generics_of(_def_id);
|
||||
|
||||
let n = if let DefKind::AssocTy = self.def_kind(_def_id)
|
||||
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id))
|
||||
{
|
||||
// If this is an inherent projection.
|
||||
|
||||
generics.params.len() + 1
|
||||
} else {
|
||||
generics.count()
|
||||
};
|
||||
assert_eq!(
|
||||
(n, Some(n)),
|
||||
substs.size_hint(),
|
||||
@ -2007,7 +2019,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
debug_assert_matches!(
|
||||
(kind, self.def_kind(alias_ty.def_id)),
|
||||
(ty::Opaque, DefKind::OpaqueTy)
|
||||
| (ty::Projection, DefKind::AssocTy)
|
||||
| (ty::Projection | ty::Inherent, DefKind::AssocTy)
|
||||
| (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder)
|
||||
);
|
||||
self.mk_ty_from_kind(Alias(kind, alias_ty))
|
||||
|
@ -265,7 +265,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
ty::Infer(ty::FreshTy(_)) => "fresh type".into(),
|
||||
ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(),
|
||||
ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(),
|
||||
ty::Alias(ty::Projection, _) => "associated type".into(),
|
||||
ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(),
|
||||
ty::Param(p) => format!("type parameter `{p}`").into(),
|
||||
ty::Alias(ty::Opaque, ..) => if tcx.ty_is_opaque_future(self) { "future".into() } else { "opaque type".into() },
|
||||
ty::Error(_) => "type error".into(),
|
||||
@ -312,7 +312,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
ty::Tuple(..) => "tuple".into(),
|
||||
ty::Placeholder(..) => "higher-ranked type".into(),
|
||||
ty::Bound(..) => "bound type variable".into(),
|
||||
ty::Alias(ty::Projection, _) => "associated type".into(),
|
||||
ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(),
|
||||
ty::Param(_) => "type parameter".into(),
|
||||
ty::Alias(ty::Opaque, ..) => "opaque type".into(),
|
||||
}
|
||||
|
@ -176,14 +176,14 @@ impl FlagComputation {
|
||||
self.add_substs(substs);
|
||||
}
|
||||
|
||||
&ty::Alias(ty::Projection, data) => {
|
||||
self.add_flags(TypeFlags::HAS_TY_PROJECTION);
|
||||
self.add_alias_ty(data);
|
||||
}
|
||||
&ty::Alias(kind, data) => {
|
||||
self.add_flags(match kind {
|
||||
ty::Projection => TypeFlags::HAS_TY_PROJECTION,
|
||||
ty::Inherent => TypeFlags::HAS_TY_INHERENT,
|
||||
ty::Opaque => TypeFlags::HAS_TY_OPAQUE,
|
||||
});
|
||||
|
||||
&ty::Alias(ty::Opaque, ty::AliasTy { substs, .. }) => {
|
||||
self.add_flags(TypeFlags::HAS_TY_OPAQUE);
|
||||
self.add_substs(substs);
|
||||
self.add_alias_ty(data);
|
||||
}
|
||||
|
||||
&ty::Dynamic(obj, r, _) => {
|
||||
|
@ -113,6 +113,12 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
Never => InhabitedPredicate::False,
|
||||
Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self),
|
||||
// FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above.
|
||||
// However it's unclear if the substs passed to `InhabitedPredicate::subst` are of the correct
|
||||
// format, i.e. don't contain parent substs. If you hit this case, please verify this beforehand.
|
||||
Alias(ty::Inherent, _) => {
|
||||
bug!("unimplemented: inhabitedness checking for inherent projections")
|
||||
}
|
||||
Tuple(tys) if tys.is_empty() => InhabitedPredicate::True,
|
||||
// use a query for more complex cases
|
||||
Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self),
|
||||
|
@ -115,7 +115,7 @@ impl<'tcx> Instance<'tcx> {
|
||||
/// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization.
|
||||
pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
|
||||
let ty = tcx.type_of(self.def.def_id());
|
||||
tcx.subst_and_normalize_erasing_regions(self.substs, param_env, ty.skip_binder())
|
||||
tcx.subst_and_normalize_erasing_regions(self.substs, param_env, ty)
|
||||
}
|
||||
|
||||
/// Finds a crate that contains a monomorphization of this instance that
|
||||
@ -578,14 +578,15 @@ impl<'tcx> Instance<'tcx> {
|
||||
self.def.has_polymorphic_mir_body().then_some(self.substs)
|
||||
}
|
||||
|
||||
pub fn subst_mir<T>(&self, tcx: TyCtxt<'tcx>, v: &T) -> T
|
||||
pub fn subst_mir<T>(&self, tcx: TyCtxt<'tcx>, v: EarlyBinder<&T>) -> T
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
|
||||
{
|
||||
let v = v.map_bound(|v| *v);
|
||||
if let Some(substs) = self.substs_for_mir_body() {
|
||||
EarlyBinder(*v).subst(tcx, substs)
|
||||
v.subst(tcx, substs)
|
||||
} else {
|
||||
*v
|
||||
v.skip_binder()
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,7 +595,7 @@ impl<'tcx> Instance<'tcx> {
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
v: T,
|
||||
v: EarlyBinder<T>,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>> + Clone,
|
||||
@ -602,7 +603,7 @@ impl<'tcx> Instance<'tcx> {
|
||||
if let Some(substs) = self.substs_for_mir_body() {
|
||||
tcx.subst_and_normalize_erasing_regions(substs, param_env, v)
|
||||
} else {
|
||||
tcx.normalize_erasing_regions(param_env, v)
|
||||
tcx.normalize_erasing_regions(param_env, v.skip_binder())
|
||||
}
|
||||
}
|
||||
|
||||
@ -611,7 +612,7 @@ impl<'tcx> Instance<'tcx> {
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
v: T,
|
||||
v: EarlyBinder<T>,
|
||||
) -> Result<T, NormalizationError<'tcx>>
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>> + Clone,
|
||||
@ -619,7 +620,7 @@ impl<'tcx> Instance<'tcx> {
|
||||
if let Some(substs) = self.substs_for_mir_body() {
|
||||
tcx.try_subst_and_normalize_erasing_regions(substs, param_env, v)
|
||||
} else {
|
||||
tcx.try_normalize_erasing_regions(param_env, v)
|
||||
tcx.try_normalize_erasing_regions(param_env, v.skip_binder())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,7 +324,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
||||
let non_zero = !ty.is_unsafe_ptr();
|
||||
let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
|
||||
match tail.kind() {
|
||||
ty::Param(_) | ty::Alias(ty::Projection, _) => {
|
||||
ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => {
|
||||
debug_assert!(tail.has_non_region_param());
|
||||
Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
|
||||
}
|
||||
|
@ -1004,7 +1004,7 @@ impl<'tcx> Term<'tcx> {
|
||||
match self.unpack() {
|
||||
TermKind::Ty(ty) => match ty.kind() {
|
||||
ty::Alias(kind, alias_ty) => match kind {
|
||||
AliasKind::Projection => Some(*alias_ty),
|
||||
AliasKind::Projection | AliasKind::Inherent => Some(*alias_ty),
|
||||
AliasKind::Opaque => None,
|
||||
},
|
||||
_ => None,
|
||||
@ -1739,7 +1739,7 @@ pub struct Destructor {
|
||||
|
||||
bitflags! {
|
||||
#[derive(HashStable, TyEncodable, TyDecodable)]
|
||||
pub struct VariantFlags: u32 {
|
||||
pub struct VariantFlags: u8 {
|
||||
const NO_VARIANT_FLAGS = 0;
|
||||
/// Indicates whether the field list of this variant is `#[non_exhaustive]`.
|
||||
const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0;
|
||||
|
@ -139,7 +139,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
self,
|
||||
param_substs: SubstsRef<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
value: T,
|
||||
value: EarlyBinder<T>,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
@ -151,7 +151,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
param_env={:?})",
|
||||
param_substs, value, param_env,
|
||||
);
|
||||
let substituted = EarlyBinder(value).subst(self, param_substs);
|
||||
let substituted = value.subst(self, param_substs);
|
||||
self.normalize_erasing_regions(param_env, substituted)
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
self,
|
||||
param_substs: SubstsRef<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
value: T,
|
||||
value: EarlyBinder<T>,
|
||||
) -> Result<T, NormalizationError<'tcx>>
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
@ -175,7 +175,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
param_env={:?})",
|
||||
param_substs, value, param_env,
|
||||
);
|
||||
let substituted = EarlyBinder(value).subst(self, param_substs);
|
||||
let substituted = value.subst(self, param_substs);
|
||||
self.try_normalize_erasing_regions(param_env, substituted)
|
||||
}
|
||||
}
|
||||
|
@ -729,7 +729,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||
ty::Foreign(def_id) => {
|
||||
p!(print_def_path(def_id, &[]));
|
||||
}
|
||||
ty::Alias(ty::Projection, ref data) => {
|
||||
ty::Alias(ty::Projection | ty::Inherent, ref data) => {
|
||||
if !(self.should_print_verbose() || NO_QUERIES.with(|q| q.get()))
|
||||
&& self.tcx().is_impl_trait_in_trait(data.def_id)
|
||||
{
|
||||
|
@ -550,6 +550,11 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>(
|
||||
Ok(tcx.mk_projection(projection_ty.def_id, projection_ty.substs))
|
||||
}
|
||||
|
||||
(&ty::Alias(ty::Inherent, a_data), &ty::Alias(ty::Inherent, b_data)) => {
|
||||
let alias_ty = relation.relate(a_data, b_data)?;
|
||||
Ok(tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(alias_ty.def_id, alias_ty.substs)))
|
||||
}
|
||||
|
||||
(
|
||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, substs: a_substs, .. }),
|
||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, substs: b_substs, .. }),
|
||||
|
@ -1190,9 +1190,9 @@ where
|
||||
|
||||
/// Represents the projection of an associated type.
|
||||
///
|
||||
/// For a projection, this would be `<Ty as Trait<...>>::N`.
|
||||
///
|
||||
/// For an opaque type, there is no explicit syntax.
|
||||
/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
|
||||
/// * For an inherent projection, this would be `Ty::N<...>`.
|
||||
/// * For an opaque type, there is no explicit syntax.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
|
||||
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
|
||||
pub struct AliasTy<'tcx> {
|
||||
@ -1201,12 +1201,16 @@ pub struct AliasTy<'tcx> {
|
||||
/// For a projection, these are the substitutions for the trait and the
|
||||
/// GAT substitutions, if there are any.
|
||||
///
|
||||
/// For an inherent projection, they consist of the self type and the GAT substitutions,
|
||||
/// if there are any.
|
||||
///
|
||||
/// For RPIT the substitutions are for the generics of the function,
|
||||
/// while for TAIT it is used for the generic parameters of the alias.
|
||||
pub substs: SubstsRef<'tcx>,
|
||||
|
||||
/// The `DefId` of the `TraitItem` for the associated type `N` if this is a projection,
|
||||
/// or the `OpaqueType` item if this is an opaque.
|
||||
/// The `DefId` of the `TraitItem` or `ImplItem` for the associated type `N` depending on whether
|
||||
/// this is a projection or an inherent projection or the `DefId` of the `OpaqueType` item if
|
||||
/// this is an opaque.
|
||||
///
|
||||
/// During codegen, `tcx.type_of(def_id)` can be used to get the type of the
|
||||
/// underlying type if the type is an opaque.
|
||||
@ -1224,6 +1228,7 @@ pub struct AliasTy<'tcx> {
|
||||
impl<'tcx> AliasTy<'tcx> {
|
||||
pub fn kind(self, tcx: TyCtxt<'tcx>) -> ty::AliasKind {
|
||||
match tcx.def_kind(self.def_id) {
|
||||
DefKind::AssocTy if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(self.def_id)) => ty::Inherent,
|
||||
DefKind::AssocTy | DefKind::ImplTraitPlaceholder => ty::Projection,
|
||||
DefKind::OpaqueTy => ty::Opaque,
|
||||
kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),
|
||||
@ -1236,6 +1241,17 @@ impl<'tcx> AliasTy<'tcx> {
|
||||
}
|
||||
|
||||
/// The following methods work only with associated type projections.
|
||||
impl<'tcx> AliasTy<'tcx> {
|
||||
pub fn self_ty(self) -> Ty<'tcx> {
|
||||
self.substs.type_at(0)
|
||||
}
|
||||
|
||||
pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
||||
tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1)))
|
||||
}
|
||||
}
|
||||
|
||||
/// The following methods work only with trait associated type projections.
|
||||
impl<'tcx> AliasTy<'tcx> {
|
||||
pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
|
||||
match tcx.def_kind(self.def_id) {
|
||||
@ -1274,13 +1290,28 @@ impl<'tcx> AliasTy<'tcx> {
|
||||
let def_id = self.trait_def_id(tcx);
|
||||
ty::TraitRef::new(tcx, def_id, self.substs.truncate_to(tcx, tcx.generics_of(def_id)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn self_ty(self) -> Ty<'tcx> {
|
||||
self.substs.type_at(0)
|
||||
}
|
||||
/// The following methods work only with inherent associated type projections.
|
||||
impl<'tcx> AliasTy<'tcx> {
|
||||
/// Transform the substitutions to have the given `impl` substs as the base and the GAT substs on top of that.
|
||||
///
|
||||
/// Does the following transformation:
|
||||
///
|
||||
/// ```text
|
||||
/// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m]
|
||||
///
|
||||
/// I_i impl subst
|
||||
/// P_j GAT subst
|
||||
/// ```
|
||||
pub fn rebase_substs_onto_impl(
|
||||
self,
|
||||
impl_substs: ty::SubstsRef<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> ty::SubstsRef<'tcx> {
|
||||
debug_assert_eq!(self.kind(tcx), ty::Inherent);
|
||||
|
||||
pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
||||
tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1)))
|
||||
tcx.mk_substs_from_iter(impl_substs.into_iter().chain(self.substs.into_iter().skip(1)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,9 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
|
||||
fn has_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PROJECTION)
|
||||
}
|
||||
fn has_inherent_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
|
||||
}
|
||||
fn has_opaque_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ pub use self::borrowed_locals::borrowed_locals;
|
||||
pub use self::borrowed_locals::MaybeBorrowedLocals;
|
||||
pub use self::liveness::MaybeLiveLocals;
|
||||
pub use self::liveness::MaybeTransitiveLiveLocals;
|
||||
pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive};
|
||||
pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive};
|
||||
|
||||
/// `MaybeInitializedPlaces` tracks all places that might be
|
||||
/// initialized upon reaching a particular point in the control flow
|
||||
|
@ -74,6 +74,73 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MaybeStorageDead {
|
||||
always_live_locals: BitSet<Local>,
|
||||
}
|
||||
|
||||
impl MaybeStorageDead {
|
||||
pub fn new(always_live_locals: BitSet<Local>) -> Self {
|
||||
MaybeStorageDead { always_live_locals }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead {
|
||||
type Domain = BitSet<Local>;
|
||||
|
||||
const NAME: &'static str = "maybe_storage_dead";
|
||||
|
||||
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
|
||||
// bottom = live
|
||||
BitSet::new_empty(body.local_decls.len())
|
||||
}
|
||||
|
||||
fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
|
||||
assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
|
||||
// Do not iterate on return place and args, as they are trivially always live.
|
||||
for local in body.vars_and_temps_iter() {
|
||||
if !self.always_live_locals.contains(local) {
|
||||
on_entry.insert(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
|
||||
type Idx = Local;
|
||||
|
||||
fn statement_effect(
|
||||
&self,
|
||||
trans: &mut impl GenKill<Self::Idx>,
|
||||
stmt: &mir::Statement<'tcx>,
|
||||
_: Location,
|
||||
) {
|
||||
match stmt.kind {
|
||||
StatementKind::StorageLive(l) => trans.kill(l),
|
||||
StatementKind::StorageDead(l) => trans.gen(l),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn terminator_effect(
|
||||
&self,
|
||||
_trans: &mut impl GenKill<Self::Idx>,
|
||||
_: &mir::Terminator<'tcx>,
|
||||
_: Location,
|
||||
) {
|
||||
// Terminators have no effect
|
||||
}
|
||||
|
||||
fn call_return_effect(
|
||||
&self,
|
||||
_trans: &mut impl GenKill<Self::Idx>,
|
||||
_block: BasicBlock,
|
||||
_return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
// Nothing to do when a call returns successfully
|
||||
}
|
||||
}
|
||||
|
||||
type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
|
||||
|
||||
/// Dataflow analysis that determines whether each local requires storage at a
|
||||
|
@ -24,6 +24,8 @@ rustc_session = { path = "../rustc_session" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
|
||||
[dev-dependencies]
|
||||
coverage_test_macros = { path = "src/coverage/test_macros" }
|
||||
|
66
compiler/rustc_mir_transform/messages.ftl
Normal file
66
compiler/rustc_mir_transform/messages.ftl
Normal file
@ -0,0 +1,66 @@
|
||||
mir_transform_const_modify = attempting to modify a `const` item
|
||||
.note = each usage of a `const` item creates a new temporary; the original `const` item will not be modified
|
||||
|
||||
mir_transform_const_mut_borrow = taking a mutable reference to a `const` item
|
||||
.note = each usage of a `const` item creates a new temporary
|
||||
.note2 = the mutable reference will refer to this temporary, not the original `const` item
|
||||
.note3 = mutable reference created due to call to this method
|
||||
|
||||
mir_transform_const_defined_here = `const` item defined here
|
||||
|
||||
mir_transform_unaligned_packed_ref = reference to packed field is unaligned
|
||||
.note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
|
||||
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
|
||||
.help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
|
||||
|
||||
mir_transform_unused_unsafe = unnecessary `unsafe` block
|
||||
.label = because it's nested under this `unsafe` block
|
||||
|
||||
mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in_unsafe_fn_allowed ->
|
||||
[true] function or block
|
||||
*[false] block
|
||||
}
|
||||
.not_inherited = items do not inherit unsafety from separate enclosing items
|
||||
|
||||
mir_transform_call_to_unsafe_label = call to unsafe function
|
||||
mir_transform_call_to_unsafe_note = consult the function's documentation for information on how to avoid undefined behavior
|
||||
mir_transform_use_of_asm_label = use of inline assembly
|
||||
mir_transform_use_of_asm_note = inline assembly is entirely unchecked and can cause undefined behavior
|
||||
mir_transform_initializing_valid_range_label = initializing type with `rustc_layout_scalar_valid_range` attr
|
||||
mir_transform_initializing_valid_range_note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
|
||||
mir_transform_const_ptr2int_label = cast of pointer to int
|
||||
mir_transform_const_ptr2int_note = casting pointers to integers in constants
|
||||
mir_transform_use_of_static_mut_label = use of mutable static
|
||||
mir_transform_use_of_static_mut_note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
|
||||
mir_transform_use_of_extern_static_label = use of extern static
|
||||
mir_transform_use_of_extern_static_note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
|
||||
mir_transform_deref_ptr_label = dereference of raw pointer
|
||||
mir_transform_deref_ptr_note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
|
||||
mir_transform_union_access_label = access to union field
|
||||
mir_transform_union_access_note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
|
||||
mir_transform_mutation_layout_constrained_label = mutation of layout constrained field
|
||||
mir_transform_mutation_layout_constrained_note = mutating layout constrained fields cannot statically be checked for valid values
|
||||
mir_transform_mutation_layout_constrained_borrow_label = borrow of layout constrained field with interior mutability
|
||||
mir_transform_mutation_layout_constrained_borrow_note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
|
||||
mir_transform_target_feature_call_label = call to function with `#[target_feature]`
|
||||
mir_transform_target_feature_call_note = can only be called if the required target features are available
|
||||
|
||||
mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe block (error E0133)
|
||||
|
||||
mir_transform_arithmetic_overflow = this arithmetic operation will overflow
|
||||
mir_transform_operation_will_panic = this operation will panic at runtime
|
||||
|
||||
mir_transform_ffi_unwind_call = call to {$foreign ->
|
||||
[true] foreign function
|
||||
*[false] function pointer
|
||||
} with FFI-unwind ABI
|
||||
|
||||
mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer
|
||||
.suggestion = cast `{$ident}` to obtain a function pointer
|
||||
|
||||
mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be
|
||||
.label = the value is held across this suspend point
|
||||
.note = {$reason}
|
||||
.help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point
|
||||
|
||||
mir_transform_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item
|
@ -1,11 +1,12 @@
|
||||
use rustc_errors::{DiagnosticBuilder, DiagnosticMessage};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::MirLint;
|
||||
use crate::{errors, MirLint};
|
||||
|
||||
pub struct CheckConstItemMutation;
|
||||
|
||||
@ -58,16 +59,14 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_const_item_usage(
|
||||
/// If we should lint on this usage, return the [`HirId`], source [`Span`]
|
||||
/// and [`Span`] of the const item to use in the lint.
|
||||
fn should_lint_const_item_usage(
|
||||
&self,
|
||||
place: &Place<'tcx>,
|
||||
const_item: DefId,
|
||||
location: Location,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
decorate: impl for<'a, 'b> FnOnce(
|
||||
&'a mut DiagnosticBuilder<'b, ()>,
|
||||
) -> &'a mut DiagnosticBuilder<'b, ()>,
|
||||
) {
|
||||
) -> Option<(HirId, Span, Span)> {
|
||||
// Don't lint on borrowing/assigning when a dereference is involved.
|
||||
// If we 'leave' the temporary via a dereference, we must
|
||||
// be modifying something else
|
||||
@ -83,16 +82,9 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> {
|
||||
.assert_crate_local()
|
||||
.lint_root;
|
||||
|
||||
self.tcx.struct_span_lint_hir(
|
||||
CONST_ITEM_MUTATION,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
msg,
|
||||
|lint| {
|
||||
decorate(lint)
|
||||
.span_note(self.tcx.def_span(const_item), "`const` item defined here")
|
||||
},
|
||||
);
|
||||
Some((lint_root, source_info.span, self.tcx.def_span(const_item)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,10 +96,14 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
|
||||
// Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
|
||||
// so emitting a lint would be redundant.
|
||||
if !lhs.projection.is_empty() {
|
||||
if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) {
|
||||
self.lint_const_item_usage(&lhs, def_id, loc, "attempting to modify a `const` item",|lint| {
|
||||
lint.note("each usage of a `const` item creates a new temporary; the original `const` item will not be modified")
|
||||
})
|
||||
if let Some(def_id) = self.is_const_item_without_destructor(lhs.local)
|
||||
&& let Some((lint_root, span, item)) = self.should_lint_const_item_usage(&lhs, def_id, loc) {
|
||||
self.tcx.emit_spanned_lint(
|
||||
CONST_ITEM_MUTATION,
|
||||
lint_root,
|
||||
span,
|
||||
errors::ConstMutate::Modify { konst: item }
|
||||
);
|
||||
}
|
||||
}
|
||||
// We are looking for MIR of the form:
|
||||
@ -143,17 +139,22 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
|
||||
});
|
||||
let lint_loc =
|
||||
if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
|
||||
self.lint_const_item_usage(place, def_id, lint_loc, "taking a mutable reference to a `const` item", |lint| {
|
||||
lint
|
||||
.note("each usage of a `const` item creates a new temporary")
|
||||
.note("the mutable reference will refer to this temporary, not the original `const` item");
|
||||
|
||||
if let Some((method_did, _substs)) = method_did {
|
||||
lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
|
||||
}
|
||||
|
||||
lint
|
||||
});
|
||||
let method_call = if let Some((method_did, _)) = method_did {
|
||||
Some(self.tcx.def_span(method_did))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some((lint_root, span, item)) =
|
||||
self.should_lint_const_item_usage(place, def_id, lint_loc)
|
||||
{
|
||||
self.tcx.emit_spanned_lint(
|
||||
CONST_ITEM_MUTATION,
|
||||
lint_root,
|
||||
span,
|
||||
errors::ConstMutate::MutBorrow { method_call, konst: item },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.super_rvalue(rvalue, loc);
|
||||
|
@ -1,10 +1,9 @@
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
|
||||
use crate::util;
|
||||
use crate::MirLint;
|
||||
use crate::{errors, util};
|
||||
|
||||
pub struct CheckPackedRef;
|
||||
|
||||
@ -49,25 +48,7 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
|
||||
// shouldn't do.
|
||||
span_bug!(self.source_info.span, "builtin derive created an unaligned reference");
|
||||
} else {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
self.source_info.span,
|
||||
E0793,
|
||||
"reference to packed field is unaligned"
|
||||
)
|
||||
.note(
|
||||
"packed structs are only aligned by one byte, and many modern architectures \
|
||||
penalize unaligned field accesses"
|
||||
)
|
||||
.note(
|
||||
"creating a misaligned reference is undefined behavior (even if that \
|
||||
reference is never dereferenced)",
|
||||
).help(
|
||||
"copy the field contents to a local variable, or replace the \
|
||||
reference with a raw pointer and use `read_unaligned`/`write_unaligned` \
|
||||
(loads and stores via `*p` must be properly aligned even when using raw pointers)"
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::UnalignedPackedRef { span: self.source_info.span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use rustc_data_structures::unord::{UnordItems, UnordSet};
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
@ -15,6 +14,8 @@ use rustc_session::lint::Level;
|
||||
|
||||
use std::ops::Bound;
|
||||
|
||||
use crate::errors;
|
||||
|
||||
pub struct UnsafetyChecker<'a, 'tcx> {
|
||||
body: &'a Body<'tcx>,
|
||||
body_did: LocalDefId,
|
||||
@ -509,21 +510,12 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResu
|
||||
|
||||
fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
|
||||
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
|
||||
let msg = "unnecessary `unsafe` block";
|
||||
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg, |lint| {
|
||||
lint.span_label(span, msg);
|
||||
match kind {
|
||||
UnusedUnsafe::Unused => {}
|
||||
UnusedUnsafe::InUnsafeBlock(id) => {
|
||||
lint.span_label(
|
||||
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
|
||||
"because it's nested under this `unsafe` block",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
lint
|
||||
});
|
||||
let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind {
|
||||
Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
tcx.emit_spanned_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent });
|
||||
}
|
||||
|
||||
pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
@ -537,26 +529,11 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id);
|
||||
|
||||
for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
|
||||
let (description, note) = details.description_and_note();
|
||||
let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span };
|
||||
|
||||
match kind {
|
||||
UnsafetyViolationKind::General => {
|
||||
// once
|
||||
let unsafe_fn_msg = if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) {
|
||||
" function or"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
tcx.sess,
|
||||
source_info.span,
|
||||
E0133,
|
||||
"{} is unsafe and requires unsafe{} block",
|
||||
description,
|
||||
unsafe_fn_msg,
|
||||
);
|
||||
err.span_label(source_info.span, description).note(note);
|
||||
let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root);
|
||||
let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| {
|
||||
if let Node::Expr(block) = node
|
||||
&& let ExprKind::Block(block, _) = block.kind
|
||||
@ -572,22 +549,23 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
false
|
||||
}
|
||||
});
|
||||
if let Some((id, _)) = note_non_inherited {
|
||||
let span = tcx.hir().span(id);
|
||||
err.span_label(
|
||||
tcx.sess.source_map().guess_head_span(span),
|
||||
"items do not inherit unsafety from separate enclosing items",
|
||||
);
|
||||
}
|
||||
|
||||
err.emit();
|
||||
let enclosing = if let Some((id, _)) = note_non_inherited {
|
||||
Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
tcx.sess.emit_err(errors::RequiresUnsafe {
|
||||
span: source_info.span,
|
||||
enclosing,
|
||||
details,
|
||||
op_in_unsafe_fn_allowed,
|
||||
});
|
||||
}
|
||||
UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
|
||||
UnsafetyViolationKind::UnsafeFn => tcx.emit_spanned_lint(
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
format!("{} is unsafe and requires unsafe block (error E0133)", description,),
|
||||
|lint| lint.span_label(source_info.span, description).note(note),
|
||||
errors::UnsafeOpInUnsafeFn { details },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -806,6 +806,24 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn process_projection_elem(
|
||||
&mut self,
|
||||
elem: PlaceElem<'tcx>,
|
||||
_: Location,
|
||||
) -> Option<PlaceElem<'tcx>> {
|
||||
if let PlaceElem::Index(local) = elem
|
||||
&& let Some(value) = self.get_const(local.into())
|
||||
&& self.should_const_prop(&value)
|
||||
&& let interpret::Operand::Immediate(interpret::Immediate::Scalar(scalar)) = *value
|
||||
&& let Ok(offset) = scalar.to_target_usize(&self.tcx)
|
||||
&& let Some(min_length) = offset.checked_add(1)
|
||||
{
|
||||
Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_assign(
|
||||
&mut self,
|
||||
place: &mut Place<'tcx>,
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! Propagates constants for early reporting of statically known
|
||||
//! assertion failures
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use either::Left;
|
||||
|
||||
use rustc_const_eval::interpret::Immediate;
|
||||
@ -17,7 +19,6 @@ use rustc_middle::ty::InternalSubsts;
|
||||
use rustc_middle::ty::{
|
||||
self, ConstInt, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt,
|
||||
};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_trait_selection::traits;
|
||||
@ -25,6 +26,7 @@ use rustc_trait_selection::traits;
|
||||
use crate::const_prop::CanConstProp;
|
||||
use crate::const_prop::ConstPropMachine;
|
||||
use crate::const_prop::ConstPropMode;
|
||||
use crate::errors::AssertLint;
|
||||
use crate::MirLint;
|
||||
|
||||
/// The maximum number of bytes that we'll allocate space for a local or the return value.
|
||||
@ -311,18 +313,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn report_assert_as_lint(
|
||||
&self,
|
||||
lint: &'static lint::Lint,
|
||||
location: Location,
|
||||
message: &'static str,
|
||||
panic: AssertKind<impl std::fmt::Debug>,
|
||||
) {
|
||||
let source_info = self.body().source_info(location);
|
||||
fn report_assert_as_lint(&self, source_info: &SourceInfo, lint: AssertLint<impl Debug>) {
|
||||
if let Some(lint_root) = self.lint_root(*source_info) {
|
||||
self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, message, |lint| {
|
||||
lint.span_label(source_info.span, format!("{:?}", panic))
|
||||
});
|
||||
self.tcx.emit_spanned_lint(lint.lint(), lint_root, source_info.span, lint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,11 +328,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
// `AssertKind` only has an `OverflowNeg` variant, so make sure that is
|
||||
// appropriate to use.
|
||||
assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow");
|
||||
let source_info = self.body().source_info(location);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
location,
|
||||
"this arithmetic operation will overflow",
|
||||
AssertKind::OverflowNeg(val.to_const_int()),
|
||||
source_info,
|
||||
AssertLint::ArithmeticOverflow(
|
||||
source_info.span,
|
||||
AssertKind::OverflowNeg(val.to_const_int()),
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@ -370,23 +365,23 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
let r_bits = r.to_scalar().to_bits(right_size).ok();
|
||||
if r_bits.map_or(false, |b| b >= left_size.bits() as u128) {
|
||||
debug!("check_binary_op: reporting assert for {:?}", location);
|
||||
let source_info = self.body().source_info(location);
|
||||
let panic = AssertKind::Overflow(
|
||||
op,
|
||||
match l {
|
||||
Some(l) => l.to_const_int(),
|
||||
// Invent a dummy value, the diagnostic ignores it anyway
|
||||
None => ConstInt::new(
|
||||
ScalarInt::try_from_uint(1_u8, left_size).unwrap(),
|
||||
left_ty.is_signed(),
|
||||
left_ty.is_ptr_sized_integral(),
|
||||
),
|
||||
},
|
||||
r.to_const_int(),
|
||||
);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
location,
|
||||
"this arithmetic operation will overflow",
|
||||
AssertKind::Overflow(
|
||||
op,
|
||||
match l {
|
||||
Some(l) => l.to_const_int(),
|
||||
// Invent a dummy value, the diagnostic ignores it anyway
|
||||
None => ConstInt::new(
|
||||
ScalarInt::try_from_uint(1_u8, left_size).unwrap(),
|
||||
left_ty.is_signed(),
|
||||
left_ty.is_ptr_sized_integral(),
|
||||
),
|
||||
},
|
||||
r.to_const_int(),
|
||||
),
|
||||
source_info,
|
||||
AssertLint::ArithmeticOverflow(source_info.span, panic),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@ -398,11 +393,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, &l, &r)?;
|
||||
Ok(overflow)
|
||||
})? {
|
||||
let source_info = self.body().source_info(location);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
location,
|
||||
"this arithmetic operation will overflow",
|
||||
AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()),
|
||||
source_info,
|
||||
AssertLint::ArithmeticOverflow(
|
||||
source_info.span,
|
||||
AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()),
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@ -543,11 +540,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
// Need proper const propagator for these.
|
||||
_ => return None,
|
||||
};
|
||||
let source_info = self.body().source_info(location);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::UNCONDITIONAL_PANIC,
|
||||
location,
|
||||
"this operation will panic at runtime",
|
||||
msg,
|
||||
source_info,
|
||||
AssertLint::UnconditionalPanic(source_info.span, msg),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,8 @@ impl<'tcx> MirPass<'tcx> for CopyProp {
|
||||
}
|
||||
|
||||
fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
||||
let borrowed_locals = borrowed_locals(body);
|
||||
let ssa = SsaLocals::new(tcx, param_env, body, &borrowed_locals);
|
||||
let ssa = SsaLocals::new(body);
|
||||
|
||||
let fully_moved = fully_moved_locals(&ssa, body);
|
||||
debug!(?fully_moved);
|
||||
@ -76,7 +75,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> BitSet<Local> {
|
||||
let mut fully_moved = BitSet::new_filled(body.local_decls.len());
|
||||
|
||||
for (_, rvalue) in ssa.assignments(body) {
|
||||
for (_, rvalue, _) in ssa.assignments(body) {
|
||||
let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) | Rvalue::CopyForDeref(place))
|
||||
= rvalue
|
||||
else { continue };
|
||||
|
245
compiler/rustc_mir_transform/src/errors.rs
Normal file
245
compiler/rustc_mir_transform/src/errors.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use rustc_errors::{
|
||||
DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler, IntoDiagnostic,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
|
||||
use rustc_session::lint::{self, Lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
pub(crate) enum ConstMutate {
|
||||
#[diag(mir_transform_const_modify)]
|
||||
#[note]
|
||||
Modify {
|
||||
#[note(mir_transform_const_defined_here)]
|
||||
konst: Span,
|
||||
},
|
||||
#[diag(mir_transform_const_mut_borrow)]
|
||||
#[note]
|
||||
#[note(mir_transform_note2)]
|
||||
MutBorrow {
|
||||
#[note(mir_transform_note3)]
|
||||
method_call: Option<Span>,
|
||||
#[note(mir_transform_const_defined_here)]
|
||||
konst: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_transform_unaligned_packed_ref, code = "E0793")]
|
||||
#[note]
|
||||
#[note(mir_transform_note_ub)]
|
||||
#[help]
|
||||
pub(crate) struct UnalignedPackedRef {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_unused_unsafe)]
|
||||
pub(crate) struct UnusedUnsafe {
|
||||
#[label(mir_transform_unused_unsafe)]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub nested_parent: Option<Span>,
|
||||
}
|
||||
|
||||
pub(crate) struct RequiresUnsafe {
|
||||
pub span: Span,
|
||||
pub details: RequiresUnsafeDetail,
|
||||
pub enclosing: Option<Span>,
|
||||
pub op_in_unsafe_fn_allowed: bool,
|
||||
}
|
||||
|
||||
// The primary message for this diagnostic should be '{$label} is unsafe and...',
|
||||
// so we need to eagerly translate the label here, which isn't supported by the derive API
|
||||
// We could also exhaustively list out the primary messages for all unsafe violations,
|
||||
// but this would result in a lot of duplication.
|
||||
impl<'sess, G: EmissionGuarantee> IntoDiagnostic<'sess, G> for RequiresUnsafe {
|
||||
#[track_caller]
|
||||
fn into_diagnostic(self, handler: &'sess Handler) -> DiagnosticBuilder<'sess, G> {
|
||||
let mut diag =
|
||||
handler.struct_diagnostic(crate::fluent_generated::mir_transform_requires_unsafe);
|
||||
diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string()));
|
||||
diag.set_span(self.span);
|
||||
diag.span_label(self.span, self.details.label());
|
||||
diag.note(self.details.note());
|
||||
let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
||||
diag.set_arg("details", desc);
|
||||
diag.set_arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
|
||||
if let Some(sp) = self.enclosing {
|
||||
diag.span_label(sp, crate::fluent_generated::mir_transform_not_inherited);
|
||||
}
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct RequiresUnsafeDetail {
|
||||
pub span: Span,
|
||||
pub violation: UnsafetyViolationDetails,
|
||||
}
|
||||
|
||||
impl RequiresUnsafeDetail {
|
||||
fn note(self) -> DiagnosticMessage {
|
||||
use UnsafetyViolationDetails::*;
|
||||
match self.violation {
|
||||
CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_note,
|
||||
UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_note,
|
||||
InitializingTypeWith => {
|
||||
crate::fluent_generated::mir_transform_initializing_valid_range_note
|
||||
}
|
||||
CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_note,
|
||||
UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_note,
|
||||
UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_note,
|
||||
DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_note,
|
||||
AccessToUnionField => crate::fluent_generated::mir_transform_union_access_note,
|
||||
MutationOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_note
|
||||
}
|
||||
BorrowOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_note
|
||||
}
|
||||
CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_note,
|
||||
}
|
||||
}
|
||||
|
||||
fn label(self) -> DiagnosticMessage {
|
||||
use UnsafetyViolationDetails::*;
|
||||
match self.violation {
|
||||
CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_label,
|
||||
UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_label,
|
||||
InitializingTypeWith => {
|
||||
crate::fluent_generated::mir_transform_initializing_valid_range_label
|
||||
}
|
||||
CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_label,
|
||||
UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_label,
|
||||
UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_label,
|
||||
DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_label,
|
||||
AccessToUnionField => crate::fluent_generated::mir_transform_union_access_label,
|
||||
MutationOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_label
|
||||
}
|
||||
BorrowOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_label
|
||||
}
|
||||
CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_label,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UnsafeOpInUnsafeFn {
|
||||
pub details: RequiresUnsafeDetail,
|
||||
}
|
||||
|
||||
impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
|
||||
#[track_caller]
|
||||
fn decorate_lint<'b>(
|
||||
self,
|
||||
diag: &'b mut DiagnosticBuilder<'a, ()>,
|
||||
) -> &'b mut DiagnosticBuilder<'a, ()> {
|
||||
let desc = diag
|
||||
.handler()
|
||||
.expect("lint should not yet be emitted")
|
||||
.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
||||
diag.set_arg("details", desc);
|
||||
diag.span_label(self.details.span, self.details.label());
|
||||
diag.note(self.details.note());
|
||||
diag
|
||||
}
|
||||
|
||||
fn msg(&self) -> DiagnosticMessage {
|
||||
crate::fluent_generated::mir_transform_unsafe_op_in_unsafe_fn
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum AssertLint<P> {
|
||||
ArithmeticOverflow(Span, AssertKind<P>),
|
||||
UnconditionalPanic(Span, AssertKind<P>),
|
||||
}
|
||||
|
||||
impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
|
||||
fn decorate_lint<'b>(
|
||||
self,
|
||||
diag: &'b mut DiagnosticBuilder<'a, ()>,
|
||||
) -> &'b mut DiagnosticBuilder<'a, ()> {
|
||||
diag.span_label(self.span(), format!("{:?}", self.panic()));
|
||||
diag
|
||||
}
|
||||
|
||||
fn msg(&self) -> DiagnosticMessage {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(..) => {
|
||||
crate::fluent_generated::mir_transform_arithmetic_overflow
|
||||
}
|
||||
AssertLint::UnconditionalPanic(..) => {
|
||||
crate::fluent_generated::mir_transform_operation_will_panic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> AssertLint<P> {
|
||||
pub fn lint(&self) -> &'static Lint {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(..) => lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
AssertLint::UnconditionalPanic(..) => lint::builtin::UNCONDITIONAL_PANIC,
|
||||
}
|
||||
}
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(sp, _) | AssertLint::UnconditionalPanic(sp, _) => *sp,
|
||||
}
|
||||
}
|
||||
pub fn panic(&self) -> &AssertKind<P> {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(_, p) | AssertLint::UnconditionalPanic(_, p) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_ffi_unwind_call)]
|
||||
pub(crate) struct FfiUnwindCall {
|
||||
#[label(mir_transform_ffi_unwind_call)]
|
||||
pub span: Span,
|
||||
pub foreign: bool,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_fn_item_ref)]
|
||||
pub(crate) struct FnItemRef {
|
||||
#[suggestion(code = "{sugg}", applicability = "unspecified")]
|
||||
pub span: Span,
|
||||
pub sugg: String,
|
||||
pub ident: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_must_not_suspend)]
|
||||
pub(crate) struct MustNotSupend<'a> {
|
||||
#[label]
|
||||
pub yield_sp: Span,
|
||||
#[subdiagnostic]
|
||||
pub reason: Option<MustNotSuspendReason>,
|
||||
#[help]
|
||||
pub src_sp: Span,
|
||||
pub pre: &'a str,
|
||||
pub def_path: String,
|
||||
pub post: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(mir_transform_note)]
|
||||
pub(crate) struct MustNotSuspendReason {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_transform_simd_shuffle_last_const)]
|
||||
pub(crate) struct SimdShuffleLastConst {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
@ -8,6 +8,8 @@ use rustc_session::lint::builtin::FFI_UNWIND_CALLS;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use crate::errors;
|
||||
|
||||
fn abi_can_unwind(abi: Abi) -> bool {
|
||||
use Abi::*;
|
||||
match abi {
|
||||
@ -107,13 +109,13 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool {
|
||||
.lint_root;
|
||||
let span = terminator.source_info.span;
|
||||
|
||||
let msg = match fn_def_id {
|
||||
Some(_) => "call to foreign function with FFI-unwind ABI",
|
||||
None => "call to function pointer with FFI-unwind ABI",
|
||||
};
|
||||
tcx.struct_span_lint_hir(FFI_UNWIND_CALLS, lint_root, span, msg, |lint| {
|
||||
lint.span_label(span, msg)
|
||||
});
|
||||
let foreign = fn_def_id.is_some();
|
||||
tcx.emit_spanned_lint(
|
||||
FFI_UNWIND_CALLS,
|
||||
lint_root,
|
||||
span,
|
||||
errors::FfiUnwindCall { span, foreign },
|
||||
);
|
||||
|
||||
tainted = true;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
@ -8,7 +7,7 @@ use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
|
||||
use rustc_span::{symbol::sym, Span};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::MirLint;
|
||||
use crate::{errors, MirLint};
|
||||
|
||||
pub struct FunctionItemReferences;
|
||||
|
||||
@ -174,27 +173,21 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
|
||||
let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
|
||||
let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
|
||||
let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
|
||||
self.tcx.struct_span_lint_hir(
|
||||
let sugg = format!(
|
||||
"{} as {}{}fn({}{}){}",
|
||||
if params.is_empty() { ident.clone() } else { format!("{}::<{}>", ident, params) },
|
||||
unsafety,
|
||||
abi,
|
||||
vec!["_"; num_args].join(", "),
|
||||
variadic,
|
||||
ret,
|
||||
);
|
||||
|
||||
self.tcx.emit_spanned_lint(
|
||||
FUNCTION_ITEM_REFERENCES,
|
||||
lint_root,
|
||||
span,
|
||||
"taking a reference to a function item does not give a function pointer",
|
||||
|lint| {
|
||||
lint.span_suggestion(
|
||||
span,
|
||||
format!("cast `{}` to obtain a function pointer", ident),
|
||||
format!(
|
||||
"{} as {}{}fn({}{}){}",
|
||||
if params.is_empty() { ident } else { format!("{}::<{}>", ident, params) },
|
||||
unsafety,
|
||||
abi,
|
||||
vec!["_"; num_args].join(", "),
|
||||
variadic,
|
||||
ret,
|
||||
),
|
||||
Applicability::Unspecified,
|
||||
)
|
||||
},
|
||||
errors::FnItemRef { span, sugg, ident },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@
|
||||
//! Otherwise it drops all the values in scope at the last suspension point.
|
||||
|
||||
use crate::deref_separator::deref_finder;
|
||||
use crate::errors;
|
||||
use crate::simplify;
|
||||
use crate::MirPass;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -1891,36 +1892,21 @@ fn check_must_not_suspend_def(
|
||||
data: SuspendCheckData<'_>,
|
||||
) -> bool {
|
||||
if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
|
||||
let msg = rustc_errors::DelayDm(|| {
|
||||
format!(
|
||||
"{}`{}`{} held across a suspend point, but should not be",
|
||||
data.descr_pre,
|
||||
tcx.def_path_str(def_id),
|
||||
data.descr_post,
|
||||
)
|
||||
let reason = attr.value_str().map(|s| errors::MustNotSuspendReason {
|
||||
span: data.source_span,
|
||||
reason: s.as_str().to_string(),
|
||||
});
|
||||
tcx.struct_span_lint_hir(
|
||||
tcx.emit_spanned_lint(
|
||||
rustc_session::lint::builtin::MUST_NOT_SUSPEND,
|
||||
hir_id,
|
||||
data.source_span,
|
||||
msg,
|
||||
|lint| {
|
||||
// add span pointing to the offending yield/await
|
||||
lint.span_label(data.yield_span, "the value is held across this suspend point");
|
||||
|
||||
// Add optional reason note
|
||||
if let Some(note) = attr.value_str() {
|
||||
// FIXME(guswynn): consider formatting this better
|
||||
lint.span_note(data.source_span, note.as_str());
|
||||
}
|
||||
|
||||
// Add some quick suggestions on what to do
|
||||
// FIXME: can `drop` work as a suggestion here as well?
|
||||
lint.span_help(
|
||||
data.source_span,
|
||||
"consider using a block (`{ ... }`) \
|
||||
to shrink the value's scope, ending before the suspend point",
|
||||
)
|
||||
errors::MustNotSupend {
|
||||
yield_sp: data.yield_span,
|
||||
reason,
|
||||
src_sp: data.source_span,
|
||||
pre: data.descr_pre,
|
||||
def_path: tcx.def_path_str(def_id),
|
||||
post: data.descr_post,
|
||||
},
|
||||
);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user