mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 10:13:54 +00:00
Auto merge of #2955 - rust-lang:rustup2023-07-03, r=oli-obk
Automatic sync from rustc
This commit is contained in:
commit
a8b6ec1667
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -31,7 +31,7 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
|
||||
group: "${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
pr:
|
||||
|
1141
Cargo.lock
1141
Cargo.lock
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@ members = [
|
||||
"src/tools/cargotest",
|
||||
"src/tools/clippy",
|
||||
"src/tools/clippy/clippy_dev",
|
||||
"src/tools/clippy/clippy_test_deps",
|
||||
"src/tools/compiletest",
|
||||
"src/tools/error_index_generator",
|
||||
"src/tools/linkchecker",
|
||||
|
@ -1539,9 +1539,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
);
|
||||
debug!(?opaque_ty_def_id);
|
||||
|
||||
// Contains the new lifetime definitions created for the TAIT (if any).
|
||||
let mut collected_lifetimes = Vec::new();
|
||||
|
||||
// If this came from a TAIT (as opposed to a function that returns an RPIT), we only want
|
||||
// to capture the lifetimes that appear in the bounds. So visit the bounds to find out
|
||||
// exactly which ones those are.
|
||||
@ -1558,20 +1555,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
};
|
||||
debug!(?lifetimes_to_remap);
|
||||
|
||||
let mut new_remapping = FxHashMap::default();
|
||||
|
||||
// Contains the new lifetime definitions created for the TAIT (if any).
|
||||
// If this opaque type is only capturing a subset of the lifetimes (those that appear in
|
||||
// bounds), then create the new lifetime parameters required and create a mapping from the
|
||||
// old `'a` (on the function) to the new `'a` (on the opaque type).
|
||||
let collected_lifetimes =
|
||||
self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping);
|
||||
debug!(?collected_lifetimes);
|
||||
debug!(?new_remapping);
|
||||
|
||||
// This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type
|
||||
// TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`.
|
||||
let collected_lifetime_mapping: Vec<_> = collected_lifetimes
|
||||
.iter()
|
||||
.map(|(node_id, lifetime)| {
|
||||
let id = self.next_node_id();
|
||||
let lifetime = self.new_named_lifetime(lifetime.id, id, lifetime.ident);
|
||||
let def_id = self.local_def_id(*node_id);
|
||||
(lifetime, def_id)
|
||||
})
|
||||
.collect();
|
||||
debug!(?collected_lifetime_mapping);
|
||||
|
||||
self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
|
||||
let mut new_remapping = FxHashMap::default();
|
||||
|
||||
// If this opaque type is only capturing a subset of the lifetimes (those that appear
|
||||
// in bounds), then create the new lifetime parameters required and create a mapping
|
||||
// from the old `'a` (on the function) to the new `'a` (on the opaque type).
|
||||
collected_lifetimes = lctx.create_lifetime_defs(
|
||||
opaque_ty_def_id,
|
||||
&lifetimes_to_remap,
|
||||
&mut new_remapping,
|
||||
);
|
||||
debug!(?collected_lifetimes);
|
||||
debug!(?new_remapping);
|
||||
|
||||
// Install the remapping from old to new (if any):
|
||||
lctx.with_remapping(new_remapping, |lctx| {
|
||||
// This creates HIR lifetime definitions as `hir::GenericParam`, in the given
|
||||
@ -1610,6 +1618,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let hir_bounds = lctx.lower_param_bounds(bounds, itctx);
|
||||
debug!(?hir_bounds);
|
||||
|
||||
let lifetime_mapping = if in_trait {
|
||||
self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.iter()
|
||||
.map(|(lifetime, def_id)| (**lifetime, *def_id)),
|
||||
)
|
||||
} else {
|
||||
&mut []
|
||||
};
|
||||
|
||||
let opaque_ty_item = hir::OpaqueTy {
|
||||
generics: self.arena.alloc(hir::Generics {
|
||||
params: lifetime_defs,
|
||||
@ -1620,6 +1638,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}),
|
||||
bounds: hir_bounds,
|
||||
origin,
|
||||
lifetime_mapping,
|
||||
in_trait,
|
||||
};
|
||||
debug!(?opaque_ty_item);
|
||||
@ -1628,20 +1647,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
})
|
||||
});
|
||||
|
||||
// This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type
|
||||
// TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`.
|
||||
let lifetimes =
|
||||
self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(|(_, lifetime)| {
|
||||
let id = self.next_node_id();
|
||||
let l = self.new_named_lifetime(lifetime.id, id, lifetime.ident);
|
||||
hir::GenericArg::Lifetime(l)
|
||||
}));
|
||||
debug!(?lifetimes);
|
||||
|
||||
// `impl Trait` now just becomes `Foo<'a, 'b, ..>`.
|
||||
hir::TyKind::OpaqueDef(
|
||||
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
|
||||
lifetimes,
|
||||
self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.iter()
|
||||
.map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
|
||||
),
|
||||
in_trait,
|
||||
)
|
||||
}
|
||||
@ -1655,7 +1668,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
span: Span,
|
||||
opaque_ty_span: Span,
|
||||
) -> hir::OwnerNode<'hir> {
|
||||
let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(opaque_ty_item);
|
||||
let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(self.arena.alloc(opaque_ty_item));
|
||||
// Generate an `type Foo = impl Trait;` declaration.
|
||||
trace!("registering opaque type with id {:#?}", opaque_ty_id);
|
||||
let opaque_ty_item = hir::Item {
|
||||
@ -1983,7 +1996,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let lifetime = Lifetime { id: outer_node_id, ident };
|
||||
collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res)));
|
||||
}
|
||||
|
||||
debug!(?collected_lifetimes);
|
||||
|
||||
// We only want to capture the lifetimes that appear in the bounds. So visit the bounds to
|
||||
@ -1993,22 +2005,36 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let lifetimes_to_remap = lifetime_collector::lifetimes_in_ret_ty(&self.resolver, output);
|
||||
debug!(?lifetimes_to_remap);
|
||||
|
||||
self.with_hir_id_owner(opaque_ty_node_id, |this| {
|
||||
// If this opaque type is only capturing a subset of the lifetimes (those that appear
|
||||
// in bounds), then create the new lifetime parameters required and create a mapping
|
||||
// from the old `'a` (on the function) to the new `'a` (on the opaque type).
|
||||
collected_lifetimes.extend(
|
||||
this.create_lifetime_defs(
|
||||
opaque_ty_def_id,
|
||||
&lifetimes_to_remap,
|
||||
&mut new_remapping,
|
||||
)
|
||||
// If this opaque type is only capturing a subset of the lifetimes (those that appear in
|
||||
// bounds), then create the new lifetime parameters required and create a mapping from the
|
||||
// old `'a` (on the function) to the new `'a` (on the opaque type).
|
||||
collected_lifetimes.extend(
|
||||
self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping)
|
||||
.into_iter()
|
||||
.map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)),
|
||||
);
|
||||
debug!(?collected_lifetimes);
|
||||
debug!(?new_remapping);
|
||||
);
|
||||
debug!(?collected_lifetimes);
|
||||
debug!(?new_remapping);
|
||||
|
||||
// This creates pairs of HIR lifetimes and def_ids. In the given example `type
|
||||
// TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing the
|
||||
// new lifetime of the RPIT 'x and the def_id of the lifetime 'x corresponding to
|
||||
// `TestReturn`.
|
||||
let collected_lifetime_mapping: Vec<_> = collected_lifetimes
|
||||
.iter()
|
||||
.map(|(node_id, lifetime, res)| {
|
||||
let id = self.next_node_id();
|
||||
let res = res.unwrap_or(
|
||||
self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
|
||||
);
|
||||
let lifetime = self.new_named_lifetime_with_res(id, lifetime.ident, res);
|
||||
let def_id = self.local_def_id(*node_id);
|
||||
(lifetime, def_id)
|
||||
})
|
||||
.collect();
|
||||
debug!(?collected_lifetime_mapping);
|
||||
|
||||
self.with_hir_id_owner(opaque_ty_node_id, |this| {
|
||||
// Install the remapping from old to new (if any):
|
||||
this.with_remapping(new_remapping, |this| {
|
||||
// We have to be careful to get elision right here. The
|
||||
@ -2063,6 +2089,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
));
|
||||
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
|
||||
|
||||
let lifetime_mapping = if in_trait {
|
||||
self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.iter()
|
||||
.map(|(lifetime, def_id)| (**lifetime, *def_id)),
|
||||
)
|
||||
} else {
|
||||
&mut []
|
||||
};
|
||||
|
||||
let opaque_ty_item = hir::OpaqueTy {
|
||||
generics: this.arena.alloc(hir::Generics {
|
||||
params: generic_params,
|
||||
@ -2073,6 +2109,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}),
|
||||
bounds: arena_vec![this; future_bound],
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
|
||||
lifetime_mapping,
|
||||
in_trait,
|
||||
};
|
||||
|
||||
@ -2096,15 +2133,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
//
|
||||
// For the "output" lifetime parameters, we just want to
|
||||
// generate `'_`.
|
||||
let generic_args = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(
|
||||
|(_, lifetime, res)| {
|
||||
let id = self.next_node_id();
|
||||
let res = res.unwrap_or(
|
||||
self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
|
||||
);
|
||||
hir::GenericArg::Lifetime(self.new_named_lifetime_with_res(id, lifetime.ident, res))
|
||||
},
|
||||
));
|
||||
let generic_args = self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.iter()
|
||||
.map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
|
||||
);
|
||||
|
||||
// Create the `Foo<...>` reference itself. Note that the `type
|
||||
// Foo = impl Trait` is, internally, created as a child of the
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::iter;
|
||||
|
||||
use either::Either;
|
||||
use hir::PatField;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{
|
||||
@ -28,6 +27,7 @@ use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use std::iter;
|
||||
|
||||
use crate::borrow_set::TwoPhaseActivation;
|
||||
use crate::borrowck_errors;
|
||||
@ -982,7 +982,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
&msg_borrow,
|
||||
None,
|
||||
);
|
||||
self.suggest_split_at_mut_if_applicable(
|
||||
self.suggest_slice_method_if_applicable(
|
||||
&mut err,
|
||||
place,
|
||||
issued_borrow.borrowed_place,
|
||||
@ -992,6 +992,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
issued_borrow.borrowed_place,
|
||||
&issued_spans,
|
||||
);
|
||||
self.explain_iterator_advancement_in_for_loop_if_applicable(
|
||||
&mut err,
|
||||
span,
|
||||
&issued_spans,
|
||||
);
|
||||
err
|
||||
}
|
||||
|
||||
@ -1262,7 +1267,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
fn suggest_split_at_mut_if_applicable(
|
||||
fn suggest_slice_method_if_applicable(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
place: Place<'tcx>,
|
||||
@ -1274,7 +1279,75 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
err.help(
|
||||
"consider using `.split_at_mut(position)` or similar method to obtain \
|
||||
two mutable non-overlapping sub-slices",
|
||||
);
|
||||
)
|
||||
.help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggest using `while let` for call `next` on an iterator in a for loop.
|
||||
///
|
||||
/// For example:
|
||||
/// ```ignore (illustrative)
|
||||
///
|
||||
/// for x in iter {
|
||||
/// ...
|
||||
/// iter.next()
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
span: Span,
|
||||
issued_spans: &UseSpans<'tcx>,
|
||||
) {
|
||||
let issue_span = issued_spans.args_or_use();
|
||||
let tcx = self.infcx.tcx;
|
||||
let hir = tcx.hir();
|
||||
|
||||
let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return };
|
||||
let typeck_results = tcx.typeck(self.mir_def_id());
|
||||
|
||||
struct ExprFinder<'hir> {
|
||||
issue_span: Span,
|
||||
expr_span: Span,
|
||||
body_expr: Option<&'hir hir::Expr<'hir>>,
|
||||
loop_bind: Option<Symbol>,
|
||||
}
|
||||
impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
|
||||
fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
|
||||
if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind &&
|
||||
let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind &&
|
||||
let hir::ExprKind::Call(path, _args) = call.kind &&
|
||||
let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind &&
|
||||
let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind &&
|
||||
let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path &&
|
||||
let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field &&
|
||||
self.issue_span.source_equal(call.span) {
|
||||
self.loop_bind = Some(ident.name);
|
||||
}
|
||||
|
||||
if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind &&
|
||||
body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) {
|
||||
self.body_expr = Some(ex);
|
||||
}
|
||||
|
||||
hir::intravisit::walk_expr(self, ex);
|
||||
}
|
||||
}
|
||||
let mut finder =
|
||||
ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None };
|
||||
finder.visit_expr(hir.body(body_id).value);
|
||||
|
||||
if let Some(loop_bind) = finder.loop_bind &&
|
||||
let Some(body_expr) = finder.body_expr &&
|
||||
let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) &&
|
||||
let Some(trait_did) = tcx.trait_of_item(def_id) &&
|
||||
tcx.is_diagnostic_item(sym::Iterator, trait_did) {
|
||||
err.note(format!(
|
||||
"a for loop advances the iterator for you, the result is stored in `{}`.",
|
||||
loop_bind
|
||||
));
|
||||
err.help("if you want to call `next` on a iterator within the loop, consider using `while let`.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1730,18 +1803,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
(
|
||||
Some(name),
|
||||
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
|
||||
) => self.report_escaping_closure_capture(
|
||||
borrow_spans,
|
||||
borrow_span,
|
||||
&RegionName {
|
||||
name: self.synthesize_region_name(),
|
||||
source: RegionNameSource::Static,
|
||||
},
|
||||
ConstraintCategory::CallArgument(None),
|
||||
var_or_use_span,
|
||||
&format!("`{}`", name),
|
||||
"block",
|
||||
),
|
||||
) if borrow_spans.for_generator() || borrow_spans.for_closure() => self
|
||||
.report_escaping_closure_capture(
|
||||
borrow_spans,
|
||||
borrow_span,
|
||||
&RegionName {
|
||||
name: self.synthesize_region_name(),
|
||||
source: RegionNameSource::Static,
|
||||
},
|
||||
ConstraintCategory::CallArgument(None),
|
||||
var_or_use_span,
|
||||
&format!("`{}`", name),
|
||||
"block",
|
||||
),
|
||||
(
|
||||
Some(name),
|
||||
BorrowExplanation::MustBeValidFor {
|
||||
@ -1754,7 +1828,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
span,
|
||||
..
|
||||
},
|
||||
) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
|
||||
) if borrow_spans.for_generator() || borrow_spans.for_closure() => self
|
||||
.report_escaping_closure_capture(
|
||||
borrow_spans,
|
||||
borrow_span,
|
||||
|
@ -1146,6 +1146,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// Avoid pointing to the same function in multiple different
|
||||
// error messages.
|
||||
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
|
||||
self.explain_iterator_advancement_in_for_loop_if_applicable(
|
||||
err,
|
||||
span,
|
||||
&move_spans,
|
||||
);
|
||||
|
||||
let func = tcx.def_path_str(method_did);
|
||||
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
|
||||
func,
|
||||
|
@ -222,7 +222,7 @@ fn do_mir_borrowck<'tcx>(
|
||||
|
||||
let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) =
|
||||
match MoveData::gather_moves(&body, tcx, param_env) {
|
||||
Ok((_, move_data)) => (move_data, Vec::new()),
|
||||
Ok(move_data) => (move_data, Vec::new()),
|
||||
Err((move_data, move_errors)) => (move_data, move_errors),
|
||||
};
|
||||
let promoted_errors = promoted
|
||||
|
@ -1,4 +1,5 @@
|
||||
builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function
|
||||
builtin_macros_alloc_must_statics = allocators must be statics
|
||||
|
||||
builtin_macros_asm_clobber_abi = clobber_abi
|
||||
builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs
|
||||
@ -56,6 +57,9 @@ builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `e
|
||||
.label = not applicable here
|
||||
.label2 = not a `struct`, `enum` or `union`
|
||||
|
||||
builtin_macros_bench_sig = functions used as benches must have signature `fn(&mut Bencher) -> impl Termination`
|
||||
|
||||
|
||||
builtin_macros_cannot_derive_union = this trait cannot be derived for unions
|
||||
|
||||
builtin_macros_cfg_accessible_has_args = `cfg_accessible` path cannot accept arguments
|
||||
@ -84,6 +88,7 @@ builtin_macros_concat_bytes_non_u8 = numeric literal is not a `u8`
|
||||
builtin_macros_concat_bytes_oob = numeric literal is out of bounds
|
||||
|
||||
builtin_macros_concat_bytestr = cannot concatenate a byte string literal
|
||||
builtin_macros_concat_c_str_lit = cannot concatenate a C string literal
|
||||
|
||||
builtin_macros_concat_idents_ident_args = `concat_idents!()` requires ident args
|
||||
|
||||
@ -111,6 +116,10 @@ builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments
|
||||
|
||||
builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern
|
||||
|
||||
builtin_macros_expected_register_class_or_explicit_register = expected register class or explicit register
|
||||
|
||||
builtin_macros_export_macro_rules = cannot export macro_rules! macros from a `proc-macro` crate type currently
|
||||
|
||||
builtin_macros_format_duplicate_arg = duplicate argument named `{$ident}`
|
||||
.label1 = previously here
|
||||
.label2 = duplicate argument
|
||||
@ -158,6 +167,8 @@ builtin_macros_format_unused_args = multiple unused formatting arguments
|
||||
|
||||
builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!`
|
||||
|
||||
builtin_macros_invalid_crate_attribute = invalid crate attribute
|
||||
|
||||
builtin_macros_multiple_default_attrs = multiple `#[default]` attributes
|
||||
.note = only one `#[default]` attribute is needed
|
||||
.label = `#[default]` used here
|
||||
@ -177,6 +188,8 @@ builtin_macros_no_default_variant = no default declared
|
||||
.help = make a unit variant default by placing `#[default]` above it
|
||||
.suggestion = make `{$ident}` default
|
||||
|
||||
builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi`
|
||||
|
||||
builtin_macros_non_exhaustive_default = default variant must be exhaustive
|
||||
.label = declared `#[non_exhaustive]` here
|
||||
.help = consider a manual implementation of `Default`
|
||||
@ -184,12 +197,20 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive
|
||||
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants
|
||||
.help = consider a manual implementation of `Default`
|
||||
|
||||
builtin_macros_proc_macro = `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
|
||||
|
||||
builtin_macros_requires_cfg_pattern =
|
||||
macro requires a cfg-pattern as an argument
|
||||
.label = cfg-pattern required
|
||||
|
||||
builtin_macros_should_panic = functions using `#[should_panic]` must return `()`
|
||||
|
||||
builtin_macros_sugg = consider using a positional formatting argument instead
|
||||
|
||||
builtin_macros_test_arg_non_lifetime = functions used as tests can not have any non-lifetime generic parameters
|
||||
|
||||
builtin_macros_test_args = functions used as tests can not have any arguments
|
||||
|
||||
builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
|
||||
.label = `{$kind}` because of this
|
||||
|
||||
@ -198,6 +219,10 @@ builtin_macros_test_case_non_item = `#[test_case]` attribute is only allowed on
|
||||
builtin_macros_test_runner_invalid = `test_runner` argument must be a path
|
||||
builtin_macros_test_runner_nargs = `#![test_runner(..)]` accepts exactly 1 argument
|
||||
|
||||
builtin_macros_tests_not_support = building tests with panic=abort is not supported without `-Zpanic_abort_tests`
|
||||
|
||||
builtin_macros_trace_macros = trace_macros! accepts only `true` or `false`
|
||||
|
||||
builtin_macros_unexpected_lit = expected path to a trait, found literal
|
||||
.label = not a trait
|
||||
.str_lit = try using `#[derive({$sym})]`
|
||||
|
@ -371,11 +371,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
|
||||
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
|
||||
|
||||
if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
|
||||
let err = p.sess.span_diagnostic.struct_span_err(
|
||||
p.token.span,
|
||||
"at least one abi must be provided as an argument to `clobber_abi`",
|
||||
);
|
||||
return Err(err);
|
||||
return Err(p.sess.span_diagnostic.create_err(errors::NonABI { span: p.token.span }));
|
||||
}
|
||||
|
||||
let mut new_abis = Vec::new();
|
||||
@ -428,9 +424,9 @@ fn parse_reg<'a>(
|
||||
ast::InlineAsmRegOrRegClass::Reg(symbol)
|
||||
}
|
||||
_ => {
|
||||
return Err(
|
||||
p.struct_span_err(p.token.span, "expected register class or explicit register")
|
||||
);
|
||||
return Err(p.sess.create_err(errors::ExpectedRegisterClassOrExplicitRegister {
|
||||
span: p.token.span,
|
||||
}));
|
||||
}
|
||||
};
|
||||
p.bump();
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Attributes injected into the crate root from command line using `-Z crate-attr`.
|
||||
|
||||
use crate::errors;
|
||||
use rustc_ast::attr::mk_attr;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::{self as ast, AttrItem, AttrStyle};
|
||||
@ -24,7 +25,9 @@ pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String])
|
||||
};
|
||||
let end_span = parser.token.span;
|
||||
if parser.token != token::Eof {
|
||||
parse_sess.span_diagnostic.span_err(start_span.to(end_span), "invalid crate attribute");
|
||||
parse_sess
|
||||
.span_diagnostic
|
||||
.emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ pub fn expand_concat(
|
||||
accumulator.push_str(&b.to_string());
|
||||
}
|
||||
Ok(ast::LitKind::CStr(..)) => {
|
||||
cx.span_err(e.span, "cannot concatenate a C string literal");
|
||||
cx.emit_err(errors::ConcatCStrLit{ span: e.span});
|
||||
has_errors = true;
|
||||
}
|
||||
Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
|
||||
|
@ -21,7 +21,7 @@ fn invalid_type_err(
|
||||
Ok(ast::LitKind::CStr(_, _)) => {
|
||||
// FIXME(c_str_literals): should concatenation of C string literals
|
||||
// include the null bytes in the end?
|
||||
cx.span_err(span, "cannot concatenate C string literals");
|
||||
cx.emit_err(errors::ConcatCStrLit { span: span });
|
||||
}
|
||||
Ok(ast::LitKind::Char(_)) => {
|
||||
let sugg =
|
||||
|
@ -87,6 +87,83 @@ pub(crate) struct ConcatBytestr {
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_concat_c_str_lit)]
|
||||
pub(crate) struct ConcatCStrLit {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_export_macro_rules)]
|
||||
pub(crate) struct ExportMacroRules {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_proc_macro)]
|
||||
pub(crate) struct ProcMacro {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_invalid_crate_attribute)]
|
||||
pub(crate) struct InvalidCrateAttr {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_non_abi)]
|
||||
pub(crate) struct NonABI {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_trace_macros)]
|
||||
pub(crate) struct TraceMacros {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_bench_sig)]
|
||||
pub(crate) struct BenchSig {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_test_arg_non_lifetime)]
|
||||
pub(crate) struct TestArgNonLifetime {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_should_panic)]
|
||||
pub(crate) struct ShouldPanic {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_test_args)]
|
||||
pub(crate) struct TestArgs {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_alloc_must_statics)]
|
||||
pub(crate) struct AllocMustStatics {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_concat_bytes_invalid)]
|
||||
pub(crate) struct ConcatBytesInvalid {
|
||||
@ -201,6 +278,10 @@ pub(crate) struct BadDeriveTarget {
|
||||
pub(crate) item: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_tests_not_support)]
|
||||
pub(crate) struct TestsNotSupport {}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_unexpected_lit, code = "E0777")]
|
||||
pub(crate) struct BadDeriveLit {
|
||||
@ -732,3 +813,10 @@ pub(crate) struct TestRunnerNargs {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_expected_register_class_or_explicit_register)]
|
||||
pub(crate) struct ExpectedRegisterClassOrExplicitRegister {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::util::check_builtin_macro_attribute;
|
||||
|
||||
use crate::errors;
|
||||
use rustc_ast::expand::allocator::{
|
||||
global_fn_name, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS,
|
||||
};
|
||||
@ -34,7 +35,7 @@ pub fn expand(
|
||||
{
|
||||
(item, true, ecx.with_def_site_ctxt(ty.span))
|
||||
} else {
|
||||
ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
|
||||
ecx.sess.parse_sess.span_diagnostic.emit_err(errors::AllocMustStatics{span: item.span()});
|
||||
return vec![orig_item];
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::errors;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_ast::{self as ast, attr, NodeId};
|
||||
@ -83,12 +84,7 @@ pub fn inject(
|
||||
impl<'a> CollectProcMacros<'a> {
|
||||
fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
|
||||
if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() {
|
||||
self.handler.span_err(
|
||||
sp,
|
||||
"`proc-macro` crate types currently cannot export any items other \
|
||||
than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, \
|
||||
or `#[proc_macro_attribute]`",
|
||||
);
|
||||
self.handler.emit_err(errors::ProcMacro { span: sp });
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,9 +153,9 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
|
||||
fn visit_item(&mut self, item: &'a ast::Item) {
|
||||
if let ast::ItemKind::MacroDef(..) = item.kind {
|
||||
if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
|
||||
let msg =
|
||||
"cannot export macro_rules! macros from a `proc-macro` crate type currently";
|
||||
self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
|
||||
self.handler.emit_err(errors::ExportMacroRules {
|
||||
span: self.source_map.guess_head_span(item.span),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,12 +576,7 @@ fn check_bench_signature(
|
||||
// N.B., inadequate check, but we're running
|
||||
// well before resolve, can't get too deep.
|
||||
if f.sig.decl.inputs.len() != 1 {
|
||||
return Err(cx.sess.parse_sess.span_diagnostic.span_err(
|
||||
i.span,
|
||||
"functions used as benches must have \
|
||||
signature `fn(&mut Bencher) -> impl Termination`",
|
||||
));
|
||||
return Err(cx.sess.parse_sess.span_diagnostic.emit_err(errors::BenchSig { span: i.span }));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -63,10 +63,7 @@ pub fn inject(krate: &mut ast::Crate, sess: &Session, resolver: &mut dyn Resolve
|
||||
// Silently allow compiling with panic=abort on these platforms,
|
||||
// but with old behavior (abort if a test fails).
|
||||
} else {
|
||||
span_diagnostic.err(
|
||||
"building tests with panic=abort is not supported \
|
||||
without `-Zpanic_abort_tests`",
|
||||
);
|
||||
span_diagnostic.emit_err(errors::TestsNotSupport {});
|
||||
}
|
||||
PanicStrategy::Unwind
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::errors;
|
||||
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||
use rustc_expand::base::{self, ExtCtxt};
|
||||
use rustc_span::symbol::kw;
|
||||
@ -20,7 +21,7 @@ pub fn expand_trace_macros(
|
||||
};
|
||||
err |= cursor.next().is_some();
|
||||
if err {
|
||||
cx.span_err(sp, "trace_macros! accepts only `true` or `false`")
|
||||
cx.emit_err(errors::TraceMacros { span: sp });
|
||||
} else {
|
||||
cx.set_trace_macros(value);
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ impl DebugContext {
|
||||
|
||||
match tcx.sess.source_map().lookup_line(span.lo()) {
|
||||
Ok(SourceFileAndLine { sf: file, line }) => {
|
||||
let line_pos = file.line_begin_pos(span.lo());
|
||||
let line_pos = file.lines(|lines| lines[line]);
|
||||
|
||||
(
|
||||
file,
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Set and unset common attributes on LLVM values.
|
||||
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_data_structures::small_str::SmallStr;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
@ -481,8 +480,8 @@ pub fn from_fn_attrs<'ll, 'tcx>(
|
||||
|
||||
let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
|
||||
let function_features = function_features.iter().map(|s| s.as_str());
|
||||
let target_features =
|
||||
global_features.chain(function_features).intersperse(",").collect::<SmallStr<1024>>();
|
||||
let target_features: String =
|
||||
global_features.chain(function_features).intersperse(",").collect();
|
||||
if !target_features.is_empty() {
|
||||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::back::write::{self, save_temp_bitcode, DiagnosticHandlers};
|
||||
use crate::back::write::{self, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers};
|
||||
use crate::errors::{
|
||||
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
|
||||
};
|
||||
@ -302,7 +302,13 @@ fn fat_lto(
|
||||
// The linking steps below may produce errors and diagnostics within LLVM
|
||||
// which we'd like to handle and print, so set up our diagnostic handlers
|
||||
// (which get unregistered when they go out of scope below).
|
||||
let _handler = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
|
||||
let _handler = DiagnosticHandlers::new(
|
||||
cgcx,
|
||||
diag_handler,
|
||||
llcx,
|
||||
&module,
|
||||
CodegenDiagnosticsStage::LTO,
|
||||
);
|
||||
|
||||
// For all other modules we codegened we'll need to link them into our own
|
||||
// bitcode. All modules were codegened in their own LLVM context, however,
|
||||
|
@ -268,6 +268,16 @@ pub(crate) fn save_temp_bitcode(
|
||||
}
|
||||
}
|
||||
|
||||
/// In what context is a dignostic handler being attached to a codegen unit?
|
||||
pub enum CodegenDiagnosticsStage {
|
||||
/// Prelink optimization stage.
|
||||
Opt,
|
||||
/// LTO/ThinLTO postlink optimization stage.
|
||||
LTO,
|
||||
/// Code generation.
|
||||
Codegen,
|
||||
}
|
||||
|
||||
pub struct DiagnosticHandlers<'a> {
|
||||
data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a Handler),
|
||||
llcx: &'a llvm::Context,
|
||||
@ -279,6 +289,8 @@ impl<'a> DiagnosticHandlers<'a> {
|
||||
cgcx: &'a CodegenContext<LlvmCodegenBackend>,
|
||||
handler: &'a Handler,
|
||||
llcx: &'a llvm::Context,
|
||||
module: &ModuleCodegen<ModuleLlvm>,
|
||||
stage: CodegenDiagnosticsStage,
|
||||
) -> Self {
|
||||
let remark_passes_all: bool;
|
||||
let remark_passes: Vec<CString>;
|
||||
@ -295,6 +307,20 @@ impl<'a> DiagnosticHandlers<'a> {
|
||||
};
|
||||
let remark_passes: Vec<*const c_char> =
|
||||
remark_passes.iter().map(|name: &CString| name.as_ptr()).collect();
|
||||
let remark_file = cgcx
|
||||
.remark_dir
|
||||
.as_ref()
|
||||
// Use the .opt.yaml file suffix, which is supported by LLVM's opt-viewer.
|
||||
.map(|dir| {
|
||||
let stage_suffix = match stage {
|
||||
CodegenDiagnosticsStage::Codegen => "codegen",
|
||||
CodegenDiagnosticsStage::Opt => "opt",
|
||||
CodegenDiagnosticsStage::LTO => "lto",
|
||||
};
|
||||
dir.join(format!("{}.{stage_suffix}.opt.yaml", module.name))
|
||||
})
|
||||
.and_then(|dir| dir.to_str().and_then(|p| CString::new(p).ok()));
|
||||
|
||||
let data = Box::into_raw(Box::new((cgcx, handler)));
|
||||
unsafe {
|
||||
let old_handler = llvm::LLVMRustContextGetDiagnosticHandler(llcx);
|
||||
@ -305,6 +331,9 @@ impl<'a> DiagnosticHandlers<'a> {
|
||||
remark_passes_all,
|
||||
remark_passes.as_ptr(),
|
||||
remark_passes.len(),
|
||||
// The `as_ref()` is important here, otherwise the `CString` will be dropped
|
||||
// too soon!
|
||||
remark_file.as_ref().map(|dir| dir.as_ptr()).unwrap_or(std::ptr::null()),
|
||||
);
|
||||
DiagnosticHandlers { data, llcx, old_handler }
|
||||
}
|
||||
@ -523,7 +552,8 @@ pub(crate) unsafe fn optimize(
|
||||
|
||||
let llmod = module.module_llvm.llmod();
|
||||
let llcx = &*module.module_llvm.llcx;
|
||||
let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
|
||||
let _handlers =
|
||||
DiagnosticHandlers::new(cgcx, diag_handler, llcx, module, CodegenDiagnosticsStage::Opt);
|
||||
|
||||
let module_name = module.name.clone();
|
||||
let module_name = Some(&module_name[..]);
|
||||
@ -582,7 +612,13 @@ pub(crate) unsafe fn codegen(
|
||||
let tm = &*module.module_llvm.tm;
|
||||
let module_name = module.name.clone();
|
||||
let module_name = Some(&module_name[..]);
|
||||
let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
|
||||
let _handlers = DiagnosticHandlers::new(
|
||||
cgcx,
|
||||
diag_handler,
|
||||
llcx,
|
||||
&module,
|
||||
CodegenDiagnosticsStage::Codegen,
|
||||
);
|
||||
|
||||
if cgcx.msvc_imps_needed {
|
||||
create_msvc_imps(cgcx, llcx, llmod);
|
||||
@ -775,7 +811,6 @@ pub(crate) unsafe fn codegen(
|
||||
}
|
||||
|
||||
record_llvm_cgu_instructions_stats(&cgcx.prof, llmod);
|
||||
drop(handlers);
|
||||
}
|
||||
|
||||
// `.dwo` files are only emitted if:
|
||||
@ -875,14 +910,19 @@ unsafe fn embed_bitcode(
|
||||
// passed though then these sections will show up in the final output.
|
||||
// Additionally the flag that we need to set here is `SHF_EXCLUDE`.
|
||||
//
|
||||
// * XCOFF - AIX linker ignores content in .ipa and .info if no auxiliary
|
||||
// symbol associated with these sections.
|
||||
//
|
||||
// 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");
|
||||
if is_apple
|
||||
|| is_aix
|
||||
|| cgcx.opts.target_triple.triple().starts_with("wasm")
|
||||
|| cgcx.opts.target_triple.triple().starts_with("asmjs")
|
||||
{
|
||||
@ -895,7 +935,13 @@ unsafe fn embed_bitcode(
|
||||
);
|
||||
llvm::LLVMSetInitializer(llglobal, llconst);
|
||||
|
||||
let section = if is_apple { c"__LLVM,__bitcode" } else { c".llvmbc" };
|
||||
let section = if is_apple {
|
||||
c"__LLVM,__bitcode"
|
||||
} else if is_aix {
|
||||
c".ipa"
|
||||
} else {
|
||||
c".llvmbc"
|
||||
};
|
||||
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
||||
@ -907,7 +953,13 @@ unsafe fn embed_bitcode(
|
||||
c"rustc.embedded.cmdline".as_ptr().cast(),
|
||||
);
|
||||
llvm::LLVMSetInitializer(llglobal, llconst);
|
||||
let section = if is_apple { c"__LLVM,__cmdline" } else { c".llvmcmd" };
|
||||
let section = if is_apple {
|
||||
c"__LLVM,__cmdline"
|
||||
} else if is_aix {
|
||||
c".info"
|
||||
} else {
|
||||
c".llvmcmd"
|
||||
};
|
||||
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
} else {
|
||||
|
@ -23,6 +23,7 @@ use rustc_span::Span;
|
||||
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions};
|
||||
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
|
||||
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
@ -225,7 +226,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
let args = self.check_call("invoke", llty, llfn, args);
|
||||
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
|
||||
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
|
||||
let mut bundles = vec![funclet_bundle];
|
||||
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
|
||||
if let Some(funclet_bundle) = funclet_bundle {
|
||||
bundles.push(funclet_bundle);
|
||||
}
|
||||
|
||||
// Emit CFI pointer type membership test
|
||||
self.cfi_type_test(fn_attrs, fn_abi, llfn);
|
||||
@ -233,9 +237,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
// Emit KCFI operand bundle
|
||||
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
if let Some(kcfi_bundle) = kcfi_bundle {
|
||||
bundles.push(kcfi_bundle);
|
||||
}
|
||||
|
||||
bundles.retain(|bundle| bundle.is_some());
|
||||
let invoke = unsafe {
|
||||
llvm::LLVMRustBuildInvoke(
|
||||
self.llbuilder,
|
||||
@ -1181,7 +1186,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
|
||||
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
|
||||
let mut bundles = vec![funclet_bundle];
|
||||
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
|
||||
if let Some(funclet_bundle) = funclet_bundle {
|
||||
bundles.push(funclet_bundle);
|
||||
}
|
||||
|
||||
// Emit CFI pointer type membership test
|
||||
self.cfi_type_test(fn_attrs, fn_abi, llfn);
|
||||
@ -1189,9 +1197,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
// Emit KCFI operand bundle
|
||||
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
if let Some(kcfi_bundle) = kcfi_bundle {
|
||||
bundles.push(kcfi_bundle);
|
||||
}
|
||||
|
||||
bundles.retain(|bundle| bundle.is_some());
|
||||
let call = unsafe {
|
||||
llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
|
@ -8,7 +8,7 @@ use crate::llvm_util;
|
||||
use crate::type_::Type;
|
||||
use crate::value::Value;
|
||||
|
||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||
use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_data_structures::base_n;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
@ -532,19 +532,28 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
if let Some(llpersonality) = self.eh_personality.get() {
|
||||
return llpersonality;
|
||||
}
|
||||
|
||||
let name = if wants_msvc_seh(self.sess()) {
|
||||
Some("__CxxFrameHandler3")
|
||||
} else if wants_wasm_eh(self.sess()) {
|
||||
// LLVM specifically tests for the name of the personality function
|
||||
// There is no need for this function to exist anywhere, it will
|
||||
// not be called. However, its name has to be "__gxx_wasm_personality_v0"
|
||||
// for native wasm exceptions.
|
||||
Some("__gxx_wasm_personality_v0")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let tcx = self.tcx;
|
||||
let llfn = match tcx.lang_items().eh_personality() {
|
||||
Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr(
|
||||
Some(def_id) if name.is_none() => self.get_fn_addr(
|
||||
ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, ty::List::empty())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
),
|
||||
_ => {
|
||||
let name = if wants_msvc_seh(self.sess()) {
|
||||
"__CxxFrameHandler3"
|
||||
} else {
|
||||
"rust_eh_personality"
|
||||
};
|
||||
let name = name.unwrap_or("rust_eh_personality");
|
||||
if let Some(llfn) = self.get_declared_value(name) {
|
||||
llfn
|
||||
} else {
|
||||
@ -662,6 +671,10 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
let t_f32 = self.type_f32();
|
||||
let t_f64 = self.type_f64();
|
||||
let t_metadata = self.type_metadata();
|
||||
let t_token = self.type_token();
|
||||
|
||||
ifn!("llvm.wasm.get.exception", fn(t_token) -> i8p);
|
||||
ifn!("llvm.wasm.get.ehselector", fn(t_token) -> t_i32);
|
||||
|
||||
ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32);
|
||||
ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32);
|
||||
|
@ -65,10 +65,10 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
debug_context.scopes[parent]
|
||||
} else {
|
||||
// The root is the function itself.
|
||||
let loc = cx.lookup_debug_loc(mir.span.lo());
|
||||
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
|
||||
debug_context.scopes[scope] = DebugScope {
|
||||
file_start_pos: loc.file.start_pos,
|
||||
file_end_pos: loc.file.end_pos,
|
||||
file_start_pos: file.start_pos,
|
||||
file_end_pos: file.end_pos,
|
||||
..debug_context.scopes[scope]
|
||||
};
|
||||
instantiated.insert(scope);
|
||||
|
@ -262,7 +262,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.line_begin_pos(pos);
|
||||
let line_pos = file.lines(|lines| lines[line]);
|
||||
|
||||
// Use 1-based indexing.
|
||||
let line = (line + 1) as u32;
|
||||
@ -331,7 +331,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), fn_signature)
|
||||
};
|
||||
|
||||
let mut name = String::new();
|
||||
let mut name = String::with_capacity(64);
|
||||
type_names::push_item_name(tcx, def_id, false, &mut name);
|
||||
|
||||
// Find the enclosing function, in case this is a closure.
|
||||
|
@ -28,7 +28,7 @@ pub fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DISco
|
||||
.map(|parent| item_namespace(cx, DefId { krate: def_id.krate, index: parent }));
|
||||
|
||||
let namespace_name_string = {
|
||||
let mut output = String::new();
|
||||
let mut output = String::with_capacity(64);
|
||||
type_names::push_item_name(cx.tcx, def_id, false, &mut output);
|
||||
output
|
||||
};
|
||||
|
@ -7,7 +7,7 @@ use crate::type_of::LayoutLlvmExt;
|
||||
use crate::va_arg::emit_va_arg;
|
||||
use crate::value::Value;
|
||||
|
||||
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh};
|
||||
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
|
||||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||
use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization};
|
||||
use rustc_codegen_ssa::mir::operand::OperandRef;
|
||||
@ -452,6 +452,8 @@ fn try_intrinsic<'ll>(
|
||||
bx.store(bx.const_i32(0), dest, ret_align);
|
||||
} else if wants_msvc_seh(bx.sess()) {
|
||||
codegen_msvc_try(bx, try_func, data, catch_func, dest);
|
||||
} else if wants_wasm_eh(bx.sess()) {
|
||||
codegen_wasm_try(bx, try_func, data, catch_func, dest);
|
||||
} else if bx.sess().target.os == "emscripten" {
|
||||
codegen_emcc_try(bx, try_func, data, catch_func, dest);
|
||||
} else {
|
||||
@ -610,6 +612,80 @@ fn codegen_msvc_try<'ll>(
|
||||
bx.store(ret, dest, i32_align);
|
||||
}
|
||||
|
||||
// WASM's definition of the `rust_try` function.
|
||||
fn codegen_wasm_try<'ll>(
|
||||
bx: &mut Builder<'_, 'll, '_>,
|
||||
try_func: &'ll Value,
|
||||
data: &'ll Value,
|
||||
catch_func: &'ll Value,
|
||||
dest: &'ll Value,
|
||||
) {
|
||||
let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
|
||||
bx.set_personality_fn(bx.eh_personality());
|
||||
|
||||
let normal = bx.append_sibling_block("normal");
|
||||
let catchswitch = bx.append_sibling_block("catchswitch");
|
||||
let catchpad = bx.append_sibling_block("catchpad");
|
||||
let caught = bx.append_sibling_block("caught");
|
||||
|
||||
let try_func = llvm::get_param(bx.llfn(), 0);
|
||||
let data = llvm::get_param(bx.llfn(), 1);
|
||||
let catch_func = llvm::get_param(bx.llfn(), 2);
|
||||
|
||||
// We're generating an IR snippet that looks like:
|
||||
//
|
||||
// declare i32 @rust_try(%try_func, %data, %catch_func) {
|
||||
// %slot = alloca i8*
|
||||
// invoke %try_func(%data) to label %normal unwind label %catchswitch
|
||||
//
|
||||
// normal:
|
||||
// ret i32 0
|
||||
//
|
||||
// catchswitch:
|
||||
// %cs = catchswitch within none [%catchpad] unwind to caller
|
||||
//
|
||||
// catchpad:
|
||||
// %tok = catchpad within %cs [null]
|
||||
// %ptr = call @llvm.wasm.get.exception(token %tok)
|
||||
// %sel = call @llvm.wasm.get.ehselector(token %tok)
|
||||
// call %catch_func(%data, %ptr)
|
||||
// catchret from %tok to label %caught
|
||||
//
|
||||
// caught:
|
||||
// ret i32 1
|
||||
// }
|
||||
//
|
||||
let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
|
||||
bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None);
|
||||
|
||||
bx.switch_to_block(normal);
|
||||
bx.ret(bx.const_i32(0));
|
||||
|
||||
bx.switch_to_block(catchswitch);
|
||||
let cs = bx.catch_switch(None, None, &[catchpad]);
|
||||
|
||||
bx.switch_to_block(catchpad);
|
||||
let null = bx.const_null(bx.type_i8p());
|
||||
let funclet = bx.catch_pad(cs, &[null]);
|
||||
|
||||
let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]);
|
||||
let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]);
|
||||
|
||||
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
|
||||
bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet));
|
||||
bx.catch_ret(&funclet, caught);
|
||||
|
||||
bx.switch_to_block(caught);
|
||||
bx.ret(bx.const_i32(1));
|
||||
});
|
||||
|
||||
// Note that no invoke is used here because by definition this function
|
||||
// can't panic (that's what it's catching).
|
||||
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None);
|
||||
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
||||
bx.store(ret, dest, i32_align);
|
||||
}
|
||||
|
||||
// Definition of the standard `try` function for Rust using the GNU-like model
|
||||
// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
|
||||
// instructions).
|
||||
|
@ -1071,6 +1071,7 @@ extern "C" {
|
||||
|
||||
// Operations on other types
|
||||
pub fn LLVMVoidTypeInContext(C: &Context) -> &Type;
|
||||
pub fn LLVMTokenTypeInContext(C: &Context) -> &Type;
|
||||
pub fn LLVMMetadataTypeInContext(C: &Context) -> &Type;
|
||||
|
||||
// Operations on all values
|
||||
@ -1301,7 +1302,7 @@ extern "C" {
|
||||
NumArgs: c_uint,
|
||||
Then: &'a BasicBlock,
|
||||
Catch: &'a BasicBlock,
|
||||
OpBundles: *const Option<&OperandBundleDef<'a>>,
|
||||
OpBundles: *const &OperandBundleDef<'a>,
|
||||
NumOpBundles: c_uint,
|
||||
Name: *const c_char,
|
||||
) -> &'a Value;
|
||||
@ -1673,7 +1674,7 @@ extern "C" {
|
||||
Fn: &'a Value,
|
||||
Args: *const &'a Value,
|
||||
NumArgs: c_uint,
|
||||
OpBundles: *const Option<&OperandBundleDef<'a>>,
|
||||
OpBundles: *const &OperandBundleDef<'a>,
|
||||
NumOpBundles: c_uint,
|
||||
) -> &'a Value;
|
||||
pub fn LLVMRustBuildMemCpy<'a>(
|
||||
@ -2512,6 +2513,7 @@ extern "C" {
|
||||
remark_all_passes: bool,
|
||||
remark_passes: *const *const c_char,
|
||||
remark_passes_len: usize,
|
||||
remark_file: *const c_char,
|
||||
);
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
|
@ -52,6 +52,10 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
unsafe { llvm::LLVMVoidTypeInContext(self.llcx) }
|
||||
}
|
||||
|
||||
pub(crate) fn type_token(&self) -> &'ll Type {
|
||||
unsafe { llvm::LLVMTokenTypeInContext(self.llcx) }
|
||||
}
|
||||
|
||||
pub(crate) fn type_metadata(&self) -> &'ll Type {
|
||||
unsafe { llvm::LLVMMetadataTypeInContext(self.llcx) }
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
|
||||
|
||||
codegen_ssa_erroneous_constant = erroneous constant encountered
|
||||
|
||||
codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
|
||||
|
||||
codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
|
||||
|
||||
codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
|
||||
|
@ -12,7 +12,7 @@ use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME};
|
||||
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
|
||||
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip};
|
||||
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
|
||||
use rustc_session::cstore::DllImport;
|
||||
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
|
||||
@ -1688,7 +1688,7 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
|
||||
/// instead of being found somewhere on the host system.
|
||||
/// We only provide such support for a very limited number of targets.
|
||||
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
|
||||
if let Some(self_contained) = sess.opts.cg.link_self_contained {
|
||||
if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
|
||||
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
|
||||
sess.emit_err(errors::UnsupportedLinkSelfContained);
|
||||
}
|
||||
@ -2246,7 +2246,8 @@ fn add_order_independent_options(
|
||||
out_filename: &Path,
|
||||
tmpdir: &Path,
|
||||
) {
|
||||
add_gcc_ld_path(cmd, sess, flavor);
|
||||
// Take care of the flavors and CLI options requesting the `lld` linker.
|
||||
add_lld_args(cmd, sess, flavor);
|
||||
|
||||
add_apple_sdk(cmd, sess, flavor);
|
||||
|
||||
@ -2948,55 +2949,66 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro
|
||||
}
|
||||
}
|
||||
|
||||
fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||
if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld {
|
||||
if let LinkerFlavor::Gnu(Cc::Yes, _)
|
||||
| LinkerFlavor::Darwin(Cc::Yes, _)
|
||||
| LinkerFlavor::WasmLld(Cc::Yes) = flavor
|
||||
{
|
||||
match ld_impl {
|
||||
LdImpl::Lld => {
|
||||
// Implement the "self-contained" part of -Zgcc-ld
|
||||
// by adding rustc distribution directories to the tool search path.
|
||||
for path in sess.get_tools_search_paths(false) {
|
||||
cmd.arg({
|
||||
let mut arg = OsString::from("-B");
|
||||
arg.push(path.join("gcc-ld"));
|
||||
arg
|
||||
});
|
||||
}
|
||||
// Implement the "linker flavor" part of -Zgcc-ld
|
||||
// by asking cc to use some kind of lld.
|
||||
cmd.arg("-fuse-ld=lld");
|
||||
/// When using the linker flavors opting in to `lld`, or the unstable `-Zgcc-ld=lld` flag, add the
|
||||
/// necessary paths and arguments to invoke it:
|
||||
/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc,
|
||||
/// - or any `lld` available to `cc`.
|
||||
fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||
let unstable_use_lld = sess.opts.unstable_opts.gcc_ld.is_some();
|
||||
debug!("add_lld_args requested, flavor: '{flavor:?}', `-Zgcc-ld=lld`: {unstable_use_lld}");
|
||||
|
||||
if !flavor.is_gnu() {
|
||||
// Tell clang to use a non-default LLD flavor.
|
||||
// Gcc doesn't understand the target option, but we currently assume
|
||||
// that gcc is not used for Apple and Wasm targets (#97402).
|
||||
//
|
||||
// Note that we don't want to do that by default on macOS: e.g. passing a
|
||||
// 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
|
||||
// shown in issue #101653 and the discussion in PR #101792.
|
||||
//
|
||||
// It could be required in some cases of cross-compiling with
|
||||
// `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
|
||||
// which specific versions of clang, macOS SDK, host and target OS
|
||||
// combinations impact us here.
|
||||
//
|
||||
// So we do a simple first-approximation until we know more of what the
|
||||
// Apple targets require (and which would be handled prior to hitting this
|
||||
// `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
|
||||
// this should be manually passed if needed. We specify the target when
|
||||
// targeting a different linker flavor on macOS, and that's also always
|
||||
// the case when targeting WASM.
|
||||
if sess.target.linker_flavor != sess.host.linker_flavor {
|
||||
cmd.arg(format!("--target={}", sess.target.llvm_target));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sess.emit_fatal(errors::OptionGccOnly);
|
||||
// Sanity check: using the old unstable `-Zgcc-ld=lld` option requires a `cc`-using flavor.
|
||||
let flavor_uses_cc = flavor.uses_cc();
|
||||
if unstable_use_lld && !flavor_uses_cc {
|
||||
sess.emit_fatal(errors::OptionGccOnly);
|
||||
}
|
||||
|
||||
// If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`,
|
||||
// we don't need to do anything.
|
||||
let use_lld = flavor.uses_lld() || unstable_use_lld;
|
||||
if !flavor_uses_cc || !use_lld {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Implement the "self-contained" part of this feature by adding rustc distribution
|
||||
// directories to the tool's search path.
|
||||
let self_contained_linker = sess.opts.cg.link_self_contained.linker() || unstable_use_lld;
|
||||
if self_contained_linker {
|
||||
for path in sess.get_tools_search_paths(false) {
|
||||
cmd.arg({
|
||||
let mut arg = OsString::from("-B");
|
||||
arg.push(path.join("gcc-ld"));
|
||||
arg
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of
|
||||
// `lld` as the linker.
|
||||
cmd.arg("-fuse-ld=lld");
|
||||
|
||||
if !flavor.is_gnu() {
|
||||
// Tell clang to use a non-default LLD flavor.
|
||||
// Gcc doesn't understand the target option, but we currently assume
|
||||
// that gcc is not used for Apple and Wasm targets (#97402).
|
||||
//
|
||||
// Note that we don't want to do that by default on macOS: e.g. passing a
|
||||
// 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
|
||||
// shown in issue #101653 and the discussion in PR #101792.
|
||||
//
|
||||
// It could be required in some cases of cross-compiling with
|
||||
// `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
|
||||
// which specific versions of clang, macOS SDK, host and target OS
|
||||
// combinations impact us here.
|
||||
//
|
||||
// So we do a simple first-approximation until we know more of what the
|
||||
// Apple targets require (and which would be handled prior to hitting this
|
||||
// `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
|
||||
// this should be manually passed if needed. We specify the target when
|
||||
// targeting a different linker flavor on macOS, and that's also always
|
||||
// the case when targeting WASM.
|
||||
if sess.target.linker_flavor != sess.host.linker_flavor {
|
||||
cmd.arg(format!("--target={}", sess.target.llvm_target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use rustc_metadata::EncodedMetadata;
|
||||
use rustc_session::cstore::MetadataLoader;
|
||||
use rustc_session::Session;
|
||||
use rustc_target::abi::Endian;
|
||||
use rustc_target::spec::{RelocModel, Target};
|
||||
use rustc_target::spec::{ef_avr_arch, RelocModel, Target};
|
||||
|
||||
/// The default metadata loader. This is used by cg_llvm and cg_clif.
|
||||
///
|
||||
@ -284,8 +284,24 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
|
||||
e_flags
|
||||
}
|
||||
Architecture::LoongArch64 => {
|
||||
// Source: https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_e_flags_identifies_abi_type_and_version
|
||||
elf::EF_LARCH_OBJABI_V1 | elf::EF_LARCH_ABI_DOUBLE_FLOAT
|
||||
// Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
|
||||
let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
|
||||
let features = &sess.target.options.features;
|
||||
|
||||
// Select the appropriate floating-point ABI
|
||||
if features.contains("+d") {
|
||||
e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT;
|
||||
} else if features.contains("+f") {
|
||||
e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT;
|
||||
} else {
|
||||
e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT;
|
||||
}
|
||||
e_flags
|
||||
}
|
||||
Architecture::Avr => {
|
||||
// Resolve the ISA revision and set
|
||||
// the appropriate EF_AVR_ARCH flag.
|
||||
ef_avr_arch(&sess.target.options.cpu)
|
||||
}
|
||||
_ => 0,
|
||||
};
|
||||
|
@ -35,6 +35,7 @@ use rustc_span::symbol::sym;
|
||||
use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span};
|
||||
use rustc_target::spec::{MergeFunctions, SanitizerSet};
|
||||
|
||||
use crate::errors::ErrorCreatingRemarkDir;
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::fs;
|
||||
@ -345,6 +346,9 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
||||
pub diag_emitter: SharedEmitter,
|
||||
/// LLVM optimizations for which we want to print remarks.
|
||||
pub remark: Passes,
|
||||
/// Directory into which should the LLVM optimization remarks be written.
|
||||
/// If `None`, they will be written to stderr.
|
||||
pub remark_dir: Option<PathBuf>,
|
||||
/// Worker thread number
|
||||
pub worker: usize,
|
||||
/// The incremental compilation session directory, or None if we are not
|
||||
@ -698,28 +702,49 @@ impl<B: WriteBackendMethods> WorkItem<B> {
|
||||
|
||||
/// Generate a short description of this work item suitable for use as a thread name.
|
||||
fn short_description(&self) -> String {
|
||||
// `pthread_setname()` on *nix is limited to 15 characters and longer names are ignored.
|
||||
// Use very short descriptions in this case to maximize the space available for the module name.
|
||||
// Windows does not have that limitation so use slightly more descriptive names there.
|
||||
// `pthread_setname()` on *nix ignores anything beyond the first 15
|
||||
// bytes. Use short descriptions to maximize the space available for
|
||||
// the module name.
|
||||
#[cfg(not(windows))]
|
||||
fn desc(short: &str, _long: &str, name: &str) -> String {
|
||||
// The short label is three bytes, and is followed by a space. That
|
||||
// leaves 11 bytes for the CGU name. How we obtain those 11 bytes
|
||||
// depends on the the CGU name form.
|
||||
//
|
||||
// - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part
|
||||
// before the `-cgu.0` is the same for every CGU, so use the
|
||||
// `cgu.0` part. The number suffix will be different for each
|
||||
// CGU.
|
||||
//
|
||||
// - Incremental (normal), e.g. `2i52vvl2hco29us0`: use the whole
|
||||
// name because each CGU will have a unique ASCII hash, and the
|
||||
// first 11 bytes will be enough to identify it.
|
||||
//
|
||||
// - Incremental (with `-Zhuman-readable-cgu-names`), e.g.
|
||||
// `regex.f10ba03eb5ec7975-re_builder.volatile`: use the whole
|
||||
// name. The first 11 bytes won't be enough to uniquely identify
|
||||
// it, but no obvious substring will, and this is a rarely used
|
||||
// option so it doesn't matter much.
|
||||
//
|
||||
assert_eq!(short.len(), 3);
|
||||
let name = if let Some(index) = name.find("-cgu.") {
|
||||
&name[index + 1..] // +1 skips the leading '-'.
|
||||
} else {
|
||||
name
|
||||
};
|
||||
format!("{short} {name}")
|
||||
}
|
||||
|
||||
// Windows has no thread name length limit, so use more descriptive names.
|
||||
#[cfg(windows)]
|
||||
fn desc(_short: &str, long: &str, name: &str) -> String {
|
||||
format!("{long} {name}")
|
||||
}
|
||||
|
||||
match self {
|
||||
WorkItem::Optimize(m) => {
|
||||
#[cfg(windows)]
|
||||
return format!("optimize module {}", m.name);
|
||||
#[cfg(not(windows))]
|
||||
return format!("opt {}", m.name);
|
||||
}
|
||||
WorkItem::CopyPostLtoArtifacts(m) => {
|
||||
#[cfg(windows)]
|
||||
return format!("copy LTO artifacts for {}", m.name);
|
||||
#[cfg(not(windows))]
|
||||
return format!("copy {}", m.name);
|
||||
}
|
||||
WorkItem::LTO(m) => {
|
||||
#[cfg(windows)]
|
||||
return format!("LTO module {}", m.name());
|
||||
#[cfg(not(windows))]
|
||||
return format!("LTO {}", m.name());
|
||||
}
|
||||
WorkItem::Optimize(m) => desc("opt", "optimize module {}", &m.name),
|
||||
WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for {}", &m.name),
|
||||
WorkItem::LTO(m) => desc("lto", "LTO module {}", m.name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1041,6 +1066,17 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||
tcx.backend_optimization_level(())
|
||||
};
|
||||
let backend_features = tcx.global_backend_features(());
|
||||
|
||||
let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir {
|
||||
let result = fs::create_dir_all(dir).and_then(|_| dir.canonicalize());
|
||||
match result {
|
||||
Ok(dir) => Some(dir),
|
||||
Err(error) => sess.emit_fatal(ErrorCreatingRemarkDir { error }),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cgcx = CodegenContext::<B> {
|
||||
crate_types: sess.crate_types().to_vec(),
|
||||
each_linked_rlib_for_lto,
|
||||
@ -1052,6 +1088,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||
prof: sess.prof.clone(),
|
||||
exported_symbols,
|
||||
remark: sess.opts.cg.remark.clone(),
|
||||
remark_dir,
|
||||
worker: 0,
|
||||
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
|
||||
cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
|
||||
|
@ -357,6 +357,13 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
}
|
||||
}
|
||||
|
||||
// Returns `true` if this session's target will use native wasm
|
||||
// exceptions. This means that the VM does the unwinding for
|
||||
// us
|
||||
pub fn wants_wasm_eh(sess: &Session) -> bool {
|
||||
sess.target.is_like_wasm && sess.target.os != "emscripten"
|
||||
}
|
||||
|
||||
/// Returns `true` if this session's target will use SEH-based unwinding.
|
||||
///
|
||||
/// This is only true for MSVC targets, and even then the 64-bit MSVC target
|
||||
@ -366,6 +373,13 @@ pub fn wants_msvc_seh(sess: &Session) -> bool {
|
||||
sess.target.is_like_msvc
|
||||
}
|
||||
|
||||
/// Returns `true` if this session's target requires the new exception
|
||||
/// handling LLVM IR instructions (catchpad / cleanuppad / ... instead
|
||||
/// of landingpad)
|
||||
pub fn wants_new_eh_instructions(sess: &Session) -> bool {
|
||||
wants_wasm_eh(sess) || wants_msvc_seh(sess)
|
||||
}
|
||||
|
||||
pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
bx: &mut Bx,
|
||||
dst: Bx::Value,
|
||||
|
@ -1023,3 +1023,9 @@ pub struct TargetFeatureSafeTrait {
|
||||
#[label(codegen_ssa_label_def)]
|
||||
pub def: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_error_creating_remark_dir)]
|
||||
pub struct ErrorCreatingRemarkDir {
|
||||
pub error: std::io::Error,
|
||||
}
|
||||
|
@ -79,8 +79,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
|
||||
lltarget = fx.landing_pad_for(target);
|
||||
}
|
||||
if is_cleanupret {
|
||||
// MSVC cross-funclet jump - need a trampoline
|
||||
debug_assert!(base::wants_msvc_seh(fx.cx.tcx().sess));
|
||||
// Cross-funclet jump - need a trampoline
|
||||
debug_assert!(base::wants_new_eh_instructions(fx.cx.tcx().sess));
|
||||
debug!("llbb_with_cleanup: creating cleanup trampoline for {:?}", target);
|
||||
let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target);
|
||||
let trampoline_llbb = Bx::append_block(fx.cx, fx.llfn, name);
|
||||
@ -177,9 +177,16 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
|
||||
mir::UnwindAction::Continue => None,
|
||||
mir::UnwindAction::Unreachable => None,
|
||||
mir::UnwindAction::Terminate => {
|
||||
if fx.mir[self.bb].is_cleanup && base::wants_msvc_seh(fx.cx.tcx().sess) {
|
||||
// SEH will abort automatically if an exception tries to
|
||||
if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) {
|
||||
// MSVC SEH will abort automatically if an exception tries to
|
||||
// propagate out from cleanup.
|
||||
|
||||
// FIXME(@mirkootter): For wasm, we currently do not support terminate during
|
||||
// cleanup, because this requires a few more changes: The current code
|
||||
// caches the `terminate_block` for each function; funclet based code - however -
|
||||
// requires a different terminate_block for each funclet
|
||||
// Until this is implemented, we just do not unwind inside cleanup blocks
|
||||
|
||||
None
|
||||
} else {
|
||||
Some(fx.terminate_block())
|
||||
@ -863,13 +870,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
// promotes any complex rvalues to constants.
|
||||
if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") {
|
||||
if let mir::Operand::Constant(constant) = arg {
|
||||
let c = self.eval_mir_constant(constant);
|
||||
let (llval, ty) = self.simd_shuffle_indices(
|
||||
&bx,
|
||||
constant.span,
|
||||
self.monomorphize(constant.ty()),
|
||||
c,
|
||||
);
|
||||
let (llval, ty) = self.simd_shuffle_indices(&bx, constant);
|
||||
return OperandRef {
|
||||
val: Immediate(llval),
|
||||
layout: bx.layout_of(ty),
|
||||
@ -1528,7 +1529,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
// FIXME(eddyb) rename this to `eh_pad_for_uncached`.
|
||||
fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
|
||||
let llbb = self.llbb(bb);
|
||||
if base::wants_msvc_seh(self.cx.sess()) {
|
||||
if base::wants_new_eh_instructions(self.cx.sess()) {
|
||||
let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
|
||||
let mut cleanup_bx = Bx::build(self.cx, cleanup_bb);
|
||||
let funclet = cleanup_bx.cleanup_pad(None, &[]);
|
||||
@ -1587,6 +1588,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
// } catch (...) {
|
||||
// bar();
|
||||
// }
|
||||
//
|
||||
// which creates an IR snippet like
|
||||
//
|
||||
// cs_terminate:
|
||||
// %cs = catchswitch within none [%cp_terminate] unwind to caller
|
||||
// cp_terminate:
|
||||
// %cp = catchpad within %cs [null, i32 64, null]
|
||||
// ...
|
||||
|
||||
llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
|
||||
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
|
||||
|
||||
|
@ -5,7 +5,6 @@ use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
|
||||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_target::abi::Abi;
|
||||
|
||||
use super::FunctionCx;
|
||||
@ -59,22 +58,40 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
|
||||
/// that the given `constant` is an `ConstantKind::Unevaluated` and must be convertible to
|
||||
/// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip.
|
||||
pub fn eval_unevaluated_mir_constant_to_valtree(
|
||||
&self,
|
||||
constant: &mir::Constant<'tcx>,
|
||||
) -> Result<Option<ty::ValTree<'tcx>>, ErrorHandled> {
|
||||
let uv = match constant.literal {
|
||||
mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(),
|
||||
other => span_bug!(constant.span, "{other:#?}"),
|
||||
};
|
||||
let uv = self.monomorphize(uv);
|
||||
self.cx.tcx().const_eval_resolve_for_typeck(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
uv,
|
||||
Some(constant.span),
|
||||
)
|
||||
}
|
||||
|
||||
/// process constant containing SIMD shuffle indices
|
||||
pub fn simd_shuffle_indices(
|
||||
&mut self,
|
||||
bx: &Bx,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>,
|
||||
constant: Result<ConstValue<'tcx>, ErrorHandled>,
|
||||
constant: &mir::Constant<'tcx>,
|
||||
) -> (Bx::Value, Ty<'tcx>) {
|
||||
constant
|
||||
let ty = self.monomorphize(constant.ty());
|
||||
let val = self
|
||||
.eval_unevaluated_mir_constant_to_valtree(constant)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|val| {
|
||||
let field_ty = ty.builtin_index().unwrap();
|
||||
let c = mir::ConstantKind::from_value(val, ty);
|
||||
let values: Vec<_> = bx
|
||||
.tcx()
|
||||
.destructure_mir_constant(ty::ParamEnv::reveal_all(), c)
|
||||
.fields
|
||||
let values: Vec<_> = val
|
||||
.unwrap_branch()
|
||||
.iter()
|
||||
.map(|field| {
|
||||
if let Some(prim) = field.try_to_scalar() {
|
||||
@ -88,15 +105,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let llval = bx.const_struct(&values, false);
|
||||
(llval, c.ty())
|
||||
bx.const_struct(&values, false)
|
||||
})
|
||||
.unwrap_or_else(|_| {
|
||||
bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span });
|
||||
.unwrap_or_else(|| {
|
||||
bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span: constant.span });
|
||||
// We've errored, so we don't have to produce working code.
|
||||
let ty = self.monomorphize(ty);
|
||||
let llty = bx.backend_type(bx.layout_of(ty));
|
||||
(bx.const_undef(llty), ty)
|
||||
})
|
||||
bx.const_undef(llty)
|
||||
});
|
||||
(val, ty)
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +179,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
start_bx.set_personality_fn(cx.eh_personality());
|
||||
}
|
||||
|
||||
let cleanup_kinds = base::wants_msvc_seh(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir));
|
||||
let cleanup_kinds =
|
||||
base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir));
|
||||
|
||||
let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
|
||||
mir.basic_blocks
|
||||
|
@ -284,6 +284,7 @@ const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
|
||||
// tidy-alphabetical-start
|
||||
("atomics", Some(sym::wasm_target_feature)),
|
||||
("bulk-memory", Some(sym::wasm_target_feature)),
|
||||
("exception-handling", Some(sym::wasm_target_feature)),
|
||||
("multivalue", Some(sym::wasm_target_feature)),
|
||||
("mutable-globals", Some(sym::wasm_target_feature)),
|
||||
("nontrapping-fptoint", Some(sym::wasm_target_feature)),
|
||||
|
@ -92,7 +92,6 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
val: mir::ConstantKind<'tcx>,
|
||||
) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
|
||||
trace!("destructure_mir_constant: {:?}", val);
|
||||
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
|
||||
let op = ecx.eval_mir_constant(&val, None, None)?;
|
||||
|
||||
|
@ -72,7 +72,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
|
||||
}
|
||||
sym::pref_align_of => {
|
||||
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
|
||||
let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?;
|
||||
let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(*e)))?;
|
||||
ConstValue::from_target_usize(layout.align.pref.bytes(), &tcx)
|
||||
}
|
||||
sym::type_id => {
|
||||
|
@ -344,15 +344,18 @@ where
|
||||
};
|
||||
|
||||
// Check the qualifs of the value of `const` items.
|
||||
// FIXME(valtrees): check whether const qualifs should behave the same
|
||||
// way for type and mir constants.
|
||||
let uneval = match constant.literal {
|
||||
ConstantKind::Ty(ct)
|
||||
if matches!(ct.kind(), ty::ConstKind::Param(_) | ty::ConstKind::Error(_)) =>
|
||||
if matches!(
|
||||
ct.kind(),
|
||||
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
|
||||
) =>
|
||||
{
|
||||
None
|
||||
}
|
||||
ConstantKind::Ty(c) => bug!("expected ConstKind::Param here, found {:?}", c),
|
||||
ConstantKind::Ty(c) => {
|
||||
bug!("expected ConstKind::Param or ConstKind::Value here, found {:?}", c)
|
||||
}
|
||||
ConstantKind::Unevaluated(uv, _) => Some(uv),
|
||||
ConstantKind::Val(..) => None,
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ pub fn check_validity_requirement<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
kind: ValidityRequirement,
|
||||
param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
) -> Result<bool, LayoutError<'tcx>> {
|
||||
) -> Result<bool, &'tcx LayoutError<'tcx>> {
|
||||
let layout = tcx.layout_of(param_env_and_ty)?;
|
||||
|
||||
// There is nothing strict or lax about inhabitedness.
|
||||
@ -43,7 +43,7 @@ fn might_permit_raw_init_strict<'tcx>(
|
||||
ty: TyAndLayout<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
kind: ValidityRequirement,
|
||||
) -> Result<bool, LayoutError<'tcx>> {
|
||||
) -> Result<bool, &'tcx LayoutError<'tcx>> {
|
||||
let machine = CompileTimeInterpreter::new(CanAccessStatics::No, CheckAlignment::Error);
|
||||
|
||||
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
|
||||
@ -75,7 +75,7 @@ fn might_permit_raw_init_lax<'tcx>(
|
||||
this: TyAndLayout<'tcx>,
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
init_kind: ValidityRequirement,
|
||||
) -> Result<bool, LayoutError<'tcx>> {
|
||||
) -> Result<bool, &'tcx LayoutError<'tcx>> {
|
||||
let scalar_allows_raw_init = move |s: Scalar| -> bool {
|
||||
match init_kind {
|
||||
ValidityRequirement::Inhabited => {
|
||||
|
@ -68,7 +68,6 @@ pub mod macros;
|
||||
pub mod obligation_forest;
|
||||
pub mod sip128;
|
||||
pub mod small_c_str;
|
||||
pub mod small_str;
|
||||
pub mod snapshot_map;
|
||||
pub mod svh;
|
||||
pub use ena::snapshot_vec;
|
||||
|
@ -1,68 +0,0 @@
|
||||
use smallvec::SmallVec;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Like SmallVec but for strings.
|
||||
#[derive(Default)]
|
||||
pub struct SmallStr<const N: usize>(SmallVec<[u8; N]>);
|
||||
|
||||
impl<const N: usize> SmallStr<N> {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
SmallStr(SmallVec::default())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push_str(&mut self, s: &str) {
|
||||
self.0.extend_from_slice(s.as_bytes());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn spilled(&self) -> bool {
|
||||
self.0.spilled()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
unsafe { std::str::from_utf8_unchecked(self.0.as_slice()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> std::ops::Deref for SmallStr<N> {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, A: AsRef<str>> FromIterator<A> for SmallStr<N> {
|
||||
#[inline]
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where
|
||||
T: IntoIterator<Item = A>,
|
||||
{
|
||||
let mut s = SmallStr::default();
|
||||
s.extend(iter);
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, A: AsRef<str>> Extend<A> for SmallStr<N> {
|
||||
#[inline]
|
||||
fn extend<T>(&mut self, iter: T)
|
||||
where
|
||||
T: IntoIterator<Item = A>,
|
||||
{
|
||||
for a in iter.into_iter() {
|
||||
self.push_str(a.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let s = SmallStr::<1>::new();
|
||||
assert!(s.empty());
|
||||
assert_eq!("", s.as_str());
|
||||
assert!(!s.spilled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_iter() {
|
||||
let s = ["aa", "bb", "cc"].iter().collect::<SmallStr<6>>();
|
||||
assert_eq!("aabbcc", s.as_str());
|
||||
assert!(!s.spilled());
|
||||
|
||||
let s = ["aa", "bb", "cc", "dd"].iter().collect::<SmallStr<6>>();
|
||||
assert_eq!("aabbccdd", s.as_str());
|
||||
assert!(s.spilled());
|
||||
}
|
@ -8,3 +8,6 @@ crate-type = ["dylib"]
|
||||
|
||||
[dependencies]
|
||||
rustc_driver_impl = { path = "../rustc_driver_impl" }
|
||||
# FIXME(Nilstrieb): 0.37.12 adds eventfd support for FreeBSD,
|
||||
# but FreeBSD 12 does not support it: https://github.com/bytecodealliance/rustix/issues/716
|
||||
rustix = "=0.37.11"
|
||||
|
@ -543,6 +543,8 @@ declare_features! (
|
||||
/// Allows creation of instances of a struct by moving fields that have
|
||||
/// not changed from prior instances of the same struct (RFC #2528)
|
||||
(active, type_changing_struct_update, "1.58.0", Some(86555), None),
|
||||
/// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`).
|
||||
(active, type_privacy_lints, "CURRENT_RUSTC_VERSION", Some(48054), None),
|
||||
/// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
|
||||
(active, unix_sigpipe, "1.65.0", Some(97889), None),
|
||||
/// Allows unsized fn parameters.
|
||||
|
@ -2664,6 +2664,10 @@ pub struct OpaqueTy<'hir> {
|
||||
pub generics: &'hir Generics<'hir>,
|
||||
pub bounds: GenericBounds<'hir>,
|
||||
pub origin: OpaqueTyOrigin,
|
||||
// Opaques have duplicated lifetimes, this mapping connects the original lifetime with the copy
|
||||
// so we can later generate bidirectional outlives predicates to enforce that these lifetimes
|
||||
// stay in sync.
|
||||
pub lifetime_mapping: &'hir [(Lifetime, LocalDefId)],
|
||||
pub in_trait: bool,
|
||||
}
|
||||
|
||||
@ -3315,7 +3319,7 @@ pub enum ItemKind<'hir> {
|
||||
/// A type alias, e.g., `type Foo = Bar<u8>`.
|
||||
TyAlias(&'hir Ty<'hir>, &'hir Generics<'hir>),
|
||||
/// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`.
|
||||
OpaqueTy(OpaqueTy<'hir>),
|
||||
OpaqueTy(&'hir OpaqueTy<'hir>),
|
||||
/// An enum definition, e.g., `enum Foo<A, B> {C<A>, D<B>}`.
|
||||
Enum(EnumDef<'hir>, &'hir Generics<'hir>),
|
||||
/// A struct definition, e.g., `struct Foo<A> {x: A}`.
|
||||
|
@ -502,7 +502,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
|
||||
visitor.visit_ty(ty);
|
||||
visitor.visit_generics(generics)
|
||||
}
|
||||
ItemKind::OpaqueTy(OpaqueTy { ref generics, bounds, .. }) => {
|
||||
ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => {
|
||||
visitor.visit_id(item.hir_id());
|
||||
walk_generics(visitor, generics);
|
||||
walk_list!(visitor, visit_param_bound, bounds);
|
||||
|
@ -99,6 +99,15 @@ hir_analysis_invalid_union_field =
|
||||
hir_analysis_invalid_union_field_sugg =
|
||||
wrap the field type in `ManuallyDrop<...>`
|
||||
|
||||
hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
|
||||
.label = const parameter declared here
|
||||
|
||||
hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetimes from an fn or impl
|
||||
.label = lifetime declared here
|
||||
|
||||
hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
|
||||
.label = type parameter declared here
|
||||
|
||||
hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
|
||||
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
|
||||
.label = lifetimes do not match {$item_kind} in trait
|
||||
|
@ -1984,7 +1984,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
.copied()
|
||||
.filter(|&(impl_, _)| {
|
||||
infcx.probe(|_| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
ocx.register_obligations(obligations.clone());
|
||||
|
||||
let impl_substs = infcx.fresh_substs_for_item(span, impl_);
|
||||
@ -2809,7 +2809,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
let opaque_ty = tcx.hir().item(item_id);
|
||||
|
||||
match opaque_ty.kind {
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
|
||||
hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin, .. }) => {
|
||||
let local_def_id = item_id.owner_id.def_id;
|
||||
// If this is an RPITIT and we are using the new RPITIT lowering scheme, we
|
||||
// generate the def_id of an associated type for the trait and return as
|
||||
|
@ -161,7 +161,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
|
||||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(self.infcx);
|
||||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.infcx);
|
||||
|
||||
let cause = traits::ObligationCause::misc(self.span, self.body_id);
|
||||
let normalized_ty = match self
|
||||
|
@ -299,7 +299,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
|
||||
}
|
||||
}
|
||||
|
||||
if let ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||
if let ItemKind::OpaqueTy(&hir::OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
|
||||
in_trait,
|
||||
..
|
||||
|
@ -19,7 +19,7 @@ use rustc_middle::ty::{
|
||||
self, InternalSubsts, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
|
||||
};
|
||||
use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{
|
||||
@ -651,11 +651,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
let impl_sig = ocx.normalize(
|
||||
&norm_cause,
|
||||
param_env,
|
||||
infcx.instantiate_binder_with_fresh_vars(
|
||||
return_span,
|
||||
infer::HigherRankedType,
|
||||
tcx.fn_sig(impl_m.def_id).subst_identity(),
|
||||
),
|
||||
tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(impl_m.def_id).subst_identity()),
|
||||
);
|
||||
impl_sig.error_reported()?;
|
||||
let impl_return_ty = impl_sig.output();
|
||||
@ -665,9 +661,10 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
// them with inference variables.
|
||||
// We will use these inference variables to collect the hidden types of RPITITs.
|
||||
let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_def_id);
|
||||
let unnormalized_trait_sig = tcx
|
||||
.liberate_late_bound_regions(
|
||||
impl_m.def_id,
|
||||
let unnormalized_trait_sig = infcx
|
||||
.instantiate_binder_with_fresh_vars(
|
||||
return_span,
|
||||
infer::HigherRankedType,
|
||||
tcx.fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
|
||||
)
|
||||
.fold_with(&mut collector);
|
||||
@ -760,15 +757,17 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
|
||||
let mut collected_tys = FxHashMap::default();
|
||||
for (def_id, (ty, substs)) in collected_types {
|
||||
match infcx.fully_resolve(ty) {
|
||||
Ok(ty) => {
|
||||
match infcx.fully_resolve((ty, substs)) {
|
||||
Ok((ty, substs)) => {
|
||||
// `ty` contains free regions that we created earlier while liberating the
|
||||
// trait fn signature. However, projection normalization expects `ty` to
|
||||
// contains `def_id`'s early-bound regions.
|
||||
let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
|
||||
debug!(?id_substs, ?substs);
|
||||
let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> =
|
||||
std::iter::zip(substs, id_substs).collect();
|
||||
let map: FxHashMap<_, _> = std::iter::zip(substs, id_substs)
|
||||
.skip(tcx.generics_of(trait_m.def_id).count())
|
||||
.filter_map(|(a, b)| Some((a.as_region()?, b.as_region()?)))
|
||||
.collect();
|
||||
debug!(?map);
|
||||
|
||||
// NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound
|
||||
@ -793,25 +792,19 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
// same generics.
|
||||
let num_trait_substs = trait_to_impl_substs.len();
|
||||
let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len();
|
||||
let ty = tcx.fold_regions(ty, |region, _| {
|
||||
match region.kind() {
|
||||
// Remap all free regions, which correspond to late-bound regions in the function.
|
||||
ty::ReFree(_) => {}
|
||||
// Remap early-bound regions as long as they don't come from the `impl` itself.
|
||||
ty::ReEarlyBound(ebr) if tcx.parent(ebr.def_id) != impl_m.container_id(tcx) => {}
|
||||
_ => return region,
|
||||
}
|
||||
let Some(ty::ReEarlyBound(e)) = map.get(®ion.into()).map(|r| r.expect_region().kind())
|
||||
else {
|
||||
return ty::Region::new_error_with_message(tcx, return_span, "expected ReFree to map to ReEarlyBound")
|
||||
};
|
||||
ty::Region::new_early_bound(tcx, ty::EarlyBoundRegion {
|
||||
def_id: e.def_id,
|
||||
name: e.name,
|
||||
index: (e.index as usize - num_trait_substs + num_impl_substs) as u32,
|
||||
})
|
||||
});
|
||||
debug!(%ty);
|
||||
let ty = match ty.try_fold_with(&mut RemapHiddenTyRegions {
|
||||
tcx,
|
||||
map,
|
||||
num_trait_substs,
|
||||
num_impl_substs,
|
||||
def_id,
|
||||
impl_def_id: impl_m.container_id(tcx),
|
||||
ty,
|
||||
return_span,
|
||||
}) {
|
||||
Ok(ty) => ty,
|
||||
Err(guar) => tcx.ty_error(guar),
|
||||
};
|
||||
collected_tys.insert(def_id, ty::EarlyBinder::bind(ty));
|
||||
}
|
||||
Err(err) => {
|
||||
@ -895,6 +888,97 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
struct RemapHiddenTyRegions<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
map: FxHashMap<ty::Region<'tcx>, ty::Region<'tcx>>,
|
||||
num_trait_substs: usize,
|
||||
num_impl_substs: usize,
|
||||
def_id: DefId,
|
||||
impl_def_id: DefId,
|
||||
ty: Ty<'tcx>,
|
||||
return_span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
|
||||
type Error = ErrorGuaranteed;
|
||||
|
||||
fn interner(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
|
||||
if let ty::Alias(ty::Opaque, ty::AliasTy { substs, def_id, .. }) = *t.kind() {
|
||||
let mut mapped_substs = Vec::with_capacity(substs.len());
|
||||
for (arg, v) in std::iter::zip(substs, self.tcx.variances_of(def_id)) {
|
||||
mapped_substs.push(match (arg.unpack(), v) {
|
||||
// Skip uncaptured opaque substs
|
||||
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg,
|
||||
_ => arg.try_fold_with(self)?,
|
||||
});
|
||||
}
|
||||
Ok(self.tcx.mk_opaque(def_id, self.tcx.mk_substs(&mapped_substs)))
|
||||
} else {
|
||||
t.try_super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn try_fold_region(
|
||||
&mut self,
|
||||
region: ty::Region<'tcx>,
|
||||
) -> Result<ty::Region<'tcx>, Self::Error> {
|
||||
match region.kind() {
|
||||
// Remap all free regions, which correspond to late-bound regions in the function.
|
||||
ty::ReFree(_) => {}
|
||||
// Remap early-bound regions as long as they don't come from the `impl` itself,
|
||||
// in which case we don't really need to renumber them.
|
||||
ty::ReEarlyBound(ebr) if self.tcx.parent(ebr.def_id) != self.impl_def_id => {}
|
||||
_ => return Ok(region),
|
||||
}
|
||||
|
||||
let e = if let Some(region) = self.map.get(®ion) {
|
||||
if let ty::ReEarlyBound(e) = region.kind() { e } else { bug!() }
|
||||
} else {
|
||||
let guar = match region.kind() {
|
||||
ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. })
|
||||
| ty::ReFree(ty::FreeRegion {
|
||||
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
|
||||
..
|
||||
}) => {
|
||||
let return_span = if let ty::Alias(ty::Opaque, opaque_ty) = self.ty.kind() {
|
||||
self.tcx.def_span(opaque_ty.def_id)
|
||||
} else {
|
||||
self.return_span
|
||||
};
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
return_span,
|
||||
"return type captures more lifetimes than trait definition",
|
||||
)
|
||||
.span_label(self.tcx.def_span(def_id), "this lifetime was captured")
|
||||
.span_note(
|
||||
self.tcx.def_span(self.def_id),
|
||||
"hidden type must only reference lifetimes captured by this impl trait",
|
||||
)
|
||||
.note(format!("hidden type inferred to be `{}`", self.ty))
|
||||
.emit()
|
||||
}
|
||||
_ => self.tcx.sess.delay_span_bug(DUMMY_SP, "should've been able to remap region"),
|
||||
};
|
||||
return Err(guar);
|
||||
};
|
||||
|
||||
Ok(ty::Region::new_early_bound(
|
||||
self.tcx,
|
||||
ty::EarlyBoundRegion {
|
||||
def_id: e.def_id,
|
||||
name: e.name,
|
||||
index: (e.index as usize - self.num_trait_substs + self.num_impl_substs) as u32,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn report_trait_method_mismatch<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
mut cause: ObligationCause<'tcx>,
|
||||
|
@ -1328,7 +1328,7 @@ fn suggest_impl_trait<'tcx>(
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let item_ty = ocx.normalize(
|
||||
&ObligationCause::misc(span, def_id),
|
||||
param_env,
|
||||
|
@ -144,7 +144,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
Some(tcx.typeck_root_def_id(def_id.to_def_id()))
|
||||
}
|
||||
Node::Item(item) => match item.kind {
|
||||
ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||
ItemKind::OpaqueTy(&hir::OpaqueTy {
|
||||
origin:
|
||||
hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
|
||||
in_trait,
|
||||
|
@ -2,7 +2,7 @@ use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
|
||||
use crate::bounds::Bounds;
|
||||
use crate::collect::ItemCtxt;
|
||||
use crate::constrained_generic_params as cgp;
|
||||
use hir::{HirId, Node};
|
||||
use hir::{HirId, Lifetime, Node};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -10,9 +10,9 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_middle::ty::subst::InternalSubsts;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{GenericPredicates, ToPredicate};
|
||||
use rustc_middle::ty::{GenericPredicates, Generics, ImplTraitInTraitData, ToPredicate};
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_span::{Span, Symbol, DUMMY_SP};
|
||||
|
||||
/// Returns a list of all type predicates (explicit and implicit) for the definition with
|
||||
/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
|
||||
@ -62,6 +62,54 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
|
||||
fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> {
|
||||
use rustc_hir::*;
|
||||
|
||||
match tcx.opt_rpitit_info(def_id.to_def_id()) {
|
||||
Some(ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
|
||||
let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local());
|
||||
let opaque_ty_node = tcx.hir().get(opaque_ty_id);
|
||||
let Node::Item(&Item { kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }), .. }) = opaque_ty_node else {
|
||||
bug!("unexpected {opaque_ty_node:?}")
|
||||
};
|
||||
|
||||
let mut predicates = Vec::new();
|
||||
compute_bidirectional_outlives_predicates(
|
||||
tcx,
|
||||
def_id,
|
||||
lifetime_mapping.iter().map(|(lifetime, def_id)| {
|
||||
(*lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span))
|
||||
}),
|
||||
tcx.generics_of(def_id.to_def_id()),
|
||||
&mut predicates,
|
||||
);
|
||||
|
||||
return ty::GenericPredicates {
|
||||
parent: Some(tcx.parent(def_id.to_def_id())),
|
||||
predicates: tcx.arena.alloc_from_iter(predicates),
|
||||
};
|
||||
}
|
||||
|
||||
Some(ImplTraitInTraitData::Impl { fn_def_id }) => {
|
||||
let assoc_item = tcx.associated_item(def_id);
|
||||
let trait_assoc_predicates = tcx.predicates_of(assoc_item.trait_item_def_id.unwrap());
|
||||
|
||||
let impl_assoc_identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
|
||||
let impl_def_id = tcx.parent(fn_def_id);
|
||||
let impl_trait_ref_substs =
|
||||
tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder().substs;
|
||||
|
||||
let impl_assoc_substs =
|
||||
impl_assoc_identity_substs.rebase_onto(tcx, impl_def_id, impl_trait_ref_substs);
|
||||
|
||||
let impl_predicates = trait_assoc_predicates.instantiate_own(tcx, impl_assoc_substs);
|
||||
|
||||
return ty::GenericPredicates {
|
||||
parent: Some(impl_def_id),
|
||||
predicates: tcx.arena.alloc_from_iter(impl_predicates),
|
||||
};
|
||||
}
|
||||
|
||||
None => {}
|
||||
}
|
||||
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
let node = tcx.hir().get(hir_id);
|
||||
|
||||
@ -289,38 +337,22 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
||||
bug!("unexpected {opaque_ty_node:?}")
|
||||
};
|
||||
debug!(?lifetimes);
|
||||
for (arg, duplicate) in std::iter::zip(lifetimes, ast_generics.params) {
|
||||
let hir::GenericArg::Lifetime(arg) = arg else { bug!() };
|
||||
let orig_region = icx.astconv().ast_region_to_region(&arg, None);
|
||||
if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) {
|
||||
// Only early-bound regions can point to the original generic parameter.
|
||||
continue;
|
||||
}
|
||||
|
||||
let hir::GenericParamKind::Lifetime { .. } = duplicate.kind else { continue };
|
||||
let dup_def = duplicate.def_id.to_def_id();
|
||||
let lifetime_mapping = std::iter::zip(lifetimes, ast_generics.params)
|
||||
.map(|(arg, dup)| {
|
||||
let hir::GenericArg::Lifetime(arg) = arg else { bug!() };
|
||||
(**arg, dup)
|
||||
})
|
||||
.filter(|(_, dup)| matches!(dup.kind, hir::GenericParamKind::Lifetime { .. }))
|
||||
.map(|(lifetime, dup)| (lifetime, (dup.def_id, dup.name.ident().name, dup.span)));
|
||||
|
||||
let Some(dup_index) = generics.param_def_id_to_index(tcx, dup_def) else { bug!() };
|
||||
|
||||
let dup_region = ty::Region::new_early_bound(
|
||||
tcx,
|
||||
ty::EarlyBoundRegion {
|
||||
def_id: dup_def,
|
||||
index: dup_index,
|
||||
name: duplicate.name.ident().name,
|
||||
},
|
||||
);
|
||||
predicates.push((
|
||||
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_region, dup_region))
|
||||
.to_predicate(icx.tcx),
|
||||
duplicate.span,
|
||||
));
|
||||
predicates.push((
|
||||
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_region, orig_region))
|
||||
.to_predicate(icx.tcx),
|
||||
duplicate.span,
|
||||
));
|
||||
}
|
||||
compute_bidirectional_outlives_predicates(
|
||||
tcx,
|
||||
def_id,
|
||||
lifetime_mapping,
|
||||
generics,
|
||||
&mut predicates,
|
||||
);
|
||||
debug!(?predicates);
|
||||
}
|
||||
|
||||
@ -330,6 +362,46 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaques have duplicated lifetimes and we need to compute bidirectional outlives predicates to
|
||||
/// enforce that these lifetimes stay in sync.
|
||||
fn compute_bidirectional_outlives_predicates<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: LocalDefId,
|
||||
lifetime_mapping: impl Iterator<Item = (Lifetime, (LocalDefId, Symbol, Span))>,
|
||||
generics: &Generics,
|
||||
predicates: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
) {
|
||||
let icx = ItemCtxt::new(tcx, item_def_id);
|
||||
|
||||
for (arg, (dup_def, name, span)) in lifetime_mapping {
|
||||
let orig_region = icx.astconv().ast_region_to_region(&arg, None);
|
||||
if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) {
|
||||
// There is no late-bound lifetime to actually match up here, since the lifetime doesn't
|
||||
// show up in the opaque's parent's substs.
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(dup_index) = generics.param_def_id_to_index(icx.tcx, dup_def.to_def_id()) else { bug!() };
|
||||
|
||||
let dup_region = ty::Region::new_early_bound(
|
||||
tcx,
|
||||
ty::EarlyBoundRegion { def_id: dup_def.to_def_id(), index: dup_index, name },
|
||||
);
|
||||
|
||||
predicates.push((
|
||||
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_region, dup_region))
|
||||
.to_predicate(tcx),
|
||||
span,
|
||||
));
|
||||
|
||||
predicates.push((
|
||||
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_region, orig_region))
|
||||
.to_predicate(tcx),
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn const_evaluatable_predicates_of(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: LocalDefId,
|
||||
@ -668,7 +740,7 @@ pub(super) fn type_param_predicates(
|
||||
ItemKind::Fn(.., generics, _)
|
||||
| ItemKind::Impl(&hir::Impl { generics, .. })
|
||||
| ItemKind::TyAlias(_, generics)
|
||||
| ItemKind::OpaqueTy(OpaqueTy {
|
||||
| ItemKind::OpaqueTy(&OpaqueTy {
|
||||
generics,
|
||||
origin: hir::OpaqueTyOrigin::TyAlias { .. },
|
||||
..
|
||||
|
@ -556,7 +556,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
||||
});
|
||||
}
|
||||
}
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||
hir::ItemKind::OpaqueTy(&hir::OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent),
|
||||
generics,
|
||||
..
|
||||
@ -1344,12 +1344,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
Scope::Binder {
|
||||
where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
|
||||
} => {
|
||||
let mut err = self.tcx.sess.struct_span_err(
|
||||
lifetime_ref.ident.span,
|
||||
"`impl Trait` can only mention lifetimes bound at the fn or impl level",
|
||||
);
|
||||
err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here");
|
||||
err.emit();
|
||||
self.tcx.sess.emit_err(errors::LateBoundInApit::Lifetime {
|
||||
span: lifetime_ref.ident.span,
|
||||
param_span: self.tcx.def_span(region_def_id),
|
||||
});
|
||||
return;
|
||||
}
|
||||
Scope::Root { .. } => break,
|
||||
@ -1379,6 +1377,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
let mut late_depth = 0;
|
||||
let mut scope = self.scope;
|
||||
let mut crossed_anon_const = false;
|
||||
|
||||
let result = loop {
|
||||
match *scope {
|
||||
Scope::Body { s, .. } => {
|
||||
@ -1446,6 +1445,50 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
// We may fail to resolve higher-ranked ty/const vars that are mentioned by APIT.
|
||||
// AST-based resolution does not care for impl-trait desugaring, which are the
|
||||
// responsibility of lowering. This may create a mismatch between the resolution
|
||||
// AST found (`param_def_id`) which points to HRTB, and what HIR allows.
|
||||
// ```
|
||||
// fn foo(x: impl for<T> Trait<Assoc = impl Trait2<T>>) {}
|
||||
// ```
|
||||
//
|
||||
// In such case, walk back the binders to diagnose it properly.
|
||||
let mut scope = self.scope;
|
||||
loop {
|
||||
match *scope {
|
||||
Scope::Binder {
|
||||
where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
|
||||
} => {
|
||||
let guar = self.tcx.sess.emit_err(match self.tcx.def_kind(param_def_id) {
|
||||
DefKind::TyParam => errors::LateBoundInApit::Type {
|
||||
span: self.tcx.hir().span(hir_id),
|
||||
param_span: self.tcx.def_span(param_def_id),
|
||||
},
|
||||
DefKind::ConstParam => errors::LateBoundInApit::Const {
|
||||
span: self.tcx.hir().span(hir_id),
|
||||
param_span: self.tcx.def_span(param_def_id),
|
||||
},
|
||||
kind => {
|
||||
bug!("unexpected def-kind: {}", kind.descr(param_def_id.to_def_id()))
|
||||
}
|
||||
});
|
||||
self.map.defs.insert(hir_id, ResolvedArg::Error(guar));
|
||||
return;
|
||||
}
|
||||
Scope::Root { .. } => break,
|
||||
Scope::Binder { s, .. }
|
||||
| Scope::Body { s, .. }
|
||||
| Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. }
|
||||
| Scope::AnonConstBoundary { s } => {
|
||||
scope = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.sess.delay_span_bug(
|
||||
self.tcx.hir().span(hir_id),
|
||||
format!("could not resolve {param_def_id:?}"),
|
||||
|
@ -435,7 +435,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
|
||||
..
|
||||
}) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
|
||||
// Opaque types desugared from `impl Trait`.
|
||||
ItemKind::OpaqueTy(OpaqueTy {
|
||||
ItemKind::OpaqueTy(&OpaqueTy {
|
||||
origin:
|
||||
hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner),
|
||||
in_trait,
|
||||
|
@ -875,3 +875,28 @@ pub(crate) enum ReturnTypeNotationIllegalParam {
|
||||
param_span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub(crate) enum LateBoundInApit {
|
||||
#[diag(hir_analysis_late_bound_type_in_apit)]
|
||||
Type {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[label]
|
||||
param_span: Span,
|
||||
},
|
||||
#[diag(hir_analysis_late_bound_const_in_apit)]
|
||||
Const {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[label]
|
||||
param_span: Span,
|
||||
},
|
||||
#[diag(hir_analysis_late_bound_lifetime_in_apit)]
|
||||
Lifetime {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[label]
|
||||
param_span: Span,
|
||||
},
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use rustc_arena::DroplessArena;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, CrateVariancesMap, ImplTraitInTraitData, SubstsRef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
@ -51,20 +51,26 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
|
||||
| DefKind::Struct
|
||||
| DefKind::Union
|
||||
| DefKind::Variant
|
||||
| DefKind::Ctor(..) => {}
|
||||
| DefKind::Ctor(..) => {
|
||||
// These are inferred.
|
||||
let crate_map = tcx.crate_variances(());
|
||||
return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]);
|
||||
}
|
||||
DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder => {
|
||||
return variance_of_opaque(tcx, item_def_id);
|
||||
}
|
||||
_ => {
|
||||
// Variance not relevant.
|
||||
span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item")
|
||||
DefKind::AssocTy => {
|
||||
if let Some(ImplTraitInTraitData::Trait { .. }) =
|
||||
tcx.opt_rpitit_info(item_def_id.to_def_id())
|
||||
{
|
||||
return variance_of_opaque(tcx, item_def_id);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Everything else must be inferred.
|
||||
|
||||
let crate_map = tcx.crate_variances(());
|
||||
crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
|
||||
// Variance not relevant.
|
||||
span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item");
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(tcx), ret)]
|
||||
|
@ -1033,7 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let Ok(ok) = coerce.coerce(source, target) else {
|
||||
return false;
|
||||
};
|
||||
let ocx = ObligationCtxt::new_in_snapshot(self);
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
ocx.register_obligations(ok.obligations);
|
||||
ocx.select_where_possible().is_empty()
|
||||
})
|
||||
|
@ -2962,7 +2962,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
self.commit_if_ok(|_| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(self);
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let impl_substs = self.fresh_substs_for_item(base_expr.span, impl_def_id);
|
||||
let impl_trait_ref =
|
||||
self.tcx.impl_trait_ref(impl_def_id).unwrap().subst(self.tcx, impl_substs);
|
||||
|
@ -2,7 +2,7 @@ use crate::callee::{self, DeferredCallResolution};
|
||||
use crate::errors::CtorIsPrivate;
|
||||
use crate::method::{self, MethodCallee, SelfSource};
|
||||
use crate::rvalue_scopes;
|
||||
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy};
|
||||
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, RawTy};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey};
|
||||
@ -135,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
format!("{:p}", self)
|
||||
}
|
||||
|
||||
pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
|
||||
pub fn local_ty(&self, span: Span, nid: hir::HirId) -> Ty<'tcx> {
|
||||
self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
|
||||
span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
|
||||
})
|
||||
@ -746,7 +746,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
let expect_args = self
|
||||
.fudge_inference_if_ok(|| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(self);
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
|
||||
// Attempt to apply a subtyping relationship between the formal
|
||||
// return type (likely containing type variables if the function
|
||||
@ -1152,7 +1152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
|
||||
if let Res::Local(hid) = res {
|
||||
let ty = self.local_ty(span, hid).decl_ty;
|
||||
let ty = self.local_ty(span, hid);
|
||||
let ty = self.normalize(span, ty);
|
||||
self.write_ty(hir_id, ty);
|
||||
return (ty, res);
|
||||
|
@ -6,8 +6,7 @@ use crate::method::MethodCallee;
|
||||
use crate::TupleArgumentsFlag::*;
|
||||
use crate::{errors, Expectation::*};
|
||||
use crate::{
|
||||
struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
|
||||
TupleArgumentsFlag,
|
||||
struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy, TupleArgumentsFlag,
|
||||
};
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
@ -1423,7 +1422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// See #44848.
|
||||
let ref_bindings = pat.contains_explicit_ref_binding();
|
||||
|
||||
let local_ty = self.local_ty(init.span, hir_id).revealed_ty;
|
||||
let local_ty = self.local_ty(init.span, hir_id);
|
||||
if let Some(m) = ref_bindings {
|
||||
// Somewhat subtle: if we have a `ref` binding in the pattern,
|
||||
// we want to avoid introducing coercions for the RHS. This is
|
||||
@ -1453,7 +1452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
|
||||
// Determine and write the type which we'll check the pattern against.
|
||||
let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
|
||||
let decl_ty = self.local_ty(decl.span, decl.hir_id);
|
||||
self.write_ty(decl.hir_id, decl_ty);
|
||||
|
||||
// Type check the initializer.
|
||||
@ -1799,9 +1798,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let err = self.tcx.ty_error(guar);
|
||||
self.write_ty(hir_id, err);
|
||||
self.write_ty(pat.hir_id, err);
|
||||
let local_ty = LocalTy { decl_ty: err, revealed_ty: err };
|
||||
self.locals.borrow_mut().insert(hir_id, local_ty);
|
||||
self.locals.borrow_mut().insert(pat.hir_id, local_ty);
|
||||
self.locals.borrow_mut().insert(hir_id, err);
|
||||
self.locals.borrow_mut().insert(pat.hir_id, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return fn_sig;
|
||||
}
|
||||
self.probe(|_| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(self);
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let normalized_fn_sig =
|
||||
ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{FnCtxt, LocalTy};
|
||||
use crate::FnCtxt;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::PatKind;
|
||||
@ -48,7 +48,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
|
||||
Self { fcx, outermost_fn_param_pat: None }
|
||||
}
|
||||
|
||||
fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
|
||||
fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
|
||||
match ty_opt {
|
||||
None => {
|
||||
// Infer the variable's type.
|
||||
@ -56,23 +56,20 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
|
||||
kind: TypeVariableOriginKind::TypeInference,
|
||||
span,
|
||||
});
|
||||
self.fcx
|
||||
.locals
|
||||
.borrow_mut()
|
||||
.insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty });
|
||||
self.fcx.locals.borrow_mut().insert(nid, var_ty);
|
||||
var_ty
|
||||
}
|
||||
Some(typ) => {
|
||||
// Take type that the user specified.
|
||||
self.fcx.locals.borrow_mut().insert(nid, typ);
|
||||
typ.revealed_ty
|
||||
typ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a [LocalTy] for a declaration, which may have a type annotation. If it does have
|
||||
/// a type annotation, then the LocalTy stored will be the resolved type. This may be found
|
||||
/// again during type checking by querying [FnCtxt::local_ty] for the same hir_id.
|
||||
/// Allocates a type for a declaration, which may have a type annotation. If it does have
|
||||
/// a type annotation, then the [`Ty`] stored will be the resolved type. This may be found
|
||||
/// again during type checking by querying [`FnCtxt::local_ty`] for the same hir_id.
|
||||
fn declare(&mut self, decl: Declaration<'tcx>) {
|
||||
let local_ty = match decl.ty {
|
||||
Some(ref ty) => {
|
||||
@ -87,7 +84,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
|
||||
.user_provided_types_mut()
|
||||
.insert(ty.hir_id, c_ty);
|
||||
|
||||
Some(LocalTy { decl_ty: o_ty.normalized, revealed_ty: o_ty.normalized })
|
||||
Some(o_ty.normalized)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
@ -96,7 +93,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
|
||||
debug!(
|
||||
"local variable {:?} is assigned type {}",
|
||||
decl.pat,
|
||||
self.fcx.ty_to_string(self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty)
|
||||
self.fcx.ty_to_string(*self.fcx.locals.borrow().get(&decl.hir_id).unwrap())
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -151,7 +148,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
|
||||
debug!(
|
||||
"pattern binding {} is assigned to {} with type {:?}",
|
||||
ident,
|
||||
self.fcx.ty_to_string(self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
|
||||
self.fcx.ty_to_string(*self.fcx.locals.borrow().get(&p.hir_id).unwrap()),
|
||||
var_ty
|
||||
);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ pub struct Inherited<'tcx> {
|
||||
|
||||
pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>,
|
||||
|
||||
pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>,
|
||||
pub(super) locals: RefCell<HirIdMap<Ty<'tcx>>>,
|
||||
|
||||
pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
|
||||
|
||||
|
@ -81,7 +81,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
// Try to display a sensible error with as much information as possible.
|
||||
let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
|
||||
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
|
||||
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
|
||||
Ok(SizeSkeleton::Known(size)) => {
|
||||
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
|
||||
@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
Err(LayoutError::Unknown(bad)) => {
|
||||
if bad == ty {
|
||||
if *bad == ty {
|
||||
"this type does not have a fixed size".to_owned()
|
||||
} else {
|
||||
format!("size can vary because of {bad}")
|
||||
|
@ -89,13 +89,6 @@ macro_rules! type_error_struct {
|
||||
})
|
||||
}
|
||||
|
||||
/// The type of a local binding, including the revealed type for anon types.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LocalTy<'tcx> {
|
||||
decl_ty: Ty<'tcx>,
|
||||
revealed_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
/// If this `DefId` is a "primary tables entry", returns
|
||||
/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`.
|
||||
///
|
||||
|
@ -594,7 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm);
|
||||
|
||||
let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty;
|
||||
let local_ty = self.local_ty(pat.span, pat.hir_id);
|
||||
let eq_ty = match bm {
|
||||
ty::BindByReference(mutbl) => {
|
||||
// If the binding is like `ref x | ref mut x`,
|
||||
@ -635,7 +635,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ty: Ty<'tcx>,
|
||||
ti: TopInfo<'tcx>,
|
||||
) {
|
||||
let var_ty = self.local_ty(span, var_id).decl_ty;
|
||||
let var_ty = self.local_ty(span, var_id);
|
||||
if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
|
||||
let hir = self.tcx.hir();
|
||||
let var_ty = self.resolve_vars_with_obligations(var_ty);
|
||||
|
@ -348,7 +348,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
|
||||
intravisit::walk_local(self, l);
|
||||
let var_ty = self.fcx.local_ty(l.span, l.hir_id).decl_ty;
|
||||
let var_ty = self.fcx.local_ty(l.span, l.hir_id);
|
||||
let var_ty = self.resolve(var_ty, &l.span);
|
||||
self.write_ty_to_typeck_results(l.hir_id, var_ty);
|
||||
}
|
||||
|
@ -79,7 +79,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
reported_closure_mismatch: self.reported_closure_mismatch.clone(),
|
||||
tainted_by_errors: self.tainted_by_errors.clone(),
|
||||
err_count_on_creation: self.err_count_on_creation,
|
||||
in_snapshot: self.in_snapshot.clone(),
|
||||
universe: self.universe.clone(),
|
||||
intercrate: self.intercrate,
|
||||
next_trait_solver: self.next_trait_solver,
|
||||
|
@ -6,6 +6,7 @@ pub use self::RegionVariableOrigin::*;
|
||||
pub use self::SubregionOrigin::*;
|
||||
pub use self::ValuePairs::*;
|
||||
pub use combine::ObligationEmittingRelation;
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
|
||||
use self::opaque_types::OpaqueTypeStorage;
|
||||
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
|
||||
@ -297,9 +298,6 @@ pub struct InferCtxt<'tcx> {
|
||||
// FIXME(matthewjasper) Merge into `tainted_by_errors`
|
||||
err_count_on_creation: usize,
|
||||
|
||||
/// This flag is true while there is an active snapshot.
|
||||
in_snapshot: Cell<bool>,
|
||||
|
||||
/// What is the innermost universe we have created? Starts out as
|
||||
/// `UniverseIndex::root()` but grows from there as we enter
|
||||
/// universal quantifiers.
|
||||
@ -643,7 +641,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||
reported_closure_mismatch: Default::default(),
|
||||
tainted_by_errors: Cell::new(None),
|
||||
err_count_on_creation: tcx.sess.err_count(),
|
||||
in_snapshot: Cell::new(false),
|
||||
universe: Cell::new(ty::UniverseIndex::ROOT),
|
||||
intercrate,
|
||||
next_trait_solver,
|
||||
@ -679,7 +676,6 @@ pub struct CombinedSnapshot<'tcx> {
|
||||
undo_snapshot: Snapshot<'tcx>,
|
||||
region_constraints_snapshot: RegionSnapshot,
|
||||
universe: ty::UniverseIndex,
|
||||
was_in_snapshot: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
@ -702,10 +698,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_in_snapshot(&self) -> bool {
|
||||
self.in_snapshot.get()
|
||||
}
|
||||
|
||||
pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
|
||||
t.fold_with(&mut self.freshener())
|
||||
}
|
||||
@ -766,31 +758,30 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_snapshot(&self) -> bool {
|
||||
UndoLogs::<UndoLog<'tcx>>::in_snapshot(&self.inner.borrow_mut().undo_log)
|
||||
}
|
||||
|
||||
pub fn num_open_snapshots(&self) -> usize {
|
||||
UndoLogs::<UndoLog<'tcx>>::num_open_snapshots(&self.inner.borrow_mut().undo_log)
|
||||
}
|
||||
|
||||
fn start_snapshot(&self) -> CombinedSnapshot<'tcx> {
|
||||
debug!("start_snapshot()");
|
||||
|
||||
let in_snapshot = self.in_snapshot.replace(true);
|
||||
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
CombinedSnapshot {
|
||||
undo_snapshot: inner.undo_log.start_snapshot(),
|
||||
region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(),
|
||||
universe: self.universe(),
|
||||
was_in_snapshot: in_snapshot,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, snapshot), level = "debug")]
|
||||
fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'tcx>) {
|
||||
let CombinedSnapshot {
|
||||
undo_snapshot,
|
||||
region_constraints_snapshot,
|
||||
universe,
|
||||
was_in_snapshot,
|
||||
} = snapshot;
|
||||
let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot;
|
||||
|
||||
self.in_snapshot.set(was_in_snapshot);
|
||||
self.universe.set(universe);
|
||||
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
@ -800,14 +791,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
|
||||
#[instrument(skip(self, snapshot), level = "debug")]
|
||||
fn commit_from(&self, snapshot: CombinedSnapshot<'tcx>) {
|
||||
let CombinedSnapshot {
|
||||
undo_snapshot,
|
||||
region_constraints_snapshot: _,
|
||||
universe: _,
|
||||
was_in_snapshot,
|
||||
} = snapshot;
|
||||
|
||||
self.in_snapshot.set(was_in_snapshot);
|
||||
let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } =
|
||||
snapshot;
|
||||
|
||||
self.inner.borrow_mut().commit(undo_snapshot);
|
||||
}
|
||||
|
@ -125,10 +125,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
/// right before lexical region resolution.
|
||||
#[instrument(level = "debug", skip(self, outlives_env))]
|
||||
pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
|
||||
assert!(
|
||||
!self.in_snapshot.get(),
|
||||
"cannot process registered region obligations in a snapshot"
|
||||
);
|
||||
assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
|
||||
|
||||
let my_region_obligations = self.take_registered_region_obligations();
|
||||
|
||||
|
@ -80,14 +80,14 @@ pub struct Elaborator<'tcx, O> {
|
||||
pub trait Elaboratable<'tcx> {
|
||||
fn predicate(&self) -> ty::Predicate<'tcx>;
|
||||
|
||||
// Makes a new `Self` but with a different predicate.
|
||||
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self;
|
||||
// Makes a new `Self` but with a different clause that comes from elaboration.
|
||||
fn child(&self, clause: ty::Clause<'tcx>) -> Self;
|
||||
|
||||
// Makes a new `Self` but with a different predicate and a different cause
|
||||
// code (if `Self` has one).
|
||||
// Makes a new `Self` but with a different clause and a different cause
|
||||
// code (if `Self` has one, such as [`PredicateObligation`]).
|
||||
fn child_with_derived_cause(
|
||||
&self,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
clause: ty::Clause<'tcx>,
|
||||
span: Span,
|
||||
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
index: usize,
|
||||
@ -99,18 +99,18 @@ impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
|
||||
self.predicate
|
||||
}
|
||||
|
||||
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
|
||||
fn child(&self, clause: ty::Clause<'tcx>) -> Self {
|
||||
Obligation {
|
||||
cause: self.cause.clone(),
|
||||
param_env: self.param_env,
|
||||
recursion_depth: 0,
|
||||
predicate,
|
||||
predicate: clause.as_predicate(),
|
||||
}
|
||||
}
|
||||
|
||||
fn child_with_derived_cause(
|
||||
&self,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
clause: ty::Clause<'tcx>,
|
||||
span: Span,
|
||||
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
index: usize,
|
||||
@ -123,7 +123,12 @@ impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
|
||||
span,
|
||||
}))
|
||||
});
|
||||
Obligation { cause, param_env: self.param_env, recursion_depth: 0, predicate }
|
||||
Obligation {
|
||||
cause,
|
||||
param_env: self.param_env,
|
||||
recursion_depth: 0,
|
||||
predicate: clause.as_predicate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,18 +137,18 @@ impl<'tcx> Elaboratable<'tcx> for ty::Predicate<'tcx> {
|
||||
*self
|
||||
}
|
||||
|
||||
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
|
||||
predicate
|
||||
fn child(&self, clause: ty::Clause<'tcx>) -> Self {
|
||||
clause.as_predicate()
|
||||
}
|
||||
|
||||
fn child_with_derived_cause(
|
||||
&self,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
clause: ty::Clause<'tcx>,
|
||||
_span: Span,
|
||||
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
_index: usize,
|
||||
) -> Self {
|
||||
predicate
|
||||
clause.as_predicate()
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,18 +157,18 @@ impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
|
||||
(predicate, self.1)
|
||||
fn child(&self, clause: ty::Clause<'tcx>) -> Self {
|
||||
(clause.as_predicate(), self.1)
|
||||
}
|
||||
|
||||
fn child_with_derived_cause(
|
||||
&self,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
clause: ty::Clause<'tcx>,
|
||||
_span: Span,
|
||||
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
_index: usize,
|
||||
) -> Self {
|
||||
(predicate, self.1)
|
||||
(clause.as_predicate(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,18 +177,18 @@ impl<'tcx> Elaboratable<'tcx> for (ty::Clause<'tcx>, Span) {
|
||||
self.0.as_predicate()
|
||||
}
|
||||
|
||||
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
|
||||
(predicate.expect_clause(), self.1)
|
||||
fn child(&self, clause: ty::Clause<'tcx>) -> Self {
|
||||
(clause, self.1)
|
||||
}
|
||||
|
||||
fn child_with_derived_cause(
|
||||
&self,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
clause: ty::Clause<'tcx>,
|
||||
_span: Span,
|
||||
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
_index: usize,
|
||||
) -> Self {
|
||||
(predicate.expect_clause(), self.1)
|
||||
(clause, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,18 +197,18 @@ impl<'tcx> Elaboratable<'tcx> for ty::Clause<'tcx> {
|
||||
self.as_predicate()
|
||||
}
|
||||
|
||||
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
|
||||
predicate.expect_clause()
|
||||
fn child(&self, clause: ty::Clause<'tcx>) -> Self {
|
||||
clause
|
||||
}
|
||||
|
||||
fn child_with_derived_cause(
|
||||
&self,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
clause: ty::Clause<'tcx>,
|
||||
_span: Span,
|
||||
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
_index: usize,
|
||||
) -> Self {
|
||||
predicate.expect_clause()
|
||||
clause
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,14 +257,13 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
|
||||
};
|
||||
|
||||
let obligations =
|
||||
predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
|
||||
predicates.predicates.iter().enumerate().map(|(index, &(mut clause, span))| {
|
||||
// when parent predicate is non-const, elaborate it to non-const predicates.
|
||||
if data.constness == ty::BoundConstness::NotConst {
|
||||
pred = pred.without_const(tcx);
|
||||
clause = clause.without_const(tcx);
|
||||
}
|
||||
elaboratable.child_with_derived_cause(
|
||||
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref))
|
||||
.as_predicate(),
|
||||
clause.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
||||
span,
|
||||
bound_predicate.rebind(data),
|
||||
index,
|
||||
@ -333,17 +337,15 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
|
||||
if r.is_late_bound() {
|
||||
None
|
||||
} else {
|
||||
Some(ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
|
||||
ty::OutlivesPredicate(r, r_min),
|
||||
Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
|
||||
r, r_min,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
Component::Param(p) => {
|
||||
let ty = tcx.mk_ty_param(p.index, p.name);
|
||||
Some(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
|
||||
ty::OutlivesPredicate(ty, r_min),
|
||||
)))
|
||||
Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
|
||||
}
|
||||
|
||||
Component::UnresolvedInferenceVariable(_) => None,
|
||||
@ -351,8 +353,9 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
|
||||
Component::Alias(alias_ty) => {
|
||||
// We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`.
|
||||
// With this, we can deduce that `<Bar as Baz>::Assoc: 'a`.
|
||||
Some(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
|
||||
ty::OutlivesPredicate(alias_ty.to_ty(tcx), r_min),
|
||||
Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
|
||||
alias_ty.to_ty(tcx),
|
||||
r_min,
|
||||
)))
|
||||
}
|
||||
|
||||
@ -362,10 +365,9 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|predicate_kind| {
|
||||
bound_predicate.rebind(predicate_kind).to_predicate(tcx)
|
||||
})
|
||||
.map(|predicate| elaboratable.child(predicate)),
|
||||
.map(|clause| {
|
||||
elaboratable.child(bound_predicate.rebind(clause).to_predicate(tcx))
|
||||
}),
|
||||
);
|
||||
}
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::TypeWellFormedFromEnv(..)) => {
|
||||
|
@ -8,6 +8,7 @@ use rustc_session::config::rustc_optgroups;
|
||||
use rustc_session::config::DebugInfo;
|
||||
use rustc_session::config::Input;
|
||||
use rustc_session::config::InstrumentXRay;
|
||||
use rustc_session::config::LinkSelfContained;
|
||||
use rustc_session::config::TraitSolver;
|
||||
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
|
||||
use rustc_session::config::{
|
||||
@ -579,7 +580,7 @@ fn test_codegen_options_tracking_hash() {
|
||||
untracked!(incremental, Some(String::from("abc")));
|
||||
// `link_arg` is omitted because it just forwards to `link_args`.
|
||||
untracked!(link_args, vec![String::from("abc"), String::from("def")]);
|
||||
untracked!(link_self_contained, Some(true));
|
||||
untracked!(link_self_contained, LinkSelfContained::on());
|
||||
untracked!(linker, Some(PathBuf::from("linker")));
|
||||
untracked!(linker_flavor, Some(LinkerFlavorCli::Gcc));
|
||||
untracked!(no_stack_check, true);
|
||||
|
@ -665,9 +665,7 @@ declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS])
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id)
|
||||
&& cx.tcx.local_visibility(item.owner_id.def_id).is_public())
|
||||
{
|
||||
if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
|
||||
return;
|
||||
}
|
||||
let (def, ty) = match item.kind {
|
||||
@ -786,9 +784,7 @@ impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id)
|
||||
&& cx.tcx.local_visibility(item.owner_id.def_id).is_public())
|
||||
{
|
||||
if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ fn is_single_call_in_arm<'tcx>(
|
||||
arg: &'tcx Expr<'_>,
|
||||
drop_expr: &'tcx Expr<'_>,
|
||||
) -> bool {
|
||||
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
|
||||
if arg.can_have_side_effects() {
|
||||
let parent_node = cx.tcx.hir().find_parent(drop_expr.hir_id);
|
||||
if let Some(Node::Arm(Arm { body, .. })) = &parent_node {
|
||||
return body.hir_id == drop_expr.hir_id;
|
||||
|
@ -4264,6 +4264,7 @@ declare_lint! {
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// # #![feature(type_privacy_lints)]
|
||||
/// # #![allow(unused)]
|
||||
/// # #![allow(private_in_public)]
|
||||
/// #![deny(private_interfaces)]
|
||||
@ -4288,6 +4289,7 @@ declare_lint! {
|
||||
pub PRIVATE_INTERFACES,
|
||||
Allow,
|
||||
"private type in primary interface of an item",
|
||||
@feature_gate = sym::type_privacy_lints;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
@ -4298,6 +4300,7 @@ declare_lint! {
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// # #![feature(type_privacy_lints)]
|
||||
/// # #![allow(private_in_public)]
|
||||
/// # #![allow(unused)]
|
||||
/// #![deny(private_bounds)]
|
||||
@ -4317,7 +4320,8 @@ declare_lint! {
|
||||
/// the item actually provides.
|
||||
pub PRIVATE_BOUNDS,
|
||||
Allow,
|
||||
"private type in secondary interface of an item"
|
||||
"private type in secondary interface of an item",
|
||||
@feature_gate = sym::type_privacy_lints;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
@ -4327,6 +4331,7 @@ declare_lint! {
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// # #![feature(type_privacy_lints)]
|
||||
/// # #![allow(unused)]
|
||||
/// #![deny(unnameable_types)]
|
||||
/// mod m {
|
||||
@ -4345,5 +4350,6 @@ declare_lint! {
|
||||
/// you can name the type `T` as well, this lint attempts to enforce this rule.
|
||||
pub UNNAMEABLE_TYPES,
|
||||
Allow,
|
||||
"effective visibility of a type is larger than the area in which it can be named"
|
||||
"effective visibility of a type is larger than the area in which it can be named",
|
||||
@feature_gate = sym::type_privacy_lints;
|
||||
}
|
||||
|
@ -7,7 +7,12 @@
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/IntrinsicsARM.h"
|
||||
#include "llvm/IR/LLVMRemarkStreamer.h"
|
||||
#include "llvm/IR/Mangler.h"
|
||||
#include "llvm/Remarks/RemarkStreamer.h"
|
||||
#include "llvm/Remarks/RemarkSerializer.h"
|
||||
#include "llvm/Remarks/RemarkFormat.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
#if LLVM_VERSION_GE(16, 0)
|
||||
#include "llvm/Support/ModRef.h"
|
||||
#endif
|
||||
@ -1855,23 +1860,44 @@ using LLVMDiagnosticHandlerTy = DiagnosticHandler::DiagnosticHandlerTy;
|
||||
// When RemarkAllPasses is true, remarks are enabled for all passes. Otherwise
|
||||
// the RemarkPasses array specifies individual passes for which remarks will be
|
||||
// enabled.
|
||||
//
|
||||
// If RemarkFilePath is not NULL, optimization remarks will be streamed directly into this file,
|
||||
// bypassing the diagnostics handler.
|
||||
extern "C" void LLVMRustContextConfigureDiagnosticHandler(
|
||||
LLVMContextRef C, LLVMDiagnosticHandlerTy DiagnosticHandlerCallback,
|
||||
void *DiagnosticHandlerContext, bool RemarkAllPasses,
|
||||
const char * const * RemarkPasses, size_t RemarkPassesLen) {
|
||||
const char * const * RemarkPasses, size_t RemarkPassesLen,
|
||||
const char * RemarkFilePath
|
||||
) {
|
||||
|
||||
class RustDiagnosticHandler final : public DiagnosticHandler {
|
||||
public:
|
||||
RustDiagnosticHandler(LLVMDiagnosticHandlerTy DiagnosticHandlerCallback,
|
||||
void *DiagnosticHandlerContext,
|
||||
bool RemarkAllPasses,
|
||||
std::vector<std::string> RemarkPasses)
|
||||
RustDiagnosticHandler(
|
||||
LLVMDiagnosticHandlerTy DiagnosticHandlerCallback,
|
||||
void *DiagnosticHandlerContext,
|
||||
bool RemarkAllPasses,
|
||||
std::vector<std::string> RemarkPasses,
|
||||
std::unique_ptr<ToolOutputFile> RemarksFile,
|
||||
std::unique_ptr<llvm::remarks::RemarkStreamer> RemarkStreamer,
|
||||
std::unique_ptr<LLVMRemarkStreamer> LlvmRemarkStreamer
|
||||
)
|
||||
: DiagnosticHandlerCallback(DiagnosticHandlerCallback),
|
||||
DiagnosticHandlerContext(DiagnosticHandlerContext),
|
||||
RemarkAllPasses(RemarkAllPasses),
|
||||
RemarkPasses(RemarkPasses) {}
|
||||
RemarkPasses(std::move(RemarkPasses)),
|
||||
RemarksFile(std::move(RemarksFile)),
|
||||
RemarkStreamer(std::move(RemarkStreamer)),
|
||||
LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {}
|
||||
|
||||
virtual bool handleDiagnostics(const DiagnosticInfo &DI) override {
|
||||
if (this->LlvmRemarkStreamer) {
|
||||
if (auto *OptDiagBase = dyn_cast<DiagnosticInfoOptimizationBase>(&DI)) {
|
||||
if (OptDiagBase->isEnabled()) {
|
||||
this->LlvmRemarkStreamer->emit(*OptDiagBase);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DiagnosticHandlerCallback) {
|
||||
DiagnosticHandlerCallback(DI, DiagnosticHandlerContext);
|
||||
return true;
|
||||
@ -1912,14 +1938,64 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
|
||||
|
||||
bool RemarkAllPasses = false;
|
||||
std::vector<std::string> RemarkPasses;
|
||||
|
||||
// Since LlvmRemarkStreamer contains a pointer to RemarkStreamer, the ordering of the three
|
||||
// members below is important.
|
||||
std::unique_ptr<ToolOutputFile> RemarksFile;
|
||||
std::unique_ptr<llvm::remarks::RemarkStreamer> RemarkStreamer;
|
||||
std::unique_ptr<LLVMRemarkStreamer> LlvmRemarkStreamer;
|
||||
};
|
||||
|
||||
std::vector<std::string> Passes;
|
||||
for (size_t I = 0; I != RemarkPassesLen; ++I)
|
||||
{
|
||||
Passes.push_back(RemarkPasses[I]);
|
||||
}
|
||||
|
||||
// We need to hold onto both the streamers and the opened file
|
||||
std::unique_ptr<ToolOutputFile> RemarkFile;
|
||||
std::unique_ptr<llvm::remarks::RemarkStreamer> RemarkStreamer;
|
||||
std::unique_ptr<LLVMRemarkStreamer> LlvmRemarkStreamer;
|
||||
|
||||
if (RemarkFilePath != nullptr) {
|
||||
std::error_code EC;
|
||||
RemarkFile = std::make_unique<ToolOutputFile>(
|
||||
RemarkFilePath,
|
||||
EC,
|
||||
llvm::sys::fs::OF_TextWithCRLF
|
||||
);
|
||||
if (EC) {
|
||||
std::string Error = std::string("Cannot create remark file: ") +
|
||||
toString(errorCodeToError(EC));
|
||||
report_fatal_error(Twine(Error));
|
||||
}
|
||||
|
||||
// Do not delete the file after we gather remarks
|
||||
RemarkFile->keep();
|
||||
|
||||
auto RemarkSerializer = remarks::createRemarkSerializer(
|
||||
llvm::remarks::Format::YAML,
|
||||
remarks::SerializerMode::Separate,
|
||||
RemarkFile->os()
|
||||
);
|
||||
if (Error E = RemarkSerializer.takeError())
|
||||
{
|
||||
std::string Error = std::string("Cannot create remark serializer: ") + toString(std::move(E));
|
||||
report_fatal_error(Twine(Error));
|
||||
}
|
||||
RemarkStreamer = std::make_unique<llvm::remarks::RemarkStreamer>(std::move(*RemarkSerializer));
|
||||
LlvmRemarkStreamer = std::make_unique<LLVMRemarkStreamer>(*RemarkStreamer);
|
||||
}
|
||||
|
||||
unwrap(C)->setDiagnosticHandler(std::make_unique<RustDiagnosticHandler>(
|
||||
DiagnosticHandlerCallback, DiagnosticHandlerContext, RemarkAllPasses, Passes));
|
||||
DiagnosticHandlerCallback,
|
||||
DiagnosticHandlerContext,
|
||||
RemarkAllPasses,
|
||||
Passes,
|
||||
std::move(RemarkFile),
|
||||
std::move(RemarkStreamer),
|
||||
std::move(LlvmRemarkStreamer)
|
||||
));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustGetMangledName(LLVMValueRef V, RustStringRef Str) {
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
tracing = "0.1.28"
|
||||
tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
|
||||
tracing-tree = "0.2.0"
|
||||
tracing-core = "0.1.28"
|
||||
tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635
|
||||
|
||||
[dev-dependencies]
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
|
@ -8,6 +8,7 @@ proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
synstructure = "0.13.0"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
# FIXME(Nilstrieb): Updating this causes changes in the diagnostics output.
|
||||
syn = { version = "=2.0.8", features = ["full"] }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
|
@ -25,6 +25,9 @@ metadata_conflicting_alloc_error_handler =
|
||||
metadata_conflicting_global_alloc =
|
||||
the `#[global_allocator]` in {$other_crate_name} conflicts with global allocator in: {$crate_name}
|
||||
|
||||
metadata_consider_adding_std =
|
||||
consider adding the standard library to the sysroot with `x build library --target {$locator_triple}`
|
||||
|
||||
metadata_consider_building_std =
|
||||
consider building the standard library from source with `cargo build -Zbuild-std`
|
||||
|
||||
|
@ -646,12 +646,18 @@ impl IntoDiagnostic<'_> for CannotFindCrate {
|
||||
} else {
|
||||
diag.note(fluent::metadata_target_no_std_support);
|
||||
}
|
||||
// NOTE: this suggests using rustup, even though the user may not have it installed.
|
||||
// That's because they could choose to install it; or this may give them a hint which
|
||||
// target they need to install from their distro.
|
||||
|
||||
if self.missing_core {
|
||||
diag.help(fluent::metadata_consider_downloading_target);
|
||||
if env!("CFG_RELEASE_CHANNEL") == "dev" {
|
||||
diag.help(fluent::metadata_consider_adding_std);
|
||||
} else {
|
||||
// NOTE: this suggests using rustup, even though the user may not have it installed.
|
||||
// That's because they could choose to install it; or this may give them a hint which
|
||||
// target they need to install from their distro.
|
||||
diag.help(fluent::metadata_consider_downloading_target);
|
||||
}
|
||||
}
|
||||
|
||||
// Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway.
|
||||
// NOTE: this is a dummy span if `extern crate std` was injected by the compiler.
|
||||
// If it's not a dummy, that means someone added `extern crate std` explicitly and
|
||||
|
@ -1447,6 +1447,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
.is_type_alias_impl_trait
|
||||
.set(def_id.index, self.tcx.is_type_alias_impl_trait(def_id));
|
||||
}
|
||||
if let DefKind::ImplTraitPlaceholder = def_kind {
|
||||
self.encode_explicit_item_bounds(def_id);
|
||||
}
|
||||
if tcx.impl_method_has_trait_impl_trait_tys(def_id)
|
||||
&& let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
chalk-ir = "0.87.0"
|
||||
chalk-ir = "0.92.0"
|
||||
derive_more = "0.99.17"
|
||||
either = "1.5.0"
|
||||
gsgdt = "0.1.2"
|
||||
|
@ -4,6 +4,7 @@
|
||||
use crate::ty::{TyCtxt, Visibility};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_query_system::ich::StableHashingContext;
|
||||
use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID};
|
||||
@ -148,13 +149,12 @@ impl EffectiveVisibilities {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
|
||||
pub fn check_invariants(&self, tcx: TyCtxt<'_>) {
|
||||
if !cfg!(debug_assertions) {
|
||||
return;
|
||||
}
|
||||
for (&def_id, ev) in &self.map {
|
||||
// More direct visibility levels can never go farther than less direct ones,
|
||||
// neither of effective visibilities can go farther than nominal visibility,
|
||||
// and all effective visibilities are larger or equal than private visibility.
|
||||
let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
|
||||
let span = tcx.def_span(def_id.to_def_id());
|
||||
@ -175,17 +175,20 @@ impl EffectiveVisibilities {
|
||||
ev.reachable_through_impl_trait
|
||||
);
|
||||
}
|
||||
let nominal_vis = tcx.visibility(def_id);
|
||||
// FIXME: `rustc_privacy` is not yet updated for the new logic and can set
|
||||
// effective visibilities that are larger than the nominal one.
|
||||
if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
|
||||
span_bug!(
|
||||
span,
|
||||
"{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
|
||||
def_id,
|
||||
ev.reachable_through_impl_trait,
|
||||
nominal_vis
|
||||
);
|
||||
// All effective visibilities except `reachable_through_impl_trait` are limited to
|
||||
// nominal visibility. For some items nominal visibility doesn't make sense so we
|
||||
// don't check this condition for them.
|
||||
if !matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) {
|
||||
let nominal_vis = tcx.visibility(def_id);
|
||||
if !nominal_vis.is_at_least(ev.reachable, tcx) {
|
||||
span_bug!(
|
||||
span,
|
||||
"{:?}: reachable {:?} > nominal {:?}",
|
||||
def_id,
|
||||
ev.reachable,
|
||||
nominal_vis
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -212,7 +215,7 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
id: Id,
|
||||
nominal_vis: Option<Visibility>,
|
||||
max_vis: Option<Visibility>,
|
||||
lazy_private_vis: impl FnOnce() -> Visibility,
|
||||
inherited_effective_vis: EffectiveVisibility,
|
||||
level: Level,
|
||||
@ -236,8 +239,8 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
|
||||
if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
|
||||
&& level != l)
|
||||
{
|
||||
calculated_effective_vis = if let Some(nominal_vis) = nominal_vis && !nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
|
||||
nominal_vis
|
||||
calculated_effective_vis = if let Some(max_vis) = max_vis && !max_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
|
||||
max_vis
|
||||
} else {
|
||||
inherited_effective_vis_at_level
|
||||
}
|
||||
|
@ -95,11 +95,15 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
// used generic parameters is a bug of evaluation, so checking for it
|
||||
// here does feel somewhat sensible.
|
||||
if !self.features().generic_const_exprs && ct.substs.has_non_region_param() {
|
||||
assert!(matches!(
|
||||
self.def_kind(ct.def),
|
||||
DefKind::InlineConst | DefKind::AnonConst
|
||||
));
|
||||
let mir_body = self.mir_for_ctfe(ct.def);
|
||||
let def_kind = self.def_kind(instance.def_id());
|
||||
assert!(
|
||||
matches!(
|
||||
def_kind,
|
||||
DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst
|
||||
),
|
||||
"{cid:?} is {def_kind:?}",
|
||||
);
|
||||
let mir_body = self.mir_for_ctfe(instance.def_id());
|
||||
if mir_body.is_polymorphic {
|
||||
let Some(local_def_id) = ct.def.as_local() else { return };
|
||||
self.struct_span_lint_hir(
|
||||
@ -239,15 +243,3 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
|
||||
self.eval_to_allocation_raw(param_env.and(gid))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
/// Destructure a mir constant ADT or array into its variant index and its field values.
|
||||
/// Panics if the destructuring fails, use `try_destructure_mir_constant` for fallible version.
|
||||
pub fn destructure_mir_constant(
|
||||
self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
constant: mir::ConstantKind<'tcx>,
|
||||
) -> mir::DestructuredConstant<'tcx> {
|
||||
self.try_destructure_mir_constant(param_env.and(constant)).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -2581,10 +2581,9 @@ pub struct UnevaluatedConst<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> UnevaluatedConst<'tcx> {
|
||||
// FIXME: probably should get rid of this method. It's also wrong to
|
||||
// shrink and then later expand a promoted.
|
||||
#[inline]
|
||||
pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
|
||||
assert_eq!(self.promoted, None);
|
||||
ty::UnevaluatedConst { def: self.def, substs: self.substs }
|
||||
}
|
||||
}
|
||||
|
@ -71,8 +71,8 @@ impl<T> EraseType for Result<&'_ T, traits::CodegenObligationError> {
|
||||
type Result = [u8; size_of::<Result<&'static (), traits::CodegenObligationError>>()];
|
||||
}
|
||||
|
||||
impl<T> EraseType for Result<&'_ T, ty::layout::FnAbiError<'_>> {
|
||||
type Result = [u8; size_of::<Result<&'static (), ty::layout::FnAbiError<'static>>>()];
|
||||
impl<T> EraseType for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> {
|
||||
type Result = [u8; size_of::<Result<&'static (), &'static ty::layout::FnAbiError<'static>>>()];
|
||||
}
|
||||
|
||||
impl<T> EraseType for Result<(&'_ T, rustc_middle::thir::ExprId), rustc_errors::ErrorGuaranteed> {
|
||||
@ -96,15 +96,17 @@ impl EraseType for Result<ty::GenericArg<'_>, traits::query::NoSolution> {
|
||||
type Result = [u8; size_of::<Result<ty::GenericArg<'static>, traits::query::NoSolution>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Result<bool, ty::layout::LayoutError<'_>> {
|
||||
type Result = [u8; size_of::<Result<bool, ty::layout::LayoutError<'static>>>()];
|
||||
impl EraseType for Result<bool, &ty::layout::LayoutError<'_>> {
|
||||
type Result = [u8; size_of::<Result<bool, &'static ty::layout::LayoutError<'static>>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Result<rustc_target::abi::TyAndLayout<'_, Ty<'_>>, ty::layout::LayoutError<'_>> {
|
||||
impl EraseType
|
||||
for Result<rustc_target::abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>>
|
||||
{
|
||||
type Result = [u8; size_of::<
|
||||
Result<
|
||||
rustc_target::abi::TyAndLayout<'static, Ty<'static>>,
|
||||
ty::layout::LayoutError<'static>,
|
||||
&'static ty::layout::LayoutError<'static>,
|
||||
>,
|
||||
>()];
|
||||
}
|
||||
|
@ -1383,7 +1383,7 @@ rustc_queries! {
|
||||
/// executes in "reveal all" mode, and will normalize the input type.
|
||||
query layout_of(
|
||||
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
|
||||
) -> Result<ty::layout::TyAndLayout<'tcx>, ty::layout::LayoutError<'tcx>> {
|
||||
) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
|
||||
depth_limit
|
||||
desc { "computing layout of `{}`", key.value }
|
||||
}
|
||||
@ -1394,7 +1394,7 @@ rustc_queries! {
|
||||
/// instead, where the instance is an `InstanceDef::Virtual`.
|
||||
query fn_abi_of_fn_ptr(
|
||||
key: ty::ParamEnvAnd<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>
|
||||
) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, ty::layout::FnAbiError<'tcx>> {
|
||||
) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> {
|
||||
desc { "computing call ABI of `{}` function pointers", key.value.0 }
|
||||
}
|
||||
|
||||
@ -1405,7 +1405,7 @@ rustc_queries! {
|
||||
/// to an `InstanceDef::Virtual` instance (of `<dyn Trait as Trait>::fn`).
|
||||
query fn_abi_of_instance(
|
||||
key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>)>
|
||||
) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, ty::layout::FnAbiError<'tcx>> {
|
||||
) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> {
|
||||
desc { "computing call ABI of `{}`", key.value.0 }
|
||||
}
|
||||
|
||||
@ -2164,7 +2164,7 @@ rustc_queries! {
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
query check_validity_requirement(key: (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>)) -> Result<bool, ty::layout::LayoutError<'tcx>> {
|
||||
query check_validity_requirement(key: (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>)) -> Result<bool, &'tcx ty::layout::LayoutError<'tcx>> {
|
||||
desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 }
|
||||
}
|
||||
|
||||
|
@ -310,7 +310,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
||||
ty: Ty<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Result<SizeSkeleton<'tcx>, LayoutError<'tcx>> {
|
||||
) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
debug_assert!(!ty.has_non_region_infer());
|
||||
|
||||
// First try computing a static layout.
|
||||
@ -353,13 +353,13 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
||||
let size = s
|
||||
.bytes()
|
||||
.checked_mul(c)
|
||||
.ok_or_else(|| LayoutError::SizeOverflow(ty))?;
|
||||
.ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?;
|
||||
return Ok(SizeSkeleton::Known(Size::from_bytes(size)));
|
||||
}
|
||||
let len = tcx.expand_abstract_consts(len);
|
||||
let prev = ty::Const::from_target_usize(tcx, s.bytes());
|
||||
let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, prev) else {
|
||||
return Err(LayoutError::SizeOverflow(ty));
|
||||
return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty)));
|
||||
};
|
||||
Ok(SizeSkeleton::Generic(gen_size))
|
||||
}
|
||||
@ -367,7 +367,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
||||
SizeSkeleton::Generic(g) => {
|
||||
let len = tcx.expand_abstract_consts(len);
|
||||
let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, g) else {
|
||||
return Err(LayoutError::SizeOverflow(ty));
|
||||
return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty)));
|
||||
};
|
||||
Ok(SizeSkeleton::Generic(gen_size))
|
||||
}
|
||||
@ -672,7 +672,7 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
|
||||
|
||||
MaybeResult::from(
|
||||
tcx.layout_of(self.param_env().and(ty))
|
||||
.map_err(|err| self.handle_layout_err(err, span, ty)),
|
||||
.map_err(|err| self.handle_layout_err(*err, span, ty)),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -680,16 +680,21 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
|
||||
impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}
|
||||
|
||||
impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||
type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
|
||||
type LayoutOfResult = Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>>;
|
||||
|
||||
#[inline]
|
||||
fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
|
||||
err
|
||||
fn handle_layout_err(
|
||||
&self,
|
||||
err: LayoutError<'tcx>,
|
||||
_: Span,
|
||||
_: Ty<'tcx>,
|
||||
) -> &'tcx LayoutError<'tcx> {
|
||||
self.tcx.arena.alloc(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxtAt<'tcx>> {
|
||||
type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
|
||||
type LayoutOfResult = Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>>;
|
||||
|
||||
#[inline]
|
||||
fn layout_tcx_at_span(&self) -> Span {
|
||||
@ -697,8 +702,13 @@ impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxtAt<'tcx>> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
|
||||
err
|
||||
fn handle_layout_err(
|
||||
&self,
|
||||
err: LayoutError<'tcx>,
|
||||
_: Span,
|
||||
_: Ty<'tcx>,
|
||||
) -> &'tcx LayoutError<'tcx> {
|
||||
self.tcx.arena.alloc(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1250,18 +1260,6 @@ pub enum FnAbiError<'tcx> {
|
||||
AdjustForForeignAbi(call::AdjustForForeignAbiError),
|
||||
}
|
||||
|
||||
impl<'tcx> From<LayoutError<'tcx>> for FnAbiError<'tcx> {
|
||||
fn from(err: LayoutError<'tcx>) -> Self {
|
||||
Self::Layout(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<call::AdjustForForeignAbiError> for FnAbiError<'_> {
|
||||
fn from(err: call::AdjustForForeignAbiError) -> Self {
|
||||
Self::AdjustForForeignAbi(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoDiagnostic<'a, !> for FnAbiError<'b> {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> {
|
||||
match self {
|
||||
@ -1321,7 +1319,7 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
|
||||
let tcx = self.tcx().at(span);
|
||||
|
||||
MaybeResult::from(tcx.fn_abi_of_fn_ptr(self.param_env().and((sig, extra_args))).map_err(
|
||||
|err| self.handle_fn_abi_err(err, span, FnAbiRequest::OfFnPtr { sig, extra_args }),
|
||||
|err| self.handle_fn_abi_err(*err, span, FnAbiRequest::OfFnPtr { sig, extra_args }),
|
||||
))
|
||||
}
|
||||
|
||||
@ -1348,7 +1346,11 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
|
||||
// However, we don't do this early in order to avoid calling
|
||||
// `def_span` unconditionally (which may have a perf penalty).
|
||||
let span = if !span.is_dummy() { span } else { tcx.def_span(instance.def_id()) };
|
||||
self.handle_fn_abi_err(err, span, FnAbiRequest::OfInstance { instance, extra_args })
|
||||
self.handle_fn_abi_err(
|
||||
*err,
|
||||
span,
|
||||
FnAbiRequest::OfInstance { instance, extra_args },
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -106,9 +106,12 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<ty::Binder<'_, ty::F
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> Value<TyCtxt<'tcx>, DepKind> for Result<T, ty::layout::LayoutError<'_>> {
|
||||
impl<'tcx, T> Value<TyCtxt<'tcx>, DepKind> for Result<T, &'_ ty::layout::LayoutError<'_>> {
|
||||
fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo<DepKind>]) -> Self {
|
||||
Err(ty::layout::LayoutError::Cycle)
|
||||
// tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
|
||||
// min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
|
||||
// tcx.arena.alloc is pretty much equal to leaking).
|
||||
Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,6 @@ pub mod impls;
|
||||
pub mod move_paths;
|
||||
pub mod rustc_peek;
|
||||
pub mod storage;
|
||||
pub mod un_derefer;
|
||||
pub mod value_analysis;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::move_paths::FxHashMap;
|
||||
use crate::un_derefer::UnDerefer;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::tcx::RvalueInitializationState;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
|
||||
use super::abs_domain::Lift;
|
||||
@ -21,7 +20,6 @@ struct MoveDataBuilder<'a, 'tcx> {
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
data: MoveData<'tcx>,
|
||||
errors: Vec<(Place<'tcx>, MoveError<'tcx>)>,
|
||||
un_derefer: UnDerefer<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
@ -35,25 +33,29 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
tcx,
|
||||
param_env,
|
||||
errors: Vec::new(),
|
||||
un_derefer: UnDerefer { tcx: tcx, derefer_sidetable: Default::default() },
|
||||
data: MoveData {
|
||||
moves: IndexVec::new(),
|
||||
loc_map: LocationMap::new(body),
|
||||
rev_lookup: MovePathLookup {
|
||||
locals: body
|
||||
.local_decls
|
||||
.indices()
|
||||
.map(|i| {
|
||||
Self::new_move_path(
|
||||
&mut move_paths,
|
||||
&mut path_map,
|
||||
&mut init_path_map,
|
||||
None,
|
||||
Place::from(i),
|
||||
.iter_enumerated()
|
||||
.filter(|(_, l)| !l.is_deref_temp())
|
||||
.map(|(i, _)| {
|
||||
(
|
||||
i,
|
||||
Self::new_move_path(
|
||||
&mut move_paths,
|
||||
&mut path_map,
|
||||
&mut init_path_map,
|
||||
None,
|
||||
Place::from(i),
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
projections: Default::default(),
|
||||
derefer_sidetable: Default::default(),
|
||||
},
|
||||
move_paths,
|
||||
path_map,
|
||||
@ -98,13 +100,11 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
///
|
||||
/// Maybe we should have separate "borrowck" and "moveck" modes.
|
||||
fn move_path_for(&mut self, place: Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> {
|
||||
if let Some(new_place) = self.builder.un_derefer.derefer(place.as_ref(), self.builder.body)
|
||||
{
|
||||
return self.move_path_for(new_place);
|
||||
}
|
||||
let deref_chain = self.builder.data.rev_lookup.deref_chain(place.as_ref());
|
||||
|
||||
debug!("lookup({:?})", place);
|
||||
let mut base = self.builder.data.rev_lookup.locals[place.local];
|
||||
let mut base =
|
||||
self.builder.data.rev_lookup.find_local(deref_chain.first().unwrap_or(&place).local);
|
||||
|
||||
// The move path index of the first union that we find. Once this is
|
||||
// some we stop creating child move paths, since moves from unions
|
||||
@ -113,51 +113,55 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
// from `*(u.f: &_)` isn't allowed.
|
||||
let mut union_path = None;
|
||||
|
||||
for (place_ref, elem) in place.as_ref().iter_projections() {
|
||||
let body = self.builder.body;
|
||||
let tcx = self.builder.tcx;
|
||||
let place_ty = place_ref.ty(body, tcx).ty;
|
||||
|
||||
match place_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
BorrowedContent { target_place: place_ref.project_deeper(&[elem], tcx) },
|
||||
));
|
||||
}
|
||||
ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfTypeWithDestructor { container_ty: place_ty },
|
||||
));
|
||||
}
|
||||
ty::Adt(adt, _) if adt.is_union() => {
|
||||
union_path.get_or_insert(base);
|
||||
}
|
||||
ty::Slice(_) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfSliceOrArray {
|
||||
ty: place_ty,
|
||||
is_index: matches!(elem, ProjectionElem::Index(..)),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
ty::Array(..) => {
|
||||
if let ProjectionElem::Index(..) = elem {
|
||||
for place in deref_chain.into_iter().chain(iter::once(place)) {
|
||||
for (place_ref, elem) in place.as_ref().iter_projections() {
|
||||
let body = self.builder.body;
|
||||
let tcx = self.builder.tcx;
|
||||
let place_ty = place_ref.ty(body, tcx).ty;
|
||||
match place_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfSliceOrArray { ty: place_ty, is_index: true },
|
||||
BorrowedContent {
|
||||
target_place: place_ref.project_deeper(&[elem], tcx),
|
||||
},
|
||||
));
|
||||
}
|
||||
ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfTypeWithDestructor { container_ty: place_ty },
|
||||
));
|
||||
}
|
||||
ty::Adt(adt, _) if adt.is_union() => {
|
||||
union_path.get_or_insert(base);
|
||||
}
|
||||
ty::Slice(_) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfSliceOrArray {
|
||||
ty: place_ty,
|
||||
is_index: matches!(elem, ProjectionElem::Index(..)),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
ty::Array(..) => {
|
||||
if let ProjectionElem::Index(..) = elem {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfSliceOrArray { ty: place_ty, is_index: true },
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if union_path.is_none() {
|
||||
base = self
|
||||
.add_move_path(base, elem, |tcx| place_ref.project_deeper(&[elem], tcx));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if union_path.is_none() {
|
||||
base = self.add_move_path(base, elem, |tcx| place_ref.project_deeper(&[elem], tcx));
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,10 +202,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub type MoveDat<'tcx> = Result<
|
||||
(FxHashMap<Local, Place<'tcx>>, MoveData<'tcx>),
|
||||
(MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>),
|
||||
>;
|
||||
pub type MoveDat<'tcx> =
|
||||
Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)>;
|
||||
|
||||
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
fn finalize(self) -> MoveDat<'tcx> {
|
||||
@ -217,11 +219,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
"done dumping moves"
|
||||
});
|
||||
|
||||
if self.errors.is_empty() {
|
||||
Ok((self.un_derefer.derefer_sidetable, self.data))
|
||||
} else {
|
||||
Err((self.data, self.errors))
|
||||
}
|
||||
if self.errors.is_empty() { Ok(self.data) } else { Err((self.data, self.errors)) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,7 +248,7 @@ pub(super) fn gather_moves<'tcx>(
|
||||
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
fn gather_args(&mut self) {
|
||||
for arg in self.body.args_iter() {
|
||||
let path = self.data.rev_lookup.locals[arg];
|
||||
let path = self.data.rev_lookup.find_local(arg);
|
||||
|
||||
let init = self.data.inits.push(Init {
|
||||
path,
|
||||
@ -286,7 +284,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => {
|
||||
assert!(place.projection.is_empty());
|
||||
if self.builder.body.local_decls[place.local].is_deref_temp() {
|
||||
self.builder.un_derefer.derefer_sidetable.insert(place.local, *reffed);
|
||||
self.builder.data.rev_lookup.derefer_sidetable.insert(place.local, *reffed);
|
||||
}
|
||||
}
|
||||
StatementKind::Assign(box (place, rval)) => {
|
||||
@ -308,7 +306,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
StatementKind::StorageLive(_) => {}
|
||||
StatementKind::StorageDead(local) => {
|
||||
// DerefTemp locals (results of CopyForDeref) don't actually move anything.
|
||||
if !self.builder.un_derefer.derefer_sidetable.contains_key(&local) {
|
||||
if !self.builder.data.rev_lookup.derefer_sidetable.contains_key(&local) {
|
||||
self.gather_move(Place::from(*local));
|
||||
}
|
||||
}
|
||||
@ -450,12 +448,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
|
||||
fn gather_move(&mut self, place: Place<'tcx>) {
|
||||
debug!("gather_move({:?}, {:?})", self.loc, place);
|
||||
if let Some(new_place) = self.builder.un_derefer.derefer(place.as_ref(), self.builder.body)
|
||||
{
|
||||
self.gather_move(new_place);
|
||||
return;
|
||||
}
|
||||
|
||||
if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] =
|
||||
**place.projection
|
||||
{
|
||||
@ -512,11 +504,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) {
|
||||
debug!("gather_init({:?}, {:?})", self.loc, place);
|
||||
|
||||
if let Some(new_place) = self.builder.un_derefer.derefer(place, self.builder.body) {
|
||||
self.gather_init(new_place.as_ref(), kind);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut place = place;
|
||||
|
||||
// Check if we are assigning into a field of a union, if so, lookup the place
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::move_paths::builder::MoveDat;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||
@ -175,7 +175,7 @@ pub struct MoveData<'tcx> {
|
||||
/// particular path being moved.)
|
||||
pub loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>,
|
||||
pub path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
|
||||
pub rev_lookup: MovePathLookup,
|
||||
pub rev_lookup: MovePathLookup<'tcx>,
|
||||
pub inits: IndexVec<InitIndex, Init>,
|
||||
/// Each Location `l` is mapped to the Inits that are effects
|
||||
/// of executing the code at `l`.
|
||||
@ -289,8 +289,8 @@ impl Init {
|
||||
|
||||
/// Tables mapping from a place to its MovePathIndex.
|
||||
#[derive(Debug)]
|
||||
pub struct MovePathLookup {
|
||||
locals: IndexVec<Local, MovePathIndex>,
|
||||
pub struct MovePathLookup<'tcx> {
|
||||
locals: FxIndexMap<Local, MovePathIndex>,
|
||||
|
||||
/// projections are made from a base-place and a projection
|
||||
/// elem. The base-place will have a unique MovePathIndex; we use
|
||||
@ -299,6 +299,9 @@ pub struct MovePathLookup {
|
||||
/// base-place). For the remaining lookup, we map the projection
|
||||
/// elem to the associated MovePathIndex.
|
||||
projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>,
|
||||
|
||||
/// Maps `DerefTemp` locals to the `Place`s assigned to them.
|
||||
derefer_sidetable: FxHashMap<Local, Place<'tcx>>,
|
||||
}
|
||||
|
||||
mod builder;
|
||||
@ -309,27 +312,59 @@ pub enum LookupResult {
|
||||
Parent(Option<MovePathIndex>),
|
||||
}
|
||||
|
||||
impl MovePathLookup {
|
||||
impl<'tcx> MovePathLookup<'tcx> {
|
||||
// Unlike the builder `fn move_path_for` below, this lookup
|
||||
// alternative will *not* create a MovePath on the fly for an
|
||||
// unknown place, but will rather return the nearest available
|
||||
// parent.
|
||||
pub fn find(&self, place: PlaceRef<'_>) -> LookupResult {
|
||||
let mut result = self.locals[place.local];
|
||||
let deref_chain = self.deref_chain(place);
|
||||
|
||||
for elem in place.projection.iter() {
|
||||
if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
|
||||
result = subpath;
|
||||
} else {
|
||||
let local = match deref_chain.first() {
|
||||
Some(place) => place.local,
|
||||
None => place.local,
|
||||
};
|
||||
|
||||
let mut result = *self.locals.get(&local).unwrap_or_else(|| {
|
||||
bug!("base local ({local:?}) of deref_chain should not be a deref temp")
|
||||
});
|
||||
|
||||
// this needs to be a closure because `place` has a different lifetime than `prefix`'s places
|
||||
let mut subpaths_for_place = |place: PlaceRef<'_>| {
|
||||
for elem in place.projection.iter() {
|
||||
if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
|
||||
result = subpath;
|
||||
} else {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
for place in deref_chain {
|
||||
if let Some(result) = subpaths_for_place(place.as_ref()) {
|
||||
return LookupResult::Parent(Some(result));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(result) = subpaths_for_place(place) {
|
||||
return LookupResult::Parent(Some(result));
|
||||
}
|
||||
|
||||
LookupResult::Exact(result)
|
||||
}
|
||||
|
||||
pub fn find_local(&self, local: Local) -> MovePathIndex {
|
||||
self.locals[local]
|
||||
let deref_chain = self.deref_chain(Place::from(local).as_ref());
|
||||
|
||||
let local = match deref_chain.last() {
|
||||
Some(place) => place.local,
|
||||
None => local,
|
||||
};
|
||||
|
||||
*self.locals.get(&local).unwrap_or_else(|| {
|
||||
bug!("base local ({local:?}) of deref_chain should not be a deref temp")
|
||||
})
|
||||
}
|
||||
|
||||
/// An enumerated iterator of `local`s and their associated
|
||||
@ -337,7 +372,22 @@ impl MovePathLookup {
|
||||
pub fn iter_locals_enumerated(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + ExactSizeIterator + '_ {
|
||||
self.locals.iter_enumerated().map(|(l, &idx)| (l, idx))
|
||||
self.locals.iter().map(|(&l, &idx)| (l, idx))
|
||||
}
|
||||
|
||||
/// Returns the chain of places behind `DerefTemp` locals in `place`
|
||||
pub fn deref_chain(&self, place: PlaceRef<'_>) -> Vec<Place<'tcx>> {
|
||||
let mut prefix = Vec::new();
|
||||
let mut local = place.local;
|
||||
|
||||
while let Some(&reffed) = self.derefer_sidetable.get(&local) {
|
||||
prefix.insert(0, reffed);
|
||||
local = reffed.local;
|
||||
}
|
||||
|
||||
debug!("deref_chain({place:?}) = {prefix:?}");
|
||||
|
||||
prefix
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
|
||||
}
|
||||
|
||||
let param_env = tcx.param_env(def_id);
|
||||
let (_, move_data) = MoveData::gather_moves(body, tcx, param_env).unwrap();
|
||||
let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
|
||||
let mdpe = MoveDataParamEnv { move_data, param_env };
|
||||
|
||||
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user