mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-01 09:33:26 +00:00
Auto merge of #3059 - rust-lang:rustup-2023-09-12, r=RalfJung
Automatic sync from rustc
This commit is contained in:
commit
ea488f7864
1
.gitignore
vendored
1
.gitignore
vendored
@ -58,6 +58,7 @@ build/
|
||||
\#*
|
||||
\#*\#
|
||||
.#*
|
||||
rustc-ice-*.txt
|
||||
|
||||
## Tags
|
||||
tags
|
||||
|
4
.mailmap
4
.mailmap
@ -205,6 +205,10 @@ Gareth Daniel Smith <garethdanielsmith@gmail.com> gareth <gareth@gareth-N56VM.(n
|
||||
Gareth Daniel Smith <garethdanielsmith@gmail.com> Gareth Smith <garethdanielsmith@gmail.com>
|
||||
Gauri Kholkar <f2013002@goa.bits-pilani.ac.in>
|
||||
Georges Dubus <georges.dubus@gmail.com> <georges.dubus@compiletoi.net>
|
||||
Ghost <ghost> <jonasschievink@gmail.com>
|
||||
Ghost <ghost> <jonas.schievink@ferrous-systems.com>
|
||||
Ghost <ghost> <jonas@schievink.net>
|
||||
Ghost <ghost> <Jonas.Schievink@sony.com>
|
||||
Giles Cope <gilescope@gmail.com>
|
||||
Glen De Cauwsemaecker <decauwsemaecker.glen@gmail.com>
|
||||
Graham Fawcett <graham.fawcett@gmail.com> Graham Fawcett <fawcett@uwindsor.ca>
|
||||
|
@ -558,7 +558,6 @@ dependencies = [
|
||||
"declare_clippy_lint",
|
||||
"if_chain",
|
||||
"itertools",
|
||||
"pulldown-cmark",
|
||||
"quine-mc_cluskey",
|
||||
"regex",
|
||||
"regex-syntax 0.7.2",
|
||||
@ -4175,7 +4174,7 @@ dependencies = [
|
||||
name = "rustc_parse_format"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_data_structures",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
]
|
||||
|
||||
|
@ -1300,12 +1300,18 @@ impl Abi {
|
||||
matches!(*self, Abi::Uninhabited)
|
||||
}
|
||||
|
||||
/// Returns `true` is this is a scalar type
|
||||
/// Returns `true` if this is a scalar type
|
||||
#[inline]
|
||||
pub fn is_scalar(&self) -> bool {
|
||||
matches!(*self, Abi::Scalar(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a bool
|
||||
#[inline]
|
||||
pub fn is_bool(&self) -> bool {
|
||||
matches!(*self, Abi::Scalar(s) if s.is_bool())
|
||||
}
|
||||
|
||||
/// Returns the fixed alignment of this ABI, if any is mandated.
|
||||
pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> {
|
||||
Some(match *self {
|
||||
@ -1348,6 +1354,23 @@ impl Abi {
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => Abi::Aggregate { sized: true },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_up_to_validity(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
// Scalar, Vector, ScalarPair have `Scalar` in them where we ignore validity ranges.
|
||||
// We do *not* ignore the sign since it matters for some ABIs (e.g. s390x).
|
||||
(Abi::Scalar(l), Abi::Scalar(r)) => l.primitive() == r.primitive(),
|
||||
(
|
||||
Abi::Vector { element: element_l, count: count_l },
|
||||
Abi::Vector { element: element_r, count: count_r },
|
||||
) => element_l.primitive() == element_r.primitive() && count_l == count_r,
|
||||
(Abi::ScalarPair(l1, l2), Abi::ScalarPair(r1, r2)) => {
|
||||
l1.primitive() == r1.primitive() && l2.primitive() == r2.primitive()
|
||||
}
|
||||
// Everything else must be strictly identical.
|
||||
_ => self == other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
@ -1686,6 +1709,22 @@ impl LayoutS {
|
||||
Abi::Aggregate { sized } => sized && self.size.bytes() == 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if these two `Layout` are equal enough to be considered "the same for all function
|
||||
/// call ABIs". Note however that real ABIs depend on more details that are not reflected in the
|
||||
/// `Layout`; the `PassMode` need to be compared as well.
|
||||
pub fn eq_abi(&self, other: &Self) -> bool {
|
||||
// The one thing that we are not capturing here is that for unsized types, the metadata must
|
||||
// also have the same ABI, and moreover that the same metadata leads to the same size. The
|
||||
// 2nd point is quite hard to check though.
|
||||
self.size == other.size
|
||||
&& self.is_sized() == other.is_sized()
|
||||
&& self.abi.eq_up_to_validity(&other.abi)
|
||||
&& self.abi.is_bool() == other.abi.is_bool()
|
||||
&& self.align.abi == other.align.abi
|
||||
&& self.max_repr_align == other.max_repr_align
|
||||
&& self.unadjusted_abi_align == other.unadjusted_abi_align
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -2,19 +2,17 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::definitions;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::*;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
/// A visitor that walks over the HIR and collects `Node`s into a HIR map.
|
||||
pub(super) struct NodeCollector<'a, 'hir> {
|
||||
/// Source map
|
||||
source_map: &'a SourceMap,
|
||||
tcx: TyCtxt<'hir>,
|
||||
|
||||
bodies: &'a SortedMap<ItemLocalId, &'hir Body<'hir>>,
|
||||
|
||||
/// Outputs
|
||||
@ -25,14 +23,11 @@ pub(super) struct NodeCollector<'a, 'hir> {
|
||||
parent_node: hir::ItemLocalId,
|
||||
|
||||
owner: OwnerId,
|
||||
|
||||
definitions: &'a definitions::Definitions,
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(sess, definitions, bodies))]
|
||||
#[instrument(level = "debug", skip(tcx, bodies))]
|
||||
pub(super) fn index_hir<'hir>(
|
||||
sess: &Session,
|
||||
definitions: &definitions::Definitions,
|
||||
tcx: TyCtxt<'hir>,
|
||||
item: hir::OwnerNode<'hir>,
|
||||
bodies: &SortedMap<ItemLocalId, &'hir Body<'hir>>,
|
||||
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) {
|
||||
@ -42,8 +37,7 @@ pub(super) fn index_hir<'hir>(
|
||||
// used.
|
||||
nodes.push(Some(ParentedNode { parent: ItemLocalId::INVALID, node: item.into() }));
|
||||
let mut collector = NodeCollector {
|
||||
source_map: sess.source_map(),
|
||||
definitions,
|
||||
tcx,
|
||||
owner: item.def_id(),
|
||||
parent_node: ItemLocalId::new(0),
|
||||
nodes,
|
||||
@ -79,11 +73,17 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
|
||||
span,
|
||||
"inconsistent HirId at `{:?}` for `{:?}`: \
|
||||
current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})",
|
||||
self.source_map.span_to_diagnostic_string(span),
|
||||
self.tcx.sess.source_map().span_to_diagnostic_string(span),
|
||||
node,
|
||||
self.definitions.def_path(self.owner.def_id).to_string_no_crate_verbose(),
|
||||
self.tcx
|
||||
.definitions_untracked()
|
||||
.def_path(self.owner.def_id)
|
||||
.to_string_no_crate_verbose(),
|
||||
self.owner,
|
||||
self.definitions.def_path(hir_id.owner.def_id).to_string_no_crate_verbose(),
|
||||
self.tcx
|
||||
.definitions_untracked()
|
||||
.def_path(hir_id.owner.def_id)
|
||||
.to_string_no_crate_verbose(),
|
||||
hir_id.owner,
|
||||
)
|
||||
}
|
||||
|
@ -671,8 +671,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let (nodes, parenting) =
|
||||
index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
|
||||
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies);
|
||||
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
|
||||
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
|
||||
|
||||
@ -771,7 +770,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
/// Intercept all spans entering HIR.
|
||||
/// Mark a span as relative to the current owning item.
|
||||
fn lower_span(&self, span: Span) -> Span {
|
||||
if self.tcx.sess.opts.incremental_relative_spans() {
|
||||
if self.tcx.sess.opts.incremental.is_some() {
|
||||
span.with_parent(Some(self.current_hir_id_owner.def_id))
|
||||
} else {
|
||||
// Do not make spans relative when not using incremental compilation.
|
||||
|
@ -166,6 +166,8 @@ borrowck_returned_lifetime_wrong =
|
||||
borrowck_returned_ref_escaped =
|
||||
returns a reference to a captured variable which escapes the closure body
|
||||
|
||||
borrowck_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item
|
||||
|
||||
borrowck_suggest_create_freash_reborrow =
|
||||
consider reborrowing the `Pin` instead of moving it
|
||||
|
||||
|
@ -2130,21 +2130,27 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
/// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`.
|
||||
/// We could expand the analysis to suggest hoising all of the relevant parts of
|
||||
/// the users' code to make the code compile, but that could be too much.
|
||||
struct NestedStatementVisitor {
|
||||
/// We found the `prop_expr` by the way to check whether the expression is a `FormatArguments`,
|
||||
/// which is a special case since it's generated by the compiler.
|
||||
struct NestedStatementVisitor<'tcx> {
|
||||
span: Span,
|
||||
current: usize,
|
||||
found: usize,
|
||||
prop_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
|
||||
fn visit_block(&mut self, block: &hir::Block<'tcx>) {
|
||||
impl<'tcx> Visitor<'tcx> for NestedStatementVisitor<'tcx> {
|
||||
fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
|
||||
self.current += 1;
|
||||
walk_block(self, block);
|
||||
self.current -= 1;
|
||||
}
|
||||
fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if self.span == expr.span.source_callsite() {
|
||||
self.found = self.current;
|
||||
if self.prop_expr.is_none() {
|
||||
self.prop_expr = Some(expr);
|
||||
}
|
||||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
@ -2162,22 +2168,40 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
span: proper_span,
|
||||
current: 0,
|
||||
found: 0,
|
||||
prop_expr: None,
|
||||
};
|
||||
visitor.visit_stmt(stmt);
|
||||
|
||||
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
let expr_ty: Option<Ty<'_>> = visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
|
||||
|
||||
let is_format_arguments_item =
|
||||
if let Some(expr_ty) = expr_ty
|
||||
&& let ty::Adt(adt, _) = expr_ty.kind() {
|
||||
self.infcx.tcx.lang_items().get(LangItem::FormatArguments) == Some(adt.did())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if visitor.found == 0
|
||||
&& stmt.span.contains(proper_span)
|
||||
&& let Some(p) = sm.span_to_margin(stmt.span)
|
||||
&& let Ok(s) = sm.span_to_snippet(proper_span)
|
||||
{
|
||||
let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
|
||||
err.multipart_suggestion_verbose(
|
||||
msg,
|
||||
vec![
|
||||
(stmt.span.shrink_to_lo(), addition),
|
||||
(proper_span, "binding".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if !is_format_arguments_item {
|
||||
let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
|
||||
err.multipart_suggestion_verbose(
|
||||
msg,
|
||||
vec![
|
||||
(stmt.span.shrink_to_lo(), addition),
|
||||
(proper_span, "binding".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.note("the result of `format_args!` can only be assigned directly if no placeholders in it's arguments are used");
|
||||
err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
|
||||
}
|
||||
suggested = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use hir::ExprKind;
|
||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
|
||||
use rustc_middle::{
|
||||
hir::place::PlaceBase,
|
||||
mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
|
||||
@ -370,12 +371,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
err.span_label(span, format!("cannot {act}"));
|
||||
}
|
||||
if suggest {
|
||||
err.span_suggestion_verbose(
|
||||
local_decl.source_info.span.shrink_to_lo(),
|
||||
"consider changing this to be mutable",
|
||||
"mut ",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
|
||||
let tcx = self.infcx.tcx;
|
||||
if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
|
||||
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
|
||||
@ -491,6 +487,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
),
|
||||
);
|
||||
|
||||
self.suggest_using_iter_mut(&mut err);
|
||||
self.suggest_make_local_mut(&mut err, local, name);
|
||||
}
|
||||
_ => {
|
||||
@ -710,6 +707,83 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
)
|
||||
}
|
||||
|
||||
fn construct_mut_suggestion_for_local_binding_patterns(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
|
||||
local: Local,
|
||||
) {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
debug!("local_decl: {:?}", local_decl);
|
||||
let pat_span = match *local_decl.local_info() {
|
||||
LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
|
||||
binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
|
||||
opt_ty_info: _,
|
||||
opt_match_place: _,
|
||||
pat_span,
|
||||
})) => pat_span,
|
||||
_ => local_decl.source_info.span,
|
||||
};
|
||||
|
||||
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 = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let mut v = BindingFinder {
|
||||
span: pat_span,
|
||||
hir_id: None,
|
||||
};
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// With ref-binding patterns, the mutability suggestion has to apply to
|
||||
// the binding, not the reference (which would be a type error):
|
||||
//
|
||||
// `let &b = a;` -> `let &(mut b) = a;`
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let Some(hir::Node::Local(hir::Local {
|
||||
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
|
||||
..
|
||||
})) = hir_map.find(hir_id)
|
||||
&& let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
|
||||
{
|
||||
err.span_suggestion(
|
||||
pat_span,
|
||||
"consider changing this to be mutable",
|
||||
format!("&(mut {name})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
err.span_suggestion_verbose(
|
||||
local_decl.source_info.span.shrink_to_lo(),
|
||||
"consider changing this to be mutable",
|
||||
"mut ",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
// point to span of upvar making closure call require mutable borrow
|
||||
fn show_mutating_upvar(
|
||||
&self,
|
||||
@ -953,6 +1027,44 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_using_iter_mut(&self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>) {
|
||||
let source = self.body.source;
|
||||
let hir = self.infcx.tcx.hir();
|
||||
if let InstanceDef::Item(def_id) = source.instance
|
||||
&& let Some(Node::Expr(hir::Expr { hir_id, kind, ..})) = hir.get_if_local(def_id)
|
||||
&& let ExprKind::Closure(closure) = kind && closure.movability == None
|
||||
&& let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) {
|
||||
let mut cur_expr = expr;
|
||||
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
|
||||
if path_segment.ident.name == sym::iter {
|
||||
// check `_ty` has `iter_mut` method
|
||||
let res = self
|
||||
.infcx
|
||||
.tcx
|
||||
.typeck(path_segment.hir_id.owner.def_id)
|
||||
.type_dependent_def_id(cur_expr.hir_id)
|
||||
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
|
||||
.map(|def_id| self.infcx.tcx.associated_items(def_id))
|
||||
.map(|assoc_items| {
|
||||
assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
|
||||
});
|
||||
|
||||
if let Some(mut res) = res && res.peek().is_some() {
|
||||
err.span_suggestion_verbose(
|
||||
path_segment.ident.span,
|
||||
"you may want to use `iter_mut` here",
|
||||
"iter_mut",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
cur_expr = recv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_make_local_mut(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
|
||||
|
@ -10,6 +10,7 @@ use rustc_middle::mir::{
|
||||
Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted,
|
||||
START_BLOCK,
|
||||
};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt};
|
||||
use rustc_span::symbol::sym;
|
||||
use std::env;
|
||||
@ -441,7 +442,10 @@ fn for_each_region_constraint<'tcx>(
|
||||
let subject = match req.subject {
|
||||
ClosureOutlivesSubject::Region(subject) => format!("{subject:?}"),
|
||||
ClosureOutlivesSubject::Ty(ty) => {
|
||||
format!("{:?}", ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid)))
|
||||
with_no_trimmed_paths!(format!(
|
||||
"{}",
|
||||
ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid))
|
||||
))
|
||||
}
|
||||
};
|
||||
with_msg(format!("where {}: {:?}", subject, req.outlived_free_region,))?;
|
||||
|
@ -328,19 +328,26 @@ fn check_opaque_type_well_formed<'tcx>(
|
||||
|
||||
// Require that the hidden type actually fulfills all the bounds of the opaque type, even without
|
||||
// the bounds that the function supplies.
|
||||
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), identity_args);
|
||||
ocx.eq(&ObligationCause::misc(definition_span, def_id), param_env, opaque_ty, definition_ty)
|
||||
.map_err(|err| {
|
||||
infcx
|
||||
.err_ctxt()
|
||||
.report_mismatched_types(
|
||||
&ObligationCause::misc(definition_span, def_id),
|
||||
opaque_ty,
|
||||
definition_ty,
|
||||
err,
|
||||
)
|
||||
.emit()
|
||||
})?;
|
||||
let mut obligations = vec![];
|
||||
infcx
|
||||
.insert_hidden_type(
|
||||
OpaqueTypeKey { def_id, args: identity_args },
|
||||
&ObligationCause::misc(definition_span, def_id),
|
||||
param_env,
|
||||
definition_ty,
|
||||
true,
|
||||
&mut obligations,
|
||||
)
|
||||
.unwrap();
|
||||
infcx.add_item_bounds_for_hidden_type(
|
||||
def_id.to_def_id(),
|
||||
identity_args,
|
||||
ObligationCause::misc(definition_span, def_id),
|
||||
param_env,
|
||||
definition_ty,
|
||||
&mut obligations,
|
||||
);
|
||||
ocx.register_obligations(obligations);
|
||||
|
||||
// Require the hidden type to be well-formed with only the generics of the opaque type.
|
||||
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
|
||||
|
@ -452,3 +452,10 @@ pub(crate) enum TypeNoCopy<'a, 'tcx> {
|
||||
#[note(borrowck_ty_no_impl_copy)]
|
||||
Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str },
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_simd_shuffle_last_const)]
|
||||
pub(crate) struct SimdShuffleLastConst {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
|
||||
use crate::session_diagnostics::MoveUnsized;
|
||||
use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst};
|
||||
use crate::{
|
||||
borrow_set::BorrowSet,
|
||||
constraints::{OutlivesConstraint, OutlivesConstraintSet},
|
||||
@ -1426,7 +1426,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
.add_element(region_vid, term_location);
|
||||
}
|
||||
|
||||
self.check_call_inputs(body, term, &sig, args, term_location, *call_source);
|
||||
self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source);
|
||||
}
|
||||
TerminatorKind::Assert { cond, msg, .. } => {
|
||||
self.check_operand(cond, term_location);
|
||||
@ -1546,25 +1546,36 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, body, term, func, term_location, call_source))]
|
||||
fn check_call_inputs(
|
||||
&mut self,
|
||||
body: &Body<'tcx>,
|
||||
term: &Terminator<'tcx>,
|
||||
func: &Operand<'tcx>,
|
||||
sig: &ty::FnSig<'tcx>,
|
||||
args: &[Operand<'tcx>],
|
||||
term_location: Location,
|
||||
call_source: CallSource,
|
||||
) {
|
||||
debug!("check_call_inputs({:?}, {:?})", sig, args);
|
||||
if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) {
|
||||
span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
|
||||
}
|
||||
|
||||
let func_ty = if let TerminatorKind::Call { func, .. } = &term.kind {
|
||||
Some(func.ty(body, self.infcx.tcx))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let func_ty = func.ty(body, self.infcx.tcx);
|
||||
if let ty::FnDef(def_id, _) = *func_ty.kind() {
|
||||
if self.tcx().is_intrinsic(def_id) {
|
||||
match self.tcx().item_name(def_id) {
|
||||
sym::simd_shuffle => {
|
||||
if !matches!(args[2], Operand::Constant(_)) {
|
||||
self.tcx()
|
||||
.sess
|
||||
.emit_err(SimdShuffleLastConst { span: term.source_info.span });
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!(?func_ty);
|
||||
|
||||
for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() {
|
||||
@ -1572,7 +1583,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
|
||||
let op_arg_ty = self.normalize(op_arg_ty, term_location);
|
||||
let category = if call_source.from_hir_call() {
|
||||
ConstraintCategory::CallArgument(self.infcx.tcx.erase_regions(func_ty))
|
||||
ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty)))
|
||||
} else {
|
||||
ConstraintCategory::Boring
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ use rustc_hir::BodyOwnerKind;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::NllRegionVariableOrigin;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{GenericArgs, GenericArgsRef};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
@ -332,10 +333,16 @@ impl<'tcx> UniversalRegions<'tcx> {
|
||||
pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
|
||||
match self.defining_ty {
|
||||
DefiningTy::Closure(def_id, args) => {
|
||||
let v = with_no_trimmed_paths!(
|
||||
args[tcx.generics_of(def_id).parent_count..]
|
||||
.iter()
|
||||
.map(|arg| arg.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
err.note(format!(
|
||||
"defining type: {} with closure args {:#?}",
|
||||
"defining type: {} with closure args [\n {},\n]",
|
||||
tcx.def_path_str_with_args(def_id, args),
|
||||
&args[tcx.generics_of(def_id).parent_count..],
|
||||
v.join(",\n "),
|
||||
));
|
||||
|
||||
// FIXME: It'd be nice to print the late-bound regions
|
||||
@ -348,10 +355,16 @@ impl<'tcx> UniversalRegions<'tcx> {
|
||||
});
|
||||
}
|
||||
DefiningTy::Generator(def_id, args, _) => {
|
||||
let v = with_no_trimmed_paths!(
|
||||
args[tcx.generics_of(def_id).parent_count..]
|
||||
.iter()
|
||||
.map(|arg| arg.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
err.note(format!(
|
||||
"defining type: {} with generator args {:#?}",
|
||||
"defining type: {} with generator args [\n {},\n]",
|
||||
tcx.def_path_str_with_args(def_id, args),
|
||||
&args[tcx.generics_of(def_id).parent_count..],
|
||||
v.join(",\n "),
|
||||
));
|
||||
|
||||
// FIXME: As above, we'd like to print out the region
|
||||
|
@ -54,7 +54,7 @@ These are a few functions that allow you to easily run rust code from the shell
|
||||
|
||||
```bash
|
||||
function jit_naked() {
|
||||
echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic
|
||||
echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic
|
||||
}
|
||||
|
||||
function jit() {
|
||||
|
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.20.0"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"gimli",
|
||||
@ -140,9 +140,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.27.2"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
@ -205,9 +205,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.31.1"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
|
||||
checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"memchr",
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2023-08-08"
|
||||
channel = "nightly-2023-09-06"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools"]
|
||||
|
@ -45,6 +45,7 @@ rm tests/ui/proc-macro/quote-debug.rs
|
||||
rm tests/ui/proc-macro/no-missing-docs.rs
|
||||
rm tests/ui/rust-2018/proc-macro-crate-in-paths.rs
|
||||
rm tests/ui/proc-macro/allowed-signatures.rs
|
||||
rm tests/ui/proc-macro/no-mangle-in-proc-macro-issue-111888.rs
|
||||
|
||||
# vendor intrinsics
|
||||
rm tests/ui/sse2.rs # cpuid not supported, so sse2 not detected
|
||||
@ -114,6 +115,7 @@ rm tests/ui/mir/mir_misc_casts.rs # depends on deduplication of constants
|
||||
rm tests/ui/mir/mir_raw_fat_ptr.rs # same
|
||||
rm tests/ui/consts/issue-33537.rs # same
|
||||
rm tests/ui/layout/valid_range_oob.rs # different ICE message
|
||||
rm tests/ui/const-generics/generic_const_exprs/issue-80742.rs # gives error instead of ICE with cg_clif
|
||||
|
||||
rm tests/ui/consts/issue-miri-1910.rs # different error message
|
||||
rm tests/ui/consts/offset_ub.rs # same
|
||||
|
@ -81,7 +81,7 @@ impl DebugContext {
|
||||
|
||||
match tcx.sess.source_map().lookup_line(span.lo()) {
|
||||
Ok(SourceFileAndLine { sf: file, line }) => {
|
||||
let line_pos = file.lines(|lines| lines[line]);
|
||||
let line_pos = file.lines()[line];
|
||||
let col = file.relative_position(span.lo()) - line_pos;
|
||||
|
||||
(file, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1)
|
||||
|
@ -177,244 +177,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
||||
bool_to_zero_or_max_uint(fx, res_lane_ty, res_lane)
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrli.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.psrli.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrai.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.psrai.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.pslli.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.pslli.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrli.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.psrli.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrai.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.psrai.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.pslli.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.pslli.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx.psrli.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrli.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx.psrai.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrai.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrli.q" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrli.q imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 64 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.pslli.q" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.pslli.q imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 64 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx.pslli.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.pslli.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx2.psrli.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrli.w imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx2.psrai.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrai.w imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx2.pslli.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.pslli.w imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.ssse3.pshuf.b.128" | "llvm.x86.avx2.pshuf.b" => {
|
||||
let (a, b) = match args {
|
||||
[a, b] => (a, b),
|
||||
@ -506,14 +268,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
||||
ret.place_lane(fx, 2).to_ptr().store(fx, res_2, MemFlags::trusted());
|
||||
ret.place_lane(fx, 3).to_ptr().store(fx, res_3, MemFlags::trusted());
|
||||
}
|
||||
"llvm.x86.sse2.storeu.dq" | "llvm.x86.sse2.storeu.pd" => {
|
||||
intrinsic_args!(fx, args => (mem_addr, a); intrinsic);
|
||||
let mem_addr = mem_addr.load_scalar(fx);
|
||||
|
||||
// FIXME correctly handle the unalignment
|
||||
let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout());
|
||||
dest.write_cvalue(fx, a);
|
||||
}
|
||||
"llvm.x86.ssse3.pabs.b.128" | "llvm.x86.ssse3.pabs.w.128" | "llvm.x86.ssse3.pabs.d.128" => {
|
||||
let a = match args {
|
||||
[a] => a,
|
||||
@ -571,8 +325,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
||||
// llvm.x86.avx2.vperm2i128
|
||||
// llvm.x86.ssse3.pshuf.b.128
|
||||
// llvm.x86.avx2.pshuf.b
|
||||
// llvm.x86.avx2.psrli.w
|
||||
// llvm.x86.sse2.psrli.w
|
||||
|
||||
fn llvm_add_sub<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
|
@ -55,7 +55,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
_llfn: RValue<'gcc>,
|
||||
_mir: &mir::Body<'tcx>,
|
||||
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
|
||||
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> {
|
||||
// TODO(antoyo)
|
||||
None
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ codegen_llvm_unknown_ctarget_feature_prefix =
|
||||
unknown feature specified for `-Ctarget-feature`: `{$feature}`
|
||||
.note = features must begin with a `+` to enable or `-` to disable it
|
||||
|
||||
codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
|
||||
|
||||
codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
|
||||
|
||||
codegen_llvm_write_ir = failed to write LLVM IR to {$path}
|
||||
|
@ -340,15 +340,50 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
};
|
||||
|
||||
for arg in args {
|
||||
// Note that the exact number of arguments pushed here is carefully synchronized with
|
||||
// code all over the place, both in the codegen_llvm and codegen_ssa crates. That's how
|
||||
// other code then knows which LLVM argument(s) correspond to the n-th Rust argument.
|
||||
let llarg_ty = match &arg.mode {
|
||||
PassMode::Ignore => continue,
|
||||
PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
|
||||
PassMode::Direct(_) => {
|
||||
// ABI-compatible Rust types have the same `layout.abi` (up to validity ranges),
|
||||
// and for Scalar ABIs the LLVM type is fully determined by `layout.abi`,
|
||||
// guarnateeing that we generate ABI-compatible LLVM IR. Things get tricky for
|
||||
// aggregates...
|
||||
if matches!(arg.layout.abi, abi::Abi::Aggregate { .. }) {
|
||||
// This really shouldn't happen, since `immediate_llvm_type` will use
|
||||
// `layout.fields` to turn this Rust type into an LLVM type. This means all
|
||||
// sorts of Rust type details leak into the ABI. However wasm sadly *does*
|
||||
// currently use this mode so we have to allow it -- but we absolutely
|
||||
// shouldn't let any more targets do that.
|
||||
// (Also see <https://github.com/rust-lang/rust/issues/115666>.)
|
||||
assert!(
|
||||
matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64"),
|
||||
"`PassMode::Direct` for aggregates only allowed on wasm targets\nProblematic type: {:#?}",
|
||||
arg.layout,
|
||||
);
|
||||
}
|
||||
arg.layout.immediate_llvm_type(cx)
|
||||
}
|
||||
PassMode::Pair(..) => {
|
||||
// ABI-compatible Rust types have the same `layout.abi` (up to validity ranges),
|
||||
// so for ScalarPair we can easily be sure that we are generating ABI-compatible
|
||||
// LLVM IR.
|
||||
assert!(
|
||||
matches!(arg.layout.abi, abi::Abi::ScalarPair(..)),
|
||||
"PassMode::Pair for type {}",
|
||||
arg.layout.ty
|
||||
);
|
||||
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true));
|
||||
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
|
||||
continue;
|
||||
}
|
||||
PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
|
||||
assert!(arg.layout.is_unsized());
|
||||
// Construct the type of a (wide) pointer to `ty`, and pass its two fields.
|
||||
// Any two ABI-compatible unsized types have the same metadata type and
|
||||
// moreover the same metadata value leads to the same dynamic size and
|
||||
// alignment, so this respects ABI compatibility.
|
||||
let ptr_ty = Ty::new_mut_ptr(cx.tcx, arg.layout.ty);
|
||||
let ptr_layout = cx.layout_of(ptr_ty);
|
||||
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
|
||||
@ -360,6 +395,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
if *pad_i32 {
|
||||
llargument_tys.push(Reg::i32().llvm_type(cx));
|
||||
}
|
||||
// Compute the LLVM type we use for this function from the cast type.
|
||||
// We assume here that ABI-compatible Rust types have the same cast type.
|
||||
cast.llvm_type(cx)
|
||||
}
|
||||
PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => cx.type_ptr(),
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::back::write::{self, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers};
|
||||
use crate::back::write::{
|
||||
self, bitcode_section_name, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers,
|
||||
};
|
||||
use crate::errors::{
|
||||
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
|
||||
};
|
||||
@ -120,6 +122,7 @@ fn prepare_lto(
|
||||
info!("adding bitcode from {}", name);
|
||||
match get_bitcode_slice_from_object_data(
|
||||
child.data(&*archive_data).expect("corrupt rlib"),
|
||||
cgcx,
|
||||
) {
|
||||
Ok(data) => {
|
||||
let module = SerializedModule::FromRlib(data.to_vec());
|
||||
@ -141,10 +144,29 @@ fn prepare_lto(
|
||||
Ok((symbols_below_threshold, upstream_modules))
|
||||
}
|
||||
|
||||
fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], LtoBitcodeFromRlib> {
|
||||
fn get_bitcode_slice_from_object_data<'a>(
|
||||
obj: &'a [u8],
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
) -> Result<&'a [u8], LtoBitcodeFromRlib> {
|
||||
// We're about to assume the data here is an object file with sections, but if it's raw LLVM IR that
|
||||
// won't work. Fortunately, if that's what we have we can just return the object directly, so we sniff
|
||||
// the relevant magic strings here and return.
|
||||
if obj.starts_with(b"\xDE\xC0\x17\x0B") || obj.starts_with(b"BC\xC0\xDE") {
|
||||
return Ok(obj);
|
||||
}
|
||||
// We drop the "__LLVM," prefix here because on Apple platforms there's a notion of "segment name"
|
||||
// which in the public API for sections gets treated as part of the section name, but internally
|
||||
// in MachOObjectFile.cpp gets treated separately.
|
||||
let section_name = bitcode_section_name(cgcx).trim_start_matches("__LLVM,");
|
||||
let mut len = 0;
|
||||
let data =
|
||||
unsafe { llvm::LLVMRustGetBitcodeSliceFromObjectData(obj.as_ptr(), obj.len(), &mut len) };
|
||||
let data = unsafe {
|
||||
llvm::LLVMRustGetSliceFromObjectDataByName(
|
||||
obj.as_ptr(),
|
||||
obj.len(),
|
||||
section_name.as_ptr(),
|
||||
&mut len,
|
||||
)
|
||||
};
|
||||
if !data.is_null() {
|
||||
assert!(len != 0);
|
||||
let bc = unsafe { slice::from_raw_parts(data, len) };
|
||||
|
@ -5,13 +5,17 @@ use crate::back::profiling::{
|
||||
use crate::base;
|
||||
use crate::common;
|
||||
use crate::errors::{
|
||||
CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, WithLlvmError, WriteBytecode,
|
||||
CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression,
|
||||
WithLlvmError, WriteBytecode,
|
||||
};
|
||||
use crate::llvm::{self, DiagnosticInfo, PassManager};
|
||||
use crate::llvm_util;
|
||||
use crate::type_::Type;
|
||||
use crate::LlvmCodegenBackend;
|
||||
use crate::ModuleLlvm;
|
||||
use llvm::{
|
||||
LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
|
||||
};
|
||||
use rustc_codegen_ssa::back::link::ensure_removed;
|
||||
use rustc_codegen_ssa::back::write::{
|
||||
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
|
||||
@ -216,6 +220,40 @@ pub fn target_machine_factory(
|
||||
|
||||
let force_emulated_tls = sess.target.force_emulated_tls;
|
||||
|
||||
// copy the exe path, followed by path all into one buffer
|
||||
// null terminating them so we can use them as null terminated strings
|
||||
let args_cstr_buff = {
|
||||
let mut args_cstr_buff: Vec<u8> = Vec::new();
|
||||
let exe_path = std::env::current_exe().unwrap_or_default();
|
||||
let exe_path_str = exe_path.into_os_string().into_string().unwrap_or_default();
|
||||
|
||||
args_cstr_buff.extend_from_slice(exe_path_str.as_bytes());
|
||||
args_cstr_buff.push(0);
|
||||
|
||||
for arg in sess.expanded_args.iter() {
|
||||
args_cstr_buff.extend_from_slice(arg.as_bytes());
|
||||
args_cstr_buff.push(0);
|
||||
}
|
||||
|
||||
args_cstr_buff
|
||||
};
|
||||
|
||||
let debuginfo_compression = sess.opts.debuginfo_compression.to_string();
|
||||
match sess.opts.debuginfo_compression {
|
||||
rustc_session::config::DebugInfoCompression::Zlib => {
|
||||
if !unsafe { LLVMRustLLVMHasZlibCompressionForDebugSymbols() } {
|
||||
sess.emit_warning(UnknownCompression { algorithm: "zlib" });
|
||||
}
|
||||
}
|
||||
rustc_session::config::DebugInfoCompression::Zstd => {
|
||||
if !unsafe { LLVMRustLLVMHasZstdCompressionForDebugSymbols() } {
|
||||
sess.emit_warning(UnknownCompression { algorithm: "zstd" });
|
||||
}
|
||||
}
|
||||
rustc_session::config::DebugInfoCompression::None => {}
|
||||
};
|
||||
let debuginfo_compression = SmallCStr::new(&debuginfo_compression);
|
||||
|
||||
Arc::new(move |config: TargetMachineFactoryConfig| {
|
||||
let split_dwarf_file =
|
||||
path_mapping.map_prefix(config.split_dwarf_file.unwrap_or_default()).0;
|
||||
@ -241,7 +279,10 @@ pub fn target_machine_factory(
|
||||
relax_elf_relocations,
|
||||
use_init_array,
|
||||
split_dwarf_file.as_ptr(),
|
||||
debuginfo_compression.as_ptr(),
|
||||
force_emulated_tls,
|
||||
args_cstr_buff.as_ptr() as *const c_char,
|
||||
args_cstr_buff.len(),
|
||||
)
|
||||
};
|
||||
|
||||
@ -853,6 +894,27 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data:
|
||||
asm
|
||||
}
|
||||
|
||||
fn target_is_apple(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool {
|
||||
cgcx.opts.target_triple.triple().contains("-ios")
|
||||
|| cgcx.opts.target_triple.triple().contains("-darwin")
|
||||
|| cgcx.opts.target_triple.triple().contains("-tvos")
|
||||
|| cgcx.opts.target_triple.triple().contains("-watchos")
|
||||
}
|
||||
|
||||
fn target_is_aix(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool {
|
||||
cgcx.opts.target_triple.triple().contains("-aix")
|
||||
}
|
||||
|
||||
pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) -> &'static str {
|
||||
if target_is_apple(cgcx) {
|
||||
"__LLVM,__bitcode\0"
|
||||
} else if target_is_aix(cgcx) {
|
||||
".ipa\0"
|
||||
} else {
|
||||
".llvmbc\0"
|
||||
}
|
||||
}
|
||||
|
||||
/// Embed the bitcode of an LLVM module in the LLVM module itself.
|
||||
///
|
||||
/// This is done primarily for iOS where it appears to be standard to compile C
|
||||
@ -913,11 +975,8 @@ unsafe fn embed_bitcode(
|
||||
// Unfortunately, LLVM provides no way to set custom section flags. For ELF
|
||||
// and COFF we emit the sections using module level inline assembly for that
|
||||
// reason (see issue #90326 for historical background).
|
||||
let is_aix = cgcx.opts.target_triple.triple().contains("-aix");
|
||||
let is_apple = cgcx.opts.target_triple.triple().contains("-ios")
|
||||
|| cgcx.opts.target_triple.triple().contains("-darwin")
|
||||
|| cgcx.opts.target_triple.triple().contains("-tvos")
|
||||
|| cgcx.opts.target_triple.triple().contains("-watchos");
|
||||
let is_aix = target_is_aix(cgcx);
|
||||
let is_apple = target_is_apple(cgcx);
|
||||
if is_apple
|
||||
|| is_aix
|
||||
|| cgcx.opts.target_triple.triple().starts_with("wasm")
|
||||
@ -932,13 +991,7 @@ unsafe fn embed_bitcode(
|
||||
);
|
||||
llvm::LLVMSetInitializer(llglobal, llconst);
|
||||
|
||||
let section = if is_apple {
|
||||
"__LLVM,__bitcode\0"
|
||||
} else if is_aix {
|
||||
".ipa\0"
|
||||
} else {
|
||||
".llvmbc\0"
|
||||
};
|
||||
let section = bitcode_section_name(cgcx);
|
||||
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo;
|
||||
use crate::coverageinfo::ffi::{Counter, CounterExpression, CounterMappingRegion};
|
||||
use crate::coverageinfo::ffi::CounterMappingRegion;
|
||||
use crate::coverageinfo::map_data::FunctionCoverage;
|
||||
use crate::llvm;
|
||||
|
||||
use rustc_codegen_ssa::traits::ConstMethods;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_llvm::RustString;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::coverage::CodeRegion;
|
||||
@ -55,7 +56,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut mapgen = CoverageMapGenerator::new(tcx);
|
||||
let mut global_file_table = GlobalFileTable::new(tcx);
|
||||
|
||||
// Encode coverage mappings and generate function records
|
||||
let mut function_data = Vec::new();
|
||||
@ -64,12 +65,9 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
let mangled_function_name = tcx.symbol_name(instance).name;
|
||||
let source_hash = function_coverage.source_hash();
|
||||
let is_used = function_coverage.is_used();
|
||||
let (expressions, counter_regions) =
|
||||
function_coverage.get_expressions_and_counter_regions();
|
||||
|
||||
let coverage_mapping_buffer = llvm::build_byte_buffer(|coverage_mapping_buffer| {
|
||||
mapgen.write_coverage_mapping(expressions, counter_regions, coverage_mapping_buffer);
|
||||
});
|
||||
let coverage_mapping_buffer =
|
||||
encode_mappings_for_function(&mut global_file_table, &function_coverage);
|
||||
|
||||
if coverage_mapping_buffer.is_empty() {
|
||||
if function_coverage.is_used() {
|
||||
@ -87,19 +85,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
}
|
||||
|
||||
// Encode all filenames referenced by counters/expressions in this module
|
||||
let filenames_buffer = llvm::build_byte_buffer(|filenames_buffer| {
|
||||
coverageinfo::write_filenames_section_to_buffer(
|
||||
mapgen.filenames.iter().map(Symbol::as_str),
|
||||
filenames_buffer,
|
||||
);
|
||||
});
|
||||
let filenames_buffer = global_file_table.into_filenames_buffer();
|
||||
|
||||
let filenames_size = filenames_buffer.len();
|
||||
let filenames_val = cx.const_bytes(&filenames_buffer);
|
||||
let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer);
|
||||
|
||||
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
|
||||
let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val);
|
||||
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
|
||||
|
||||
let covfun_section_name = coverageinfo::covfun_section_name(cx);
|
||||
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
|
||||
@ -118,13 +111,13 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
|
||||
}
|
||||
|
||||
struct CoverageMapGenerator {
|
||||
filenames: FxIndexSet<Symbol>,
|
||||
struct GlobalFileTable {
|
||||
global_file_table: FxIndexSet<Symbol>,
|
||||
}
|
||||
|
||||
impl CoverageMapGenerator {
|
||||
impl GlobalFileTable {
|
||||
fn new(tcx: TyCtxt<'_>) -> Self {
|
||||
let mut filenames = FxIndexSet::default();
|
||||
let mut global_file_table = FxIndexSet::default();
|
||||
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
|
||||
// requires setting the first filename to the compilation directory.
|
||||
// Since rustc generates coverage maps with relative paths, the
|
||||
@ -133,94 +126,114 @@ impl CoverageMapGenerator {
|
||||
let working_dir = Symbol::intern(
|
||||
&tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy(),
|
||||
);
|
||||
filenames.insert(working_dir);
|
||||
Self { filenames }
|
||||
global_file_table.insert(working_dir);
|
||||
Self { global_file_table }
|
||||
}
|
||||
|
||||
/// Using the `expressions` and `counter_regions` collected for the current function, generate
|
||||
/// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
|
||||
/// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
|
||||
/// the given `coverage_mapping` byte buffer, compliant with the LLVM Coverage Mapping format.
|
||||
fn write_coverage_mapping<'a>(
|
||||
&mut self,
|
||||
expressions: Vec<CounterExpression>,
|
||||
counter_regions: impl Iterator<Item = (Counter, &'a CodeRegion)>,
|
||||
coverage_mapping_buffer: &RustString,
|
||||
) {
|
||||
let mut counter_regions = counter_regions.collect::<Vec<_>>();
|
||||
if counter_regions.is_empty() {
|
||||
return;
|
||||
}
|
||||
fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> u32 {
|
||||
let (global_file_id, _) = self.global_file_table.insert_full(file_name);
|
||||
global_file_id as u32
|
||||
}
|
||||
|
||||
let mut virtual_file_mapping = Vec::new();
|
||||
let mut mapping_regions = Vec::new();
|
||||
let mut current_file_name = None;
|
||||
let mut current_file_id = 0;
|
||||
fn into_filenames_buffer(self) -> Vec<u8> {
|
||||
// This method takes `self` so that the caller can't accidentally
|
||||
// modify the original file table after encoding it into a buffer.
|
||||
|
||||
// Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted
|
||||
// by filename and position. Capture any new files to compute the `CounterMappingRegion`s
|
||||
// `file_id` (indexing files referenced by the current function), and construct the
|
||||
// function-specific `virtual_file_mapping` from `file_id` to its index in the module's
|
||||
// `filenames` array.
|
||||
counter_regions.sort_unstable_by_key(|(_counter, region)| *region);
|
||||
for (counter, region) in counter_regions {
|
||||
let CodeRegion { file_name, start_line, start_col, end_line, end_col } = *region;
|
||||
let same_file = current_file_name.is_some_and(|p| p == file_name);
|
||||
if !same_file {
|
||||
if current_file_name.is_some() {
|
||||
current_file_id += 1;
|
||||
}
|
||||
current_file_name = Some(file_name);
|
||||
debug!(" file_id: {} = '{:?}'", current_file_id, file_name);
|
||||
let (filenames_index, _) = self.filenames.insert_full(file_name);
|
||||
virtual_file_mapping.push(filenames_index as u32);
|
||||
}
|
||||
debug!("Adding counter {:?} to map for {:?}", counter, region);
|
||||
llvm::build_byte_buffer(|buffer| {
|
||||
coverageinfo::write_filenames_section_to_buffer(
|
||||
self.global_file_table.iter().map(Symbol::as_str),
|
||||
buffer,
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Using the expressions and counter regions collected for a single function,
|
||||
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
|
||||
/// entry. The payload is returned as a vector of bytes.
|
||||
///
|
||||
/// Newly-encountered filenames will be added to the global file table.
|
||||
fn encode_mappings_for_function(
|
||||
global_file_table: &mut GlobalFileTable,
|
||||
function_coverage: &FunctionCoverage<'_>,
|
||||
) -> Vec<u8> {
|
||||
let (expressions, counter_regions) = function_coverage.get_expressions_and_counter_regions();
|
||||
|
||||
let mut counter_regions = counter_regions.collect::<Vec<_>>();
|
||||
if counter_regions.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut virtual_file_mapping = IndexVec::<u32, u32>::new();
|
||||
let mut mapping_regions = Vec::with_capacity(counter_regions.len());
|
||||
|
||||
// Sort the list of (counter, region) mapping pairs by region, so that they
|
||||
// can be grouped by filename. Prepare file IDs for each filename, and
|
||||
// prepare the mapping data so that we can pass it through FFI to LLVM.
|
||||
counter_regions.sort_by_key(|(_counter, region)| *region);
|
||||
for counter_regions_for_file in
|
||||
counter_regions.group_by(|(_, a), (_, b)| a.file_name == b.file_name)
|
||||
{
|
||||
// Look up (or allocate) the global file ID for this filename.
|
||||
let file_name = counter_regions_for_file[0].1.file_name;
|
||||
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
|
||||
|
||||
// Associate that global file ID with a local file ID for this function.
|
||||
let local_file_id: u32 = virtual_file_mapping.push(global_file_id);
|
||||
debug!(" file id: local {local_file_id} => global {global_file_id} = '{file_name:?}'");
|
||||
|
||||
// For each counter/region pair in this function+file, convert it to a
|
||||
// form suitable for FFI.
|
||||
for &(counter, region) in counter_regions_for_file {
|
||||
let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region;
|
||||
|
||||
debug!("Adding counter {counter:?} to map for {region:?}");
|
||||
mapping_regions.push(CounterMappingRegion::code_region(
|
||||
counter,
|
||||
current_file_id,
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Encode and append the current function's coverage mapping data
|
||||
// Encode the function's coverage mappings into a buffer.
|
||||
llvm::build_byte_buffer(|buffer| {
|
||||
coverageinfo::write_mapping_to_buffer(
|
||||
virtual_file_mapping,
|
||||
virtual_file_mapping.raw,
|
||||
expressions,
|
||||
mapping_regions,
|
||||
coverage_mapping_buffer,
|
||||
buffer,
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct coverage map header and the array of function records, and combine them into the
|
||||
/// coverage map. Save the coverage map data into the LLVM IR as a static global using a
|
||||
/// specific, well-known section and name.
|
||||
fn generate_coverage_map<'ll>(
|
||||
self,
|
||||
cx: &CodegenCx<'ll, '_>,
|
||||
version: u32,
|
||||
filenames_size: usize,
|
||||
filenames_val: &'ll llvm::Value,
|
||||
) -> &'ll llvm::Value {
|
||||
debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
|
||||
/// Construct coverage map header and the array of function records, and combine them into the
|
||||
/// coverage map. Save the coverage map data into the LLVM IR as a static global using a
|
||||
/// specific, well-known section and name.
|
||||
fn generate_coverage_map<'ll>(
|
||||
cx: &CodegenCx<'ll, '_>,
|
||||
version: u32,
|
||||
filenames_size: usize,
|
||||
filenames_val: &'ll llvm::Value,
|
||||
) -> &'ll llvm::Value {
|
||||
debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
|
||||
|
||||
// Create the coverage data header (Note, fields 0 and 2 are now always zero,
|
||||
// as of `llvm::coverage::CovMapVersion::Version4`.)
|
||||
let zero_was_n_records_val = cx.const_u32(0);
|
||||
let filenames_size_val = cx.const_u32(filenames_size as u32);
|
||||
let zero_was_coverage_size_val = cx.const_u32(0);
|
||||
let version_val = cx.const_u32(version);
|
||||
let cov_data_header_val = cx.const_struct(
|
||||
&[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
|
||||
/*packed=*/ false,
|
||||
);
|
||||
// Create the coverage data header (Note, fields 0 and 2 are now always zero,
|
||||
// as of `llvm::coverage::CovMapVersion::Version4`.)
|
||||
let zero_was_n_records_val = cx.const_u32(0);
|
||||
let filenames_size_val = cx.const_u32(filenames_size as u32);
|
||||
let zero_was_coverage_size_val = cx.const_u32(0);
|
||||
let version_val = cx.const_u32(version);
|
||||
let cov_data_header_val = cx.const_struct(
|
||||
&[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
|
||||
/*packed=*/ false,
|
||||
);
|
||||
|
||||
// Create the complete LLVM coverage data value to add to the LLVM IR
|
||||
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false)
|
||||
}
|
||||
// Create the complete LLVM coverage data value to add to the LLVM IR
|
||||
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false)
|
||||
}
|
||||
|
||||
/// Construct a function record and combine it with the function's coverage mapping data.
|
||||
|
@ -20,7 +20,7 @@ pub fn compute_mir_scopes<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
mir: &Body<'tcx>,
|
||||
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
|
||||
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
|
||||
) {
|
||||
// Find all scopes with variables defined in them.
|
||||
let variables = if cx.sess().opts.debuginfo == DebugInfo::Full {
|
||||
@ -51,7 +51,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
instance: Instance<'tcx>,
|
||||
mir: &Body<'tcx>,
|
||||
variables: &Option<BitSet<SourceScope>>,
|
||||
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
|
||||
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
|
||||
instantiated: &mut BitSet<SourceScope>,
|
||||
scope: SourceScope,
|
||||
) {
|
||||
@ -86,7 +86,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
let loc = cx.lookup_debug_loc(scope_data.span.lo());
|
||||
let file_metadata = file_metadata(cx, &loc.file);
|
||||
|
||||
let dbg_scope = match scope_data.inlined {
|
||||
let parent_dbg_scope = match scope_data.inlined {
|
||||
Some((callee, _)) => {
|
||||
// FIXME(eddyb) this would be `self.monomorphize(&callee)`
|
||||
// if this is moved to `rustc_codegen_ssa::mir::debuginfo`.
|
||||
@ -95,18 +95,22 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
ty::EarlyBinder::bind(callee),
|
||||
);
|
||||
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
|
||||
cx.dbg_scope_fn(callee, callee_fn_abi, None)
|
||||
debug_context.inlined_function_scopes.entry(callee).or_insert_with(|| {
|
||||
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
|
||||
cx.dbg_scope_fn(callee, callee_fn_abi, None)
|
||||
})
|
||||
}
|
||||
None => unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateLexicalBlock(
|
||||
DIB(cx),
|
||||
parent_scope.dbg_scope,
|
||||
file_metadata,
|
||||
loc.line,
|
||||
loc.col,
|
||||
)
|
||||
},
|
||||
None => parent_scope.dbg_scope,
|
||||
};
|
||||
|
||||
let dbg_scope = unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateLexicalBlock(
|
||||
DIB(cx),
|
||||
parent_dbg_scope,
|
||||
file_metadata,
|
||||
loc.line,
|
||||
loc.col,
|
||||
)
|
||||
};
|
||||
|
||||
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
|
||||
|
@ -263,7 +263,7 @@ impl CodegenCx<'_, '_> {
|
||||
pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc {
|
||||
let (file, line, col) = match self.sess().source_map().lookup_line(pos) {
|
||||
Ok(SourceFileAndLine { sf: file, line }) => {
|
||||
let line_pos = file.lines(|lines| lines[line]);
|
||||
let line_pos = file.lines()[line];
|
||||
|
||||
// Use 1-based indexing.
|
||||
let line = (line + 1) as u32;
|
||||
@ -292,7 +292,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
llfn: &'ll Value,
|
||||
mir: &mir::Body<'tcx>,
|
||||
) -> Option<FunctionDebugContext<&'ll DIScope, &'ll DILocation>> {
|
||||
) -> Option<FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>> {
|
||||
if self.sess().opts.debuginfo == DebugInfo::None {
|
||||
return None;
|
||||
}
|
||||
@ -304,8 +304,10 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
file_start_pos: BytePos(0),
|
||||
file_end_pos: BytePos(0),
|
||||
};
|
||||
let mut fn_debug_context =
|
||||
FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) };
|
||||
let mut fn_debug_context = FunctionDebugContext {
|
||||
scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
|
||||
inlined_function_scopes: Default::default(),
|
||||
};
|
||||
|
||||
// Fill in all the scopes, with the information from the MIR body.
|
||||
compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
|
||||
|
@ -226,3 +226,9 @@ pub(crate) struct WriteBytecode<'a> {
|
||||
pub(crate) struct CopyBitcode {
|
||||
pub err: std::io::Error,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_unknown_debuginfo_compression)]
|
||||
pub struct UnknownCompression {
|
||||
pub algorithm: &'static str,
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(slice_group_by)]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
|
@ -2131,8 +2131,12 @@ extern "C" {
|
||||
RelaxELFRelocations: bool,
|
||||
UseInitArray: bool,
|
||||
SplitDwarfFile: *const c_char,
|
||||
DebugInfoCompression: *const c_char,
|
||||
ForceEmulatedTls: bool,
|
||||
ArgsCstrBuff: *const c_char,
|
||||
ArgsCstrBuffLen: usize,
|
||||
) -> Option<&'static mut TargetMachine>;
|
||||
|
||||
pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine);
|
||||
pub fn LLVMRustAddLibraryInfo<'a>(
|
||||
PM: &PassManager<'a>,
|
||||
@ -2319,6 +2323,12 @@ extern "C" {
|
||||
len: usize,
|
||||
out_len: &mut usize,
|
||||
) -> *const u8;
|
||||
pub fn LLVMRustGetSliceFromObjectDataByName(
|
||||
data: *const u8,
|
||||
len: usize,
|
||||
name: *const u8,
|
||||
out_len: &mut usize,
|
||||
) -> *const u8;
|
||||
|
||||
pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>;
|
||||
pub fn LLVMRustLinkerAdd(
|
||||
@ -2357,6 +2367,10 @@ extern "C" {
|
||||
|
||||
pub fn LLVMRustIsBitcode(ptr: *const u8, len: usize) -> bool;
|
||||
|
||||
pub fn LLVMRustLLVMHasZlibCompressionForDebugSymbols() -> bool;
|
||||
|
||||
pub fn LLVMRustLLVMHasZstdCompressionForDebugSymbols() -> bool;
|
||||
|
||||
pub fn LLVMRustGetSymbols(
|
||||
buf_ptr: *const u8,
|
||||
buf_len: usize,
|
||||
|
@ -343,6 +343,12 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
||||
pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
|
||||
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
|
||||
|
||||
/// All commandline args used to invoke the compiler, with @file args fully expanded.
|
||||
/// This will only be used within debug info, e.g. in the pdb file on windows
|
||||
/// This is mainly useful for other tools that reads that debuginfo to figure out
|
||||
/// how to call the compiler with the same arguments.
|
||||
pub expanded_args: Vec<String>,
|
||||
|
||||
/// Handler to use for diagnostics produced during codegen.
|
||||
pub diag_emitter: SharedEmitter,
|
||||
/// LLVM optimizations for which we want to print remarks.
|
||||
@ -1108,6 +1114,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
|
||||
cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
|
||||
coordinator_send,
|
||||
expanded_args: tcx.sess.expanded_args.clone(),
|
||||
diag_emitter: shared_emitter.clone(),
|
||||
output_filenames: tcx.output_filenames(()).clone(),
|
||||
regular_module_config: regular_config,
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::traits::*;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::config::DebugInfo;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
@ -17,10 +19,13 @@ use super::{FunctionCx, LocalRef};
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
pub struct FunctionDebugContext<S, L> {
|
||||
pub struct FunctionDebugContext<'tcx, S, L> {
|
||||
/// Maps from source code to the corresponding debug info scope.
|
||||
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
|
||||
}
|
||||
|
||||
/// Maps from an inlined function to its debug info declaration.
|
||||
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum VariableKind {
|
||||
ArgumentVariable(usize /*index*/),
|
||||
@ -484,54 +489,89 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
None
|
||||
};
|
||||
|
||||
let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
|
||||
let (var_ty, var_kind) = match var.value {
|
||||
let var_ty = if let Some(ref fragment) = var.composite {
|
||||
self.monomorphize(fragment.ty)
|
||||
} else {
|
||||
match var.value {
|
||||
mir::VarDebugInfoContents::Place(place) => {
|
||||
let var_ty = self.monomorphized_place_ty(place.as_ref());
|
||||
let var_kind = if let Some(arg_index) = var.argument_index
|
||||
&& place.projection.is_empty()
|
||||
{
|
||||
let arg_index = arg_index as usize;
|
||||
if target_is_msvc {
|
||||
// ScalarPair parameters are spilled to the stack so they need to
|
||||
// be marked as a `LocalVariable` for MSVC debuggers to visualize
|
||||
// their data correctly. (See #81894 & #88625)
|
||||
let var_ty_layout = self.cx.layout_of(var_ty);
|
||||
if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
|
||||
VariableKind::LocalVariable
|
||||
} else {
|
||||
VariableKind::ArgumentVariable(arg_index)
|
||||
}
|
||||
} else {
|
||||
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
|
||||
// offset in closures to account for the hidden environment?
|
||||
VariableKind::ArgumentVariable(arg_index)
|
||||
}
|
||||
} else {
|
||||
self.monomorphized_place_ty(place.as_ref())
|
||||
}
|
||||
mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
|
||||
}
|
||||
};
|
||||
|
||||
let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
|
||||
let var_kind = if let Some(arg_index) = var.argument_index
|
||||
&& var.composite.is_none()
|
||||
&& let mir::VarDebugInfoContents::Place(place) = var.value
|
||||
&& place.projection.is_empty()
|
||||
{
|
||||
let arg_index = arg_index as usize;
|
||||
if target_is_msvc {
|
||||
// ScalarPair parameters are spilled to the stack so they need to
|
||||
// be marked as a `LocalVariable` for MSVC debuggers to visualize
|
||||
// their data correctly. (See #81894 & #88625)
|
||||
let var_ty_layout = self.cx.layout_of(var_ty);
|
||||
if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
|
||||
VariableKind::LocalVariable
|
||||
};
|
||||
(var_ty, var_kind)
|
||||
}
|
||||
mir::VarDebugInfoContents::Const(c) => {
|
||||
let ty = self.monomorphize(c.ty());
|
||||
(ty, VariableKind::LocalVariable)
|
||||
}
|
||||
mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
|
||||
let ty = self.monomorphize(ty);
|
||||
(ty, VariableKind::LocalVariable)
|
||||
} else {
|
||||
VariableKind::ArgumentVariable(arg_index)
|
||||
}
|
||||
} else {
|
||||
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
|
||||
// offset in closures to account for the hidden environment?
|
||||
VariableKind::ArgumentVariable(arg_index)
|
||||
}
|
||||
} else {
|
||||
VariableKind::LocalVariable
|
||||
};
|
||||
|
||||
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
|
||||
});
|
||||
|
||||
let fragment = if let Some(ref fragment) = var.composite {
|
||||
let var_layout = self.cx.layout_of(var_ty);
|
||||
|
||||
let mut fragment_start = Size::ZERO;
|
||||
let mut fragment_layout = var_layout;
|
||||
|
||||
for elem in &fragment.projection {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Field(field, _) => {
|
||||
let i = field.index();
|
||||
fragment_start += fragment_layout.fields.offset(i);
|
||||
fragment_layout = fragment_layout.field(self.cx, i);
|
||||
}
|
||||
_ => span_bug!(
|
||||
var.source_info.span,
|
||||
"unsupported fragment projection `{:?}`",
|
||||
elem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if fragment_layout.size == Size::ZERO {
|
||||
// Fragment is a ZST, so does not represent anything. Avoid generating anything
|
||||
// as this may conflict with a fragment that covers the entire variable.
|
||||
continue;
|
||||
} else if fragment_layout.size == var_layout.size {
|
||||
// Fragment covers entire variable, so as far as
|
||||
// DWARF is concerned, it's not really a fragment.
|
||||
None
|
||||
} else {
|
||||
Some(fragment_start..fragment_start + fragment_layout.size)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match var.value {
|
||||
mir::VarDebugInfoContents::Place(place) => {
|
||||
per_local[place.local].push(PerLocalVarDebugInfo {
|
||||
name: var.name,
|
||||
source_info: var.source_info,
|
||||
dbg_var,
|
||||
fragment: None,
|
||||
fragment,
|
||||
projection: place.projection,
|
||||
});
|
||||
}
|
||||
@ -547,53 +587,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
bx,
|
||||
);
|
||||
|
||||
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
|
||||
bx.dbg_var_addr(
|
||||
dbg_var,
|
||||
dbg_loc,
|
||||
base.llval,
|
||||
Size::ZERO,
|
||||
&[],
|
||||
fragment,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
|
||||
let var_ty = self.monomorphize(ty);
|
||||
let var_layout = self.cx.layout_of(var_ty);
|
||||
for fragment in fragments {
|
||||
let mut fragment_start = Size::ZERO;
|
||||
let mut fragment_layout = var_layout;
|
||||
|
||||
for elem in &fragment.projection {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Field(field, _) => {
|
||||
let i = field.index();
|
||||
fragment_start += fragment_layout.fields.offset(i);
|
||||
fragment_layout = fragment_layout.field(self.cx, i);
|
||||
}
|
||||
_ => span_bug!(
|
||||
var.source_info.span,
|
||||
"unsupported fragment projection `{:?}`",
|
||||
elem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
let place = fragment.contents;
|
||||
let fragment = if fragment_layout.size == Size::ZERO {
|
||||
// Fragment is a ZST, so does not represent anything.
|
||||
continue;
|
||||
} else if fragment_layout.size == var_layout.size {
|
||||
// Fragment covers entire variable, so as far as
|
||||
// DWARF is concerned, it's not really a fragment.
|
||||
None
|
||||
} else {
|
||||
Some(fragment_start..fragment_start + fragment_layout.size)
|
||||
};
|
||||
|
||||
per_local[place.local].push(PerLocalVarDebugInfo {
|
||||
name: var.name,
|
||||
source_info: var.source_info,
|
||||
dbg_var,
|
||||
fragment,
|
||||
projection: place.projection,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(per_local)
|
||||
|
@ -46,7 +46,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
|
||||
|
||||
mir: &'tcx mir::Body<'tcx>,
|
||||
|
||||
debug_context: Option<FunctionDebugContext<Bx::DIScope, Bx::DILocation>>,
|
||||
debug_context: Option<FunctionDebugContext<'tcx, Bx::DIScope, Bx::DILocation>>,
|
||||
|
||||
llfn: Bx::Function,
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
|
||||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
llfn: Self::Function,
|
||||
mir: &mir::Body<'tcx>,
|
||||
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>>;
|
||||
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>>;
|
||||
|
||||
// FIXME(eddyb) find a common convention for all of the debuginfo-related
|
||||
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
|
||||
|
@ -482,6 +482,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
||||
use UndefinedBehaviorInfo::*;
|
||||
match self {
|
||||
Ub(msg) => msg.clone().into(),
|
||||
Custom(x) => (x.msg)(),
|
||||
ValidationError(e) => e.diagnostic_message(),
|
||||
|
||||
Unreachable => const_eval_unreachable,
|
||||
BoundsCheckFailed { .. } => const_eval_bounds_check_failed,
|
||||
DivisionByZero => const_eval_division_by_zero,
|
||||
@ -513,8 +516,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
||||
ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
|
||||
UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
|
||||
UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
|
||||
ValidationError(e) => e.diagnostic_message(),
|
||||
Custom(x) => (x.msg)(),
|
||||
AbiMismatchArgument { .. } => const_eval_incompatible_types,
|
||||
AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,8 +528,15 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
||||
) {
|
||||
use UndefinedBehaviorInfo::*;
|
||||
match self {
|
||||
Ub(_)
|
||||
| Unreachable
|
||||
Ub(_) => {}
|
||||
Custom(custom) => {
|
||||
(custom.add_args)(&mut |name, value| {
|
||||
builder.set_arg(name, value);
|
||||
});
|
||||
}
|
||||
ValidationError(e) => e.add_args(handler, builder),
|
||||
|
||||
Unreachable
|
||||
| DivisionByZero
|
||||
| RemainderByZero
|
||||
| DivisionOverflow
|
||||
@ -593,11 +603,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
||||
builder.set_arg("target_size", info.target_size);
|
||||
builder.set_arg("data_size", info.data_size);
|
||||
}
|
||||
ValidationError(e) => e.add_args(handler, builder),
|
||||
Custom(custom) => {
|
||||
(custom.add_args)(&mut |name, value| {
|
||||
builder.set_arg(name, value);
|
||||
});
|
||||
AbiMismatchArgument { caller_ty, callee_ty }
|
||||
| AbiMismatchReturn { caller_ty, callee_ty } => {
|
||||
builder.set_arg("caller_ty", caller_ty.to_string());
|
||||
builder.set_arg("callee_ty", callee_ty.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -796,6 +796,13 @@ where
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
allow_transmute: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Generally for transmutation, data must be valid both at the old and new type.
|
||||
// But if the types are the same, the 2nd validation below suffices.
|
||||
if src.layout().ty != dest.layout().ty && M::enforce_validity(self, src.layout()) {
|
||||
self.validate_operand(&src.to_op(self)?)?;
|
||||
}
|
||||
|
||||
// Do the actual copy.
|
||||
self.copy_op_no_validate(src, dest, allow_transmute)?;
|
||||
|
||||
if M::enforce_validity(self, dest.layout()) {
|
||||
|
@ -6,12 +6,16 @@ use rustc_middle::{
|
||||
mir,
|
||||
ty::{
|
||||
self,
|
||||
layout::{FnAbiOf, LayoutOf, TyAndLayout},
|
||||
Instance, Ty,
|
||||
layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout},
|
||||
AdtDef, Instance, Ty,
|
||||
},
|
||||
};
|
||||
use rustc_target::abi::call::{ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode};
|
||||
use rustc_span::sym;
|
||||
use rustc_target::abi::{self, FieldIdx};
|
||||
use rustc_target::abi::{
|
||||
call::{ArgAbi, FnAbi, PassMode},
|
||||
Integer,
|
||||
};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::{
|
||||
@ -255,9 +259,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
|
||||
/// Find the wrapped inner type of a transparent wrapper.
|
||||
/// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
|
||||
fn unfold_transparent(&self, layout: TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
|
||||
///
|
||||
/// We work with `TyAndLayout` here since that makes it much easier to iterate over all fields.
|
||||
fn unfold_transparent(
|
||||
&self,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
may_unfold: impl Fn(AdtDef<'tcx>) -> bool,
|
||||
) -> TyAndLayout<'tcx> {
|
||||
match layout.ty.kind() {
|
||||
ty::Adt(adt_def, _) if adt_def.repr().transparent() => {
|
||||
ty::Adt(adt_def, _) if adt_def.repr().transparent() && may_unfold(*adt_def) => {
|
||||
assert!(!adt_def.is_enum());
|
||||
// Find the non-1-ZST field.
|
||||
let mut non_1zst_fields = (0..layout.fields.count()).filter_map(|idx| {
|
||||
@ -271,131 +281,157 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
);
|
||||
|
||||
// Found it!
|
||||
self.unfold_transparent(first)
|
||||
self.unfold_transparent(first, may_unfold)
|
||||
}
|
||||
// Not a transparent type, no further unfolding.
|
||||
_ => layout,
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap types that are guaranteed a null-pointer-optimization
|
||||
fn unfold_npo(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
|
||||
// Check if this is `Option` wrapping some type.
|
||||
let inner = match layout.ty.kind() {
|
||||
ty::Adt(def, args) if self.tcx.is_diagnostic_item(sym::Option, def.did()) => {
|
||||
args[0].as_type().unwrap()
|
||||
}
|
||||
_ => {
|
||||
// Not an `Option`.
|
||||
return Ok(layout);
|
||||
}
|
||||
};
|
||||
let inner = self.layout_of(inner)?;
|
||||
// Check if the inner type is one of the NPO-guaranteed ones.
|
||||
// For that we first unpeel transparent *structs* (but not unions).
|
||||
let is_npo = |def: AdtDef<'tcx>| {
|
||||
self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
|
||||
};
|
||||
let inner = self.unfold_transparent(inner, /* may_unfold */ |def| {
|
||||
// Stop at NPO tpyes so that we don't miss that attribute in the check below!
|
||||
def.is_struct() && !is_npo(def)
|
||||
});
|
||||
Ok(match inner.ty.kind() {
|
||||
ty::Ref(..) | ty::FnPtr(..) => {
|
||||
// Option<&T> behaves like &T, and same for fn()
|
||||
inner
|
||||
}
|
||||
ty::Adt(def, _) if is_npo(*def) => {
|
||||
// Once we found a `nonnull_optimization_guaranteed` type, further strip off
|
||||
// newtype structs from it to find the underlying ABI type.
|
||||
self.unfold_transparent(inner, /* may_unfold */ |def| def.is_struct())
|
||||
}
|
||||
_ => {
|
||||
// Everything else we do not unfold.
|
||||
layout
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if these two layouts look like they are fn-ABI-compatible.
|
||||
/// (We also compare the `PassMode`, so this doesn't have to check everything. But it turns out
|
||||
/// that only checking the `PassMode` is insufficient.)
|
||||
fn layout_compat(
|
||||
&self,
|
||||
caller_layout: TyAndLayout<'tcx>,
|
||||
callee_layout: TyAndLayout<'tcx>,
|
||||
) -> bool {
|
||||
if caller_layout.ty == callee_layout.ty {
|
||||
// Fast path: equal types are definitely compatible.
|
||||
return true;
|
||||
caller: TyAndLayout<'tcx>,
|
||||
callee: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
// Fast path: equal types are definitely compatible.
|
||||
if caller.ty == callee.ty {
|
||||
return Ok(true);
|
||||
}
|
||||
// 1-ZST are compatible with all 1-ZST (and with nothing else).
|
||||
if caller.is_1zst() || callee.is_1zst() {
|
||||
return Ok(caller.is_1zst() && callee.is_1zst());
|
||||
}
|
||||
// Unfold newtypes and NPO optimizations.
|
||||
let unfold = |layout: TyAndLayout<'tcx>| {
|
||||
self.unfold_npo(self.unfold_transparent(layout, /* may_unfold */ |_def| true))
|
||||
};
|
||||
let caller = unfold(caller)?;
|
||||
let callee = unfold(callee)?;
|
||||
// Now see if these inner types are compatible.
|
||||
|
||||
// Compatible pointer types. For thin pointers, we have to accept even non-`repr(transparent)`
|
||||
// things as compatible due to `DispatchFromDyn`. For instance, `Rc<i32>` and `*mut i32`
|
||||
// must be compatible. So we just accept everything with Pointer ABI as compatible,
|
||||
// even if this will accept some code that is not stably guaranteed to work.
|
||||
// This also handles function pointers.
|
||||
let thin_pointer = |layout: TyAndLayout<'tcx>| match layout.abi {
|
||||
abi::Abi::Scalar(s) => match s.primitive() {
|
||||
abi::Primitive::Pointer(addr_space) => Some(addr_space),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let (Some(caller), Some(callee)) = (thin_pointer(caller), thin_pointer(callee)) {
|
||||
return Ok(caller == callee);
|
||||
}
|
||||
// For wide pointers we have to get the pointee type.
|
||||
let pointee_ty = |ty: Ty<'tcx>| -> InterpResult<'tcx, Option<Ty<'tcx>>> {
|
||||
// We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`.
|
||||
Ok(Some(match ty.kind() {
|
||||
ty::Ref(_, ty, _) => *ty,
|
||||
ty::RawPtr(mt) => mt.ty,
|
||||
// We should only accept `Box` with the default allocator.
|
||||
// It's hard to test for that though so we accept every 1-ZST allocator.
|
||||
ty::Adt(def, args)
|
||||
if def.is_box()
|
||||
&& self.layout_of(args[1].expect_ty()).is_ok_and(|l| l.is_1zst()) =>
|
||||
{
|
||||
args[0].expect_ty()
|
||||
}
|
||||
_ => return Ok(None),
|
||||
}))
|
||||
};
|
||||
if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) {
|
||||
// This is okay if they have the same metadata type.
|
||||
let meta_ty = |ty: Ty<'tcx>| {
|
||||
let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, |ty| ty);
|
||||
assert!(
|
||||
!only_if_sized,
|
||||
"there should be no more 'maybe has that metadata' types during interpretation"
|
||||
);
|
||||
meta
|
||||
};
|
||||
return Ok(meta_ty(caller) == meta_ty(callee));
|
||||
}
|
||||
|
||||
match (caller_layout.abi, callee_layout.abi) {
|
||||
// If both sides have Scalar/Vector/ScalarPair ABI, we can easily directly compare them.
|
||||
// Different valid ranges are okay (the validity check will complain if this leads to
|
||||
// invalid transmutes). Different signs are *not* okay on some targets (e.g. `extern
|
||||
// "C"` on `s390x` where small integers are passed zero/sign-extended in large
|
||||
// registers), so we generally reject them to increase portability.
|
||||
// NOTE: this is *not* a stable guarantee! It just reflects a property of our current
|
||||
// ABIs. It's also fragile; the same pair of types might be considered ABI-compatible
|
||||
// when used directly by-value but not considered compatible as a struct field or array
|
||||
// element.
|
||||
(abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {
|
||||
caller.primitive() == callee.primitive()
|
||||
}
|
||||
(
|
||||
abi::Abi::Vector { element: caller_element, count: caller_count },
|
||||
abi::Abi::Vector { element: callee_element, count: callee_count },
|
||||
) => {
|
||||
caller_element.primitive() == callee_element.primitive()
|
||||
&& caller_count == callee_count
|
||||
}
|
||||
(abi::Abi::ScalarPair(caller1, caller2), abi::Abi::ScalarPair(callee1, callee2)) => {
|
||||
caller1.primitive() == callee1.primitive()
|
||||
&& caller2.primitive() == callee2.primitive()
|
||||
}
|
||||
(abi::Abi::Aggregate { .. }, abi::Abi::Aggregate { .. }) => {
|
||||
// Aggregates are compatible only if they newtype-wrap the same type, or if they are both 1-ZST.
|
||||
// (The latter part is needed to ensure e.g. that `struct Zst` is compatible with `struct Wrap((), Zst)`.)
|
||||
// This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode`,
|
||||
// which means having ABI-compatibility on one target is much more likely to imply compatibility for other targets.
|
||||
if caller_layout.is_1zst() || callee_layout.is_1zst() {
|
||||
// If either is a 1-ZST, both must be.
|
||||
caller_layout.is_1zst() && callee_layout.is_1zst()
|
||||
} else {
|
||||
// Neither is a 1-ZST, so we can check what they are wrapping.
|
||||
self.unfold_transparent(caller_layout).ty
|
||||
== self.unfold_transparent(callee_layout).ty
|
||||
}
|
||||
}
|
||||
// What remains is `Abi::Uninhabited` (which can never be passed anyway) and
|
||||
// mismatching ABIs, that should all be rejected.
|
||||
_ => false,
|
||||
// Compatible integer types (in particular, usize vs ptr-sized-u32/u64).
|
||||
let int_ty = |ty: Ty<'tcx>| {
|
||||
Some(match ty.kind() {
|
||||
ty::Int(ity) => (Integer::from_int_ty(&self.tcx, *ity), /* signed */ true),
|
||||
ty::Uint(uty) => (Integer::from_uint_ty(&self.tcx, *uty), /* signed */ false),
|
||||
_ => return None,
|
||||
})
|
||||
};
|
||||
if let (Some(caller), Some(callee)) = (int_ty(caller.ty), int_ty(callee.ty)) {
|
||||
// This is okay if they are the same integer type.
|
||||
return Ok(caller == callee);
|
||||
}
|
||||
|
||||
// Fall back to exact equality.
|
||||
// FIXME: We are missing the rules for "repr(C) wrapping compatible types".
|
||||
Ok(caller == callee)
|
||||
}
|
||||
|
||||
fn check_argument_compat(
|
||||
&self,
|
||||
caller_abi: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
// When comparing the PassMode, we have to be smart about comparing the attributes.
|
||||
let arg_attr_compat = |a1: &ArgAttributes, a2: &ArgAttributes| {
|
||||
// There's only one regular attribute that matters for the call ABI: InReg.
|
||||
// Everything else is things like noalias, dereferenceable, nonnull, ...
|
||||
// (This also applies to pointee_size, pointee_align.)
|
||||
if a1.regular.contains(ArgAttribute::InReg) != a2.regular.contains(ArgAttribute::InReg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// We also compare the sign extension mode -- this could let the callee make assumptions
|
||||
// about bits that conceptually were not even passed.
|
||||
if a1.arg_ext != a2.arg_ext {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
let mode_compat = || match (&caller_abi.mode, &callee_abi.mode) {
|
||||
(PassMode::Ignore, PassMode::Ignore) => true, // can still be reached for the return type
|
||||
(PassMode::Direct(a1), PassMode::Direct(a2)) => arg_attr_compat(a1, a2),
|
||||
(PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => {
|
||||
arg_attr_compat(a1, a2) && arg_attr_compat(b1, b2)
|
||||
}
|
||||
(PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1 == c2 && pad1 == pad2,
|
||||
(
|
||||
PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 },
|
||||
PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
|
||||
) => arg_attr_compat(a1, a2) && s1 == s2,
|
||||
(
|
||||
PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 },
|
||||
PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 },
|
||||
) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && s1 == s2,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// Ideally `PassMode` would capture everything there is about argument passing, but that is
|
||||
// not the case: in `FnAbi::llvm_type`, also parts of the layout and type information are
|
||||
// used. So we need to check that *both* sufficiently agree to ensures the arguments are
|
||||
// compatible.
|
||||
// For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
|
||||
// in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
|
||||
// `abi::Primitive` but different `arg_ext`.
|
||||
if self.layout_compat(caller_abi.layout, callee_abi.layout) && mode_compat() {
|
||||
// Something went very wrong if our checks don't even imply that the layout is the same.
|
||||
assert!(
|
||||
caller_abi.layout.size == callee_abi.layout.size
|
||||
&& caller_abi.layout.align.abi == callee_abi.layout.align.abi
|
||||
&& caller_abi.layout.is_sized() == callee_abi.layout.is_sized()
|
||||
);
|
||||
return true;
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
// We do not want to accept things as ABI-compatible that just "happen to be" compatible on the current target,
|
||||
// so we implement a type-based check that reflects the guaranteed rules for ABI compatibility.
|
||||
if self.layout_compat(caller_abi.layout, callee_abi.layout)? {
|
||||
// Ensure that our checks imply actual ABI compatibility for this concrete call.
|
||||
assert!(caller_abi.eq_abi(&callee_abi));
|
||||
return Ok(true);
|
||||
} else {
|
||||
trace!(
|
||||
"check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}",
|
||||
caller_abi,
|
||||
callee_abi
|
||||
);
|
||||
return false;
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,6 +450,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
'tcx: 'x,
|
||||
'tcx: 'y,
|
||||
{
|
||||
assert_eq!(callee_ty, callee_abi.layout.ty);
|
||||
if matches!(callee_abi.mode, PassMode::Ignore) {
|
||||
// This one is skipped. Still must be made live though!
|
||||
if !already_live {
|
||||
@ -425,15 +462,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let Some((caller_arg, caller_abi)) = caller_args.next() else {
|
||||
throw_ub_custom!(fluent::const_eval_not_enough_caller_args);
|
||||
};
|
||||
assert_eq!(caller_arg.layout().layout, caller_abi.layout.layout);
|
||||
// Sadly we cannot assert that `caller_arg.layout().ty` and `caller_abi.layout.ty` are
|
||||
// equal; in closures the types sometimes differ. We just hope that `caller_abi` is the
|
||||
// right type to print to the user.
|
||||
|
||||
// Check compatibility
|
||||
if !self.check_argument_compat(caller_abi, callee_abi) {
|
||||
let callee_ty = format!("{}", callee_ty);
|
||||
let caller_ty = format!("{}", caller_arg.layout().ty);
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_incompatible_types,
|
||||
callee_ty = callee_ty,
|
||||
caller_ty = caller_ty,
|
||||
)
|
||||
if !self.check_argument_compat(caller_abi, callee_abi)? {
|
||||
throw_ub!(AbiMismatchArgument {
|
||||
caller_ty: caller_abi.layout.ty,
|
||||
callee_ty: callee_abi.layout.ty
|
||||
});
|
||||
}
|
||||
// We work with a copy of the argument for now; if this is in-place argument passing, we
|
||||
// will later protect the source it comes from. This means the callee cannot observe if we
|
||||
@ -637,7 +676,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
// taking into account the `spread_arg`. If we could write
|
||||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ZSTs.
|
||||
// not advance `caller_iter` for ignored arguments.
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter();
|
||||
for local in body.args_iter() {
|
||||
// Construct the destination place for this argument. At this point all
|
||||
@ -699,14 +738,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
throw_ub_custom!(fluent::const_eval_too_many_caller_args);
|
||||
}
|
||||
// Don't forget to check the return type!
|
||||
if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret) {
|
||||
let callee_ty = format!("{}", callee_fn_abi.ret.layout.ty);
|
||||
let caller_ty = format!("{}", caller_fn_abi.ret.layout.ty);
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_incompatible_return_types,
|
||||
callee_ty = callee_ty,
|
||||
caller_ty = caller_ty,
|
||||
)
|
||||
if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
|
||||
throw_ub!(AbiMismatchReturn {
|
||||
caller_ty: caller_fn_abi.ret.layout.ty,
|
||||
callee_ty: callee_fn_abi.ret.layout.ty
|
||||
});
|
||||
}
|
||||
// Ensure the return place is aligned and dereferenceable, and protect it for
|
||||
// in-place return value passing.
|
||||
@ -728,7 +764,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
Ok(()) => Ok(()),
|
||||
}
|
||||
}
|
||||
// cannot use the shim here, because that will only result in infinite recursion
|
||||
// `InstanceDef::Virtual` does not have callable MIR. Calls to `Virtual` instances must be
|
||||
// codegen'd / interpreted as virtual calls through the vtable.
|
||||
ty::InstanceDef::Virtual(def_id, idx) => {
|
||||
let mut args = args.to_vec();
|
||||
// We have to implement all "object safe receivers". So we have to go search for a
|
||||
@ -852,18 +889,26 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
}
|
||||
|
||||
// Adjust receiver argument. Layout can be any (thin) ptr.
|
||||
let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty);
|
||||
args[0] = FnArg::Copy(
|
||||
ImmTy::from_immediate(
|
||||
Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
|
||||
self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, dyn_ty))?,
|
||||
self.layout_of(receiver_ty)?,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
trace!("Patched receiver operand to {:#?}", args[0]);
|
||||
// Need to also adjust the type in the ABI. Strangely, the layout there is actually
|
||||
// already fine! Just the type is bogus. This is due to what `force_thin_self_ptr`
|
||||
// does in `fn_abi_new_uncached`; supposedly, codegen relies on having the bogus
|
||||
// type, so we just patch this up locally.
|
||||
let mut caller_fn_abi = caller_fn_abi.clone();
|
||||
caller_fn_abi.args[0].layout.ty = receiver_ty;
|
||||
|
||||
// recurse with concrete function
|
||||
self.eval_fn_call(
|
||||
FnVal::Instance(fn_inst),
|
||||
(caller_abi, caller_fn_abi),
|
||||
(caller_abi, &caller_fn_abi),
|
||||
&args,
|
||||
with_caller_location,
|
||||
destination,
|
||||
|
@ -6,13 +6,7 @@ use rustc_index::IndexVec;
|
||||
use rustc_infer::traits::Reveal;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{
|
||||
traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
|
||||
MirPass, MirPhase, NonDivergingIntrinsic, NullOp, Operand, Place, PlaceElem, PlaceRef,
|
||||
ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
|
||||
Terminator, TerminatorKind, UnOp, UnwindAction, UnwindTerminateReason, VarDebugInfo,
|
||||
VarDebugInfoContents, START_BLOCK,
|
||||
};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_mir_dataflow::impls::MaybeStorageLive;
|
||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||
@ -757,37 +751,37 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
|
||||
let check_place = |this: &mut Self, place: Place<'_>| {
|
||||
if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
|
||||
this.fail(
|
||||
if let Some(box VarDebugInfoFragment { ty, ref projection }) = debuginfo.composite {
|
||||
if ty.is_union() || ty.is_enum() {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
|
||||
format!("invalid type {ty:?} in debuginfo for {:?}", debuginfo.name),
|
||||
);
|
||||
}
|
||||
};
|
||||
if projection.is_empty() {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
format!("invalid empty projection in debuginfo for {:?}", debuginfo.name),
|
||||
);
|
||||
}
|
||||
if projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
format!(
|
||||
"illegal projection {:?} in debuginfo for {:?}",
|
||||
projection, debuginfo.name
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
match debuginfo.value {
|
||||
VarDebugInfoContents::Const(_) => {}
|
||||
VarDebugInfoContents::Place(place) => {
|
||||
check_place(self, place);
|
||||
}
|
||||
VarDebugInfoContents::Composite { ty, ref fragments } => {
|
||||
for f in fragments {
|
||||
check_place(self, f.contents);
|
||||
if ty.is_union() || ty.is_enum() {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
format!("invalid type {ty:?} for composite debuginfo"),
|
||||
);
|
||||
}
|
||||
if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
format!(
|
||||
"illegal projection {:?} in debuginfo for {:?}",
|
||||
f.projection, debuginfo.name
|
||||
),
|
||||
);
|
||||
}
|
||||
if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::fx::{FxHashMap, FxHasher};
|
||||
#[cfg(parallel_compiler)]
|
||||
use crate::sync::{is_dyn_thread_safe, CacheAligned};
|
||||
use crate::sync::{Lock, LockGuard};
|
||||
use crate::sync::{Lock, LockGuard, Mode};
|
||||
#[cfg(parallel_compiler)]
|
||||
use itertools::Either;
|
||||
use std::borrow::Borrow;
|
||||
@ -73,6 +73,56 @@ impl<T> Sharded<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The shard is selected by hashing `val` with `FxHasher`.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn lock_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> LockGuard<'_, T> {
|
||||
match self {
|
||||
Self::Single(single) => {
|
||||
// Syncronization is disabled so use the `lock_assume_no_sync` method optimized
|
||||
// for that case.
|
||||
|
||||
// SAFETY: We know `is_dyn_thread_safe` was false when creating the lock thus
|
||||
// `might_be_dyn_thread_safe` was also false.
|
||||
unsafe { single.lock_assume(Mode::NoSync) }
|
||||
}
|
||||
#[cfg(parallel_compiler)]
|
||||
Self::Shards(..) => self.lock_shard_by_hash(make_hash(_val)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn lock_shard_by_hash(&self, hash: u64) -> LockGuard<'_, T> {
|
||||
self.lock_shard_by_index(get_shard_hash(hash))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn lock_shard_by_index(&self, _i: usize) -> LockGuard<'_, T> {
|
||||
match self {
|
||||
Self::Single(single) => {
|
||||
// Syncronization is disabled so use the `lock_assume_no_sync` method optimized
|
||||
// for that case.
|
||||
|
||||
// SAFETY: We know `is_dyn_thread_safe` was false when creating the lock thus
|
||||
// `might_be_dyn_thread_safe` was also false.
|
||||
unsafe { single.lock_assume(Mode::NoSync) }
|
||||
}
|
||||
#[cfg(parallel_compiler)]
|
||||
Self::Shards(shards) => {
|
||||
// Syncronization is enabled so use the `lock_assume_sync` method optimized
|
||||
// for that case.
|
||||
|
||||
// SAFETY (get_unchecked): The index gets ANDed with the shard mask, ensuring it is
|
||||
// always inbounds.
|
||||
// SAFETY (lock_assume_sync): We know `is_dyn_thread_safe` was true when creating
|
||||
// the lock thus `might_be_dyn_thread_safe` was also true.
|
||||
unsafe { shards.get_unchecked(_i & (SHARDS - 1)).0.lock_assume(Mode::Sync) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lock_shards(&self) -> impl Iterator<Item = LockGuard<'_, T>> {
|
||||
match self {
|
||||
@ -124,7 +174,7 @@ impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
let hash = make_hash(value);
|
||||
let mut shard = self.get_shard_by_hash(hash).lock();
|
||||
let mut shard = self.lock_shard_by_hash(hash);
|
||||
let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, value);
|
||||
|
||||
match entry {
|
||||
@ -144,7 +194,7 @@ impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
let hash = make_hash(&value);
|
||||
let mut shard = self.get_shard_by_hash(hash).lock();
|
||||
let mut shard = self.lock_shard_by_hash(hash);
|
||||
let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, &value);
|
||||
|
||||
match entry {
|
||||
@ -166,7 +216,7 @@ pub trait IntoPointer {
|
||||
impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
|
||||
pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
|
||||
let hash = make_hash(&value);
|
||||
let shard = self.get_shard_by_hash(hash).lock();
|
||||
let shard = self.lock_shard_by_hash(hash);
|
||||
let value = value.into_pointer();
|
||||
shard.raw_entry().from_hash(hash, |entry| entry.into_pointer() == value).is_some()
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ use std::hash::{BuildHasher, Hash};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
mod lock;
|
||||
pub use lock::{Lock, LockGuard};
|
||||
pub use lock::{Lock, LockGuard, Mode};
|
||||
|
||||
mod worker_local;
|
||||
pub use worker_local::{Registry, WorkerLocal};
|
||||
@ -63,6 +63,9 @@ pub use vec::{AppendOnlyIndexVec, AppendOnlyVec};
|
||||
|
||||
mod vec;
|
||||
|
||||
mod freeze;
|
||||
pub use freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
|
||||
|
||||
mod mode {
|
||||
use super::Ordering;
|
||||
use std::sync::atomic::AtomicU8;
|
||||
@ -85,7 +88,6 @@ mod mode {
|
||||
|
||||
// Whether thread safety might be enabled.
|
||||
#[inline]
|
||||
#[cfg(parallel_compiler)]
|
||||
pub fn might_be_dyn_thread_safe() -> bool {
|
||||
DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) != DYN_NOT_THREAD_SAFE
|
||||
}
|
||||
|
200
compiler/rustc_data_structures/src/sync/freeze.rs
Normal file
200
compiler/rustc_data_structures/src/sync/freeze.rs
Normal file
@ -0,0 +1,200 @@
|
||||
use crate::sync::{AtomicBool, ReadGuard, RwLock, WriteGuard};
|
||||
#[cfg(parallel_compiler)]
|
||||
use crate::sync::{DynSend, DynSync};
|
||||
use std::{
|
||||
cell::UnsafeCell,
|
||||
intrinsics::likely,
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
sync::atomic::Ordering,
|
||||
};
|
||||
|
||||
/// A type which allows mutation using a lock until
|
||||
/// the value is frozen and can be accessed lock-free.
|
||||
///
|
||||
/// Unlike `RwLock`, it can be used to prevent mutation past a point.
|
||||
#[derive(Default)]
|
||||
pub struct FreezeLock<T> {
|
||||
data: UnsafeCell<T>,
|
||||
frozen: AtomicBool,
|
||||
|
||||
/// This lock protects writes to the `data` and `frozen` fields.
|
||||
lock: RwLock<()>,
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe impl<T: DynSync + DynSend> DynSync for FreezeLock<T> {}
|
||||
|
||||
impl<T> FreezeLock<T> {
|
||||
#[inline]
|
||||
pub fn new(value: T) -> Self {
|
||||
Self::with(value, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn frozen(value: T) -> Self {
|
||||
Self::with(value, true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with(value: T, frozen: bool) -> Self {
|
||||
Self {
|
||||
data: UnsafeCell::new(value),
|
||||
frozen: AtomicBool::new(frozen),
|
||||
lock: RwLock::new(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones the inner value along with the frozen state.
|
||||
#[inline]
|
||||
pub fn clone(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
let lock = self.read();
|
||||
Self::with(lock.clone(), self.is_frozen())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_frozen(&self) -> bool {
|
||||
self.frozen.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Get the inner value if frozen.
|
||||
#[inline]
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
if likely(self.frozen.load(Ordering::Acquire)) {
|
||||
// SAFETY: This is frozen so the data cannot be modified.
|
||||
unsafe { Some(&*self.data.get()) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read(&self) -> FreezeReadGuard<'_, T> {
|
||||
FreezeReadGuard {
|
||||
_lock_guard: if self.frozen.load(Ordering::Acquire) {
|
||||
None
|
||||
} else {
|
||||
Some(self.lock.read())
|
||||
},
|
||||
data: unsafe { NonNull::new_unchecked(self.data.get()) },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn borrow(&self) -> FreezeReadGuard<'_, T> {
|
||||
self.read()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn write(&self) -> FreezeWriteGuard<'_, T> {
|
||||
self.try_write().expect("still mutable")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_write(&self) -> Option<FreezeWriteGuard<'_, T>> {
|
||||
let _lock_guard = self.lock.write();
|
||||
// Use relaxed ordering since we're in the write lock.
|
||||
if self.frozen.load(Ordering::Relaxed) {
|
||||
None
|
||||
} else {
|
||||
Some(FreezeWriteGuard {
|
||||
_lock_guard,
|
||||
data: unsafe { NonNull::new_unchecked(self.data.get()) },
|
||||
frozen: &self.frozen,
|
||||
marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn freeze(&self) -> &T {
|
||||
if !self.frozen.load(Ordering::Acquire) {
|
||||
// Get the lock to ensure no concurrent writes and that we release the latest write.
|
||||
let _lock = self.lock.write();
|
||||
self.frozen.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
// SAFETY: This is frozen so the data cannot be modified and shared access is sound.
|
||||
unsafe { &*self.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard holding shared access to a `FreezeLock` which is in a locked state or frozen.
|
||||
#[must_use = "if unused the FreezeLock may immediately unlock"]
|
||||
pub struct FreezeReadGuard<'a, T: ?Sized> {
|
||||
_lock_guard: Option<ReadGuard<'a, ()>>,
|
||||
data: NonNull<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + 'a> Deref for FreezeReadGuard<'a, T> {
|
||||
type Target = T;
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: If the lock is not frozen, `_lock_guard` holds the lock to the `UnsafeCell` so
|
||||
// this has shared access until the `FreezeReadGuard` is dropped. If the lock is frozen,
|
||||
// the data cannot be modified and shared access is sound.
|
||||
unsafe { &*self.data.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> FreezeReadGuard<'a, T> {
|
||||
#[inline]
|
||||
pub fn map<U: ?Sized>(this: Self, f: impl FnOnce(&T) -> &U) -> FreezeReadGuard<'a, U> {
|
||||
FreezeReadGuard { data: NonNull::from(f(&*this)), _lock_guard: this._lock_guard }
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard holding mutable access to a `FreezeLock` which is in a locked state or frozen.
|
||||
#[must_use = "if unused the FreezeLock may immediately unlock"]
|
||||
pub struct FreezeWriteGuard<'a, T: ?Sized> {
|
||||
_lock_guard: WriteGuard<'a, ()>,
|
||||
frozen: &'a AtomicBool,
|
||||
data: NonNull<T>,
|
||||
marker: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<'a, T> FreezeWriteGuard<'a, T> {
|
||||
pub fn freeze(self) -> &'a T {
|
||||
self.frozen.store(true, Ordering::Release);
|
||||
|
||||
// SAFETY: This is frozen so the data cannot be modified and shared access is sound.
|
||||
unsafe { &*self.data.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> FreezeWriteGuard<'a, T> {
|
||||
#[inline]
|
||||
pub fn map<U: ?Sized>(
|
||||
mut this: Self,
|
||||
f: impl FnOnce(&mut T) -> &mut U,
|
||||
) -> FreezeWriteGuard<'a, U> {
|
||||
FreezeWriteGuard {
|
||||
data: NonNull::from(f(&mut *this)),
|
||||
_lock_guard: this._lock_guard,
|
||||
frozen: this.frozen,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + 'a> Deref for FreezeWriteGuard<'a, T> {
|
||||
type Target = T;
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has shared access.
|
||||
unsafe { &*self.data.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + 'a> DerefMut for FreezeWriteGuard<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
// SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has mutable access.
|
||||
unsafe { &mut *self.data.as_ptr() }
|
||||
}
|
||||
}
|
@ -3,224 +3,229 @@
|
||||
//!
|
||||
//! When `cfg(parallel_compiler)` is not set, the lock is instead a wrapper around `RefCell`.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
pub use maybe_sync::*;
|
||||
#[cfg(not(parallel_compiler))]
|
||||
use std::cell::RefCell;
|
||||
#[cfg(parallel_compiler)]
|
||||
use {
|
||||
crate::cold_path,
|
||||
crate::sync::DynSend,
|
||||
crate::sync::DynSync,
|
||||
parking_lot::lock_api::RawMutex,
|
||||
std::cell::Cell,
|
||||
std::cell::UnsafeCell,
|
||||
std::fmt,
|
||||
std::intrinsics::{likely, unlikely},
|
||||
std::marker::PhantomData,
|
||||
std::mem::ManuallyDrop,
|
||||
std::ops::{Deref, DerefMut},
|
||||
};
|
||||
pub use no_sync::*;
|
||||
|
||||
#[cfg(not(parallel_compiler))]
|
||||
pub use std::cell::RefMut as LockGuard;
|
||||
|
||||
#[cfg(not(parallel_compiler))]
|
||||
#[derive(Debug)]
|
||||
pub struct Lock<T>(RefCell<T>);
|
||||
|
||||
#[cfg(not(parallel_compiler))]
|
||||
impl<T> Lock<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(inner: T) -> Self {
|
||||
Lock(RefCell::new(inner))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0.into_inner()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.0.get_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
|
||||
self.0.try_borrow_mut().ok()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub fn lock(&self) -> LockGuard<'_, T> {
|
||||
self.0.borrow_mut()
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Mode {
|
||||
NoSync,
|
||||
Sync,
|
||||
}
|
||||
|
||||
/// A guard holding mutable access to a `Lock` which is in a locked state.
|
||||
#[cfg(parallel_compiler)]
|
||||
#[must_use = "if unused the Lock will immediately unlock"]
|
||||
pub struct LockGuard<'a, T> {
|
||||
lock: &'a Lock<T>,
|
||||
marker: PhantomData<&'a mut T>,
|
||||
}
|
||||
mod maybe_sync {
|
||||
use super::Mode;
|
||||
use crate::sync::mode;
|
||||
#[cfg(parallel_compiler)]
|
||||
use crate::sync::{DynSend, DynSync};
|
||||
use parking_lot::lock_api::RawMutex as _;
|
||||
use parking_lot::RawMutex;
|
||||
use std::cell::Cell;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::intrinsics::unlikely;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
impl<'a, T: 'a> Deref for LockGuard<'a, T> {
|
||||
type Target = T;
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: We have shared access to the mutable access owned by this type,
|
||||
// so we can give out a shared reference.
|
||||
unsafe { &*self.lock.data.get() }
|
||||
/// A guard holding mutable access to a `Lock` which is in a locked state.
|
||||
#[must_use = "if unused the Lock will immediately unlock"]
|
||||
pub struct LockGuard<'a, T> {
|
||||
lock: &'a Lock<T>,
|
||||
marker: PhantomData<&'a mut T>,
|
||||
|
||||
/// The syncronization mode of the lock. This is explicitly passed to let LLVM relate it
|
||||
/// to the original lock operation.
|
||||
mode: Mode,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
impl<'a, T: 'a> DerefMut for LockGuard<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
// SAFETY: We have mutable access to the data so we can give out a mutable reference.
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
impl<'a, T: 'a> Drop for LockGuard<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: We know that the lock is in a locked
|
||||
// state because it is a invariant of this type.
|
||||
unsafe { self.lock.raw.unlock() };
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
union LockRawUnion {
|
||||
/// Indicates if the cell is locked. Only used if `LockRaw.sync` is false.
|
||||
cell: ManuallyDrop<Cell<bool>>,
|
||||
|
||||
/// A lock implementation that's only used if `LockRaw.sync` is true.
|
||||
lock: ManuallyDrop<parking_lot::RawMutex>,
|
||||
}
|
||||
|
||||
/// A raw lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
|
||||
/// It contains no associated data and is used in the implementation of `Lock` which does have such data.
|
||||
///
|
||||
/// A manual implementation of a tagged union is used with the `sync` field and the `LockRawUnion` instead
|
||||
/// of using enums as it results in better code generation.
|
||||
#[cfg(parallel_compiler)]
|
||||
struct LockRaw {
|
||||
/// Indicates if synchronization is used via `opt.lock` if true,
|
||||
/// or if a non-thread safe cell is used via `opt.cell`. This is set on initialization and never changed.
|
||||
sync: bool,
|
||||
opt: LockRawUnion,
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
impl LockRaw {
|
||||
fn new() -> Self {
|
||||
if unlikely(super::mode::might_be_dyn_thread_safe()) {
|
||||
// Create the lock with synchronization enabled using the `RawMutex` type.
|
||||
LockRaw {
|
||||
sync: true,
|
||||
opt: LockRawUnion { lock: ManuallyDrop::new(parking_lot::RawMutex::INIT) },
|
||||
}
|
||||
} else {
|
||||
// Create the lock with synchronization disabled.
|
||||
LockRaw { sync: false, opt: LockRawUnion { cell: ManuallyDrop::new(Cell::new(false)) } }
|
||||
impl<'a, T: 'a> Deref for LockGuard<'a, T> {
|
||||
type Target = T;
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: We have shared access to the mutable access owned by this type,
|
||||
// so we can give out a shared reference.
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock(&self) -> bool {
|
||||
// SAFETY: This is safe since the union fields are used in accordance with `self.sync`.
|
||||
unsafe {
|
||||
if likely(!self.sync) {
|
||||
if self.opt.cell.get() {
|
||||
false
|
||||
} else {
|
||||
self.opt.cell.set(true);
|
||||
true
|
||||
impl<'a, T: 'a> DerefMut for LockGuard<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
// SAFETY: We have mutable access to the data so we can give out a mutable reference.
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Drop for LockGuard<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// SAFETY (union access): We get `self.mode` from the lock operation so it is consistent
|
||||
// with the `lock.mode` state. This means we access the right union fields.
|
||||
match self.mode {
|
||||
Mode::NoSync => {
|
||||
let cell = unsafe { &self.lock.mode_union.no_sync };
|
||||
debug_assert_eq!(cell.get(), true);
|
||||
cell.set(false);
|
||||
}
|
||||
} else {
|
||||
self.opt.lock.try_lock()
|
||||
// SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
|
||||
Mode::Sync => unsafe { self.lock.mode_union.sync.unlock() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn lock(&self) {
|
||||
if super::ERROR_CHECKING {
|
||||
// We're in the debugging mode, so assert that the lock is not held so we
|
||||
// get a panic instead of waiting for the lock.
|
||||
assert_eq!(self.try_lock(), true, "lock must not be hold");
|
||||
} else {
|
||||
// SAFETY: This is safe since the union fields are used in accordance with `self.sync`.
|
||||
unsafe {
|
||||
if likely(!self.sync) {
|
||||
if unlikely(self.opt.cell.replace(true)) {
|
||||
cold_path(|| panic!("lock was already held"))
|
||||
union ModeUnion {
|
||||
/// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`.
|
||||
no_sync: ManuallyDrop<Cell<bool>>,
|
||||
|
||||
/// A lock implementation that's only used if `Lock.mode` is `Sync`.
|
||||
sync: ManuallyDrop<RawMutex>,
|
||||
}
|
||||
|
||||
/// The value representing a locked state for the `Cell`.
|
||||
const LOCKED: bool = true;
|
||||
|
||||
/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
|
||||
/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
|
||||
pub struct Lock<T> {
|
||||
/// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a
|
||||
/// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`.
|
||||
/// This is set on initialization and never changed.
|
||||
mode: Mode,
|
||||
|
||||
mode_union: ModeUnion,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
impl<T> Lock<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(inner: T) -> Self {
|
||||
let (mode, mode_union) = if unlikely(mode::might_be_dyn_thread_safe()) {
|
||||
// Create the lock with synchronization enabled using the `RawMutex` type.
|
||||
(Mode::Sync, ModeUnion { sync: ManuallyDrop::new(RawMutex::INIT) })
|
||||
} else {
|
||||
// Create the lock with synchronization disabled.
|
||||
(Mode::NoSync, ModeUnion { no_sync: ManuallyDrop::new(Cell::new(!LOCKED)) })
|
||||
};
|
||||
Lock { mode, mode_union, data: UnsafeCell::new(inner) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.data.into_inner()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.data.get_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
|
||||
let mode = self.mode;
|
||||
// SAFETY: This is safe since the union fields are used in accordance with `self.mode`.
|
||||
match mode {
|
||||
Mode::NoSync => {
|
||||
let cell = unsafe { &self.mode_union.no_sync };
|
||||
let was_unlocked = cell.get() != LOCKED;
|
||||
if was_unlocked {
|
||||
cell.set(LOCKED);
|
||||
}
|
||||
} else {
|
||||
self.opt.lock.lock();
|
||||
was_unlocked
|
||||
}
|
||||
Mode::Sync => unsafe { self.mode_union.sync.try_lock() },
|
||||
}
|
||||
.then(|| LockGuard { lock: self, marker: PhantomData, mode })
|
||||
}
|
||||
|
||||
/// This acquires the lock assuming syncronization is in a specific mode.
|
||||
///
|
||||
/// Safety
|
||||
/// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was
|
||||
/// true on lock creation.
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub unsafe fn lock_assume(&self, mode: Mode) -> LockGuard<'_, T> {
|
||||
#[inline(never)]
|
||||
#[track_caller]
|
||||
#[cold]
|
||||
fn lock_held() -> ! {
|
||||
panic!("lock was already held")
|
||||
}
|
||||
|
||||
// SAFETY: This is safe since the union fields are used in accordance with `mode`
|
||||
// which also must match `self.mode` due to the safety precondition.
|
||||
unsafe {
|
||||
match mode {
|
||||
Mode::NoSync => {
|
||||
if unlikely(self.mode_union.no_sync.replace(LOCKED) == LOCKED) {
|
||||
lock_held()
|
||||
}
|
||||
}
|
||||
Mode::Sync => self.mode_union.sync.lock(),
|
||||
}
|
||||
}
|
||||
LockGuard { lock: self, marker: PhantomData, mode }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub fn lock(&self) -> LockGuard<'_, T> {
|
||||
unsafe { self.lock_assume(self.mode) }
|
||||
}
|
||||
}
|
||||
|
||||
/// This unlocks the lock.
|
||||
///
|
||||
/// Safety
|
||||
/// This method may only be called if the lock is currently held.
|
||||
#[inline(always)]
|
||||
unsafe fn unlock(&self) {
|
||||
// SAFETY: The union use is safe since the union fields are used in accordance with
|
||||
// `self.sync` and the `unlock` method precondition is upheld by the caller.
|
||||
unsafe {
|
||||
if likely(!self.sync) {
|
||||
debug_assert_eq!(self.opt.cell.get(), true);
|
||||
self.opt.cell.set(false);
|
||||
} else {
|
||||
self.opt.lock.unlock();
|
||||
}
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe impl<T: DynSend> DynSend for Lock<T> {}
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe impl<T: DynSend> DynSync for Lock<T> {}
|
||||
}
|
||||
|
||||
mod no_sync {
|
||||
use super::Mode;
|
||||
use std::cell::RefCell;
|
||||
|
||||
pub use std::cell::RefMut as LockGuard;
|
||||
|
||||
pub struct Lock<T>(RefCell<T>);
|
||||
|
||||
impl<T> Lock<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(inner: T) -> Self {
|
||||
Lock(RefCell::new(inner))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
|
||||
/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
|
||||
#[cfg(parallel_compiler)]
|
||||
pub struct Lock<T> {
|
||||
raw: LockRaw,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0.into_inner()
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
impl<T> Lock<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(inner: T) -> Self {
|
||||
Lock { raw: LockRaw::new(), data: UnsafeCell::new(inner) }
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.0.get_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.data.into_inner()
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
|
||||
self.0.try_borrow_mut().ok()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.data.get_mut()
|
||||
}
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
// This is unsafe to match the API for the `parallel_compiler` case.
|
||||
pub unsafe fn lock_assume(&self, _mode: Mode) -> LockGuard<'_, T> {
|
||||
self.0.borrow_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
|
||||
if self.raw.try_lock() { Some(LockGuard { lock: self, marker: PhantomData }) } else { None }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn lock(&self) -> LockGuard<'_, T> {
|
||||
self.raw.lock();
|
||||
LockGuard { lock: self, marker: PhantomData }
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub fn lock(&self) -> LockGuard<'_, T> {
|
||||
self.0.borrow_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,12 +249,13 @@ impl<T> Lock<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe impl<T: DynSend> DynSend for Lock<T> {}
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe impl<T: DynSend> DynSync for Lock<T> {}
|
||||
impl<T: Default> Default for Lock<T> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Lock::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
impl<T: fmt::Debug> fmt::Debug for Lock<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.try_lock() {
|
||||
@ -267,10 +273,3 @@ impl<T: fmt::Debug> fmt::Debug for Lock<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Lock<T> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Lock::new(T::default())
|
||||
}
|
||||
}
|
||||
|
@ -162,9 +162,10 @@ pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T
|
||||
pub trait Callbacks {
|
||||
/// Called before creating the compiler instance
|
||||
fn config(&mut self, _config: &mut interface::Config) {}
|
||||
/// Called after parsing. Return value instructs the compiler whether to
|
||||
/// Called after parsing the crate root. Submodules are not yet parsed when
|
||||
/// this callback is called. Return value instructs the compiler whether to
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_parsing<'tcx>(
|
||||
fn after_crate_root_parsing<'tcx>(
|
||||
&mut self,
|
||||
_compiler: &interface::Compiler,
|
||||
_queries: &'tcx Queries<'tcx>,
|
||||
@ -184,7 +185,6 @@ pub trait Callbacks {
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
_handler: &EarlyErrorHandler,
|
||||
_compiler: &interface::Compiler,
|
||||
_queries: &'tcx Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
@ -313,6 +313,7 @@ fn run_compiler(
|
||||
override_queries: None,
|
||||
make_codegen_backend,
|
||||
registry: diagnostics_registry(),
|
||||
expanded_args: args,
|
||||
};
|
||||
|
||||
match make_input(&early_error_handler, &matches.free) {
|
||||
@ -406,7 +407,7 @@ fn run_compiler(
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
|
||||
if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop {
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
@ -444,7 +445,7 @@ fn run_compiler(
|
||||
|
||||
queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?;
|
||||
|
||||
if callbacks.after_analysis(&handler, compiler, queries) == Compilation::Stop {
|
||||
if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Inner items do not inherit type or const parameters from the functions
|
||||
Inner items do not inherit the generic parameters from the items
|
||||
they are embedded in.
|
||||
|
||||
Erroneous code example:
|
||||
@ -32,8 +32,8 @@ fn foo<T>(x: T) {
|
||||
}
|
||||
```
|
||||
|
||||
Items inside functions are basically just like top-level items, except
|
||||
that they can only be used from the function they are in.
|
||||
Items nested inside other items are basically just like top-level items, except
|
||||
that they can only be used from the item they are in.
|
||||
|
||||
There are a couple of solutions for this.
|
||||
|
||||
|
@ -169,7 +169,7 @@ impl AnnotateSnippetEmitterWriter {
|
||||
.map(|line| {
|
||||
// Ensure the source file is present before we try
|
||||
// to load a string from it.
|
||||
source_map.ensure_source_file_source_present(file.clone());
|
||||
source_map.ensure_source_file_source_present(&file);
|
||||
(
|
||||
format!("{}", source_map.filename_for_diagnostics(&file.name)),
|
||||
source_string(file.clone(), &line),
|
||||
|
@ -1193,7 +1193,7 @@ impl EmitterWriter {
|
||||
let will_be_emitted = |span: Span| {
|
||||
!span.is_dummy() && {
|
||||
let file = sm.lookup_source_file(span.hi());
|
||||
sm.ensure_source_file_source_present(file)
|
||||
sm.ensure_source_file_source_present(&file)
|
||||
}
|
||||
};
|
||||
|
||||
@ -1388,7 +1388,7 @@ impl EmitterWriter {
|
||||
// Print out the annotate source lines that correspond with the error
|
||||
for annotated_file in annotated_files {
|
||||
// we can't annotate anything if the source is unavailable.
|
||||
if !sm.ensure_source_file_source_present(annotated_file.file.clone()) {
|
||||
if !sm.ensure_source_file_source_present(&annotated_file.file) {
|
||||
if !self.short_message {
|
||||
// We'll just print an unannotated message.
|
||||
for (annotation_id, line) in annotated_file.lines.iter().enumerate() {
|
||||
|
@ -558,7 +558,7 @@ impl DiagnosticSpanLine {
|
||||
.span_to_lines(span)
|
||||
.map(|lines| {
|
||||
// We can't get any lines if the source is unavailable.
|
||||
if !je.sm.ensure_source_file_source_present(lines.file.clone()) {
|
||||
if !je.sm.ensure_source_file_source_present(&lines.file) {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ use rustc_fluent_macro::fluent_messages;
|
||||
pub use rustc_lint_defs::{pluralize, Applicability};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
pub use rustc_span::ErrorGuaranteed;
|
||||
use rustc_span::{Loc, Span};
|
||||
use rustc_span::{Loc, Span, DUMMY_SP};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::error::Report;
|
||||
@ -273,7 +273,7 @@ impl CodeSuggestion {
|
||||
assert!(!lines.lines.is_empty() || bounding_span.is_dummy());
|
||||
|
||||
// We can't splice anything if the source is unavailable.
|
||||
if !sm.ensure_source_file_source_present(lines.file.clone()) {
|
||||
if !sm.ensure_source_file_source_present(&lines.file) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -1754,7 +1754,7 @@ impl DelayedDiagnostic {
|
||||
BacktraceStatus::Captured => {
|
||||
let inner = &self.inner;
|
||||
self.inner.subdiagnostic(DelayedAtWithNewline {
|
||||
span: inner.span.primary_span().unwrap(),
|
||||
span: inner.span.primary_span().unwrap_or(DUMMY_SP),
|
||||
emitted_at: inner.emitted_at.clone(),
|
||||
note: self.note,
|
||||
});
|
||||
@ -1764,7 +1764,7 @@ impl DelayedDiagnostic {
|
||||
_ => {
|
||||
let inner = &self.inner;
|
||||
self.inner.subdiagnostic(DelayedAtWithoutNewline {
|
||||
span: inner.span.primary_span().unwrap(),
|
||||
span: inner.span.primary_span().unwrap_or(DUMMY_SP),
|
||||
emitted_at: inner.emitted_at.clone(),
|
||||
note: self.note,
|
||||
});
|
||||
|
@ -587,7 +587,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
.resolver
|
||||
.visit_ast_fragment_with_placeholders(self.cx.current_expansion.id, &fragment);
|
||||
|
||||
if self.cx.sess.opts.incremental_relative_spans() {
|
||||
if self.cx.sess.opts.incremental.is_some() {
|
||||
for (invoc, _) in invocations.iter_mut() {
|
||||
let expn_id = invoc.expansion_data.id;
|
||||
let parent_def = self.cx.resolver.invocation_parent(expn_id);
|
||||
|
@ -222,6 +222,12 @@ hir_analysis_return_type_notation_on_non_rpitit =
|
||||
.note = function returns `{$ty}`, which is not compatible with associated type return bounds
|
||||
.label = this function must be `async` or return `impl Trait`
|
||||
|
||||
hir_analysis_rpitit_refined = impl trait in impl method signature does not match trait method signature
|
||||
.suggestion = replace the return type so that it matches the trait
|
||||
.label = return type from trait method defined here
|
||||
.unmatched_bound_label = this bound is stronger than that defined on the trait
|
||||
.note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
|
||||
|
||||
hir_analysis_self_in_impl_self =
|
||||
`Self` is not valid in the self type of an impl block
|
||||
.note = replace `Self` with a different type
|
||||
|
@ -110,16 +110,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
{
|
||||
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
|
||||
// valid span, so we point at the whole path segment instead.
|
||||
let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span };
|
||||
let is_dummy = assoc_name.span == DUMMY_SP;
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx().sess,
|
||||
span,
|
||||
if is_dummy { span } else { assoc_name.span },
|
||||
E0220,
|
||||
"associated type `{}` not found for `{}`",
|
||||
assoc_name,
|
||||
ty_param_name
|
||||
);
|
||||
|
||||
if is_dummy {
|
||||
err.span_label(span, format!("associated type `{assoc_name}` not found"));
|
||||
return err.emit();
|
||||
}
|
||||
|
||||
let all_candidate_names: Vec<_> = all_candidates()
|
||||
.flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
|
||||
.filter_map(|item| {
|
||||
@ -131,10 +137,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let (Some(suggested_name), true) = (
|
||||
find_best_match_for_name(&all_candidate_names, assoc_name.name, None),
|
||||
assoc_name.span != DUMMY_SP,
|
||||
) {
|
||||
if let Some(suggested_name) =
|
||||
find_best_match_for_name(&all_candidate_names, assoc_name.name, None)
|
||||
{
|
||||
err.span_suggestion(
|
||||
assoc_name.span,
|
||||
"there is an associated type with a similar name",
|
||||
@ -172,10 +177,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let (Some(suggested_name), true) = (
|
||||
find_best_match_for_name(&wider_candidate_names, assoc_name.name, None),
|
||||
assoc_name.span != DUMMY_SP,
|
||||
) {
|
||||
if let Some(suggested_name) =
|
||||
find_best_match_for_name(&wider_candidate_names, assoc_name.name, None)
|
||||
{
|
||||
if let [best_trait] = visible_traits
|
||||
.iter()
|
||||
.filter(|trait_def_id| {
|
||||
@ -197,7 +201,28 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}
|
||||
}
|
||||
|
||||
err.span_label(span, format!("associated type `{assoc_name}` not found"));
|
||||
// If we still couldn't find any associated type, and only one associated type exists,
|
||||
// suggests using it.
|
||||
|
||||
if all_candidate_names.len() == 1 {
|
||||
// this should still compile, except on `#![feature(associated_type_defaults)]`
|
||||
// where it could suggests `type A = Self::A`, thus recursing infinitely
|
||||
let applicability = if self.tcx().features().associated_type_defaults {
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
err.span_suggestion(
|
||||
assoc_name.span,
|
||||
format!("`{ty_param_name}` has the following associated type"),
|
||||
all_candidate_names.first().unwrap().to_string(),
|
||||
applicability,
|
||||
);
|
||||
} else {
|
||||
err.span_label(assoc_name.span, format!("associated type `{assoc_name}` not found"));
|
||||
}
|
||||
|
||||
err.emit()
|
||||
}
|
||||
|
||||
|
@ -523,7 +523,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
Ty::new_misc_error(tcx).into()
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Const { has_default } => {
|
||||
GenericParamDefKind::Const { has_default, .. } => {
|
||||
let ty = tcx
|
||||
.at(self.span)
|
||||
.type_of(param.def_id)
|
||||
|
@ -28,6 +28,8 @@ use rustc_trait_selection::traits::{
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
|
||||
mod refine;
|
||||
|
||||
/// Checks that a method from an impl conforms to the signature of
|
||||
/// the same method as declared in the trait.
|
||||
///
|
||||
@ -53,6 +55,12 @@ pub(super) fn compare_impl_method<'tcx>(
|
||||
impl_trait_ref,
|
||||
CheckImpliedWfMode::Check,
|
||||
)?;
|
||||
refine::check_refining_return_position_impl_trait_in_trait(
|
||||
tcx,
|
||||
impl_m,
|
||||
trait_m,
|
||||
impl_trait_ref,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,311 @@
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::{outlives::env::OutlivesEnvironment, TyCtxtInferExt};
|
||||
use rustc_lint_defs::builtin::REFINING_IMPL_TRAIT;
|
||||
use rustc_middle::traits::{ObligationCause, Reveal};
|
||||
use rustc_middle::ty::{
|
||||
self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
||||
};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_trait_selection::traits::{
|
||||
elaborate, normalize_param_env_or_error, outlives_bounds::InferCtxtExt, ObligationCtxt,
|
||||
};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
/// Check that an implementation does not refine an RPITIT from a trait method signature.
|
||||
pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_m: ty::AssocItem,
|
||||
trait_m: ty::AssocItem,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||
) {
|
||||
if !tcx.impl_method_has_trait_impl_trait_tys(impl_m.def_id) {
|
||||
return;
|
||||
}
|
||||
// crate-private traits don't have any library guarantees, there's no need to do this check.
|
||||
if !tcx.visibility(trait_m.container_id(tcx)).is_public() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a type in the trait ref is private, then there's also no reason to to do this check.
|
||||
let impl_def_id = impl_m.container_id(tcx);
|
||||
for arg in impl_trait_ref.args {
|
||||
if let Some(ty) = arg.as_type()
|
||||
&& let Some(self_visibility) = type_visibility(tcx, ty)
|
||||
&& !self_visibility.is_public()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let impl_m_args = ty::GenericArgs::identity_for_item(tcx, impl_m.def_id);
|
||||
let trait_m_to_impl_m_args = impl_m_args.rebase_onto(tcx, impl_def_id, impl_trait_ref.args);
|
||||
let bound_trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_to_impl_m_args);
|
||||
let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, bound_trait_m_sig);
|
||||
// replace the self type of the trait ref with `Self` so that diagnostics render better.
|
||||
let trait_m_sig_with_self_for_diag = tcx.liberate_late_bound_regions(
|
||||
impl_m.def_id,
|
||||
tcx.fn_sig(trait_m.def_id).instantiate(
|
||||
tcx,
|
||||
tcx.mk_args_from_iter(
|
||||
[tcx.types.self_param.into()]
|
||||
.into_iter()
|
||||
.chain(trait_m_to_impl_m_args.iter().skip(1)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
let Ok(hidden_tys) = tcx.collect_return_position_impl_trait_in_trait_tys(impl_m.def_id) else {
|
||||
// Error already emitted, no need to delay another.
|
||||
return;
|
||||
};
|
||||
|
||||
let mut collector = ImplTraitInTraitCollector { tcx, types: FxIndexSet::default() };
|
||||
trait_m_sig.visit_with(&mut collector);
|
||||
|
||||
// Bound that we find on RPITITs in the trait signature.
|
||||
let mut trait_bounds = vec![];
|
||||
// Bounds that we find on the RPITITs in the impl signature.
|
||||
let mut impl_bounds = vec![];
|
||||
|
||||
for trait_projection in collector.types.into_iter().rev() {
|
||||
let impl_opaque_args = trait_projection.args.rebase_onto(tcx, trait_m.def_id, impl_m_args);
|
||||
let hidden_ty = hidden_tys[&trait_projection.def_id].instantiate(tcx, impl_opaque_args);
|
||||
|
||||
// If the hidden type is not an opaque, then we have "refined" the trait signature.
|
||||
let ty::Alias(ty::Opaque, impl_opaque) = *hidden_ty.kind() else {
|
||||
report_mismatched_rpitit_signature(
|
||||
tcx,
|
||||
trait_m_sig_with_self_for_diag,
|
||||
trait_m.def_id,
|
||||
impl_m.def_id,
|
||||
None,
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
// This opaque also needs to be from the impl method -- otherwise,
|
||||
// it's a refinement to a TAIT.
|
||||
if !tcx.hir().get_if_local(impl_opaque.def_id).map_or(false, |node| {
|
||||
matches!(
|
||||
node.expect_item().expect_opaque_ty().origin,
|
||||
hir::OpaqueTyOrigin::AsyncFn(def_id) | hir::OpaqueTyOrigin::FnReturn(def_id)
|
||||
if def_id == impl_m.def_id.expect_local()
|
||||
)
|
||||
}) {
|
||||
report_mismatched_rpitit_signature(
|
||||
tcx,
|
||||
trait_m_sig_with_self_for_diag,
|
||||
trait_m.def_id,
|
||||
impl_m.def_id,
|
||||
None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
trait_bounds.extend(
|
||||
tcx.item_bounds(trait_projection.def_id).iter_instantiated(tcx, trait_projection.args),
|
||||
);
|
||||
impl_bounds.extend(elaborate(
|
||||
tcx,
|
||||
tcx.explicit_item_bounds(impl_opaque.def_id)
|
||||
.iter_instantiated_copied(tcx, impl_opaque.args),
|
||||
));
|
||||
}
|
||||
|
||||
let hybrid_preds = tcx
|
||||
.predicates_of(impl_def_id)
|
||||
.instantiate_identity(tcx)
|
||||
.into_iter()
|
||||
.chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_m_to_impl_m_args))
|
||||
.map(|(clause, _)| clause);
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds), Reveal::UserFacing);
|
||||
let param_env = normalize_param_env_or_error(tcx, param_env, ObligationCause::dummy());
|
||||
|
||||
let ref infcx = tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new(infcx);
|
||||
|
||||
// Normalize the bounds. This has two purposes:
|
||||
//
|
||||
// 1. Project the RPITIT projections from the trait to the opaques on the impl,
|
||||
// which means that they don't need to be mapped manually.
|
||||
//
|
||||
// 2. Project any other projections that show up in the bound. That makes sure that
|
||||
// we don't consider `tests/ui/async-await/in-trait/async-associated-types.rs`
|
||||
// to be refining.
|
||||
let (trait_bounds, impl_bounds) =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, (trait_bounds, impl_bounds));
|
||||
|
||||
// Since we've normalized things, we need to resolve regions, since we'll
|
||||
// possibly have introduced region vars during projection. We don't expect
|
||||
// this resolution to have incurred any region errors -- but if we do, then
|
||||
// just delay a bug.
|
||||
let mut implied_wf_types = FxIndexSet::default();
|
||||
implied_wf_types.extend(trait_m_sig.inputs_and_output);
|
||||
implied_wf_types.extend(ocx.normalize(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
trait_m_sig.inputs_and_output,
|
||||
));
|
||||
if !ocx.select_all_or_error().is_empty() {
|
||||
tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
"encountered errors when checking RPITIT refinement (selection)",
|
||||
);
|
||||
return;
|
||||
}
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types),
|
||||
);
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
if !errors.is_empty() {
|
||||
tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
"encountered errors when checking RPITIT refinement (regions)",
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Resolve any lifetime variables that may have been introduced during normalization.
|
||||
let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else {
|
||||
tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
"encountered errors when checking RPITIT refinement (resolution)",
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
// For quicker lookup, use an `IndexSet`
|
||||
// (we don't use one earlier because it's not foldable..)
|
||||
let trait_bounds = FxIndexSet::from_iter(trait_bounds);
|
||||
|
||||
// Find any clauses that are present in the impl's RPITITs that are not
|
||||
// present in the trait's RPITITs. This will trigger on trivial predicates,
|
||||
// too, since we *do not* use the trait solver to prove that the RPITIT's
|
||||
// bounds are not stronger -- we're doing a simple, syntactic compatibility
|
||||
// check between bounds. This is strictly forwards compatible, though.
|
||||
for (clause, span) in impl_bounds {
|
||||
if !trait_bounds.contains(&clause) {
|
||||
report_mismatched_rpitit_signature(
|
||||
tcx,
|
||||
trait_m_sig_with_self_for_diag,
|
||||
trait_m.def_id,
|
||||
impl_m.def_id,
|
||||
Some(span),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ImplTraitInTraitCollector<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
types: FxIndexSet<ty::AliasTy<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'tcx> {
|
||||
type BreakTy = !;
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
|
||||
if let ty::Alias(ty::Projection, proj) = *ty.kind()
|
||||
&& self.tcx.is_impl_trait_in_trait(proj.def_id)
|
||||
{
|
||||
if self.types.insert(proj) {
|
||||
for (pred, _) in self
|
||||
.tcx
|
||||
.explicit_item_bounds(proj.def_id)
|
||||
.iter_instantiated_copied(self.tcx, proj.args)
|
||||
{
|
||||
pred.visit_with(self)?;
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
ty.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_mismatched_rpitit_signature<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_m_sig: ty::FnSig<'tcx>,
|
||||
trait_m_def_id: DefId,
|
||||
impl_m_def_id: DefId,
|
||||
unmatched_bound: Option<Span>,
|
||||
) {
|
||||
let mapping = std::iter::zip(
|
||||
tcx.fn_sig(trait_m_def_id).skip_binder().bound_vars(),
|
||||
tcx.fn_sig(impl_m_def_id).skip_binder().bound_vars(),
|
||||
)
|
||||
.filter_map(|(impl_bv, trait_bv)| {
|
||||
if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
|
||||
&& let ty::BoundVariableKind::Region(trait_bv) = trait_bv
|
||||
{
|
||||
Some((impl_bv, trait_bv))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut return_ty =
|
||||
trait_m_sig.output().fold_with(&mut super::RemapLateBound { tcx, mapping: &mapping });
|
||||
|
||||
if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() {
|
||||
let ty::Alias(ty::Projection, future_ty) = return_ty.kind() else {
|
||||
bug!();
|
||||
};
|
||||
let Some(future_output_ty) = tcx
|
||||
.explicit_item_bounds(future_ty.def_id)
|
||||
.iter_instantiated_copied(tcx, future_ty.args)
|
||||
.find_map(|(clause, _)| match clause.kind().no_bound_vars()? {
|
||||
ty::ClauseKind::Projection(proj) => proj.term.ty(),
|
||||
_ => None,
|
||||
})
|
||||
else {
|
||||
bug!()
|
||||
};
|
||||
return_ty = future_output_ty;
|
||||
}
|
||||
|
||||
let (span, impl_return_span, pre, post) =
|
||||
match tcx.hir().get_by_def_id(impl_m_def_id.expect_local()).fn_decl().unwrap().output {
|
||||
hir::FnRetTy::DefaultReturn(span) => (tcx.def_span(impl_m_def_id), span, "-> ", " "),
|
||||
hir::FnRetTy::Return(ty) => (ty.span, ty.span, "", ""),
|
||||
};
|
||||
let trait_return_span =
|
||||
tcx.hir().get_if_local(trait_m_def_id).map(|node| match node.fn_decl().unwrap().output {
|
||||
hir::FnRetTy::DefaultReturn(_) => tcx.def_span(trait_m_def_id),
|
||||
hir::FnRetTy::Return(ty) => ty.span,
|
||||
});
|
||||
|
||||
let span = unmatched_bound.unwrap_or(span);
|
||||
tcx.emit_spanned_lint(
|
||||
REFINING_IMPL_TRAIT,
|
||||
tcx.local_def_id_to_hir_id(impl_m_def_id.expect_local()),
|
||||
span,
|
||||
crate::errors::ReturnPositionImplTraitInTraitRefined {
|
||||
impl_return_span,
|
||||
trait_return_span,
|
||||
pre,
|
||||
post,
|
||||
return_ty,
|
||||
unmatched_bound,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn type_visibility<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<ty::Visibility<DefId>> {
|
||||
match *ty.kind() {
|
||||
ty::Ref(_, ty, _) => type_visibility(tcx, ty),
|
||||
ty::Adt(def, args) => {
|
||||
if def.is_fundamental() {
|
||||
type_visibility(tcx, args.type_at(0))
|
||||
} else {
|
||||
Some(tcx.visibility(def.did()))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
@ -1255,7 +1255,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
|
||||
|
||||
let is_our_default = |def: &ty::GenericParamDef| match def.kind {
|
||||
GenericParamDefKind::Type { has_default, .. }
|
||||
| GenericParamDefKind::Const { has_default } => {
|
||||
| GenericParamDefKind::Const { has_default, .. } => {
|
||||
has_default && def.index >= generics.parent_count as u32
|
||||
}
|
||||
GenericParamDefKind::Lifetime => unreachable!(),
|
||||
|
@ -157,6 +157,14 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let cause = ObligationCause::misc(span, impl_did);
|
||||
|
||||
// Later parts of the compiler rely on all DispatchFromDyn types to be ABI-compatible with raw
|
||||
// pointers. This is enforced here: we only allow impls for references, raw pointers, and things
|
||||
// that are effectively repr(transparent) newtypes around types that already hav a
|
||||
// DispatchedFromDyn impl. We cannot literally use repr(transparent) on those tpyes since some
|
||||
// of them support an allocator, but we ensure that for the cases where the type implements this
|
||||
// trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else
|
||||
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
|
||||
// even if they do not carry that attribute.
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
match (source.kind(), target.kind()) {
|
||||
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
|
||||
|
@ -328,7 +328,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
name: param.name.ident().name,
|
||||
def_id: param.def_id.to_def_id(),
|
||||
pure_wrt_drop: param.pure_wrt_drop,
|
||||
kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
|
||||
kind: ty::GenericParamDefKind::Const {
|
||||
has_default: default.is_some(),
|
||||
is_host_effect: is_host_param,
|
||||
},
|
||||
})
|
||||
}
|
||||
}));
|
||||
|
@ -919,6 +919,22 @@ pub struct UnusedAssociatedTypeBounds {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(hir_analysis_rpitit_refined)]
|
||||
#[note]
|
||||
pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> {
|
||||
#[suggestion(applicability = "maybe-incorrect", code = "{pre}{return_ty}{post}")]
|
||||
pub impl_return_span: Span,
|
||||
#[label]
|
||||
pub trait_return_span: Option<Span>,
|
||||
#[label(hir_analysis_unmatched_bound_label)]
|
||||
pub unmatched_bound: Option<Span>,
|
||||
|
||||
pub pre: &'static str,
|
||||
pub post: &'static str,
|
||||
pub return_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_assoc_bound_on_const)]
|
||||
#[note]
|
||||
|
@ -237,6 +237,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
|
||||
});
|
||||
|
||||
// Freeze definitions as we don't add new ones at this point. This improves performance by
|
||||
// allowing lock-free access to them.
|
||||
tcx.untracked().definitions.freeze();
|
||||
|
||||
// FIXME: Remove this when we implement creating `DefId`s
|
||||
// for anon constants during their parents' typeck.
|
||||
// Typeck all body owners in parallel will produce queries
|
||||
|
@ -4,6 +4,7 @@ use rustc_data_structures::{
|
||||
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
|
||||
unord::{UnordBag, UnordMap, UnordSet},
|
||||
};
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
@ -23,22 +24,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
self.fulfillment_cx.borrow_mut().pending_obligations()
|
||||
);
|
||||
|
||||
// Check if we have any unsolved variables. If not, no need for fallback.
|
||||
let unsolved_variables = self.unsolved_variables();
|
||||
if unsolved_variables.is_empty() {
|
||||
let fallback_occured = self.fallback_types() || self.fallback_effects();
|
||||
|
||||
if !fallback_occured {
|
||||
return;
|
||||
}
|
||||
|
||||
let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
|
||||
|
||||
// We do fallback in two passes, to try to generate
|
||||
// better error messages.
|
||||
// The first time, we do *not* replace opaque types.
|
||||
for ty in unsolved_variables {
|
||||
debug!("unsolved_variable = {:?}", ty);
|
||||
self.fallback_if_possible(ty, &diverging_fallback);
|
||||
}
|
||||
|
||||
// We now see if we can make progress. This might cause us to
|
||||
// unify inference variables for opaque types, since we may
|
||||
// have unified some other type variables during the first
|
||||
@ -65,6 +56,53 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
self.select_obligations_where_possible(|_| {});
|
||||
}
|
||||
|
||||
fn fallback_types(&self) -> bool {
|
||||
// Check if we have any unsolved variables. If not, no need for fallback.
|
||||
let unsolved_variables = self.unsolved_variables();
|
||||
|
||||
if unsolved_variables.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
|
||||
|
||||
// We do fallback in two passes, to try to generate
|
||||
// better error messages.
|
||||
// The first time, we do *not* replace opaque types.
|
||||
for ty in unsolved_variables {
|
||||
debug!("unsolved_variable = {:?}", ty);
|
||||
self.fallback_if_possible(ty, &diverging_fallback);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn fallback_effects(&self) -> bool {
|
||||
let unsolved_effects = self.unsolved_effects();
|
||||
|
||||
if unsolved_effects.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// not setting `fallback_has_occured` here because that field is only used for type fallback
|
||||
// diagnostics.
|
||||
|
||||
for effect in unsolved_effects {
|
||||
let expected = self.tcx.consts.true_;
|
||||
let cause = self.misc(rustc_span::DUMMY_SP);
|
||||
match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) {
|
||||
Ok(InferOk { obligations, value: () }) => {
|
||||
self.register_predicates(obligations);
|
||||
}
|
||||
Err(e) => {
|
||||
bug!("cannot eq unsolved effect: {e:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
// Tries to apply a fallback to `ty` if it is an unsolved variable.
|
||||
//
|
||||
// - Unconstrained ints are replaced with `i32`.
|
||||
|
@ -1295,17 +1295,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
(GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
|
||||
self.fcx.ty_infer(Some(param), inf.span).into()
|
||||
}
|
||||
(GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
|
||||
(
|
||||
&GenericParamDefKind::Const { has_default, is_host_effect },
|
||||
GenericArg::Infer(inf),
|
||||
) => {
|
||||
let tcx = self.fcx.tcx();
|
||||
self.fcx
|
||||
.ct_infer(
|
||||
tcx.type_of(param.def_id)
|
||||
.no_bound_vars()
|
||||
.expect("const parameter types cannot be generic"),
|
||||
Some(param),
|
||||
inf.span,
|
||||
)
|
||||
.into()
|
||||
|
||||
if has_default && is_host_effect {
|
||||
self.fcx.var_for_effect(param)
|
||||
} else {
|
||||
self.fcx
|
||||
.ct_infer(
|
||||
tcx.type_of(param.def_id)
|
||||
.no_bound_vars()
|
||||
.expect("const parameter types cannot be generic"),
|
||||
Some(param),
|
||||
inf.span,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -1324,7 +1332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
GenericParamDefKind::Type { has_default, .. } => {
|
||||
if !infer_args && has_default {
|
||||
// If we have a default, then we it doesn't matter that we're not
|
||||
// If we have a default, then it doesn't matter that we're not
|
||||
// inferring the type arguments: we provide the default where any
|
||||
// is missing.
|
||||
tcx.type_of(param.def_id).instantiate(tcx, args.unwrap()).into()
|
||||
@ -1336,17 +1344,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.fcx.var_for_def(self.span, param)
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Const { has_default } => {
|
||||
if !infer_args
|
||||
&& has_default
|
||||
&& !tcx.has_attr(param.def_id, sym::rustc_host)
|
||||
{
|
||||
tcx.const_param_default(param.def_id)
|
||||
.instantiate(tcx, args.unwrap())
|
||||
.into()
|
||||
} else {
|
||||
self.fcx.var_for_def(self.span, param)
|
||||
GenericParamDefKind::Const { has_default, is_host_effect } => {
|
||||
if has_default {
|
||||
// N.B. this is a bit of a hack. `infer_args` is passed depending on
|
||||
// whether the user has provided generic args. E.g. for `Vec::new`
|
||||
// we would have to infer the generic types. However, for `Vec::<T>::new`
|
||||
// where the allocator param `A` has a default we will *not* infer. But
|
||||
// for effect params this is a different story: if the user has not written
|
||||
// anything explicit for the effect param, we always need to try to infer
|
||||
// it before falling back to default, such that a `const fn` such as
|
||||
// `needs_drop::<()>` can still be called in const contexts. (if we defaulted
|
||||
// instead of inferred, typeck would error)
|
||||
if is_host_effect {
|
||||
return self.fcx.var_for_effect(param);
|
||||
} else if !infer_args {
|
||||
return tcx
|
||||
.const_param_default(param.def_id)
|
||||
.instantiate(tcx, args.unwrap())
|
||||
.into();
|
||||
}
|
||||
}
|
||||
|
||||
self.fcx.var_for_def(self.span, param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
|
||||
param: Option<&ty::GenericParamDef>,
|
||||
span: Span,
|
||||
) -> Const<'tcx> {
|
||||
// FIXME ideally this shouldn't use unwrap
|
||||
match param {
|
||||
Some(
|
||||
param @ ty::GenericParamDef {
|
||||
kind: ty::GenericParamDefKind::Const { is_host_effect: true, .. },
|
||||
..
|
||||
},
|
||||
) => self.var_for_effect(param).as_const().unwrap(),
|
||||
Some(param) => self.var_for_def(span, param).as_const().unwrap(),
|
||||
None => self.next_const_var(
|
||||
ty,
|
||||
|
@ -643,17 +643,14 @@ fn check_must_not_suspend_ty<'tcx>(
|
||||
}
|
||||
ty::Array(ty, len) => {
|
||||
let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
|
||||
let target_usize =
|
||||
len.try_eval_target_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize;
|
||||
let plural_len = target_usize.saturating_add(1);
|
||||
check_must_not_suspend_ty(
|
||||
fcx,
|
||||
ty,
|
||||
hir_id,
|
||||
SuspendCheckData {
|
||||
descr_pre,
|
||||
plural_len: len.try_eval_target_usize(fcx.tcx, fcx.param_env).unwrap_or(0)
|
||||
as usize
|
||||
+ 1,
|
||||
..data
|
||||
},
|
||||
SuspendCheckData { descr_pre, plural_len, ..data },
|
||||
)
|
||||
}
|
||||
// If drop tracking is enabled, we want to look through references, since the referent
|
||||
|
@ -47,18 +47,6 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
|
||||
}
|
||||
|
||||
join(
|
||||
move || {
|
||||
sess.time("incr_comp_persist_result_cache", || {
|
||||
// Drop the memory map so that we can remove the file and write to it.
|
||||
if let Some(odc) = &tcx.query_system.on_disk_cache {
|
||||
odc.drop_serialized_data(tcx);
|
||||
}
|
||||
|
||||
file_format::save_in(sess, query_cache_path, "query cache", |e| {
|
||||
encode_query_cache(tcx, e)
|
||||
});
|
||||
});
|
||||
},
|
||||
move || {
|
||||
sess.time("incr_comp_persist_dep_graph", || {
|
||||
if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
|
||||
@ -73,6 +61,20 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
|
||||
}
|
||||
});
|
||||
},
|
||||
move || {
|
||||
// We execute this after `incr_comp_persist_dep_graph` for the serial compiler
|
||||
// to catch any potential query execution writing to the dep graph.
|
||||
sess.time("incr_comp_persist_result_cache", || {
|
||||
// Drop the memory map so that we can remove the file and write to it.
|
||||
if let Some(odc) = &tcx.query_system.on_disk_cache {
|
||||
odc.drop_serialized_data(tcx);
|
||||
}
|
||||
|
||||
file_format::save_in(sess, query_cache_path, "query cache", |e| {
|
||||
encode_query_cache(tcx, e)
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
})
|
||||
}
|
||||
|
@ -522,6 +522,17 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
|
||||
match self.infcx.probe_effect_var(vid) {
|
||||
Some(value) => return self.fold_const(value.as_const(self.infcx.tcx)),
|
||||
None => {
|
||||
return self.canonicalize_const_var(
|
||||
CanonicalVarInfo { kind: CanonicalVarKind::Effect },
|
||||
ct,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Infer(InferConst::Fresh(_)) => {
|
||||
bug!("encountered a fresh const during canonicalization")
|
||||
}
|
||||
@ -690,7 +701,8 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
||||
.iter()
|
||||
.map(|v| CanonicalVarInfo {
|
||||
kind: match v.kind {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
|
||||
| CanonicalVarKind::Effect => {
|
||||
return *v;
|
||||
}
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
|
||||
|
@ -151,7 +151,11 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
universe_map(ui),
|
||||
)
|
||||
.into(),
|
||||
|
||||
CanonicalVarKind::Effect => {
|
||||
let vid = self.inner.borrow_mut().effect_unification_table().new_key(None);
|
||||
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool)
|
||||
.into()
|
||||
}
|
||||
CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }, ty) => {
|
||||
let universe_mapped = universe_map(universe);
|
||||
let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound };
|
||||
|
@ -30,7 +30,7 @@ use super::{DefineOpaqueTypes, InferCtxt, TypeTrace};
|
||||
use crate::infer::generalize::{self, CombineDelegate, Generalization};
|
||||
use crate::traits::{Obligation, PredicateObligations};
|
||||
use rustc_middle::infer::canonical::OriginalQueryValues;
|
||||
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
|
||||
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::relate::{RelateResult, TypeRelation};
|
||||
@ -91,7 +91,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
.borrow_mut()
|
||||
.float_unification_table()
|
||||
.unify_var_var(a_id, b_id)
|
||||
.map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
|
||||
.map_err(|e| float_unification_error(a_is_expected, e))?;
|
||||
Ok(a)
|
||||
}
|
||||
(&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
|
||||
@ -210,10 +210,30 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
return Ok(a);
|
||||
}
|
||||
|
||||
(
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
|
||||
) => {
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.unify_var_var(a_vid, b_vid)
|
||||
.map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?;
|
||||
return Ok(a);
|
||||
}
|
||||
|
||||
// All other cases of inference with other variables are errors.
|
||||
(ty::ConstKind::Infer(InferConst::Var(_)), ty::ConstKind::Infer(_))
|
||||
| (ty::ConstKind::Infer(_), ty::ConstKind::Infer(InferConst::Var(_))) => {
|
||||
bug!("tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var)")
|
||||
(
|
||||
ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
|
||||
ty::ConstKind::Infer(_),
|
||||
)
|
||||
| (
|
||||
ty::ConstKind::Infer(_),
|
||||
ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
|
||||
) => {
|
||||
bug!(
|
||||
"tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
|
||||
)
|
||||
}
|
||||
|
||||
(ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
|
||||
@ -223,6 +243,23 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
|
||||
return self.unify_const_variable(vid, a, relation.param_env());
|
||||
}
|
||||
|
||||
(ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
|
||||
return self.unify_effect_variable(
|
||||
relation.a_is_expected(),
|
||||
vid,
|
||||
EffectVarValue::Const(b),
|
||||
);
|
||||
}
|
||||
|
||||
(_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
|
||||
return self.unify_effect_variable(
|
||||
!relation.a_is_expected(),
|
||||
vid,
|
||||
EffectVarValue::Const(a),
|
||||
);
|
||||
}
|
||||
|
||||
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
|
||||
if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
|
||||
{
|
||||
@ -340,6 +377,20 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
.map_err(|e| float_unification_error(vid_is_expected, e))?;
|
||||
Ok(Ty::new_float(self.tcx, val))
|
||||
}
|
||||
|
||||
fn unify_effect_variable(
|
||||
&self,
|
||||
vid_is_expected: bool,
|
||||
vid: ty::EffectVid<'tcx>,
|
||||
val: EffectVarValue<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Const<'tcx>> {
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.unify_var_value(vid, Some(val))
|
||||
.map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?;
|
||||
Ok(val.as_const(self.tcx))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
@ -493,3 +544,11 @@ fn float_unification_error<'tcx>(
|
||||
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
|
||||
TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
|
||||
}
|
||||
|
||||
fn effect_unification_error<'tcx>(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_a_is_expected: bool,
|
||||
(_a, _b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>),
|
||||
) -> TypeError<'tcx> {
|
||||
bug!("unexpected effect unification error")
|
||||
}
|
||||
|
@ -156,6 +156,21 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
|
||||
.known();
|
||||
self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
|
||||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
|
||||
let opt_ct = self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|effect| effect.as_const(self.infcx.tcx));
|
||||
self.freshen_const(
|
||||
opt_ct,
|
||||
ty::InferConst::EffectVar(v),
|
||||
ty::InferConst::Fresh,
|
||||
ct.ty(),
|
||||
)
|
||||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
|
||||
if i >= self.const_freshen_count {
|
||||
bug!(
|
||||
|
@ -403,6 +403,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c),
|
||||
// FIXME: remove this branch once `structurally_relate_consts` is fully
|
||||
// structural.
|
||||
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
|
||||
|
@ -21,7 +21,7 @@ use rustc_data_structures::unify as ut;
|
||||
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
|
||||
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
|
||||
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
@ -33,13 +33,14 @@ use rustc_middle::ty::relate::RelateResult;
|
||||
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
|
||||
pub use rustc_middle::ty::IntVarValue;
|
||||
use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid};
|
||||
use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
|
||||
use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use self::combine::CombineFields;
|
||||
use self::error_reporting::TypeErrCtxt;
|
||||
@ -115,6 +116,9 @@ pub struct InferCtxtInner<'tcx> {
|
||||
/// Map from floating variable to the kind of float it represents.
|
||||
float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>,
|
||||
|
||||
/// Map from effect variable to the effect param it represents.
|
||||
effect_unification_storage: ut::UnificationTableStorage<ty::EffectVid<'tcx>>,
|
||||
|
||||
/// Tracks the set of region variables and the constraints between them.
|
||||
///
|
||||
/// This is initially `Some(_)` but when
|
||||
@ -172,6 +176,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
||||
const_unification_storage: ut::UnificationTableStorage::new(),
|
||||
int_unification_storage: ut::UnificationTableStorage::new(),
|
||||
float_unification_storage: ut::UnificationTableStorage::new(),
|
||||
effect_unification_storage: ut::UnificationTableStorage::new(),
|
||||
region_constraint_storage: Some(RegionConstraintStorage::new()),
|
||||
region_obligations: vec![],
|
||||
opaque_type_storage: Default::default(),
|
||||
@ -223,6 +228,10 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
||||
self.const_unification_storage.with_log(&mut self.undo_log)
|
||||
}
|
||||
|
||||
fn effect_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::EffectVid<'tcx>> {
|
||||
self.effect_unification_storage.with_log(&mut self.undo_log)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> {
|
||||
self.region_constraint_storage
|
||||
@ -356,6 +365,7 @@ impl<'tcx> ty::InferCtxtLike<TyCtxt<'tcx>> for InferCtxt<'tcx> {
|
||||
Err(universe) => Some(universe),
|
||||
Ok(_) => None,
|
||||
},
|
||||
EffectVar(_) => None,
|
||||
Fresh(_) => None,
|
||||
}
|
||||
}
|
||||
@ -764,19 +774,32 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
.collect();
|
||||
vars.extend(
|
||||
(0..inner.int_unification_table().len())
|
||||
.map(|i| ty::IntVid { index: i as u32 })
|
||||
.map(|i| ty::IntVid::from_u32(i as u32))
|
||||
.filter(|&vid| inner.int_unification_table().probe_value(vid).is_none())
|
||||
.map(|v| Ty::new_int_var(self.tcx, v)),
|
||||
);
|
||||
vars.extend(
|
||||
(0..inner.float_unification_table().len())
|
||||
.map(|i| ty::FloatVid { index: i as u32 })
|
||||
.map(|i| ty::FloatVid::from_u32(i as u32))
|
||||
.filter(|&vid| inner.float_unification_table().probe_value(vid).is_none())
|
||||
.map(|v| Ty::new_float_var(self.tcx, v)),
|
||||
);
|
||||
vars
|
||||
}
|
||||
|
||||
pub fn unsolved_effects(&self) -> Vec<ty::Const<'tcx>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let mut table = inner.effect_unification_table();
|
||||
|
||||
(0..table.len())
|
||||
.map(|i| ty::EffectVid { index: i as u32, phantom: PhantomData })
|
||||
.filter(|&vid| table.probe_value(vid).is_none())
|
||||
.map(|v| {
|
||||
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v), self.tcx.types.bool)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn combine_fields<'a>(
|
||||
&'a self,
|
||||
trace: TypeTrace<'tcx>,
|
||||
@ -1158,7 +1181,10 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
|
||||
Ty::new_var(self.tcx, ty_var_id).into()
|
||||
}
|
||||
GenericParamDefKind::Const { .. } => {
|
||||
GenericParamDefKind::Const { is_host_effect, .. } => {
|
||||
if is_host_effect {
|
||||
return self.var_for_effect(param);
|
||||
}
|
||||
let origin = ConstVariableOrigin {
|
||||
kind: ConstVariableOriginKind::ConstParameterDefinition(
|
||||
param.name,
|
||||
@ -1184,6 +1210,17 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
|
||||
let effect_vid = self.inner.borrow_mut().effect_unification_table().new_key(None);
|
||||
let ty = self
|
||||
.tcx
|
||||
.type_of(param.def_id)
|
||||
.no_bound_vars()
|
||||
.expect("const parameter types cannot be generic");
|
||||
debug_assert_eq!(self.tcx.types.bool, ty);
|
||||
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid), ty).into()
|
||||
}
|
||||
|
||||
/// Given a set of generics defined on a type or impl, returns a substitution mapping each
|
||||
/// type/region parameter to a fresh inference variable.
|
||||
pub fn fresh_args_for_item(&self, span: Span, def_id: DefId) -> GenericArgsRef<'tcx> {
|
||||
@ -1369,6 +1406,10 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn probe_effect_var(&self, vid: EffectVid<'tcx>) -> Option<EffectVarValue<'tcx>> {
|
||||
self.inner.borrow_mut().effect_unification_table().probe_value(vid)
|
||||
}
|
||||
|
||||
/// Attempts to resolve all type/region/const variables in
|
||||
/// `value`. Region inference must have been run already (e.g.,
|
||||
/// by calling `resolve_regions_and_report_errors`). If some
|
||||
@ -1649,6 +1690,14 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
ConstVariableValue::Known { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
TyOrConstInferVar::Effect(v) => {
|
||||
// If `probe_value` returns `Some`, it never equals
|
||||
// `ty::ConstKind::Infer(ty::InferConst::Effect(v))`.
|
||||
//
|
||||
// Not `inlined_probe_value(v)` because this call site is colder.
|
||||
self.probe_effect_var(v).is_some()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1720,6 +1769,8 @@ pub enum TyOrConstInferVar<'tcx> {
|
||||
|
||||
/// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`.
|
||||
Const(ConstVid<'tcx>),
|
||||
/// Equivalent to `ty::ConstKind::Infer(ty::InferConst::EffectVar(_))`.
|
||||
Effect(EffectVid<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> TyOrConstInferVar<'tcx> {
|
||||
@ -1750,6 +1801,7 @@ impl<'tcx> TyOrConstInferVar<'tcx> {
|
||||
fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> {
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)),
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(v)) => Some(TyOrConstInferVar::Effect(v)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -1793,17 +1845,24 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ShallowResolver<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
|
||||
self.infcx
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Infer(InferConst::Var(vid)) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.const_unification_table()
|
||||
.probe_value(vid)
|
||||
.val
|
||||
.known()
|
||||
.unwrap_or(ct)
|
||||
} else {
|
||||
ct
|
||||
.unwrap_or(ct),
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.probe_value(vid)
|
||||
.map_or(ct, |val| val.as_const(self.infcx.tcx)),
|
||||
_ => ct,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +145,25 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
DefiningAnchor::Bubble => {}
|
||||
DefiningAnchor::Bubble => {
|
||||
if let ty::Alias(ty::Opaque, _) = b.kind() {
|
||||
// In bubble mode we don't know which of the two opaque types is supposed to have the other
|
||||
// as a hidden type (both, none or either one of them could be in its defining scope).
|
||||
let predicate = ty::PredicateKind::AliasRelate(
|
||||
a.into(),
|
||||
b.into(),
|
||||
ty::AliasRelationDirection::Equate,
|
||||
);
|
||||
let obligation = traits::Obligation::new(
|
||||
self.tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
predicate,
|
||||
);
|
||||
let obligations = vec![obligation];
|
||||
return Some(Ok(InferOk { value: (), obligations }));
|
||||
}
|
||||
}
|
||||
DefiningAnchor::Error => return None,
|
||||
};
|
||||
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
|
||||
|
@ -24,6 +24,7 @@ pub(crate) enum UndoLog<'tcx> {
|
||||
ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
|
||||
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
|
||||
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
|
||||
EffectUnificationTable(sv::UndoLog<ut::Delegate<ty::EffectVid<'tcx>>>),
|
||||
RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
|
||||
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
|
||||
ProjectionCache(traits::UndoLog<'tcx>),
|
||||
@ -55,6 +56,7 @@ impl_from! {
|
||||
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
|
||||
|
||||
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
|
||||
EffectUnificationTable(sv::UndoLog<ut::Delegate<ty::EffectVid<'tcx>>>),
|
||||
|
||||
ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
|
||||
|
||||
@ -71,6 +73,7 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
|
||||
UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
|
||||
UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
|
||||
UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
|
||||
UndoLog::EffectUnificationTable(undo) => self.effect_unification_storage.reverse(undo),
|
||||
UndoLog::RegionConstraintCollector(undo) => {
|
||||
self.region_constraint_storage.as_mut().unwrap().reverse(undo)
|
||||
}
|
||||
|
@ -279,6 +279,12 @@ pub struct Config {
|
||||
|
||||
/// Registry of diagnostics codes.
|
||||
pub registry: Registry,
|
||||
|
||||
/// All commandline args used to invoke the compiler, with @file args fully expanded.
|
||||
/// This will only be used within debug info, e.g. in the pdb file on windows
|
||||
/// This is mainly useful for other tools that reads that debuginfo to figure out
|
||||
/// how to call the compiler with the same arguments.
|
||||
pub expanded_args: Vec<String>,
|
||||
}
|
||||
|
||||
// JUSTIFICATION: before session exists, only config
|
||||
@ -317,6 +323,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
||||
config.make_codegen_backend,
|
||||
registry.clone(),
|
||||
config.ice_file,
|
||||
config.expanded_args,
|
||||
);
|
||||
|
||||
if let Some(parse_sess_created) = config.parse_sess_created {
|
||||
|
@ -584,7 +584,7 @@ fn resolver_for_lowering<'tcx>(
|
||||
let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver);
|
||||
|
||||
// Make sure we don't mutate the cstore from here on.
|
||||
tcx.untracked().cstore.leak();
|
||||
tcx.untracked().cstore.freeze();
|
||||
|
||||
let ty::ResolverOutputs {
|
||||
global_ctxt: untracked_resolutions,
|
||||
|
@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_codegen_ssa::CodegenResults;
|
||||
use rustc_data_structures::steal::Steal;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::{AppendOnlyIndexVec, Lrc, OnceLock, RwLock, WorkerLocal};
|
||||
use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, WorkerLocal};
|
||||
use rustc_hir::def_id::{StableCrateId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::Definitions;
|
||||
use rustc_incremental::DepGraphFuture;
|
||||
@ -114,6 +114,7 @@ impl<'tcx> Queries<'tcx> {
|
||||
.compute(|| passes::parse(self.session()).map_err(|mut parse_error| parse_error.emit()))
|
||||
}
|
||||
|
||||
#[deprecated = "pre_configure may be made private in the future. If you need it please open an issue with your use case."]
|
||||
pub fn pre_configure(&self) -> Result<QueryResult<'_, (ast::Crate, ast::AttrVec)>> {
|
||||
self.pre_configure.compute(|| {
|
||||
let mut krate = self.parse()?.steal();
|
||||
@ -171,6 +172,7 @@ impl<'tcx> Queries<'tcx> {
|
||||
pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> {
|
||||
self.gcx.compute(|| {
|
||||
let sess = self.session();
|
||||
#[allow(deprecated)]
|
||||
let (krate, pre_configured_attrs) = self.pre_configure()?.steal();
|
||||
|
||||
// parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
|
||||
@ -193,11 +195,11 @@ impl<'tcx> Queries<'tcx> {
|
||||
self.compiler.register_lints.as_deref(),
|
||||
&pre_configured_attrs,
|
||||
));
|
||||
let cstore = RwLock::new(Box::new(CStore::new(
|
||||
let cstore = FreezeLock::new(Box::new(CStore::new(
|
||||
self.codegen_backend().metadata_loader(),
|
||||
stable_crate_id,
|
||||
)) as _);
|
||||
let definitions = RwLock::new(Definitions::new(stable_crate_id));
|
||||
let definitions = FreezeLock::new(Definitions::new(stable_crate_id));
|
||||
let source_span = AppendOnlyIndexVec::new();
|
||||
let _id = source_span.push(krate.spans.inner_span);
|
||||
debug_assert_eq!(_id, CRATE_DEF_ID);
|
||||
|
@ -68,6 +68,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se
|
||||
None,
|
||||
"",
|
||||
None,
|
||||
Default::default(),
|
||||
);
|
||||
(sess, cfg)
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ pub fn create_session(
|
||||
>,
|
||||
descriptions: Registry,
|
||||
ice_file: Option<PathBuf>,
|
||||
expanded_args: Vec<String>,
|
||||
) -> (Session, Box<dyn CodegenBackend>) {
|
||||
let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
|
||||
make_codegen_backend(&sopts)
|
||||
@ -113,6 +114,7 @@ pub fn create_session(
|
||||
target_override,
|
||||
rustc_version_str().unwrap_or("unknown"),
|
||||
ice_file,
|
||||
expanded_args,
|
||||
);
|
||||
|
||||
codegen_backend.init(&sess);
|
||||
|
@ -156,15 +156,6 @@ lint_builtin_unused_doc_comment = unused doc comment
|
||||
lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}`
|
||||
.suggestion = use `loop`
|
||||
|
||||
lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name}
|
||||
|
||||
lint_check_name_removed = lint `{$lint_name}` has been removed: {$reason}
|
||||
|
||||
lint_check_name_renamed = lint `{$lint_name}` has been renamed to `{$replace}`
|
||||
|
||||
lint_check_name_unknown = unknown lint: `{$lint_name}`
|
||||
.help = did you mean: `{$suggestion}`
|
||||
|
||||
lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
|
||||
|
||||
lint_command_line_source = `forbid` lint level was set on command line
|
||||
@ -187,6 +178,7 @@ lint_default_source = `forbid` lint level is the default for {$id}
|
||||
lint_deprecated_lint_name =
|
||||
lint name `{$name}` is deprecated and may not have an effect in the future.
|
||||
.suggestion = change it to
|
||||
.help = change it to {$replace}
|
||||
|
||||
lint_diag_out_of_impl =
|
||||
diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
|
||||
@ -528,6 +520,7 @@ lint_unknown_gated_lint =
|
||||
lint_unknown_lint =
|
||||
unknown lint: `{$name}`
|
||||
.suggestion = did you mean
|
||||
.help = did you mean: `{$replace}`
|
||||
|
||||
lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`
|
||||
.help = add `#![register_tool({$tool_name})]` to the crate root
|
||||
|
@ -16,10 +16,6 @@
|
||||
|
||||
use self::TargetLint::*;
|
||||
|
||||
use crate::errors::{
|
||||
CheckNameDeprecated, CheckNameRemoved, CheckNameRenamed, CheckNameUnknown,
|
||||
CheckNameUnknownTool, RequestedLevel, UnsupportedGroup,
|
||||
};
|
||||
use crate::levels::LintLevelsBuilder;
|
||||
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
|
||||
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
|
||||
@ -330,58 +326,6 @@ impl LintStore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the validity of lint names derived from the command line.
|
||||
pub fn check_lint_name_cmdline(
|
||||
&self,
|
||||
sess: &Session,
|
||||
lint_name: &str,
|
||||
level: Level,
|
||||
registered_tools: &RegisteredTools,
|
||||
) {
|
||||
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
|
||||
if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
|
||||
sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
|
||||
return;
|
||||
}
|
||||
match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
|
||||
CheckLintNameResult::Renamed(replace) => {
|
||||
sess.emit_warning(CheckNameRenamed {
|
||||
lint_name,
|
||||
replace: &replace,
|
||||
sub: RequestedLevel { level, lint_name },
|
||||
});
|
||||
}
|
||||
CheckLintNameResult::Removed(reason) => {
|
||||
sess.emit_warning(CheckNameRemoved {
|
||||
lint_name,
|
||||
reason: &reason,
|
||||
sub: RequestedLevel { level, lint_name },
|
||||
});
|
||||
}
|
||||
CheckLintNameResult::NoLint(suggestion) => {
|
||||
sess.emit_err(CheckNameUnknown {
|
||||
lint_name,
|
||||
suggestion,
|
||||
sub: RequestedLevel { level, lint_name },
|
||||
});
|
||||
}
|
||||
CheckLintNameResult::Tool(Err((Some(_), new_name))) => {
|
||||
sess.emit_warning(CheckNameDeprecated {
|
||||
lint_name,
|
||||
new_name: &new_name,
|
||||
sub: RequestedLevel { level, lint_name },
|
||||
});
|
||||
}
|
||||
CheckLintNameResult::NoTool => {
|
||||
sess.emit_err(CheckNameUnknownTool {
|
||||
tool_name: tool_name.unwrap(),
|
||||
sub: RequestedLevel { level, lint_name },
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
/// True if this symbol represents a lint group name.
|
||||
pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
|
||||
debug!(
|
||||
@ -1402,14 +1346,3 @@ impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
|
||||
match lint_name.split_once("::") {
|
||||
Some((tool_name, lint_name)) => {
|
||||
let tool_name = Symbol::intern(tool_name);
|
||||
|
||||
(Some(tool_name), lint_name)
|
||||
}
|
||||
None => (None, lint_name),
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::fluent_generated as fluent;
|
||||
use rustc_errors::{
|
||||
AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, SubdiagnosticMessage,
|
||||
};
|
||||
use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_span::{Span, Symbol};
|
||||
@ -102,29 +100,6 @@ pub struct UnsupportedGroup {
|
||||
pub lint_group: String,
|
||||
}
|
||||
|
||||
pub struct CheckNameUnknown<'a> {
|
||||
pub lint_name: &'a str,
|
||||
pub suggestion: Option<Symbol>,
|
||||
pub sub: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
impl IntoDiagnostic<'_> for CheckNameUnknown<'_> {
|
||||
fn into_diagnostic(
|
||||
self,
|
||||
handler: &Handler,
|
||||
) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
|
||||
let mut diag = handler.struct_err(fluent::lint_check_name_unknown);
|
||||
diag.code(rustc_errors::error_code!(E0602));
|
||||
if let Some(suggestion) = self.suggestion {
|
||||
diag.help(fluent::lint_help);
|
||||
diag.set_arg("suggestion", suggestion);
|
||||
}
|
||||
diag.set_arg("lint_name", self.lint_name);
|
||||
diag.subdiagnostic(self.sub);
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(lint_check_name_unknown_tool, code = "E0602")]
|
||||
pub struct CheckNameUnknownTool<'a> {
|
||||
@ -132,30 +107,3 @@ pub struct CheckNameUnknownTool<'a> {
|
||||
#[subdiagnostic]
|
||||
pub sub: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(lint_check_name_renamed)]
|
||||
pub struct CheckNameRenamed<'a> {
|
||||
pub lint_name: &'a str,
|
||||
pub replace: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub sub: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(lint_check_name_removed)]
|
||||
pub struct CheckNameRemoved<'a> {
|
||||
pub lint_name: &'a str,
|
||||
pub reason: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub sub: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(lint_check_name_deprecated)]
|
||||
pub struct CheckNameDeprecated<'a> {
|
||||
pub lint_name: &'a str,
|
||||
pub new_name: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub sub: RequestedLevel<'a>,
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ use rustc_data_structures::sync::join;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
|
||||
use rustc_hir::intravisit as hir_visit;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::lint::LintPass;
|
||||
@ -61,6 +60,9 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
|
||||
self.context.last_node_with_lint_attrs = id;
|
||||
debug!("late context: enter_attrs({:?})", attrs);
|
||||
lint_callback!(self, enter_lint_attrs, attrs);
|
||||
for attr in attrs {
|
||||
lint_callback!(self, check_attribute, attr);
|
||||
}
|
||||
f(self);
|
||||
debug!("late context: exit_attrs({:?})", attrs);
|
||||
lint_callback!(self, exit_lint_attrs, attrs);
|
||||
@ -377,20 +379,18 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
|
||||
|
||||
let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
|
||||
|
||||
// There is no module lint that will have the crate itself as an item, so check it here.
|
||||
if hir_id == hir::CRATE_HIR_ID {
|
||||
lint_callback!(cx, check_crate,);
|
||||
}
|
||||
|
||||
cx.process_mod(module, hir_id);
|
||||
|
||||
// Visit the crate attributes
|
||||
if hir_id == hir::CRATE_HIR_ID {
|
||||
for attr in tcx.hir().attrs(hir::CRATE_HIR_ID).iter() {
|
||||
cx.visit_attribute(attr)
|
||||
cx.with_lint_attrs(hir_id, |cx| {
|
||||
// There is no module lint that will have the crate itself as an item, so check it here.
|
||||
if hir_id == hir::CRATE_HIR_ID {
|
||||
lint_callback!(cx, check_crate,);
|
||||
}
|
||||
lint_callback!(cx, check_crate_post,);
|
||||
}
|
||||
|
||||
cx.process_mod(module, hir_id);
|
||||
|
||||
if hir_id == hir::CRATE_HIR_ID {
|
||||
lint_callback!(cx, check_crate_post,);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
|
||||
@ -431,7 +431,6 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
|
||||
// item), warn for it here.
|
||||
lint_callback!(cx, check_crate,);
|
||||
tcx.hir().walk_toplevel_module(cx);
|
||||
tcx.hir().walk_attributes(cx);
|
||||
lint_callback!(cx, check_crate_post,);
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
use crate::errors::{CheckNameUnknownTool, RequestedLevel, UnsupportedGroup};
|
||||
use crate::lints::{
|
||||
DeprecatedLintNameFromCommandLine, RemovedLintFromCommandLine, RenamedLintFromCommandLine,
|
||||
UnknownLintFromCommandLine,
|
||||
};
|
||||
use crate::{
|
||||
builtin::MISSING_DOCS,
|
||||
context::{CheckLintNameResult, LintStore},
|
||||
@ -552,12 +557,55 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
||||
|
||||
fn add_command_line(&mut self) {
|
||||
for &(ref lint_name, level) in &self.sess.opts.lint_opts {
|
||||
self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
|
||||
// Checks the validity of lint names derived from the command line.
|
||||
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
|
||||
if lint_name_only == crate::WARNINGS.name_lower()
|
||||
&& matches!(level, Level::ForceWarn(_))
|
||||
{
|
||||
self.sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
|
||||
}
|
||||
match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
|
||||
CheckLintNameResult::Renamed(ref replace) => {
|
||||
let name = lint_name.as_str();
|
||||
let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
|
||||
let requested_level = RequestedLevel { level, lint_name };
|
||||
let lint = RenamedLintFromCommandLine { name, suggestion, requested_level };
|
||||
self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
|
||||
}
|
||||
CheckLintNameResult::Removed(ref reason) => {
|
||||
let name = lint_name.as_str();
|
||||
let requested_level = RequestedLevel { level, lint_name };
|
||||
let lint = RemovedLintFromCommandLine { name, reason, requested_level };
|
||||
self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
|
||||
}
|
||||
CheckLintNameResult::NoLint(suggestion) => {
|
||||
let name = lint_name.clone();
|
||||
let suggestion =
|
||||
suggestion.map(|replace| UnknownLintSuggestion::WithoutSpan { replace });
|
||||
let requested_level = RequestedLevel { level, lint_name };
|
||||
let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
|
||||
self.emit_lint(UNKNOWN_LINTS, lint);
|
||||
}
|
||||
CheckLintNameResult::Tool(Err((Some(_), ref replace))) => {
|
||||
let name = lint_name.clone();
|
||||
let requested_level = RequestedLevel { level, lint_name };
|
||||
let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
|
||||
self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
|
||||
}
|
||||
CheckLintNameResult::NoTool => {
|
||||
self.sess.emit_err(CheckNameUnknownTool {
|
||||
tool_name: tool_name.unwrap(),
|
||||
sub: RequestedLevel { level, lint_name },
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let orig_level = level;
|
||||
let lint_flag_val = Symbol::intern(lint_name);
|
||||
|
||||
let Ok(ids) = self.store.find_lints(&lint_name) else {
|
||||
// errors handled in check_lint_name_cmdline above
|
||||
// errors already handled above
|
||||
continue;
|
||||
};
|
||||
for id in ids {
|
||||
@ -915,24 +963,18 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
||||
|
||||
_ if !self.warn_about_weird_lints => {}
|
||||
|
||||
CheckLintNameResult::Renamed(new_name) => {
|
||||
CheckLintNameResult::Renamed(ref replace) => {
|
||||
let suggestion =
|
||||
RenamedLintSuggestion { suggestion: sp, replace: new_name.as_str() };
|
||||
RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
|
||||
let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
|
||||
self.emit_spanned_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
sp.into(),
|
||||
RenamedLint { name: name.as_str(), suggestion },
|
||||
);
|
||||
let lint = RenamedLint { name: name.as_str(), suggestion };
|
||||
self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
|
||||
}
|
||||
|
||||
CheckLintNameResult::Removed(reason) => {
|
||||
CheckLintNameResult::Removed(ref reason) => {
|
||||
let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
|
||||
self.emit_spanned_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
sp.into(),
|
||||
RemovedLint { name: name.as_str(), reason: reason.as_str() },
|
||||
);
|
||||
let lint = RemovedLint { name: name.as_str(), reason };
|
||||
self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
|
||||
}
|
||||
|
||||
CheckLintNameResult::NoLint(suggestion) => {
|
||||
@ -941,13 +983,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
||||
} else {
|
||||
name.to_string()
|
||||
};
|
||||
let suggestion = suggestion
|
||||
.map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
|
||||
self.emit_spanned_lint(
|
||||
UNKNOWN_LINTS,
|
||||
sp.into(),
|
||||
UnknownLint { name, suggestion },
|
||||
);
|
||||
let suggestion = suggestion.map(|replace| {
|
||||
UnknownLintSuggestion::WithSpan { suggestion: sp, replace }
|
||||
});
|
||||
let lint = UnknownLint { name, suggestion };
|
||||
self.emit_spanned_lint(UNKNOWN_LINTS, sp.into(), lint);
|
||||
}
|
||||
}
|
||||
// If this lint was renamed, apply the new lint instead of ignoring the attribute.
|
||||
@ -1092,3 +1132,14 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
|
||||
}
|
||||
|
||||
pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
|
||||
match lint_name.split_once("::") {
|
||||
Some((tool_name, lint_name)) => {
|
||||
let tool_name = Symbol::intern(tool_name);
|
||||
|
||||
(Some(tool_name), lint_name)
|
||||
}
|
||||
None => (None, lint_name),
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use crate::errors::RequestedLevel;
|
||||
use crate::fluent_generated as fluent;
|
||||
use rustc_errors::{
|
||||
AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage, DiagnosticStyledString,
|
||||
@ -1012,6 +1013,16 @@ pub struct DeprecatedLintName<'a> {
|
||||
pub replace: &'a str,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_deprecated_lint_name)]
|
||||
#[help]
|
||||
pub struct DeprecatedLintNameFromCommandLine<'a> {
|
||||
pub name: String,
|
||||
pub replace: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub requested_level: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_renamed_lint)]
|
||||
pub struct RenamedLint<'a> {
|
||||
@ -1021,11 +1032,25 @@ pub struct RenamedLint<'a> {
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
|
||||
pub struct RenamedLintSuggestion<'a> {
|
||||
#[primary_span]
|
||||
pub suggestion: Span,
|
||||
pub replace: &'a str,
|
||||
pub enum RenamedLintSuggestion<'a> {
|
||||
#[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
|
||||
WithSpan {
|
||||
#[primary_span]
|
||||
suggestion: Span,
|
||||
replace: &'a str,
|
||||
},
|
||||
#[help(lint_help)]
|
||||
WithoutSpan { replace: &'a str },
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_renamed_lint)]
|
||||
pub struct RenamedLintFromCommandLine<'a> {
|
||||
pub name: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: RenamedLintSuggestion<'a>,
|
||||
#[subdiagnostic]
|
||||
pub requested_level: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
@ -1035,6 +1060,15 @@ pub struct RemovedLint<'a> {
|
||||
pub reason: &'a str,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_removed_lint)]
|
||||
pub struct RemovedLintFromCommandLine<'a> {
|
||||
pub name: &'a str,
|
||||
pub reason: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub requested_level: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_unknown_lint)]
|
||||
pub struct UnknownLint {
|
||||
@ -1044,11 +1078,25 @@ pub struct UnknownLint {
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
|
||||
pub struct UnknownLintSuggestion {
|
||||
#[primary_span]
|
||||
pub suggestion: Span,
|
||||
pub replace: Symbol,
|
||||
pub enum UnknownLintSuggestion {
|
||||
#[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
|
||||
WithSpan {
|
||||
#[primary_span]
|
||||
suggestion: Span,
|
||||
replace: Symbol,
|
||||
},
|
||||
#[help(lint_help)]
|
||||
WithoutSpan { replace: Symbol },
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_unknown_lint, code = "E0602")]
|
||||
pub struct UnknownLintFromCommandLine<'a> {
|
||||
pub name: String,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: Option<UnknownLintSuggestion>,
|
||||
#[subdiagnostic]
|
||||
pub requested_level: RequestedLevel<'a>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
@ -128,7 +128,11 @@ fn is_operation_we_care_about<'tcx>(
|
||||
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||
let e = e.peel_blocks();
|
||||
|
||||
fn from_casts<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
fn from_casts<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
need_check_freeze: &mut bool,
|
||||
) -> Option<&'tcx Expr<'tcx>> {
|
||||
// <expr> as *mut ...
|
||||
let mut e = if let ExprKind::Cast(e, t) = e.kind
|
||||
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
|
||||
@ -138,6 +142,14 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
|
||||
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) {
|
||||
expr
|
||||
// UnsafeCell::raw_get(<expr>)
|
||||
} else if let ExprKind::Call(path, [arg]) = e.kind
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id)
|
||||
{
|
||||
*need_check_freeze = true;
|
||||
arg
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
@ -160,11 +172,18 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
|
||||
{
|
||||
had_at_least_one_cast = true;
|
||||
expr
|
||||
// ptr::from_ref(<expr>)
|
||||
// ptr::from_ref(<expr>) or UnsafeCell::raw_get(<expr>)
|
||||
} else if let ExprKind::Call(path, [arg]) = e.kind
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get)
|
||||
)
|
||||
{
|
||||
if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
|
||||
*need_check_freeze = true;
|
||||
}
|
||||
return Some(arg);
|
||||
} else if had_at_least_one_cast {
|
||||
return Some(e);
|
||||
@ -190,10 +209,25 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
|
||||
}
|
||||
}
|
||||
|
||||
let Some(e) = from_casts(cx, e).or_else(|| from_transmute(cx, e)) else {
|
||||
let mut need_check_freeze = false;
|
||||
let Some(e) = from_casts(cx, e, &mut need_check_freeze).or_else(|| from_transmute(cx, e))
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let e = e.peel_blocks();
|
||||
matches!(cx.typeck_results().node_type(e.hir_id).kind(), ty::Ref(_, _, Mutability::Not))
|
||||
let node_type = cx.typeck_results().node_type(e.hir_id);
|
||||
if let ty::Ref(_, inner_ty, Mutability::Not) = node_type.kind() {
|
||||
// If an UnsafeCell method is involved we need to additionaly check the
|
||||
// inner type for the presence of the Freeze trait (ie does NOT contain
|
||||
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
|
||||
//
|
||||
// We also consider non concrete skeleton types (ie generics)
|
||||
// to be an issue since there is no way to make it safe for abitrary types.
|
||||
!need_check_freeze
|
||||
|| inner_ty.is_freeze(cx.tcx, cx.param_env)
|
||||
|| !inner_ty.has_concrete_skeleton()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::context::parse_lint_and_tool_name;
|
||||
use crate::levels::parse_lint_and_tool_name;
|
||||
use rustc_span::{create_default_session_globals_then, Symbol};
|
||||
|
||||
#[test]
|
||||
|
@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
|
||||
};
|
||||
let def_id = trait_predicate.trait_ref.def_id;
|
||||
if cx.tcx.lang_items().drop_trait() == Some(def_id) {
|
||||
// Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.
|
||||
// Explicitly allow `impl Drop`, a drop-guards-as-unnameable-type pattern.
|
||||
if trait_predicate.trait_ref.self_ty().is_impl_trait() {
|
||||
continue;
|
||||
}
|
||||
|
@ -917,13 +917,18 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
|
||||
// At this point, the field's type is known to be nonnull and the parent enum is Option-like.
|
||||
// If the computed size for the field and the enum are different, the nonnull optimization isn't
|
||||
// being applied (and we've got a problem somewhere).
|
||||
let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).unwrap();
|
||||
if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) {
|
||||
let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).ok();
|
||||
if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
|
||||
bug!("improper_ctypes: Option nonnull optimization not applied?");
|
||||
}
|
||||
|
||||
// Return the nullable type this Option-like enum can be safely represented with.
|
||||
let field_ty_abi = &tcx.layout_of(param_env.and(field_ty)).unwrap().abi;
|
||||
let field_ty_layout = tcx.layout_of(param_env.and(field_ty));
|
||||
if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
|
||||
bug!("should be able to compute the layout of non-polymorphic type");
|
||||
}
|
||||
|
||||
let field_ty_abi = &field_ty_layout.ok()?.abi;
|
||||
if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
|
||||
match field_ty_scalar.valid_range(&tcx) {
|
||||
WrappingRange { start: 0, end }
|
||||
|
@ -3381,6 +3381,7 @@ declare_lint_pass! {
|
||||
PROC_MACRO_BACK_COMPAT,
|
||||
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
|
||||
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
||||
REFINING_IMPL_TRAIT,
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
|
||||
@ -4363,7 +4364,7 @@ declare_lint! {
|
||||
/// pub struct S;
|
||||
/// }
|
||||
///
|
||||
/// pub fn get_voldemort() -> m::S { m::S }
|
||||
/// pub fn get_unnameable() -> m::S { m::S }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
@ -4448,7 +4449,6 @@ declare_lint! {
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
///
|
||||
/// #![deny(ambiguous_glob_imports)]
|
||||
/// pub fn foo() -> u32 {
|
||||
/// use sub::*;
|
||||
@ -4484,6 +4484,50 @@ declare_lint! {
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `refining_impl_trait` lint detects usages of return-position impl
|
||||
/// traits in trait signatures which are refined by implementations.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![feature(return_position_impl_trait_in_trait)]
|
||||
/// #![deny(refining_impl_trait)]
|
||||
///
|
||||
/// use std::fmt::Display;
|
||||
///
|
||||
/// pub trait AsDisplay {
|
||||
/// fn as_display(&self) -> impl Display;
|
||||
/// }
|
||||
///
|
||||
/// impl<'s> AsDisplay for &'s str {
|
||||
/// fn as_display(&self) -> Self {
|
||||
/// *self
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// // users can observe that the return type of
|
||||
/// // `<&str as AsDisplay>::as_display()` is `&str`.
|
||||
/// let x: &str = "".as_display();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Return-position impl trait in traits (RPITITs) desugar to associated types,
|
||||
/// and callers of methods for types where the implementation is known are
|
||||
/// able to observe the types written in the impl signature. This may be
|
||||
/// intended behavior, but may also pose a semver hazard for authors of libraries
|
||||
/// who do not wish to make stronger guarantees about the types than what is
|
||||
/// written in the trait signature.
|
||||
pub REFINING_IMPL_TRAIT,
|
||||
Warn,
|
||||
"impl trait in impl method signature does not match trait method signature",
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes
|
||||
/// that were erroneously allowed in associated constants.
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
@ -9,6 +10,7 @@
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
||||
#include "llvm/CodeGen/CommandFlags.h"
|
||||
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
||||
#include "llvm/IR/AutoUpgrade.h"
|
||||
#include "llvm/IR/AssemblyAnnotationWriter.h"
|
||||
@ -50,6 +52,8 @@
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static codegen::RegisterCodeGenFlags CGF;
|
||||
|
||||
typedef struct LLVMOpaquePass *LLVMPassRef;
|
||||
typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
|
||||
|
||||
@ -406,7 +410,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
||||
bool RelaxELFRelocations,
|
||||
bool UseInitArray,
|
||||
const char *SplitDwarfFile,
|
||||
bool ForceEmulatedTls) {
|
||||
const char *DebugInfoCompression,
|
||||
bool ForceEmulatedTls,
|
||||
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {
|
||||
|
||||
auto OptLevel = fromRust(RustOptLevel);
|
||||
auto RM = fromRust(RustReloc);
|
||||
@ -421,7 +427,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TargetOptions Options;
|
||||
TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Trip);
|
||||
|
||||
Options.FloatABIType = FloatABI::Default;
|
||||
if (UseSoftFloat) {
|
||||
@ -436,6 +442,16 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
||||
if (SplitDwarfFile) {
|
||||
Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
|
||||
}
|
||||
#if LLVM_VERSION_GE(16, 0)
|
||||
if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) {
|
||||
Options.CompressDebugSections = DebugCompressionType::Zlib;
|
||||
} else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) {
|
||||
Options.CompressDebugSections = DebugCompressionType::Zstd;
|
||||
} else if (!strcmp("none", DebugInfoCompression)) {
|
||||
Options.CompressDebugSections = DebugCompressionType::None;
|
||||
}
|
||||
#endif
|
||||
|
||||
Options.RelaxELFRelocations = RelaxELFRelocations;
|
||||
Options.UseInitArray = UseInitArray;
|
||||
|
||||
@ -462,12 +478,48 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
||||
|
||||
Options.EmitStackSizeSection = EmitStackSizeSection;
|
||||
|
||||
|
||||
if (ArgsCstrBuff != nullptr)
|
||||
{
|
||||
int buffer_offset = 0;
|
||||
assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0');
|
||||
|
||||
const size_t arg0_len = std::strlen(ArgsCstrBuff);
|
||||
char* arg0 = new char[arg0_len + 1];
|
||||
memcpy(arg0, ArgsCstrBuff, arg0_len);
|
||||
arg0[arg0_len] = '\0';
|
||||
buffer_offset += arg0_len + 1;
|
||||
|
||||
const int num_cmd_arg_strings =
|
||||
std::count(&ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0');
|
||||
|
||||
std::string* cmd_arg_strings = new std::string[num_cmd_arg_strings];
|
||||
for (int i = 0; i < num_cmd_arg_strings; ++i)
|
||||
{
|
||||
assert(buffer_offset < ArgsCstrBuffLen);
|
||||
const int len = std::strlen(ArgsCstrBuff + buffer_offset);
|
||||
cmd_arg_strings[i] = std::string(&ArgsCstrBuff[buffer_offset], len);
|
||||
buffer_offset += len + 1;
|
||||
}
|
||||
|
||||
assert(buffer_offset == ArgsCstrBuffLen);
|
||||
|
||||
Options.MCOptions.Argv0 = arg0;
|
||||
Options.MCOptions.CommandLineArgs =
|
||||
llvm::ArrayRef<std::string>(cmd_arg_strings, num_cmd_arg_strings);
|
||||
}
|
||||
|
||||
TargetMachine *TM = TheTarget->createTargetMachine(
|
||||
Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel);
|
||||
return wrap(TM);
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
|
||||
|
||||
MCTargetOptions& MCOptions = unwrap(TM)->Options.MCOptions;
|
||||
delete[] MCOptions.Argv0;
|
||||
delete[] MCOptions.CommandLineArgs.data();
|
||||
|
||||
delete unwrap(TM);
|
||||
}
|
||||
|
||||
@ -1058,6 +1110,13 @@ extern "C" void LLVMRustPrintPasses() {
|
||||
extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols,
|
||||
size_t Len) {
|
||||
auto PreserveFunctions = [=](const GlobalValue &GV) {
|
||||
// Preserve LLVM-injected, ASAN-related symbols.
|
||||
// See also https://github.com/rust-lang/rust/issues/113404.
|
||||
if (GV.getName() == "___asan_globals_registered") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Preserve symbols exported from Rust modules.
|
||||
for (size_t I = 0; I < Len; I++) {
|
||||
if (GV.getName() == Symbols[I]) {
|
||||
return true;
|
||||
@ -1511,6 +1570,38 @@ LLVMRustGetBitcodeSliceFromObjectData(const char *data,
|
||||
return BitcodeOrError->getBufferStart();
|
||||
}
|
||||
|
||||
// Find a section of an object file by name. Fail if the section is missing or
|
||||
// empty.
|
||||
extern "C" const char *LLVMRustGetSliceFromObjectDataByName(const char *data,
|
||||
size_t len,
|
||||
const char *name,
|
||||
size_t *out_len) {
|
||||
*out_len = 0;
|
||||
StringRef Data(data, len);
|
||||
MemoryBufferRef Buffer(Data, ""); // The id is unused.
|
||||
file_magic Type = identify_magic(Buffer.getBuffer());
|
||||
Expected<std::unique_ptr<object::ObjectFile>> ObjFileOrError =
|
||||
object::ObjectFile::createObjectFile(Buffer, Type);
|
||||
if (!ObjFileOrError) {
|
||||
LLVMRustSetLastError(toString(ObjFileOrError.takeError()).c_str());
|
||||
return nullptr;
|
||||
}
|
||||
for (const object::SectionRef &Sec : (*ObjFileOrError)->sections()) {
|
||||
Expected<StringRef> Name = Sec.getName();
|
||||
if (Name && *Name == name) {
|
||||
Expected<StringRef> SectionOrError = Sec.getContents();
|
||||
if (!SectionOrError) {
|
||||
LLVMRustSetLastError(toString(SectionOrError.takeError()).c_str());
|
||||
return nullptr;
|
||||
}
|
||||
*out_len = SectionOrError->size();
|
||||
return SectionOrError->data();
|
||||
}
|
||||
}
|
||||
LLVMRustSetLastError("could not find requested section");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Computes the LTO cache key for the provided 'ModId' in the given 'Data',
|
||||
// storing the result in 'KeyOut'.
|
||||
// Currently, this cache key is a SHA-1 hash of anything that could affect
|
||||
|
@ -2044,3 +2044,19 @@ extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" bool LLVMRustLLVMHasZlibCompressionForDebugSymbols() {
|
||||
#if LLVM_VERSION_GE(16, 0)
|
||||
return llvm::compression::zlib::isAvailable();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" bool LLVMRustLLVMHasZstdCompressionForDebugSymbols() {
|
||||
#if LLVM_VERSION_GE(16, 0)
|
||||
return llvm::compression::zstd::isAvailable();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use rustc_ast::expand::allocator::{alloc_error_handler_name, global_fn_name, All
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::{MappedReadGuard, MappedWriteGuard, ReadGuard, WriteGuard};
|
||||
use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard};
|
||||
use rustc_expand::base::SyntaxExtension;
|
||||
use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, StableCrateIdMap, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::Definitions;
|
||||
@ -134,14 +134,14 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
|
||||
}
|
||||
|
||||
impl CStore {
|
||||
pub fn from_tcx(tcx: TyCtxt<'_>) -> MappedReadGuard<'_, CStore> {
|
||||
ReadGuard::map(tcx.untracked().cstore.read(), |cstore| {
|
||||
pub fn from_tcx(tcx: TyCtxt<'_>) -> FreezeReadGuard<'_, CStore> {
|
||||
FreezeReadGuard::map(tcx.untracked().cstore.read(), |cstore| {
|
||||
cstore.as_any().downcast_ref::<CStore>().expect("`tcx.cstore` is not a `CStore`")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> MappedWriteGuard<'_, CStore> {
|
||||
WriteGuard::map(tcx.untracked().cstore.write(), |cstore| {
|
||||
pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> FreezeWriteGuard<'_, CStore> {
|
||||
FreezeWriteGuard::map(tcx.untracked().cstore.write(), |cstore| {
|
||||
cstore.untracked_as_any().downcast_mut().expect("`tcx.cstore` is not a `CStore`")
|
||||
})
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ macro_rules! provide_one {
|
||||
$tcx.ensure().crate_hash($def_id.krate);
|
||||
}
|
||||
|
||||
let cdata = rustc_data_structures::sync::MappedReadGuard::map(CStore::from_tcx($tcx), |c| {
|
||||
let cdata = rustc_data_structures::sync::FreezeReadGuard::map(CStore::from_tcx($tcx), |c| {
|
||||
c.get_crate_data($def_id.krate).cdata
|
||||
});
|
||||
let $cdata = crate::creader::CrateMetadataRef {
|
||||
@ -510,7 +510,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
|
||||
crates: |tcx, ()| {
|
||||
// The list of loaded crates is now frozen in query cache,
|
||||
// so make sure cstore is not mutably accessed from here on.
|
||||
tcx.untracked().cstore.leak();
|
||||
tcx.untracked().cstore.freeze();
|
||||
tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum))
|
||||
},
|
||||
..*providers
|
||||
|
@ -14,7 +14,8 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{
|
||||
CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE,
|
||||
CrateNum, DefId, DefIndex, LocalDefId, LocalDefIdSet, CRATE_DEF_ID, CRATE_DEF_INDEX,
|
||||
LOCAL_CRATE,
|
||||
};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
@ -50,7 +51,6 @@ pub(super) struct EncodeContext<'a, 'tcx> {
|
||||
opaque: opaque::FileEncoder,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
feat: &'tcx rustc_feature::Features,
|
||||
|
||||
tables: TableBuilders,
|
||||
|
||||
lazy_state: LazyState,
|
||||
@ -280,8 +280,8 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for SpanData {
|
||||
// All of this logic ensures that the final result of deserialization is a 'normal'
|
||||
// Span that can be used without any additional trouble.
|
||||
let metadata_index = {
|
||||
// Introduce a new scope so that we drop the 'lock()' temporary
|
||||
match &*source_file.external_src.lock() {
|
||||
// Introduce a new scope so that we drop the 'read()' temporary
|
||||
match &*source_file.external_src.read() {
|
||||
ExternalSource::Foreign { metadata_index, .. } => *metadata_index,
|
||||
src => panic!("Unexpected external source {src:?}"),
|
||||
}
|
||||
@ -1002,15 +1002,31 @@ fn should_encode_stability(def_kind: DefKind) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether we should encode MIR.
|
||||
/// Whether we should encode MIR. Return a pair, resp. for CTFE and for LLVM.
|
||||
///
|
||||
/// Computing, optimizing and encoding the MIR is a relatively expensive operation.
|
||||
/// We want to avoid this work when not required. Therefore:
|
||||
/// - we only compute `mir_for_ctfe` on items with const-eval semantics;
|
||||
/// - we skip `optimized_mir` for check runs.
|
||||
/// - we only encode `optimized_mir` that could be generated in other crates, that is, a code that
|
||||
/// is either generic or has inline hint, and is reachable from the other crates (contained
|
||||
/// in reachable set).
|
||||
///
|
||||
/// Return a pair, resp. for CTFE and for LLVM.
|
||||
fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
|
||||
/// Note: Reachable set describes definitions that might be generated or referenced from other
|
||||
/// crates and it can be used to limit optimized MIR that needs to be encoded. On the other hand,
|
||||
/// the reachable set doesn't have much to say about which definitions might be evaluated at compile
|
||||
/// time in other crates, so it cannot be used to omit CTFE MIR. For example, `f` below is
|
||||
/// unreachable and yet it can be evaluated in other crates:
|
||||
///
|
||||
/// ```
|
||||
/// const fn f() -> usize { 0 }
|
||||
/// pub struct S { pub a: [usize; f()] }
|
||||
/// ```
|
||||
fn should_encode_mir(
|
||||
tcx: TyCtxt<'_>,
|
||||
reachable_set: &LocalDefIdSet,
|
||||
def_id: LocalDefId,
|
||||
) -> (bool, bool) {
|
||||
match tcx.def_kind(def_id) {
|
||||
// Constructors
|
||||
DefKind::Ctor(_, _) => {
|
||||
@ -1027,14 +1043,15 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
|
||||
// Full-fledged functions + closures
|
||||
DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
|
||||
let generics = tcx.generics_of(def_id);
|
||||
let needs_inline = (generics.requires_monomorphization(tcx)
|
||||
|| tcx.codegen_fn_attrs(def_id).requests_inline())
|
||||
&& tcx.sess.opts.output_types.should_codegen();
|
||||
let opt = tcx.sess.opts.unstable_opts.always_encode_mir
|
||||
|| (tcx.sess.opts.output_types.should_codegen()
|
||||
&& reachable_set.contains(&def_id)
|
||||
&& (generics.requires_monomorphization(tcx)
|
||||
|| tcx.codegen_fn_attrs(def_id).requests_inline()));
|
||||
// The function has a `const` modifier or is in a `#[const_trait]`.
|
||||
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
|
||||
|| tcx.is_const_default_method(def_id.to_def_id());
|
||||
let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir;
|
||||
(is_const_fn, needs_inline || always_encode_mir)
|
||||
(is_const_fn, opt)
|
||||
}
|
||||
// Generators require optimized MIR to compute layout.
|
||||
DefKind::Generator => (false, true),
|
||||
@ -1580,9 +1597,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
let tcx = self.tcx;
|
||||
let reachable_set = tcx.reachable_set(());
|
||||
|
||||
let keys_and_jobs = tcx.mir_keys(()).iter().filter_map(|&def_id| {
|
||||
let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
|
||||
let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id);
|
||||
if encode_const || encode_opt { Some((def_id, encode_const, encode_opt)) } else { None }
|
||||
});
|
||||
for (def_id, encode_const, encode_opt) in keys_and_jobs {
|
||||
@ -2067,8 +2085,9 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
let reachable_set = tcx.reachable_set(());
|
||||
par_for_each_in(tcx.mir_keys(()), |&def_id| {
|
||||
let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
|
||||
let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id);
|
||||
|
||||
if encode_const {
|
||||
tcx.ensure_with_value().mir_for_ctfe(def_id);
|
||||
|
@ -97,7 +97,7 @@ macro_rules! define_dep_nodes {
|
||||
// discriminants of the variants have been assigned consecutively from 0
|
||||
// so that just the one comparison suffices to check that the u16 can be
|
||||
// transmuted to a DepKind.
|
||||
const VARIANTS: u16 = {
|
||||
pub const VARIANTS: u16 = {
|
||||
let deps: &[DepKind] = &[$(DepKind::$variant,)*];
|
||||
let mut i = 0;
|
||||
while i < deps.len() {
|
||||
|
@ -26,6 +26,7 @@ pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct<TyCt
|
||||
impl rustc_query_system::dep_graph::DepKind for DepKind {
|
||||
const NULL: Self = DepKind::Null;
|
||||
const RED: Self = DepKind::Red;
|
||||
const MAX: u16 = DepKind::VARIANTS - 1;
|
||||
|
||||
fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}(", node.kind)?;
|
||||
@ -68,6 +69,21 @@ impl rustc_query_system::dep_graph::DepKind for DepKind {
|
||||
op(icx.task_deps)
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
fn from_u16(u: u16) -> Self {
|
||||
if u > Self::MAX {
|
||||
panic!("Invalid DepKind {u}");
|
||||
}
|
||||
// SAFETY: See comment on DepKind::VARIANTS
|
||||
unsafe { std::mem::transmute(u) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_u16(self) -> u16 {
|
||||
self as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> DepContext for TyCtxt<'tcx> {
|
||||
|
@ -1206,8 +1206,8 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh {
|
||||
upstream_crates.hash_stable(&mut hcx, &mut stable_hasher);
|
||||
source_file_names.hash_stable(&mut hcx, &mut stable_hasher);
|
||||
debugger_visualizers.hash_stable(&mut hcx, &mut stable_hasher);
|
||||
if tcx.sess.opts.incremental_relative_spans() {
|
||||
let definitions = tcx.definitions_untracked();
|
||||
if tcx.sess.opts.incremental.is_some() {
|
||||
let definitions = tcx.untracked().definitions.freeze();
|
||||
let mut owner_spans: Vec<_> = krate
|
||||
.owners
|
||||
.iter_enumerated()
|
||||
|
@ -27,6 +27,7 @@ use crate::ty::GenericArg;
|
||||
use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};
|
||||
use rustc_macros::HashStable;
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Index;
|
||||
|
||||
/// A "canonicalized" type `V` is one where all free inference
|
||||
@ -40,6 +41,16 @@ pub struct Canonical<'tcx, V> {
|
||||
pub variables: CanonicalVarInfos<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx, V: Display> std::fmt::Display for Canonical<'tcx, V> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}",
|
||||
self.value, self.max_universe, self.variables
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
|
||||
|
||||
impl<'tcx> ty::TypeFoldable<TyCtxt<'tcx>> for CanonicalVarInfos<'tcx> {
|
||||
@ -173,6 +184,7 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
|
||||
CanonicalVarKind::PlaceholderRegion(..) => false,
|
||||
CanonicalVarKind::Const(..) => true,
|
||||
CanonicalVarKind::PlaceholderConst(_, _) => false,
|
||||
CanonicalVarKind::Effect => true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +194,8 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
|
||||
CanonicalVarKind::Ty(_)
|
||||
| CanonicalVarKind::PlaceholderTy(_)
|
||||
| CanonicalVarKind::Const(_, _)
|
||||
| CanonicalVarKind::PlaceholderConst(_, _) => false,
|
||||
| CanonicalVarKind::PlaceholderConst(_, _)
|
||||
| CanonicalVarKind::Effect => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +203,8 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
|
||||
match self.kind {
|
||||
CanonicalVarKind::Ty(_)
|
||||
| CanonicalVarKind::Region(_)
|
||||
| CanonicalVarKind::Const(_, _) => bug!("expected placeholder: {self:?}"),
|
||||
| CanonicalVarKind::Const(_, _)
|
||||
| CanonicalVarKind::Effect => bug!("expected placeholder: {self:?}"),
|
||||
|
||||
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.bound.var.as_usize(),
|
||||
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.bound.var.as_usize(),
|
||||
@ -222,6 +236,9 @@ pub enum CanonicalVarKind<'tcx> {
|
||||
/// Some kind of const inference variable.
|
||||
Const(ty::UniverseIndex, Ty<'tcx>),
|
||||
|
||||
/// Effect variable `'?E`.
|
||||
Effect,
|
||||
|
||||
/// A "placeholder" that represents "any const".
|
||||
PlaceholderConst(ty::PlaceholderConst<'tcx>, Ty<'tcx>),
|
||||
}
|
||||
@ -229,11 +246,11 @@ pub enum CanonicalVarKind<'tcx> {
|
||||
impl<'tcx> CanonicalVarKind<'tcx> {
|
||||
pub fn universe(self) -> ty::UniverseIndex {
|
||||
match self {
|
||||
CanonicalVarKind::Ty(kind) => match kind {
|
||||
CanonicalTyVarKind::General(ui) => ui,
|
||||
CanonicalTyVarKind::Float | CanonicalTyVarKind::Int => ty::UniverseIndex::ROOT,
|
||||
},
|
||||
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => ui,
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => {
|
||||
ty::UniverseIndex::ROOT
|
||||
}
|
||||
CanonicalVarKind::Effect => ty::UniverseIndex::ROOT,
|
||||
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe,
|
||||
CanonicalVarKind::Region(ui) => ui,
|
||||
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe,
|
||||
@ -248,15 +265,14 @@ impl<'tcx> CanonicalVarKind<'tcx> {
|
||||
/// the updated universe is not the root.
|
||||
pub fn with_updated_universe(self, ui: ty::UniverseIndex) -> CanonicalVarKind<'tcx> {
|
||||
match self {
|
||||
CanonicalVarKind::Ty(kind) => match kind {
|
||||
CanonicalTyVarKind::General(_) => {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
|
||||
}
|
||||
CanonicalTyVarKind::Int | CanonicalTyVarKind::Float => {
|
||||
assert_eq!(ui, ty::UniverseIndex::ROOT);
|
||||
CanonicalVarKind::Ty(kind)
|
||||
}
|
||||
},
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
|
||||
}
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
|
||||
| CanonicalVarKind::Effect => {
|
||||
assert_eq!(ui, ty::UniverseIndex::ROOT);
|
||||
self
|
||||
}
|
||||
CanonicalVarKind::PlaceholderTy(placeholder) => {
|
||||
CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: ui, ..placeholder })
|
||||
}
|
||||
@ -443,6 +459,13 @@ impl<'tcx> CanonicalVarValues<'tcx> {
|
||||
};
|
||||
ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into()
|
||||
}
|
||||
CanonicalVarKind::Effect => ty::Const::new_bound(
|
||||
tcx,
|
||||
ty::INNERMOST,
|
||||
ty::BoundVar::from_usize(i),
|
||||
tcx.types.bool,
|
||||
)
|
||||
.into(),
|
||||
CanonicalVarKind::Const(_, ty)
|
||||
| CanonicalVarKind::PlaceholderConst(_, ty) => ty::Const::new_bound(
|
||||
tcx,
|
||||
|
@ -188,3 +188,53 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// values for the effect inference variable
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum EffectVarValue<'tcx> {
|
||||
/// The host effect is on, enabling access to syscalls, filesystem access, etc.
|
||||
Host,
|
||||
/// The host effect is off. Execution is restricted to const operations only.
|
||||
NoHost,
|
||||
Const(ty::Const<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> EffectVarValue<'tcx> {
|
||||
pub fn as_const(self, tcx: TyCtxt<'tcx>) -> ty::Const<'tcx> {
|
||||
match self {
|
||||
EffectVarValue::Host => tcx.consts.true_,
|
||||
EffectVarValue::NoHost => tcx.consts.false_,
|
||||
EffectVarValue::Const(c) => c,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
|
||||
type Error = (EffectVarValue<'tcx>, EffectVarValue<'tcx>);
|
||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
|
||||
match (value1, value2) {
|
||||
(EffectVarValue::Host, EffectVarValue::Host) => Ok(EffectVarValue::Host),
|
||||
(EffectVarValue::NoHost, EffectVarValue::NoHost) => Ok(EffectVarValue::NoHost),
|
||||
(EffectVarValue::NoHost | EffectVarValue::Host, _)
|
||||
| (_, EffectVarValue::NoHost | EffectVarValue::Host) => Err((*value1, *value2)),
|
||||
(EffectVarValue::Const(_), EffectVarValue::Const(_)) => {
|
||||
bug!("equating two const variables, both of which have known values")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UnifyKey for ty::EffectVid<'tcx> {
|
||||
type Value = Option<EffectVarValue<'tcx>>;
|
||||
#[inline]
|
||||
fn index(&self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
#[inline]
|
||||
fn from_index(i: u32) -> Self {
|
||||
ty::EffectVid { index: i, phantom: PhantomData }
|
||||
}
|
||||
fn tag() -> &'static str {
|
||||
"EffectVid"
|
||||
}
|
||||
}
|
||||
|
@ -255,9 +255,16 @@ impl_into_diagnostic_arg_through_debug! {
|
||||
|
||||
/// Error information for when the program caused Undefined Behavior.
|
||||
#[derive(Debug)]
|
||||
pub enum UndefinedBehaviorInfo<'a> {
|
||||
pub enum UndefinedBehaviorInfo<'tcx> {
|
||||
/// Free-form case. Only for errors that are never caught! Used by miri
|
||||
Ub(String),
|
||||
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
|
||||
// dispatched
|
||||
/// A custom (free-form) fluent-translated error, created by `err_ub_custom!`.
|
||||
Custom(crate::error::CustomSubdiagnostic<'tcx>),
|
||||
/// Validation error.
|
||||
ValidationError(ValidationErrorInfo<'tcx>),
|
||||
|
||||
/// Unreachable code was executed.
|
||||
Unreachable,
|
||||
/// A slice/array index projection went out-of-bounds.
|
||||
@ -319,12 +326,10 @@ pub enum UndefinedBehaviorInfo<'a> {
|
||||
UninhabitedEnumVariantWritten(VariantIdx),
|
||||
/// An uninhabited enum variant is projected.
|
||||
UninhabitedEnumVariantRead(VariantIdx),
|
||||
/// Validation error.
|
||||
ValidationError(ValidationErrorInfo<'a>),
|
||||
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
|
||||
// dispatched
|
||||
/// A custom (free-form) error, created by `err_ub_custom!`.
|
||||
Custom(crate::error::CustomSubdiagnostic<'a>),
|
||||
/// ABI-incompatible argument types.
|
||||
AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
|
||||
/// ABI-incompatible return types.
|
||||
AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user