mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Merge from rustc
This commit is contained in:
commit
e4540edf1a
59
Cargo.lock
59
Cargo.lock
@ -830,6 +830,41 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "datafrog"
|
||||
version = "2.0.1"
|
||||
@ -869,6 +904,18 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_setters"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@ -1740,6 +1787,12 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
@ -3416,7 +3469,7 @@ dependencies = [
|
||||
"libc",
|
||||
"measureme",
|
||||
"memmap2",
|
||||
"parking_lot 0.11.2",
|
||||
"parking_lot 0.12.1",
|
||||
"rustc-hash",
|
||||
"rustc-rayon",
|
||||
"rustc-rayon-core",
|
||||
@ -3524,6 +3577,7 @@ name = "rustc_errors"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"derive_setters",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_data_structures",
|
||||
@ -3566,6 +3620,7 @@ dependencies = [
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"smallvec",
|
||||
"termcolor",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
@ -4135,7 +4190,7 @@ dependencies = [
|
||||
name = "rustc_query_system"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"parking_lot 0.11.2",
|
||||
"parking_lot 0.12.1",
|
||||
"rustc-rayon-core",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
|
@ -1462,7 +1462,8 @@ pub enum ExprKind {
|
||||
/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
|
||||
Field(P<Expr>, Ident),
|
||||
/// An indexing operation (e.g., `foo[2]`).
|
||||
Index(P<Expr>, P<Expr>),
|
||||
/// The span represents the span of the `[2]`, including brackets.
|
||||
Index(P<Expr>, P<Expr>, Span),
|
||||
/// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assignment).
|
||||
Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
|
||||
/// An underscore, used in destructuring assignment to ignore a value.
|
||||
|
@ -1400,7 +1400,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
fn_decl,
|
||||
body,
|
||||
fn_decl_span,
|
||||
fn_arg_span: _,
|
||||
fn_arg_span,
|
||||
}) => {
|
||||
vis.visit_closure_binder(binder);
|
||||
visit_constness(constness, vis);
|
||||
@ -1408,6 +1408,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
vis.visit_fn_decl(fn_decl);
|
||||
vis.visit_expr(body);
|
||||
vis.visit_span(fn_decl_span);
|
||||
vis.visit_span(fn_arg_span);
|
||||
}
|
||||
ExprKind::Block(blk, label) => {
|
||||
vis.visit_block(blk);
|
||||
@ -1420,9 +1421,10 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
vis.visit_expr(expr);
|
||||
vis.visit_span(await_kw_span);
|
||||
}
|
||||
ExprKind::Assign(el, er, _) => {
|
||||
ExprKind::Assign(el, er, span) => {
|
||||
vis.visit_expr(el);
|
||||
vis.visit_expr(er);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
ExprKind::AssignOp(_op, el, er) => {
|
||||
vis.visit_expr(el);
|
||||
@ -1432,9 +1434,10 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
vis.visit_expr(el);
|
||||
vis.visit_ident(ident);
|
||||
}
|
||||
ExprKind::Index(el, er) => {
|
||||
ExprKind::Index(el, er, brackets_span) => {
|
||||
vis.visit_expr(el);
|
||||
vis.visit_expr(er);
|
||||
vis.visit_span(brackets_span);
|
||||
}
|
||||
ExprKind::Range(e1, e2, _lim) => {
|
||||
visit_opt(e1, |e1| vis.visit_expr(e1));
|
||||
|
@ -390,7 +390,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
|
||||
| ast::ExprKind::Cast(x, _)
|
||||
| ast::ExprKind::Type(x, _)
|
||||
| ast::ExprKind::Field(x, _)
|
||||
| ast::ExprKind::Index(x, _) => {
|
||||
| ast::ExprKind::Index(x, _, _) => {
|
||||
// &X { y: 1 }, X { y: 1 }.y
|
||||
contains_exterior_struct_lit(x)
|
||||
}
|
||||
|
@ -885,7 +885,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||
visitor.visit_expr(subexpression);
|
||||
visitor.visit_ident(*ident);
|
||||
}
|
||||
ExprKind::Index(main_expression, index_expression) => {
|
||||
ExprKind::Index(main_expression, index_expression, _) => {
|
||||
visitor.visit_expr(main_expression);
|
||||
visitor.visit_expr(index_expression)
|
||||
}
|
||||
|
@ -240,8 +240,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ExprKind::Field(el, ident) => {
|
||||
hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(*ident))
|
||||
}
|
||||
ExprKind::Index(el, er) => {
|
||||
hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er))
|
||||
ExprKind::Index(el, er, brackets_span) => {
|
||||
hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span)
|
||||
}
|
||||
ExprKind::Range(Some(e1), Some(e2), RangeLimits::Closed) => {
|
||||
self.lower_expr_range_closed(e.span, e1, e2)
|
||||
@ -657,14 +657,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
|
||||
/// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to
|
||||
/// `inner_hir_id` in case the `closure_track_caller` feature is enabled.
|
||||
/// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled.
|
||||
pub(super) fn maybe_forward_track_caller(
|
||||
&mut self,
|
||||
span: Span,
|
||||
outer_hir_id: hir::HirId,
|
||||
inner_hir_id: hir::HirId,
|
||||
) {
|
||||
if self.tcx.features().closure_track_caller
|
||||
if self.tcx.features().async_fn_track_caller
|
||||
&& let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
|
||||
&& attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
|
||||
{
|
||||
|
@ -56,6 +56,11 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
||||
owner: NodeId,
|
||||
f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
|
||||
) {
|
||||
let allow_gen_future = Some(if self.tcx.features().async_fn_track_caller {
|
||||
[sym::gen_future, sym::closure_track_caller][..].into()
|
||||
} else {
|
||||
[sym::gen_future][..].into()
|
||||
});
|
||||
let mut lctx = LoweringContext {
|
||||
// Pseudo-globals.
|
||||
tcx: self.tcx,
|
||||
@ -83,7 +88,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
||||
impl_trait_defs: Vec::new(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
|
||||
allow_gen_future: Some([sym::gen_future, sym::closure_track_caller][..].into()),
|
||||
allow_gen_future,
|
||||
generics_def_id_map: Default::default(),
|
||||
};
|
||||
lctx.with_hir_id_owner(owner, |lctx| f(lctx));
|
||||
|
@ -477,7 +477,7 @@ impl<'a> State<'a> {
|
||||
self.word(".");
|
||||
self.print_ident(*ident);
|
||||
}
|
||||
ast::ExprKind::Index(expr, index) => {
|
||||
ast::ExprKind::Index(expr, index, _) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
|
||||
self.word("[");
|
||||
self.print_expr(index);
|
||||
|
@ -751,9 +751,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
)
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
let msg = if let ty::Adt(def, _) = ty.kind()
|
||||
&& [
|
||||
tcx.get_diagnostic_item(sym::Arc),
|
||||
tcx.get_diagnostic_item(sym::Rc),
|
||||
].contains(&Some(def.did()))
|
||||
{
|
||||
"clone the value to increment its reference count"
|
||||
} else {
|
||||
"consider cloning the value if the performance cost is acceptable"
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"consider cloning the value if the performance cost is acceptable",
|
||||
msg,
|
||||
suggestion,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -79,7 +79,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
||||
| hir::ExprKind::Unary(hir::UnOp::Deref, inner)
|
||||
| hir::ExprKind::Field(inner, _)
|
||||
| hir::ExprKind::MethodCall(_, inner, _, _)
|
||||
| hir::ExprKind::Index(inner, _) = &expr.kind
|
||||
| hir::ExprKind::Index(inner, _, _) = &expr.kind
|
||||
{
|
||||
expr = inner;
|
||||
}
|
||||
|
@ -567,7 +567,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
|
||||
&& let hir::ExprKind::Index(val, index) = place.kind
|
||||
&& let hir::ExprKind::Index(val, index, _) = place.kind
|
||||
&& (expr.span == self.assign_span || place.span == self.assign_span)
|
||||
{
|
||||
// val[index] = rv;
|
||||
@ -620,7 +620,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
);
|
||||
self.suggested = true;
|
||||
} else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
|
||||
&& let hir::ExprKind::Index(val, index) = receiver.kind
|
||||
&& let hir::ExprKind::Index(val, index, _) = receiver.kind
|
||||
&& expr.span == self.assign_span
|
||||
{
|
||||
// val[index].path(args..);
|
||||
|
@ -784,13 +784,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// is considered a *lower bound*. If possible, we will modify
|
||||
/// the constraint to set it equal to one of the option regions.
|
||||
/// If we make any changes, returns true, else false.
|
||||
///
|
||||
/// This function only adds the member constraints to the region graph,
|
||||
/// it does not check them. They are later checked in
|
||||
/// `check_member_constraints` after the region graph has been computed.
|
||||
#[instrument(skip(self, member_constraint_index), level = "debug")]
|
||||
fn apply_member_constraint(
|
||||
&mut self,
|
||||
scc: ConstraintSccIndex,
|
||||
member_constraint_index: NllMemberConstraintIndex,
|
||||
choice_regions: &[ty::RegionVid],
|
||||
) -> bool {
|
||||
) {
|
||||
// Lazily compute the reverse graph, we'll need it later.
|
||||
self.compute_reverse_scc_graph();
|
||||
|
||||
// Create a mutable vector of the options. We'll try to winnow
|
||||
// them down.
|
||||
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
|
||||
@ -805,10 +812,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
*c_r = self.scc_representatives[scc];
|
||||
}
|
||||
|
||||
// The 'member region' in a member constraint is part of the
|
||||
// hidden type, which must be in the root universe. Therefore,
|
||||
// it cannot have any placeholders in its value.
|
||||
assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT);
|
||||
// If the member region lives in a higher universe, we currently choose
|
||||
// the most conservative option by leaving it unchanged.
|
||||
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
|
||||
return;
|
||||
}
|
||||
debug_assert!(
|
||||
self.scc_values.placeholders_contained_in(scc).next().is_none(),
|
||||
"scc {:?} in a member constraint has placeholder value: {:?}",
|
||||
@ -832,7 +840,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// free region that must outlive the member region `R0` (`UB:
|
||||
// R0`). Therefore, we need only keep an option `O` if `UB: O`
|
||||
// for all UB.
|
||||
self.compute_reverse_scc_graph();
|
||||
let universal_region_relations = &self.universal_region_relations;
|
||||
for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) {
|
||||
debug!(?ub);
|
||||
@ -867,7 +874,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
}) else {
|
||||
debug!("no unique minimum choice");
|
||||
return false;
|
||||
return;
|
||||
};
|
||||
|
||||
let min_choice_scc = self.constraint_sccs.scc(min_choice);
|
||||
@ -878,10 +885,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
min_choice,
|
||||
member_constraint_index,
|
||||
});
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,6 +185,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
{
|
||||
tcx.fold_regions(ty, |region, _| match *region {
|
||||
ty::ReVar(vid) => {
|
||||
let scc = self.constraint_sccs.scc(vid);
|
||||
|
||||
// Special handling of higher-ranked regions.
|
||||
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
|
||||
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
|
||||
// If the region contains a single placeholder then they're equal.
|
||||
Some((0, placeholder)) => {
|
||||
return ty::Region::new_placeholder(tcx, placeholder);
|
||||
}
|
||||
|
||||
// Fallback: this will produce a cryptic error message.
|
||||
_ => return region,
|
||||
}
|
||||
}
|
||||
|
||||
// Find something that we can name
|
||||
let upper_bound = self.approx_universal_upper_bound(vid);
|
||||
let upper_bound = &self.definitions[upper_bound];
|
||||
|
@ -498,14 +498,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
|
||||
/// Checks that the types internal to the `place` match up with
|
||||
/// what would be expected.
|
||||
#[instrument(level = "debug", skip(self, location), ret)]
|
||||
fn sanitize_place(
|
||||
&mut self,
|
||||
place: &Place<'tcx>,
|
||||
location: Location,
|
||||
context: PlaceContext,
|
||||
) -> PlaceTy<'tcx> {
|
||||
debug!("sanitize_place: {:?}", place);
|
||||
|
||||
let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty);
|
||||
|
||||
for elem in place.projection.iter() {
|
||||
@ -608,7 +607,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
#[instrument(skip(self, location), ret, level = "debug")]
|
||||
fn sanitize_projection(
|
||||
&mut self,
|
||||
base: PlaceTy<'tcx>,
|
||||
@ -617,7 +616,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
location: Location,
|
||||
context: PlaceContext,
|
||||
) -> PlaceTy<'tcx> {
|
||||
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
|
||||
let tcx = self.tcx();
|
||||
let base_ty = base.ty;
|
||||
match pi {
|
||||
|
@ -227,3 +227,5 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal
|
||||
.label = not a trait
|
||||
.str_lit = try using `#[derive({$sym})]`
|
||||
.other = for example, write `#[derive(Debug)]` for `Debug`
|
||||
|
||||
builtin_macros_unnameable_test_items = cannot test inner items
|
||||
|
@ -237,7 +237,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||
ExprKind::If(local_expr, _, _) => {
|
||||
self.manage_cond_expr(local_expr);
|
||||
}
|
||||
ExprKind::Index(prefix, suffix) => {
|
||||
ExprKind::Index(prefix, suffix, _) => {
|
||||
self.manage_cond_expr(prefix);
|
||||
self.manage_cond_expr(suffix);
|
||||
}
|
||||
|
@ -4,10 +4,12 @@ use rustc_ast as ast;
|
||||
use rustc_ast::entry::EntryPointType;
|
||||
use rustc_ast::mut_visit::{ExpectOne, *};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{walk_item, Visitor};
|
||||
use rustc_ast::{attr, ModKind};
|
||||
use rustc_expand::base::{ExtCtxt, ResolverExpand};
|
||||
use rustc_expand::expand::{AstFragment, ExpansionConfig};
|
||||
use rustc_feature::Features;
|
||||
use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
@ -137,11 +139,31 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
|
||||
let prev_tests = mem::take(&mut self.tests);
|
||||
noop_visit_item_kind(&mut item.kind, self);
|
||||
self.add_test_cases(item.id, span, prev_tests);
|
||||
} else {
|
||||
// But in those cases, we emit a lint to warn the user of these missing tests.
|
||||
walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item);
|
||||
}
|
||||
smallvec![P(item)]
|
||||
}
|
||||
}
|
||||
|
||||
struct InnerItemLinter<'a> {
|
||||
sess: &'a Session,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for InnerItemLinter<'_> {
|
||||
fn visit_item(&mut self, i: &'a ast::Item) {
|
||||
if let Some(attr) = attr::find_by_name(&i.attrs, sym::rustc_test_marker) {
|
||||
self.sess.parse_sess.buffer_lint(
|
||||
UNNAMEABLE_TEST_ITEMS,
|
||||
attr.span,
|
||||
i.id,
|
||||
crate::fluent_generated::builtin_macros_unnameable_test_items,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Beware, this is duplicated in librustc_passes/entry.rs (with
|
||||
// `rustc_hir::Item`), so make sure to keep them in sync.
|
||||
fn entry_point_type(item: &ast::Item, depth: usize) -> EntryPointType {
|
||||
|
@ -477,7 +477,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> {
|
||||
|
||||
#[inline]
|
||||
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
|
||||
if let layout::LayoutError::SizeOverflow(_) = err {
|
||||
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
|
||||
self.0.sess.span_fatal(span, err.to_string())
|
||||
} else {
|
||||
span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
|
||||
|
@ -476,7 +476,7 @@ impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
|
||||
#[inline]
|
||||
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
|
||||
if let LayoutError::SizeOverflow(_) = err {
|
||||
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
|
||||
self.sess().emit_fatal(respan(span, err.into_diagnostic()))
|
||||
} else {
|
||||
span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
|
||||
|
@ -985,7 +985,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
|
||||
#[inline]
|
||||
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
|
||||
if let LayoutError::SizeOverflow(_) = err {
|
||||
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
|
||||
self.sess().emit_fatal(Spanned { span, node: err.into_diagnostic() })
|
||||
} else {
|
||||
span_bug!(span, "failed to get layout for `{ty}`: {err:?}")
|
||||
|
@ -61,9 +61,6 @@ fn uncached_llvm_type<'a, 'tcx>(
|
||||
}
|
||||
Some(name)
|
||||
}
|
||||
// Use identified structure types for ADT. Due to pointee types in LLVM IR their definition
|
||||
// might be recursive. Other cases are non-recursive and we can use literal structure types.
|
||||
ty::Adt(..) => Some(String::new()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use pathdiff::diff_paths;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@ -12,7 +13,7 @@ pub struct RPathConfig<'a> {
|
||||
pub linker_is_gnu: bool,
|
||||
}
|
||||
|
||||
pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<String> {
|
||||
pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<OsString> {
|
||||
// No rpath on windows
|
||||
if !config.has_rpath {
|
||||
return Vec::new();
|
||||
@ -21,36 +22,38 @@ pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<String> {
|
||||
debug!("preparing the RPATH!");
|
||||
|
||||
let rpaths = get_rpaths(config);
|
||||
let mut flags = rpaths_to_flags(&rpaths);
|
||||
let mut flags = rpaths_to_flags(rpaths);
|
||||
|
||||
if config.linker_is_gnu {
|
||||
// Use DT_RUNPATH instead of DT_RPATH if available
|
||||
flags.push("-Wl,--enable-new-dtags".to_owned());
|
||||
flags.push("-Wl,--enable-new-dtags".into());
|
||||
|
||||
// Set DF_ORIGIN for substitute $ORIGIN
|
||||
flags.push("-Wl,-z,origin".to_owned());
|
||||
flags.push("-Wl,-z,origin".into());
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
|
||||
fn rpaths_to_flags(rpaths: Vec<OsString>) -> Vec<OsString> {
|
||||
let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity
|
||||
|
||||
for rpath in rpaths {
|
||||
if rpath.contains(',') {
|
||||
if rpath.to_string_lossy().contains(',') {
|
||||
ret.push("-Wl,-rpath".into());
|
||||
ret.push("-Xlinker".into());
|
||||
ret.push(rpath.clone());
|
||||
ret.push(rpath);
|
||||
} else {
|
||||
ret.push(format!("-Wl,-rpath,{}", &(*rpath)));
|
||||
let mut single_arg = OsString::from("-Wl,-rpath,");
|
||||
single_arg.push(rpath);
|
||||
ret.push(single_arg);
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec<String> {
|
||||
fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec<OsString> {
|
||||
debug!("output: {:?}", config.out_filename.display());
|
||||
debug!("libs:");
|
||||
for libpath in config.libs {
|
||||
@ -64,18 +67,18 @@ fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec<String> {
|
||||
|
||||
debug!("rpaths:");
|
||||
for rpath in &rpaths {
|
||||
debug!(" {}", rpath);
|
||||
debug!(" {:?}", rpath);
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
minimize_rpaths(&rpaths)
|
||||
}
|
||||
|
||||
fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec<String> {
|
||||
fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec<OsString> {
|
||||
config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
|
||||
}
|
||||
|
||||
fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> String {
|
||||
fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> OsString {
|
||||
// Mac doesn't appear to support $ORIGIN
|
||||
let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" };
|
||||
|
||||
@ -87,8 +90,11 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> Str
|
||||
let output = fs::canonicalize(&output).unwrap_or(output);
|
||||
let relative = path_relative_from(&lib, &output)
|
||||
.unwrap_or_else(|| panic!("couldn't create relative path from {output:?} to {lib:?}"));
|
||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||
format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path"))
|
||||
|
||||
let mut rpath = OsString::from(prefix);
|
||||
rpath.push("/");
|
||||
rpath.push(relative);
|
||||
rpath
|
||||
}
|
||||
|
||||
// This routine is adapted from the *old* Path's `path_relative_from`
|
||||
@ -99,7 +105,7 @@ fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
|
||||
diff_paths(path, base)
|
||||
}
|
||||
|
||||
fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
|
||||
fn minimize_rpaths(rpaths: &[OsString]) -> Vec<OsString> {
|
||||
let mut set = FxHashSet::default();
|
||||
let mut minimized = Vec::new();
|
||||
for rpath in rpaths {
|
||||
|
@ -1,32 +1,33 @@
|
||||
use super::RPathConfig;
|
||||
use super::{get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags};
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[test]
|
||||
fn test_rpaths_to_flags() {
|
||||
let flags = rpaths_to_flags(&["path1".to_string(), "path2".to_string()]);
|
||||
let flags = rpaths_to_flags(vec!["path1".into(), "path2".into()]);
|
||||
assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minimize1() {
|
||||
let res = minimize_rpaths(&["rpath1".to_string(), "rpath2".to_string(), "rpath1".to_string()]);
|
||||
let res = minimize_rpaths(&["rpath1".into(), "rpath2".into(), "rpath1".into()]);
|
||||
assert!(res == ["rpath1", "rpath2",]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minimize2() {
|
||||
let res = minimize_rpaths(&[
|
||||
"1a".to_string(),
|
||||
"2".to_string(),
|
||||
"2".to_string(),
|
||||
"1a".to_string(),
|
||||
"4a".to_string(),
|
||||
"1a".to_string(),
|
||||
"2".to_string(),
|
||||
"3".to_string(),
|
||||
"4a".to_string(),
|
||||
"3".to_string(),
|
||||
"1a".into(),
|
||||
"2".into(),
|
||||
"2".into(),
|
||||
"1a".into(),
|
||||
"4a".into(),
|
||||
"1a".into(),
|
||||
"2".into(),
|
||||
"3".into(),
|
||||
"4a".into(),
|
||||
"3".into(),
|
||||
]);
|
||||
assert!(res == ["1a", "2", "4a", "3",]);
|
||||
}
|
||||
@ -58,15 +59,15 @@ fn test_rpath_relative() {
|
||||
|
||||
#[test]
|
||||
fn test_xlinker() {
|
||||
let args = rpaths_to_flags(&["a/normal/path".to_string(), "a,comma,path".to_string()]);
|
||||
let args = rpaths_to_flags(vec!["a/normal/path".into(), "a,comma,path".into()]);
|
||||
|
||||
assert_eq!(
|
||||
args,
|
||||
vec![
|
||||
"-Wl,-rpath,a/normal/path".to_string(),
|
||||
"-Wl,-rpath".to_string(),
|
||||
"-Xlinker".to_string(),
|
||||
"a,comma,path".to_string()
|
||||
OsString::from("-Wl,-rpath,a/normal/path"),
|
||||
OsString::from("-Wl,-rpath"),
|
||||
OsString::from("-Xlinker"),
|
||||
OsString::from("a,comma,path")
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -215,14 +215,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
}
|
||||
sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
|
||||
sym::track_caller => {
|
||||
if !tcx.is_closure(did.to_def_id())
|
||||
let is_closure = tcx.is_closure(did.to_def_id());
|
||||
|
||||
if !is_closure
|
||||
&& let Some(fn_sig) = fn_sig()
|
||||
&& fn_sig.skip_binder().abi() != abi::Abi::Rust
|
||||
{
|
||||
struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
|
||||
.emit();
|
||||
}
|
||||
if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller {
|
||||
if is_closure
|
||||
&& !tcx.features().closure_track_caller
|
||||
&& !attr.span.allows_unstable(sym::closure_track_caller)
|
||||
{
|
||||
feature_err(
|
||||
&tcx.sess.parse_sess,
|
||||
sym::closure_track_caller,
|
||||
|
@ -157,7 +157,6 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
||||
}
|
||||
|
||||
/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
|
||||
/// This is the inverse of `ref_to_mplace`.
|
||||
#[inline(always)]
|
||||
pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
|
||||
match self.meta {
|
||||
@ -415,7 +414,7 @@ where
|
||||
}
|
||||
|
||||
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
||||
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
|
||||
/// Alignment is just based on the type. This is the inverse of `mplace_to_ref()`.
|
||||
///
|
||||
/// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
|
||||
/// want to ever use the place for memory access!
|
||||
@ -438,6 +437,18 @@ where
|
||||
Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta))
|
||||
}
|
||||
|
||||
/// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space.
|
||||
/// `align` information is lost!
|
||||
/// This is the inverse of `ref_to_mplace`.
|
||||
pub fn mplace_to_ref(
|
||||
&self,
|
||||
mplace: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
let imm = mplace.to_ref(self);
|
||||
let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?;
|
||||
Ok(ImmTy::from_immediate(imm, layout))
|
||||
}
|
||||
|
||||
/// Take an operand, representing a pointer, and dereference it to a place.
|
||||
/// Corresponds to the `*` operator in Rust.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
|
@ -852,10 +852,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
|
||||
|
||||
let arg = ImmTy::from_immediate(
|
||||
place.to_ref(self),
|
||||
self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, place.layout.ty))?,
|
||||
);
|
||||
let arg = self.mplace_to_ref(&place)?;
|
||||
let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?);
|
||||
|
||||
self.eval_fn_call(
|
||||
|
@ -35,7 +35,7 @@ elsa = "=1.7.1"
|
||||
itertools = "0.10.1"
|
||||
|
||||
[dependencies.parking_lot]
|
||||
version = "0.11"
|
||||
version = "0.12"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.48.0"
|
||||
|
@ -27,9 +27,7 @@ use rustc_data_structures::profiling::{
|
||||
use rustc_data_structures::sync::SeqCst;
|
||||
use rustc_errors::registry::{InvalidErrorCode, Registry};
|
||||
use rustc_errors::{markdown, ColorConfig};
|
||||
use rustc_errors::{
|
||||
DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl,
|
||||
};
|
||||
use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage};
|
||||
use rustc_feature::find_gated_cfg;
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
|
||||
@ -1405,15 +1403,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
|
||||
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
|
||||
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
|
||||
rustc_errors::ColorConfig::Auto,
|
||||
None,
|
||||
None,
|
||||
fallback_bundle,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
));
|
||||
let handler = rustc_errors::Handler::with_emitter(emitter);
|
||||
|
||||
|
@ -25,6 +25,7 @@ annotate-snippets = "0.9"
|
||||
termize = "0.1.1"
|
||||
serde = { version = "1.0.125", features = [ "derive" ] }
|
||||
serde_json = "1.0.59"
|
||||
derive_setters = "0.1.6"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.48.0"
|
||||
|
@ -7,8 +7,6 @@
|
||||
//!
|
||||
//! The output types are defined in `rustc_session::config::ErrorOutputType`.
|
||||
|
||||
use Destination::*;
|
||||
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{FileLines, SourceFile, Span};
|
||||
|
||||
@ -24,6 +22,7 @@ use crate::{
|
||||
};
|
||||
use rustc_lint_defs::pluralize;
|
||||
|
||||
use derive_setters::Setters;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_error_messages::{FluentArgs, SpanLabel};
|
||||
@ -35,8 +34,8 @@ use std::io::prelude::*;
|
||||
use std::io::{self, IsTerminal};
|
||||
use std::iter;
|
||||
use std::path::Path;
|
||||
use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream};
|
||||
use termcolor::{Buffer, Color, WriteColor};
|
||||
use termcolor::{Ansi, Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream};
|
||||
use termcolor::{Color, WriteColor};
|
||||
|
||||
/// Default column width, used in tests and when terminal dimensions cannot be determined.
|
||||
const DEFAULT_COLUMN_WIDTH: usize = 140;
|
||||
@ -60,31 +59,15 @@ impl HumanReadableErrorType {
|
||||
}
|
||||
pub fn new_emitter(
|
||||
self,
|
||||
dst: Box<dyn Write + Send>,
|
||||
source_map: Option<Lrc<SourceMap>>,
|
||||
bundle: Option<Lrc<FluentBundle>>,
|
||||
mut dst: Box<dyn WriteColor + Send>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
teach: bool,
|
||||
diagnostic_width: Option<usize>,
|
||||
macro_backtrace: bool,
|
||||
track_diagnostics: bool,
|
||||
terminal_url: TerminalUrl,
|
||||
) -> EmitterWriter {
|
||||
let (short, color_config) = self.unzip();
|
||||
let color = color_config.suggests_using_colors();
|
||||
EmitterWriter::new(
|
||||
dst,
|
||||
source_map,
|
||||
bundle,
|
||||
fallback_bundle,
|
||||
short,
|
||||
teach,
|
||||
color,
|
||||
diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
)
|
||||
if !dst.supports_color() && color {
|
||||
dst = Box::new(Ansi::new(dst));
|
||||
}
|
||||
EmitterWriter::new(dst, fallback_bundle).short_message(short)
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,10 +622,13 @@ impl ColorConfig {
|
||||
}
|
||||
|
||||
/// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short`
|
||||
#[derive(Setters)]
|
||||
pub struct EmitterWriter {
|
||||
#[setters(skip)]
|
||||
dst: Destination,
|
||||
sm: Option<Lrc<SourceMap>>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
#[setters(skip)]
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
short_message: bool,
|
||||
teach: bool,
|
||||
@ -662,65 +648,32 @@ pub struct FileWithAnnotatedLines {
|
||||
}
|
||||
|
||||
impl EmitterWriter {
|
||||
pub fn stderr(
|
||||
color_config: ColorConfig,
|
||||
source_map: Option<Lrc<SourceMap>>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
short_message: bool,
|
||||
teach: bool,
|
||||
diagnostic_width: Option<usize>,
|
||||
macro_backtrace: bool,
|
||||
track_diagnostics: bool,
|
||||
terminal_url: TerminalUrl,
|
||||
) -> EmitterWriter {
|
||||
let dst = Destination::from_stderr(color_config);
|
||||
pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter {
|
||||
let dst = from_stderr(color_config);
|
||||
Self::create(dst, fallback_bundle)
|
||||
}
|
||||
|
||||
fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> EmitterWriter {
|
||||
EmitterWriter {
|
||||
dst,
|
||||
sm: source_map,
|
||||
fluent_bundle,
|
||||
sm: None,
|
||||
fluent_bundle: None,
|
||||
fallback_bundle,
|
||||
short_message,
|
||||
teach,
|
||||
short_message: false,
|
||||
teach: false,
|
||||
ui_testing: false,
|
||||
diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
diagnostic_width: None,
|
||||
macro_backtrace: false,
|
||||
track_diagnostics: false,
|
||||
terminal_url: TerminalUrl::No,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
dst: Box<dyn Write + Send>,
|
||||
source_map: Option<Lrc<SourceMap>>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
dst: Box<dyn WriteColor + Send>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
short_message: bool,
|
||||
teach: bool,
|
||||
colored: bool,
|
||||
diagnostic_width: Option<usize>,
|
||||
macro_backtrace: bool,
|
||||
track_diagnostics: bool,
|
||||
terminal_url: TerminalUrl,
|
||||
) -> EmitterWriter {
|
||||
EmitterWriter {
|
||||
dst: Raw(dst, colored),
|
||||
sm: source_map,
|
||||
fluent_bundle,
|
||||
fallback_bundle,
|
||||
short_message,
|
||||
teach,
|
||||
ui_testing: false,
|
||||
diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui_testing(mut self, ui_testing: bool) -> Self {
|
||||
self.ui_testing = ui_testing;
|
||||
self
|
||||
Self::create(dst, fallback_bundle)
|
||||
}
|
||||
|
||||
fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> {
|
||||
@ -2203,11 +2156,10 @@ impl EmitterWriter {
|
||||
Err(e) => panic!("failed to emit error: {e}"),
|
||||
}
|
||||
|
||||
let mut dst = self.dst.writable();
|
||||
match writeln!(dst) {
|
||||
match writeln!(self.dst) {
|
||||
Err(e) => panic!("failed to emit error: {e}"),
|
||||
_ => {
|
||||
if let Err(e) = dst.flush() {
|
||||
if let Err(e) = self.dst.flush() {
|
||||
panic!("failed to emit error: {e}")
|
||||
}
|
||||
}
|
||||
@ -2618,8 +2570,6 @@ fn emit_to_destination(
|
||||
) -> io::Result<()> {
|
||||
use crate::lock;
|
||||
|
||||
let mut dst = dst.writable();
|
||||
|
||||
// In order to prevent error message interleaving, where multiple error lines get intermixed
|
||||
// when multiple compiler processes error simultaneously, we emit errors with additional
|
||||
// steps.
|
||||
@ -2635,7 +2585,8 @@ fn emit_to_destination(
|
||||
let _buffer_lock = lock::acquire_global_lock("rustc_errors");
|
||||
for (pos, line) in rendered_buffer.iter().enumerate() {
|
||||
for part in line {
|
||||
dst.apply_style(*lvl, part.style)?;
|
||||
let style = part.style.color_spec(*lvl);
|
||||
dst.set_color(&style)?;
|
||||
write!(dst, "{}", part.text)?;
|
||||
dst.reset()?;
|
||||
}
|
||||
@ -2647,61 +2598,69 @@ fn emit_to_destination(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub enum Destination {
|
||||
Terminal(StandardStream),
|
||||
Buffered(BufferWriter),
|
||||
// The bool denotes whether we should be emitting ansi color codes or not
|
||||
Raw(Box<(dyn Write + Send)>, bool),
|
||||
pub type Destination = Box<(dyn WriteColor + Send)>;
|
||||
|
||||
struct Buffy {
|
||||
buffer_writer: BufferWriter,
|
||||
buffer: Buffer,
|
||||
}
|
||||
|
||||
pub enum WritableDst<'a> {
|
||||
Terminal(&'a mut StandardStream),
|
||||
Buffered(&'a mut BufferWriter, Buffer),
|
||||
Raw(&'a mut (dyn Write + Send)),
|
||||
ColoredRaw(Ansi<&'a mut (dyn Write + Send)>),
|
||||
impl Write for Buffy {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.buffer.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.buffer_writer.print(&self.buffer)?;
|
||||
self.buffer.clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Destination {
|
||||
fn from_stderr(color: ColorConfig) -> Destination {
|
||||
let choice = color.to_color_choice();
|
||||
// On Windows we'll be performing global synchronization on the entire
|
||||
// system for emitting rustc errors, so there's no need to buffer
|
||||
// anything.
|
||||
//
|
||||
// On non-Windows we rely on the atomicity of `write` to ensure errors
|
||||
// don't get all jumbled up.
|
||||
if cfg!(windows) {
|
||||
Terminal(StandardStream::stderr(choice))
|
||||
} else {
|
||||
Buffered(BufferWriter::stderr(choice))
|
||||
}
|
||||
}
|
||||
|
||||
fn writable(&mut self) -> WritableDst<'_> {
|
||||
match *self {
|
||||
Destination::Terminal(ref mut t) => WritableDst::Terminal(t),
|
||||
Destination::Buffered(ref mut t) => {
|
||||
let buf = t.buffer();
|
||||
WritableDst::Buffered(t, buf)
|
||||
}
|
||||
Destination::Raw(ref mut t, false) => WritableDst::Raw(t),
|
||||
Destination::Raw(ref mut t, true) => WritableDst::ColoredRaw(Ansi::new(t)),
|
||||
impl Drop for Buffy {
|
||||
fn drop(&mut self) {
|
||||
if !self.buffer.is_empty() {
|
||||
self.flush().unwrap();
|
||||
panic!("buffers need to be flushed in order to print their contents");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteColor for Buffy {
|
||||
fn supports_color(&self) -> bool {
|
||||
match *self {
|
||||
Self::Terminal(ref stream) => stream.supports_color(),
|
||||
Self::Buffered(ref buffer) => buffer.buffer().supports_color(),
|
||||
Self::Raw(_, supports_color) => supports_color,
|
||||
}
|
||||
self.buffer.supports_color()
|
||||
}
|
||||
|
||||
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
|
||||
self.buffer.set_color(spec)
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> io::Result<()> {
|
||||
self.buffer.reset()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WritableDst<'a> {
|
||||
fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> {
|
||||
fn from_stderr(color: ColorConfig) -> Destination {
|
||||
let choice = color.to_color_choice();
|
||||
// On Windows we'll be performing global synchronization on the entire
|
||||
// system for emitting rustc errors, so there's no need to buffer
|
||||
// anything.
|
||||
//
|
||||
// On non-Windows we rely on the atomicity of `write` to ensure errors
|
||||
// don't get all jumbled up.
|
||||
if cfg!(windows) {
|
||||
Box::new(StandardStream::stderr(choice))
|
||||
} else {
|
||||
let buffer_writer = BufferWriter::stderr(choice);
|
||||
let buffer = buffer_writer.buffer();
|
||||
Box::new(Buffy { buffer_writer, buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl Style {
|
||||
fn color_spec(&self, lvl: Level) -> ColorSpec {
|
||||
let mut spec = ColorSpec::new();
|
||||
match style {
|
||||
match self {
|
||||
Style::Addition => {
|
||||
spec.set_fg(Some(Color::Green)).set_intense(true);
|
||||
}
|
||||
@ -2746,53 +2705,7 @@ impl<'a> WritableDst<'a> {
|
||||
spec.set_bold(true);
|
||||
}
|
||||
}
|
||||
self.set_color(&spec)
|
||||
}
|
||||
|
||||
fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> {
|
||||
match *self {
|
||||
WritableDst::Terminal(ref mut t) => t.set_color(color),
|
||||
WritableDst::Buffered(_, ref mut t) => t.set_color(color),
|
||||
WritableDst::ColoredRaw(ref mut t) => t.set_color(color),
|
||||
WritableDst::Raw(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> io::Result<()> {
|
||||
match *self {
|
||||
WritableDst::Terminal(ref mut t) => t.reset(),
|
||||
WritableDst::Buffered(_, ref mut t) => t.reset(),
|
||||
WritableDst::ColoredRaw(ref mut t) => t.reset(),
|
||||
WritableDst::Raw(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for WritableDst<'a> {
|
||||
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
WritableDst::Terminal(ref mut t) => t.write(bytes),
|
||||
WritableDst::Buffered(_, ref mut buf) => buf.write(bytes),
|
||||
WritableDst::Raw(ref mut w) => w.write(bytes),
|
||||
WritableDst::ColoredRaw(ref mut t) => t.write(bytes),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match *self {
|
||||
WritableDst::Terminal(ref mut t) => t.flush(),
|
||||
WritableDst::Buffered(_, ref mut buf) => buf.flush(),
|
||||
WritableDst::Raw(ref mut w) => w.flush(),
|
||||
WritableDst::ColoredRaw(ref mut w) => w.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for WritableDst<'a> {
|
||||
fn drop(&mut self) {
|
||||
if let WritableDst::Buffered(ref mut dst, ref mut buf) = self {
|
||||
drop(dst.print(buf));
|
||||
}
|
||||
spec
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
// FIXME: spec the JSON output properly.
|
||||
|
||||
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use termcolor::{ColorSpec, WriteColor};
|
||||
|
||||
use crate::emitter::{Emitter, HumanReadableErrorType};
|
||||
use crate::registry::Registry;
|
||||
@ -356,20 +357,29 @@ impl Diagnostic {
|
||||
self.0.lock().unwrap().flush()
|
||||
}
|
||||
}
|
||||
impl WriteColor for BufWriter {
|
||||
fn supports_color(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
let buf = BufWriter::default();
|
||||
let output = buf.clone();
|
||||
je.json_rendered
|
||||
.new_emitter(
|
||||
Box::new(buf),
|
||||
Some(je.sm.clone()),
|
||||
je.fluent_bundle.clone(),
|
||||
je.fallback_bundle.clone(),
|
||||
false,
|
||||
je.diagnostic_width,
|
||||
je.macro_backtrace,
|
||||
je.track_diagnostics,
|
||||
je.terminal_url,
|
||||
)
|
||||
.new_emitter(Box::new(buf), je.fallback_bundle.clone())
|
||||
.sm(Some(je.sm.clone()))
|
||||
.fluent_bundle(je.fluent_bundle.clone())
|
||||
.diagnostic_width(je.diagnostic_width)
|
||||
.macro_backtrace(je.macro_backtrace)
|
||||
.track_diagnostics(je.track_diagnostics)
|
||||
.terminal_url(je.terminal_url)
|
||||
.ui_testing(je.ui_testing)
|
||||
.emit_diagnostic(diag);
|
||||
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
|
||||
|
@ -557,18 +557,7 @@ impl Handler {
|
||||
sm: Option<Lrc<SourceMap>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
) -> Self {
|
||||
let emitter = Box::new(EmitterWriter::stderr(
|
||||
ColorConfig::Auto,
|
||||
sm,
|
||||
None,
|
||||
fallback_bundle,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
));
|
||||
let emitter = Box::new(EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm));
|
||||
Self::with_emitter(emitter)
|
||||
}
|
||||
pub fn disable_warnings(mut self) -> Self {
|
||||
|
@ -27,3 +27,4 @@ rustc_span = { path = "../rustc_span" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
thin-vec = "0.2.12"
|
||||
tracing = "0.1"
|
||||
termcolor = "1.2"
|
||||
|
@ -8,7 +8,8 @@ use rustc_span::{BytePos, Span};
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::emitter::EmitterWriter;
|
||||
use rustc_errors::{Handler, MultiSpan, PResult, TerminalUrl};
|
||||
use rustc_errors::{Handler, MultiSpan, PResult};
|
||||
use termcolor::WriteColor;
|
||||
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
@ -29,19 +30,9 @@ fn create_test_handler() -> (Handler, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
|
||||
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
|
||||
false,
|
||||
);
|
||||
let emitter = EmitterWriter::new(
|
||||
Box::new(Shared { data: output.clone() }),
|
||||
Some(source_map.clone()),
|
||||
None,
|
||||
fallback_bundle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Some(140),
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
);
|
||||
let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
|
||||
.sm(Some(source_map.clone()))
|
||||
.diagnostic_width(Some(140));
|
||||
let handler = Handler::with_emitter(Box::new(emitter));
|
||||
(handler, source_map, output)
|
||||
}
|
||||
@ -165,6 +156,20 @@ pub(crate) struct Shared<T: Write> {
|
||||
pub data: Arc<Mutex<T>>,
|
||||
}
|
||||
|
||||
impl<T: Write> WriteColor for Shared<T> {
|
||||
fn supports_color(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_color(&mut self, _spec: &termcolor::ColorSpec) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write> Write for Shared<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.data.lock().unwrap().write(buf)
|
||||
|
@ -339,6 +339,8 @@ declare_features! (
|
||||
(active, async_closure, "1.37.0", Some(62290), None),
|
||||
/// Allows async functions to be declared, implemented, and used in traits.
|
||||
(active, async_fn_in_trait, "1.66.0", Some(91611), None),
|
||||
/// Allows `#[track_caller]` on async functions.
|
||||
(active, async_fn_track_caller, "CURRENT_RUSTC_VERSION", Some(110011), None),
|
||||
/// Allows builtin # foo() syntax
|
||||
(active, builtin_syntax, "1.71.0", Some(110680), None),
|
||||
/// Allows `c"foo"` literals.
|
||||
|
@ -1754,7 +1754,7 @@ impl Expr<'_> {
|
||||
|
||||
ExprKind::Unary(UnOp::Deref, _) => true,
|
||||
|
||||
ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _) => {
|
||||
ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _, _) => {
|
||||
allow_projections_from(base) || base.is_place_expr(allow_projections_from)
|
||||
}
|
||||
|
||||
@ -1831,7 +1831,7 @@ impl Expr<'_> {
|
||||
ExprKind::Type(base, _)
|
||||
| ExprKind::Unary(_, base)
|
||||
| ExprKind::Field(base, _)
|
||||
| ExprKind::Index(base, _)
|
||||
| ExprKind::Index(base, _, _)
|
||||
| ExprKind::AddrOf(.., base)
|
||||
| ExprKind::Cast(base, _) => {
|
||||
// This isn't exactly true for `Index` and all `Unary`, but we are using this
|
||||
@ -2015,7 +2015,9 @@ pub enum ExprKind<'hir> {
|
||||
/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field.
|
||||
Field(&'hir Expr<'hir>, Ident),
|
||||
/// An indexing operation (`foo[2]`).
|
||||
Index(&'hir Expr<'hir>, &'hir Expr<'hir>),
|
||||
/// Similar to [`ExprKind::MethodCall`], the final `Span` represents the span of the brackets
|
||||
/// and index.
|
||||
Index(&'hir Expr<'hir>, &'hir Expr<'hir>, Span),
|
||||
|
||||
/// Path to a definition, possibly containing lifetime or type parameters.
|
||||
Path(QPath<'hir>),
|
||||
|
@ -780,7 +780,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
|
||||
visitor.visit_expr(subexpression);
|
||||
visitor.visit_ident(ident);
|
||||
}
|
||||
ExprKind::Index(ref main_expression, ref index_expression) => {
|
||||
ExprKind::Index(ref main_expression, ref index_expression, _) => {
|
||||
visitor.visit_expr(main_expression);
|
||||
visitor.visit_expr(index_expression)
|
||||
}
|
||||
|
@ -245,13 +245,14 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
||||
}
|
||||
// `ForeignItem`s are handled separately.
|
||||
hir::ItemKind::ForeignMod { .. } => {}
|
||||
hir::ItemKind::TyAlias(hir_ty, ..) => {
|
||||
hir::ItemKind::TyAlias(hir_ty, ast_generics) => {
|
||||
if tcx.features().lazy_type_alias
|
||||
|| tcx.type_of(item.owner_id).skip_binder().has_opaque_types()
|
||||
{
|
||||
// Bounds of lazy type aliases and of eager ones that contain opaque types are respected.
|
||||
// E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`.
|
||||
check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -1700,10 +1701,27 @@ fn check_variances_for_type_defn<'tcx>(
|
||||
hir_generics: &hir::Generics<'_>,
|
||||
) {
|
||||
let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id);
|
||||
for field in tcx.adt_def(item.owner_id).all_fields() {
|
||||
if field.ty(tcx, identity_args).references_error() {
|
||||
return;
|
||||
|
||||
match item.kind {
|
||||
ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
|
||||
for field in tcx.adt_def(item.owner_id).all_fields() {
|
||||
if field.ty(tcx, identity_args).references_error() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemKind::TyAlias(..) => {
|
||||
let ty = tcx.type_of(item.owner_id).instantiate_identity();
|
||||
|
||||
if tcx.features().lazy_type_alias || ty.has_opaque_types() {
|
||||
if ty.references_error() {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
bug!();
|
||||
}
|
||||
}
|
||||
_ => bug!(),
|
||||
}
|
||||
|
||||
let ty_predicates = tcx.predicates_of(item.owner_id);
|
||||
|
@ -6,7 +6,7 @@
|
||||
use hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
|
||||
|
||||
use super::terms::VarianceTerm::*;
|
||||
@ -78,6 +78,12 @@ pub fn add_constraints_from_crate<'a, 'tcx>(
|
||||
}
|
||||
}
|
||||
DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id),
|
||||
DefKind::TyAlias
|
||||
if tcx.features().lazy_type_alias
|
||||
|| tcx.type_of(def_id).instantiate_identity().has_opaque_types() =>
|
||||
{
|
||||
constraint_cx.build_constraints_for_item(def_id)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -101,7 +107,18 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
|
||||
let inferred_start = self.terms_cx.inferred_starts[&def_id];
|
||||
let current_item = &CurrentItem { inferred_start };
|
||||
match tcx.type_of(def_id).instantiate_identity().kind() {
|
||||
let ty = tcx.type_of(def_id).instantiate_identity();
|
||||
|
||||
// The type as returned by `type_of` is the underlying type and generally not a weak projection.
|
||||
// Therefore we need to check the `DefKind` first.
|
||||
if let DefKind::TyAlias = tcx.def_kind(def_id)
|
||||
&& (tcx.features().lazy_type_alias || ty.has_opaque_types())
|
||||
{
|
||||
self.add_constraints_from_ty(current_item, ty, self.covariant);
|
||||
return;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
ty::Adt(def, _) => {
|
||||
// Not entirely obvious: constraints on structs/enums do not
|
||||
// affect the variance of their type parameters. See discussion
|
||||
@ -127,6 +144,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
ty::Error(_) => {}
|
||||
|
||||
_ => {
|
||||
span_bug!(
|
||||
tcx.def_span(def_id),
|
||||
@ -252,10 +270,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
self.add_constraints_from_args(current, def.did(), args, variance);
|
||||
}
|
||||
|
||||
ty::Alias(_, ref data) => {
|
||||
ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, ref data) => {
|
||||
self.add_constraints_from_invariant_args(current, data.args, variance);
|
||||
}
|
||||
|
||||
ty::Alias(ty::Weak, ref data) => {
|
||||
self.add_constraints_from_args(current, data.def_id, data.args, variance);
|
||||
}
|
||||
|
||||
ty::Dynamic(data, r, _) => {
|
||||
// The type `dyn Trait<T> +'a` is covariant w/r/t `'a`:
|
||||
self.add_constraints_from_region(current, r, variance);
|
||||
|
@ -8,7 +8,7 @@ use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, CrateVariancesMap, GenericArgsRef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable};
|
||||
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
/// Defines the `TermsContext` basically houses an arena where we can
|
||||
@ -56,6 +56,14 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
|
||||
let crate_map = tcx.crate_variances(());
|
||||
return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]);
|
||||
}
|
||||
DefKind::TyAlias
|
||||
if tcx.features().lazy_type_alias
|
||||
|| tcx.type_of(item_def_id).instantiate_identity().has_opaque_types() =>
|
||||
{
|
||||
// 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 => {
|
||||
return variance_of_opaque(tcx, item_def_id);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
use rustc_arena::DroplessArena;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
|
||||
use std::fmt;
|
||||
|
||||
use self::VarianceTerm::*;
|
||||
@ -97,6 +97,12 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>(
|
||||
}
|
||||
}
|
||||
DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id),
|
||||
DefKind::TyAlias
|
||||
if tcx.features().lazy_type_alias
|
||||
|| tcx.type_of(def_id).instantiate_identity().has_opaque_types() =>
|
||||
{
|
||||
terms_cx.add_inferreds_for_item(def_id)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -1526,7 +1526,7 @@ impl<'a> State<'a> {
|
||||
self.word(".");
|
||||
self.print_ident(ident);
|
||||
}
|
||||
hir::ExprKind::Index(expr, index) => {
|
||||
hir::ExprKind::Index(expr, index, _) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
|
||||
self.word("[");
|
||||
self.print_expr(index);
|
||||
@ -2419,7 +2419,7 @@ fn contains_exterior_struct_lit(value: &hir::Expr<'_>) -> bool {
|
||||
| hir::ExprKind::Cast(x, _)
|
||||
| hir::ExprKind::Type(x, _)
|
||||
| hir::ExprKind::Field(x, _)
|
||||
| hir::ExprKind::Index(x, _) => {
|
||||
| hir::ExprKind::Index(x, _, _) => {
|
||||
// `&X { y: 1 }, X { y: 1 }.y`
|
||||
contains_exterior_struct_lit(x)
|
||||
}
|
||||
|
@ -345,7 +345,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
|
||||
}
|
||||
ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected),
|
||||
ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr),
|
||||
ExprKind::Index(base, idx, brackets_span) => {
|
||||
self.check_expr_index(base, idx, expr, brackets_span)
|
||||
}
|
||||
ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src),
|
||||
hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar),
|
||||
}
|
||||
@ -2840,6 +2842,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
idx: &'tcx hir::Expr<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
brackets_span: Span,
|
||||
) -> Ty<'tcx> {
|
||||
let base_t = self.check_expr(&base);
|
||||
let idx_t = self.check_expr(&idx);
|
||||
@ -2873,7 +2876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
let mut err = type_error_struct!(
|
||||
self.tcx.sess,
|
||||
expr.span,
|
||||
brackets_span,
|
||||
base_t,
|
||||
E0608,
|
||||
"cannot index into a value of type `{base_t}`",
|
||||
@ -2887,16 +2890,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&& let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node
|
||||
&& i < types.len().try_into().expect("expected tuple index to be < usize length")
|
||||
{
|
||||
let snip = self.tcx.sess.source_map().span_to_snippet(base.span);
|
||||
if let Ok(snip) = snip {
|
||||
err.span_suggestion(
|
||||
expr.span,
|
||||
"to access tuple elements, use",
|
||||
format!("{snip}.{i}"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
needs_note = false;
|
||||
}
|
||||
|
||||
err.span_suggestion(
|
||||
brackets_span,
|
||||
"to access tuple elements, use",
|
||||
format!(".{i}"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
needs_note = false;
|
||||
} else if let ExprKind::Path(..) = idx.peel_borrows().kind {
|
||||
err.span_label(idx.span, "cannot access tuple elements at a variable index");
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
self.select_from_expr(base);
|
||||
}
|
||||
|
||||
hir::ExprKind::Index(lhs, rhs) => {
|
||||
hir::ExprKind::Index(lhs, rhs, _) => {
|
||||
// lhs[rhs]
|
||||
self.select_from_expr(lhs);
|
||||
self.consume_expr(rhs);
|
||||
|
@ -1620,8 +1620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|x| x.1.hir_id == *hir_id)
|
||||
.map(|(i, _)| init_tup.get(i).unwrap())
|
||||
.next()
|
||||
.find_map(|(i, _)| init_tup.get(i))
|
||||
{
|
||||
self.note_type_is_not_clone_inner_expr(init)
|
||||
} else {
|
||||
|
@ -198,13 +198,14 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/// Like `pat_ty`, but ignores implicit `&` patterns.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> {
|
||||
let base_ty = self.node_ty(pat.hir_id)?;
|
||||
debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty);
|
||||
trace!(?base_ty);
|
||||
|
||||
// This code detects whether we are looking at a `ref x`,
|
||||
// and if so, figures out what the type *being borrowed* is.
|
||||
let ret_ty = match pat.kind {
|
||||
match pat.kind {
|
||||
PatKind::Binding(..) => {
|
||||
let bm = *self
|
||||
.typeck_results
|
||||
@ -217,21 +218,18 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
// but what we want here is the type of the underlying value being borrowed.
|
||||
// So peel off one-level, turning the &T into T.
|
||||
match base_ty.builtin_deref(false) {
|
||||
Some(t) => t.ty,
|
||||
Some(t) => Ok(t.ty),
|
||||
None => {
|
||||
debug!("By-ref binding of non-derefable type {:?}", base_ty);
|
||||
return Err(());
|
||||
debug!("By-ref binding of non-derefable type");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
base_ty
|
||||
Ok(base_ty)
|
||||
}
|
||||
}
|
||||
_ => base_ty,
|
||||
};
|
||||
debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty);
|
||||
|
||||
Ok(ret_ty)
|
||||
_ => Ok(base_ty),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> {
|
||||
@ -299,13 +297,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub(crate) fn cat_expr_unadjusted(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
) -> McResult<PlaceWithHirId<'tcx>> {
|
||||
debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr);
|
||||
|
||||
let expr_ty = self.expr_ty(expr)?;
|
||||
match expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => {
|
||||
@ -319,7 +315,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
|
||||
hir::ExprKind::Field(ref base, _) => {
|
||||
let base = self.cat_expr(base)?;
|
||||
debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base);
|
||||
debug!(?base);
|
||||
|
||||
let field_idx = self
|
||||
.typeck_results
|
||||
@ -336,7 +332,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
))
|
||||
}
|
||||
|
||||
hir::ExprKind::Index(ref base, _) => {
|
||||
hir::ExprKind::Index(ref base, _, _) => {
|
||||
if self.typeck_results.is_method_call(expr) {
|
||||
// If this is an index implemented by a method call, then it
|
||||
// will include an implicit deref of the result.
|
||||
@ -389,7 +385,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, span))]
|
||||
#[instrument(level = "debug", skip(self, span), ret)]
|
||||
pub(crate) fn cat_res(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
@ -430,6 +426,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
/// Note: the actual upvar access contains invisible derefs of closure
|
||||
/// environment and upvar reference as appropriate. Only regionck cares
|
||||
/// about these dereferences, so we let it compute them as needed.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult<PlaceWithHirId<'tcx>> {
|
||||
let closure_expr_def_id = self.body_owner;
|
||||
|
||||
@ -439,24 +436,20 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
};
|
||||
let var_ty = self.node_ty(var_id)?;
|
||||
|
||||
let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new());
|
||||
|
||||
debug!("cat_upvar ret={:?}", ret);
|
||||
Ok(ret)
|
||||
Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub(crate) fn cat_rvalue(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
expr_ty: Ty<'tcx>,
|
||||
) -> PlaceWithHirId<'tcx> {
|
||||
debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span);
|
||||
let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new());
|
||||
debug!("cat_rvalue ret={:?}", ret);
|
||||
ret
|
||||
PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, node), ret)]
|
||||
pub(crate) fn cat_projection<N: HirNode>(
|
||||
&self,
|
||||
node: &N,
|
||||
@ -464,16 +457,23 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
ty: Ty<'tcx>,
|
||||
kind: ProjectionKind,
|
||||
) -> PlaceWithHirId<'tcx> {
|
||||
let place_ty = base_place.place.ty();
|
||||
let mut projections = base_place.place.projections;
|
||||
|
||||
let node_ty = self.typeck_results.node_type(node.hir_id());
|
||||
// Opaque types can't have field projections, but we can instead convert
|
||||
// the current place in-place (heh) to the hidden type, and then apply all
|
||||
// follow up projections on that.
|
||||
if node_ty != place_ty && place_ty.has_opaque_types() {
|
||||
projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
|
||||
}
|
||||
projections.push(Projection { kind, ty });
|
||||
let ret = PlaceWithHirId::new(
|
||||
PlaceWithHirId::new(
|
||||
node.hir_id(),
|
||||
base_place.place.base_ty,
|
||||
base_place.place.base,
|
||||
projections,
|
||||
);
|
||||
debug!("cat_field ret {:?}", ret);
|
||||
ret
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
@ -497,7 +497,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
self.cat_deref(expr, base)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, node))]
|
||||
#[instrument(level = "debug", skip(self, node), ret)]
|
||||
fn cat_deref(
|
||||
&self,
|
||||
node: &impl HirNode,
|
||||
@ -514,14 +514,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
let mut projections = base_place.place.projections;
|
||||
projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty });
|
||||
|
||||
let ret = PlaceWithHirId::new(
|
||||
Ok(PlaceWithHirId::new(
|
||||
node.hir_id(),
|
||||
base_place.place.base_ty,
|
||||
base_place.place.base,
|
||||
projections,
|
||||
);
|
||||
debug!("cat_deref ret {:?}", ret);
|
||||
Ok(ret)
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn cat_pattern<F>(
|
||||
@ -603,6 +601,13 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it
|
||||
/// is being matched against.
|
||||
///
|
||||
/// In general, the way that this works is that we walk down the pattern,
|
||||
/// constructing a `PlaceWithHirId` that represents the path that will be taken
|
||||
/// to reach the value being matched.
|
||||
#[instrument(skip(self, op), ret, level = "debug")]
|
||||
fn cat_pattern_<F>(
|
||||
&self,
|
||||
mut place_with_id: PlaceWithHirId<'tcx>,
|
||||
@ -612,15 +617,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
where
|
||||
F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>),
|
||||
{
|
||||
// Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it
|
||||
// is being matched against.
|
||||
//
|
||||
// In general, the way that this works is that we walk down the pattern,
|
||||
// constructing a `PlaceWithHirId` that represents the path that will be taken
|
||||
// to reach the value being matched.
|
||||
|
||||
debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id);
|
||||
|
||||
// If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly.
|
||||
// `PlaceWithHirId`s are constructed differently from patterns. For example, in
|
||||
//
|
||||
@ -654,11 +650,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
|
||||
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
|
||||
for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) {
|
||||
debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id);
|
||||
debug!("applying adjustment to place_with_id={:?}", place_with_id);
|
||||
place_with_id = self.cat_deref(pat, place_with_id)?;
|
||||
}
|
||||
let place_with_id = place_with_id; // lose mutability
|
||||
debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id);
|
||||
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
|
||||
|
||||
// Invoke the callback, but only now, after the `place_with_id` has adjusted.
|
||||
//
|
||||
|
@ -284,7 +284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let mut exprs = vec![expr];
|
||||
|
||||
while let hir::ExprKind::Field(ref expr, _)
|
||||
| hir::ExprKind::Index(ref expr, _)
|
||||
| hir::ExprKind::Index(ref expr, _, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::Deref, ref expr) = exprs.last().unwrap().kind
|
||||
{
|
||||
exprs.push(expr);
|
||||
|
@ -40,7 +40,7 @@ fn record_rvalue_scope_rec(
|
||||
hir::ExprKind::AddrOf(_, _, subexpr)
|
||||
| hir::ExprKind::Unary(hir::UnOp::Deref, subexpr)
|
||||
| hir::ExprKind::Field(subexpr, _)
|
||||
| hir::ExprKind::Index(subexpr, _) => {
|
||||
| hir::ExprKind::Index(subexpr, _, _) => {
|
||||
expr = subexpr;
|
||||
}
|
||||
_ => {
|
||||
|
@ -264,12 +264,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty);
|
||||
|
||||
// If we have an origin, store it.
|
||||
if let Some(origin) = origin {
|
||||
let origin = if enable_precise_capture(span) {
|
||||
(origin.0, origin.1)
|
||||
} else {
|
||||
(origin.0, Place { projections: vec![], ..origin.1 })
|
||||
};
|
||||
if let Some(mut origin) = origin {
|
||||
if !enable_precise_capture(span) {
|
||||
// Without precise captures, we just capture the base and ignore
|
||||
// the projections.
|
||||
origin.1.projections.clear()
|
||||
}
|
||||
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
@ -294,10 +294,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
// Equate the type variables for the upvars with the actual types.
|
||||
let final_upvar_tys = self.final_upvar_tys(closure_def_id);
|
||||
debug!(
|
||||
"analyze_closure: id={:?} args={:?} final_upvar_tys={:?}",
|
||||
closure_hir_id, args, final_upvar_tys
|
||||
);
|
||||
debug!(?closure_hir_id, ?args, ?final_upvar_tys);
|
||||
|
||||
// Build a tuple (U0..Un) of the final upvar types U0..Un
|
||||
// and unify the upvar tuple type in the closure with it:
|
||||
@ -338,10 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let upvar_ty = captured_place.place.ty();
|
||||
let capture = captured_place.info.capture_kind;
|
||||
|
||||
debug!(
|
||||
"final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}",
|
||||
captured_place.place, upvar_ty, capture, captured_place.mutability,
|
||||
);
|
||||
debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability);
|
||||
|
||||
apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region)
|
||||
})
|
||||
@ -679,6 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
match (p1.kind, p2.kind) {
|
||||
// Paths are the same, continue to next loop.
|
||||
(ProjectionKind::Deref, ProjectionKind::Deref) => {}
|
||||
(ProjectionKind::OpaqueCast, ProjectionKind::OpaqueCast) => {}
|
||||
(ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _))
|
||||
if i1 == i2 => {}
|
||||
|
||||
@ -701,10 +696,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
l @ (ProjectionKind::Index
|
||||
| ProjectionKind::Subslice
|
||||
| ProjectionKind::Deref
|
||||
| ProjectionKind::OpaqueCast
|
||||
| ProjectionKind::Field(..)),
|
||||
r @ (ProjectionKind::Index
|
||||
| ProjectionKind::Subslice
|
||||
| ProjectionKind::Deref
|
||||
| ProjectionKind::OpaqueCast
|
||||
| ProjectionKind::Field(..)),
|
||||
) => bug!(
|
||||
"ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})",
|
||||
@ -1890,6 +1887,7 @@ fn restrict_capture_precision(
|
||||
return (place, curr_mode);
|
||||
}
|
||||
ProjectionKind::Deref => {}
|
||||
ProjectionKind::OpaqueCast => {}
|
||||
ProjectionKind::Field(..) => {} // ignore
|
||||
}
|
||||
}
|
||||
@ -1946,6 +1944,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String
|
||||
ProjectionKind::Deref => String::from("Deref"),
|
||||
ProjectionKind::Index => String::from("Index"),
|
||||
ProjectionKind::Subslice => String::from("Subslice"),
|
||||
ProjectionKind::OpaqueCast => String::from("OpaqueCast"),
|
||||
};
|
||||
if i != 0 {
|
||||
projections_str.push(',');
|
||||
|
@ -210,7 +210,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||
// to use builtin indexing because the index type is known to be
|
||||
// usize-ish
|
||||
fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) {
|
||||
if let hir::ExprKind::Index(ref base, ref index) = e.kind {
|
||||
if let hir::ExprKind::Index(ref base, ref index, _) = e.kind {
|
||||
// All valid indexing looks like this; might encounter non-valid indexes at this point.
|
||||
let base_ty = self.typeck_results.expr_ty_adjusted_opt(base);
|
||||
if base_ty.is_none() {
|
||||
|
@ -481,3 +481,31 @@ impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> {
|
||||
TypeTrace { cause: cause.clone(), values: Sigs(ExpectedFound::new(a_is_expected, a, b)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> {
|
||||
fn to_trace(
|
||||
cause: &ObligationCause<'tcx>,
|
||||
a_is_expected: bool,
|
||||
a: Self,
|
||||
b: Self,
|
||||
) -> TypeTrace<'tcx> {
|
||||
TypeTrace {
|
||||
cause: cause.clone(),
|
||||
values: ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> {
|
||||
fn to_trace(
|
||||
cause: &ObligationCause<'tcx>,
|
||||
a_is_expected: bool,
|
||||
a: Self,
|
||||
b: Self,
|
||||
) -> TypeTrace<'tcx> {
|
||||
TypeTrace {
|
||||
cause: cause.clone(),
|
||||
values: ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -562,15 +562,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
||||
V: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
let needs_canonical_flags = if canonicalize_region_mode.any() {
|
||||
TypeFlags::HAS_INFER |
|
||||
TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
|
||||
TypeFlags::HAS_TY_PLACEHOLDER |
|
||||
TypeFlags::HAS_CT_PLACEHOLDER
|
||||
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS
|
||||
} else {
|
||||
TypeFlags::HAS_INFER
|
||||
| TypeFlags::HAS_RE_PLACEHOLDER
|
||||
| TypeFlags::HAS_TY_PLACEHOLDER
|
||||
| TypeFlags::HAS_CT_PLACEHOLDER
|
||||
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER
|
||||
};
|
||||
|
||||
// Fast path: nothing that needs to be canonicalized.
|
||||
|
@ -351,6 +351,15 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
|
||||
)
|
||||
}
|
||||
}
|
||||
ty::RePlaceholder(_) => {
|
||||
explain_free_region(
|
||||
tcx,
|
||||
&mut err,
|
||||
&format!("hidden type `{}` captures ", hidden_ty),
|
||||
hidden_region,
|
||||
"",
|
||||
);
|
||||
}
|
||||
ty::ReError(_) => {
|
||||
err.delay_as_bug();
|
||||
}
|
||||
@ -1635,6 +1644,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
(false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id)))
|
||||
}
|
||||
ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),
|
||||
ValuePairs::ExistentialTraitRef(_) => {
|
||||
(false, Mismatch::Fixed("existential trait ref"))
|
||||
}
|
||||
ValuePairs::ExistentialProjection(_) => {
|
||||
(false, Mismatch::Fixed("existential projection"))
|
||||
}
|
||||
};
|
||||
let Some(vals) = self.values_str(values) else {
|
||||
// Derived error. Cancel the emitter.
|
||||
@ -2139,6 +2154,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
infer::Regions(exp_found) => self.expected_found_str(exp_found),
|
||||
infer::Terms(exp_found) => self.expected_found_str_term(exp_found),
|
||||
infer::Aliases(exp_found) => self.expected_found_str(exp_found),
|
||||
infer::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found),
|
||||
infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
|
||||
infer::TraitRefs(exp_found) => {
|
||||
let pretty_exp_found = ty::error::ExpectedFound {
|
||||
expected: exp_found.expected.print_only_trait_path(),
|
||||
|
@ -374,6 +374,8 @@ pub enum ValuePairs<'tcx> {
|
||||
TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>),
|
||||
PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>),
|
||||
Sigs(ExpectedFound<ty::FnSig<'tcx>>),
|
||||
ExistentialTraitRef(ExpectedFound<ty::PolyExistentialTraitRef<'tcx>>),
|
||||
ExistentialProjection(ExpectedFound<ty::PolyExistentialProjection<'tcx>>),
|
||||
}
|
||||
|
||||
impl<'tcx> ValuePairs<'tcx> {
|
||||
|
@ -130,8 +130,6 @@ lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name
|
||||
lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}`
|
||||
.help = was set with `--cfg` but isn't in the `--check-cfg` expected values
|
||||
|
||||
lint_builtin_unnameable_test_items = cannot test inner items
|
||||
|
||||
lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
|
||||
lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
||||
|
||||
@ -169,8 +167,9 @@ lint_check_name_warning = {$msg}
|
||||
|
||||
lint_command_line_source = `forbid` lint level was set on command line
|
||||
|
||||
lint_confusable_identifier_pair = identifier pair considered confusable between `{$existing_sym}` and `{$sym}`
|
||||
.label = this is where the previous identifier occurred
|
||||
lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike
|
||||
.current_use = this identifier can be confused with `{$existing_sym}`
|
||||
.other_use = other identifier used here
|
||||
|
||||
lint_cstring_ptr = getting the inner pointer of a temporary `CString`
|
||||
.as_ptr_label = this pointer will be invalid
|
||||
|
@ -35,7 +35,7 @@ use crate::{
|
||||
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
|
||||
BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
|
||||
BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue,
|
||||
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit,
|
||||
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
|
||||
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
|
||||
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
|
||||
BuiltinWhileTrue, SuggestChangingAssocTypes,
|
||||
@ -1259,8 +1259,8 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
|
||||
|
||||
declare_lint! {
|
||||
/// The `ungated_async_fn_track_caller` lint warns when the
|
||||
/// `#[track_caller]` attribute is used on an async function, method, or
|
||||
/// closure, without enabling the corresponding unstable feature flag.
|
||||
/// `#[track_caller]` attribute is used on an async function
|
||||
/// without enabling the corresponding unstable feature flag.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
@ -1274,13 +1274,13 @@ declare_lint! {
|
||||
/// ### Explanation
|
||||
///
|
||||
/// The attribute must be used in conjunction with the
|
||||
/// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]`
|
||||
/// [`async_fn_track_caller` feature flag]. Otherwise, the `#[track_caller]`
|
||||
/// annotation will function as a no-op.
|
||||
///
|
||||
/// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html
|
||||
/// [`async_fn_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/async-fn-track-caller.html
|
||||
UNGATED_ASYNC_FN_TRACK_CALLER,
|
||||
Warn,
|
||||
"enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled"
|
||||
"enabling track_caller on an async fn is a no-op unless the async_fn_track_caller feature is enabled"
|
||||
}
|
||||
|
||||
declare_lint_pass!(
|
||||
@ -1300,7 +1300,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
if fn_kind.asyncness() == IsAsync::Async
|
||||
&& !cx.tcx.features().closure_track_caller
|
||||
&& !cx.tcx.features().async_fn_track_caller
|
||||
// Now, check if the function has the `#[track_caller]` attribute
|
||||
&& let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
|
||||
{
|
||||
@ -1770,82 +1770,6 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `unnameable_test_items` lint detects [`#[test]`][test] functions
|
||||
/// that are not able to be run by the test harness because they are in a
|
||||
/// position where they are not nameable.
|
||||
///
|
||||
/// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,test
|
||||
/// fn main() {
|
||||
/// #[test]
|
||||
/// fn foo() {
|
||||
/// // This test will not fail because it does not run.
|
||||
/// assert_eq!(1, 2);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// In order for the test harness to run a test, the test function must be
|
||||
/// located in a position where it can be accessed from the crate root.
|
||||
/// This generally means it must be defined in a module, and not anywhere
|
||||
/// else such as inside another function. The compiler previously allowed
|
||||
/// this without an error, so a lint was added as an alert that a test is
|
||||
/// not being used. Whether or not this should be allowed has not yet been
|
||||
/// decided, see [RFC 2471] and [issue #36629].
|
||||
///
|
||||
/// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443
|
||||
/// [issue #36629]: https://github.com/rust-lang/rust/issues/36629
|
||||
UNNAMEABLE_TEST_ITEMS,
|
||||
Warn,
|
||||
"detects an item that cannot be named being marked as `#[test_case]`",
|
||||
report_in_external_macro
|
||||
}
|
||||
|
||||
pub struct UnnameableTestItems {
|
||||
boundary: Option<hir::OwnerId>, // Id of the item under which things are not nameable
|
||||
items_nameable: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]);
|
||||
|
||||
impl UnnameableTestItems {
|
||||
pub fn new() -> Self {
|
||||
Self { boundary: None, items_nameable: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
||||
if self.items_nameable {
|
||||
if let hir::ItemKind::Mod(..) = it.kind {
|
||||
} else {
|
||||
self.items_nameable = false;
|
||||
self.boundary = Some(it.owner_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let attrs = cx.tcx.hir().attrs(it.hir_id());
|
||||
if let Some(attr) = attr::find_by_name(attrs, sym::rustc_test_marker) {
|
||||
cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
||||
if !self.items_nameable && self.boundary == Some(it.owner_id) {
|
||||
self.items_nameable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `keyword_idents` lint detects edition keywords being used as an
|
||||
/// identifier.
|
||||
|
@ -189,8 +189,6 @@ late_lint_methods!(
|
||||
[
|
||||
pub BuiltinCombinedLateLintPass,
|
||||
[
|
||||
// Tracks state across modules
|
||||
UnnameableTestItems: UnnameableTestItems::new(),
|
||||
// Tracks attributes of parents
|
||||
MissingDoc: MissingDoc::new(),
|
||||
// Builds a global list of all impls of `Debug`.
|
||||
|
@ -250,7 +250,7 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
|
||||
rustc_session::parse::add_feature_diagnostics(
|
||||
diag,
|
||||
&self.parse_sess,
|
||||
sym::closure_track_caller,
|
||||
sym::async_fn_track_caller,
|
||||
);
|
||||
diag
|
||||
}
|
||||
@ -370,10 +370,6 @@ pub enum BuiltinEllipsisInclusiveRangePatternsLint {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_builtin_unnameable_test_items)]
|
||||
pub struct BuiltinUnnameableTestItems;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_builtin_keyword_idents)]
|
||||
pub struct BuiltinKeywordIdents {
|
||||
@ -1087,8 +1083,10 @@ pub struct IdentifierUncommonCodepoints;
|
||||
pub struct ConfusableIdentifierPair {
|
||||
pub existing_sym: Symbol,
|
||||
pub sym: Symbol,
|
||||
#[label]
|
||||
#[label(lint_other_use)]
|
||||
pub label: Span,
|
||||
#[label(lint_current_use)]
|
||||
pub main_label: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
@ -222,6 +222,7 @@ impl EarlyLintPass for NonAsciiIdents {
|
||||
existing_sym: *existing_symbol,
|
||||
sym: symbol,
|
||||
label: *existing_span,
|
||||
main_label: sp,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -653,7 +653,7 @@ trait UnusedDelimLint {
|
||||
ExprKind::Call(fn_, _params) => fn_,
|
||||
ExprKind::Cast(expr, _ty) => expr,
|
||||
ExprKind::Type(expr, _ty) => expr,
|
||||
ExprKind::Index(base, _subscript) => base,
|
||||
ExprKind::Index(base, _subscript, _) => base,
|
||||
_ => break,
|
||||
};
|
||||
if !classify::expr_requires_semi_to_be_stmt(innermost) {
|
||||
@ -830,7 +830,7 @@ trait UnusedDelimLint {
|
||||
(value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
|
||||
}
|
||||
|
||||
Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
|
||||
Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
|
||||
|
||||
Assign(_, ref value, _) | AssignOp(.., ref value) => {
|
||||
(value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
|
||||
|
@ -2846,6 +2846,45 @@ declare_lint! {
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `unnameable_test_items` lint detects [`#[test]`][test] functions
|
||||
/// that are not able to be run by the test harness because they are in a
|
||||
/// position where they are not nameable.
|
||||
///
|
||||
/// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,test
|
||||
/// fn main() {
|
||||
/// #[test]
|
||||
/// fn foo() {
|
||||
/// // This test will not fail because it does not run.
|
||||
/// assert_eq!(1, 2);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// In order for the test harness to run a test, the test function must be
|
||||
/// located in a position where it can be accessed from the crate root.
|
||||
/// This generally means it must be defined in a module, and not anywhere
|
||||
/// else such as inside another function. The compiler previously allowed
|
||||
/// this without an error, so a lint was added as an alert that a test is
|
||||
/// not being used. Whether or not this should be allowed has not yet been
|
||||
/// decided, see [RFC 2471] and [issue #36629].
|
||||
///
|
||||
/// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443
|
||||
/// [issue #36629]: https://github.com/rust-lang/rust/issues/36629
|
||||
pub UNNAMEABLE_TEST_ITEMS,
|
||||
Warn,
|
||||
"detects an item that cannot be named being marked as `#[test_case]`",
|
||||
report_in_external_macro
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `useless_deprecated` lint detects deprecation attributes with no effect.
|
||||
///
|
||||
@ -3403,6 +3442,7 @@ declare_lint_pass! {
|
||||
UNKNOWN_CRATE_TYPES,
|
||||
UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
|
||||
UNKNOWN_LINTS,
|
||||
UNNAMEABLE_TEST_ITEMS,
|
||||
UNNAMEABLE_TYPES,
|
||||
UNREACHABLE_CODE,
|
||||
UNREACHABLE_PATTERNS,
|
||||
|
@ -30,6 +30,7 @@ use rustc_middle::query::Providers;
|
||||
use rustc_middle::traits::specialization_graph;
|
||||
use rustc_middle::ty::codec::TyEncoder;
|
||||
use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{self, AssocItemContainer, SymbolName, Ty, TyCtxt};
|
||||
use rustc_middle::util::common::to_readable_str;
|
||||
use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder};
|
||||
@ -1034,7 +1035,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn should_encode_variances(def_kind: DefKind) -> bool {
|
||||
fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: DefKind) -> bool {
|
||||
match def_kind {
|
||||
DefKind::Struct
|
||||
| DefKind::Union
|
||||
@ -1053,7 +1054,6 @@ fn should_encode_variances(def_kind: DefKind) -> bool {
|
||||
| DefKind::Static(..)
|
||||
| DefKind::Const
|
||||
| DefKind::ForeignMod
|
||||
| DefKind::TyAlias
|
||||
| DefKind::Impl { .. }
|
||||
| DefKind::Trait
|
||||
| DefKind::TraitAlias
|
||||
@ -1067,6 +1067,10 @@ fn should_encode_variances(def_kind: DefKind) -> bool {
|
||||
| DefKind::Closure
|
||||
| DefKind::Generator
|
||||
| DefKind::ExternCrate => false,
|
||||
DefKind::TyAlias => {
|
||||
tcx.features().lazy_type_alias
|
||||
|| tcx.type_of(def_id).instantiate_identity().has_opaque_types()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1349,7 +1353,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
self.encode_default_body_stability(def_id);
|
||||
self.encode_deprecation(def_id);
|
||||
}
|
||||
if should_encode_variances(def_kind) {
|
||||
if should_encode_variances(tcx, def_id, def_kind) {
|
||||
let v = self.tcx.variances_of(def_id);
|
||||
record_array!(self.tables.variances_of[def_id] <- v);
|
||||
}
|
||||
|
@ -36,6 +36,10 @@ pub enum ProjectionKind {
|
||||
|
||||
/// A subslice covering a range of values like `B[x..y]`.
|
||||
Subslice,
|
||||
|
||||
/// A conversion from an opaque type to its hidden type so we can
|
||||
/// do further projections on it.
|
||||
OpaqueCast,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
|
||||
|
@ -749,7 +749,7 @@ rustc_queries! {
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Gets a map with the variance of every item; use `item_variance` instead.
|
||||
/// Gets a map with the variance of every item; use `variances_of` instead.
|
||||
query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> {
|
||||
arena_cache
|
||||
desc { "computing the variances for items in this crate" }
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_query_system::cache::Cache;
|
||||
|
||||
use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints};
|
||||
use crate::traits::query::NoSolution;
|
||||
@ -11,9 +10,10 @@ use crate::ty::{
|
||||
TypeVisitor,
|
||||
};
|
||||
|
||||
mod cache;
|
||||
pub mod inspect;
|
||||
|
||||
pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
|
||||
pub use cache::{CacheData, EvaluationCache};
|
||||
|
||||
/// A goal is a statement, i.e. `predicate`, we want to prove
|
||||
/// given some assumptions, i.e. `param_env`.
|
||||
@ -147,7 +147,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
|
||||
}
|
||||
|
||||
/// Additional constraints returned on success.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)]
|
||||
pub struct ExternalConstraintsData<'tcx> {
|
||||
// FIXME: implement this.
|
||||
pub region_constraints: QueryRegionConstraints<'tcx>,
|
||||
|
100
compiler/rustc_middle/src/traits/solve/cache.rs
Normal file
100
compiler/rustc_middle/src/traits/solve/cache.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use super::{CanonicalInput, QueryResult};
|
||||
use crate::ty::TyCtxt;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_query_system::cache::WithDepNode;
|
||||
use rustc_query_system::dep_graph::DepNodeIndex;
|
||||
use rustc_session::Limit;
|
||||
/// The trait solver cache used by `-Ztrait-solver=next`.
|
||||
///
|
||||
/// FIXME(@lcnr): link to some official documentation of how
|
||||
/// this works.
|
||||
#[derive(Default)]
|
||||
pub struct EvaluationCache<'tcx> {
|
||||
map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
|
||||
}
|
||||
|
||||
pub struct CacheData<'tcx> {
|
||||
pub result: QueryResult<'tcx>,
|
||||
pub reached_depth: usize,
|
||||
pub encountered_overflow: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> EvaluationCache<'tcx> {
|
||||
/// Insert a final result into the global cache.
|
||||
pub fn insert(
|
||||
&self,
|
||||
key: CanonicalInput<'tcx>,
|
||||
reached_depth: usize,
|
||||
did_overflow: bool,
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
dep_node: DepNodeIndex,
|
||||
result: QueryResult<'tcx>,
|
||||
) {
|
||||
let mut map = self.map.borrow_mut();
|
||||
let entry = map.entry(key).or_default();
|
||||
let data = WithDepNode::new(dep_node, result);
|
||||
entry.cycle_participants.extend(cycle_participants);
|
||||
if did_overflow {
|
||||
entry.with_overflow.insert(reached_depth, data);
|
||||
} else {
|
||||
entry.success = Some(Success { data, reached_depth });
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to fetch a cached result, checking the recursion limit
|
||||
/// and handling root goals of coinductive cycles.
|
||||
///
|
||||
/// If this returns `Some` the cache result can be used.
|
||||
pub fn get(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: CanonicalInput<'tcx>,
|
||||
cycle_participant_in_stack: impl FnOnce(&FxHashSet<CanonicalInput<'tcx>>) -> bool,
|
||||
available_depth: Limit,
|
||||
) -> Option<CacheData<'tcx>> {
|
||||
let map = self.map.borrow();
|
||||
let entry = map.get(&key)?;
|
||||
|
||||
if cycle_participant_in_stack(&entry.cycle_participants) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(ref success) = entry.success {
|
||||
if available_depth.value_within_limit(success.reached_depth) {
|
||||
return Some(CacheData {
|
||||
result: success.data.get(tcx),
|
||||
reached_depth: success.reached_depth,
|
||||
encountered_overflow: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
entry.with_overflow.get(&available_depth.0).map(|e| CacheData {
|
||||
result: e.get(tcx),
|
||||
reached_depth: available_depth.0,
|
||||
encountered_overflow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Success<'tcx> {
|
||||
data: WithDepNode<QueryResult<'tcx>>,
|
||||
reached_depth: usize,
|
||||
}
|
||||
|
||||
/// The cache entry for a goal `CanonicalInput`.
|
||||
///
|
||||
/// This contains results whose computation never hit the
|
||||
/// recursion limit in `success`, and all results which hit
|
||||
/// the recursion limit in `with_overflow`.
|
||||
#[derive(Default)]
|
||||
struct CacheEntry<'tcx> {
|
||||
success: Option<Success<'tcx>>,
|
||||
/// We have to be careful when caching roots of cycles.
|
||||
///
|
||||
/// See the doc comment of `StackEntry::cycle_participants` for more
|
||||
/// details.
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
with_overflow: FxHashMap<usize, WithDepNode<QueryResult<'tcx>>>,
|
||||
}
|
@ -73,12 +73,15 @@ pub struct GoalCandidate<'tcx> {
|
||||
pub enum CandidateKind<'tcx> {
|
||||
/// Probe entered when normalizing the self ty during candidate assembly
|
||||
NormalizedSelfTyAssembly,
|
||||
DynUpcastingAssembly,
|
||||
/// A normal candidate for proving a goal
|
||||
Candidate {
|
||||
name: String,
|
||||
result: QueryResult<'tcx>,
|
||||
},
|
||||
Candidate { name: String, result: QueryResult<'tcx> },
|
||||
/// Used in the probe that wraps normalizing the non-self type for the unsize
|
||||
/// trait, which is also structurally matched on.
|
||||
UnsizeAssembly,
|
||||
/// During upcasting from some source object to target object type, used to
|
||||
/// do a probe to find out what projection type(s) may be used to prove that
|
||||
/// the source type upholds all of the target type's object bounds.
|
||||
UpcastProbe,
|
||||
}
|
||||
impl Debug for GoalCandidate<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -100,8 +100,11 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||
CandidateKind::NormalizedSelfTyAssembly => {
|
||||
writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
|
||||
}
|
||||
CandidateKind::DynUpcastingAssembly => {
|
||||
writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:")
|
||||
CandidateKind::UnsizeAssembly => {
|
||||
writeln!(self.f, "ASSEMBLING CANDIDATES FOR UNSIZING:")
|
||||
}
|
||||
CandidateKind::UpcastProbe => {
|
||||
writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
|
||||
}
|
||||
CandidateKind::Candidate { name, result } => {
|
||||
writeln!(self.f, "CANDIDATE {name}: {result:?}")
|
||||
|
@ -174,6 +174,8 @@ impl<'tcx> CapturedPlace<'tcx> {
|
||||
// Ignore derefs for now, as they are likely caused by
|
||||
// autoderefs that don't appear in the original code.
|
||||
HirProjectionKind::Deref => {}
|
||||
// Just change the type to the hidden type, so we can actually project.
|
||||
HirProjectionKind::OpaqueCast => {}
|
||||
proj => bug!("Unexpected projection {:?} in captured place", proj),
|
||||
}
|
||||
ty = proj.ty;
|
||||
|
@ -1338,12 +1338,25 @@ impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx> for ProjectionPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
ty::Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(self))).to_predicate(tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
self.map_bound(|p| PredicateKind::Clause(ClauseKind::Projection(p))).to_predicate(tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for ProjectionPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
|
||||
let p: Predicate<'tcx> = self.to_predicate(tcx);
|
||||
p.expect_clause()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyProjectionPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
|
||||
let p: Predicate<'tcx> = self.to_predicate(tcx);
|
||||
|
@ -2734,8 +2734,9 @@ forward_display_to_print! {
|
||||
// HACK(eddyb) these are exhaustive instead of generic,
|
||||
// because `for<'tcx>` isn't possible yet.
|
||||
ty::PolyExistentialPredicate<'tcx>,
|
||||
ty::PolyExistentialProjection<'tcx>,
|
||||
ty::PolyExistentialTraitRef<'tcx>,
|
||||
ty::Binder<'tcx, ty::TraitRef<'tcx>>,
|
||||
ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>,
|
||||
ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
|
||||
ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>,
|
||||
ty::Binder<'tcx, ty::FnSig<'tcx>>,
|
||||
|
@ -88,14 +88,10 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
|
||||
self.has_type_flags(TypeFlags::HAS_INFER)
|
||||
}
|
||||
fn has_placeholders(&self) -> bool {
|
||||
self.has_type_flags(
|
||||
TypeFlags::HAS_RE_PLACEHOLDER
|
||||
| TypeFlags::HAS_TY_PLACEHOLDER
|
||||
| TypeFlags::HAS_CT_PLACEHOLDER,
|
||||
)
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER)
|
||||
}
|
||||
fn has_non_region_placeholders(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER)
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER)
|
||||
}
|
||||
fn has_param(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PARAM)
|
||||
|
@ -236,6 +236,9 @@ fn strip_prefix<'a, 'tcx>(
|
||||
}
|
||||
assert_matches!(iter.next(), Some(ProjectionElem::Field(..)));
|
||||
}
|
||||
HirProjectionKind::OpaqueCast => {
|
||||
assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..)));
|
||||
}
|
||||
HirProjectionKind::Index | HirProjectionKind::Subslice => {
|
||||
bug!("unexpected projection kind: {:?}", projection);
|
||||
}
|
||||
|
@ -469,11 +469,17 @@ impl<'tcx> Cx<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::Index(ref lhs, ref index) => {
|
||||
hir::ExprKind::Index(ref lhs, ref index, brackets_span) => {
|
||||
if self.typeck_results().is_method_call(expr) {
|
||||
let lhs = self.mirror_expr(lhs);
|
||||
let index = self.mirror_expr(index);
|
||||
self.overloaded_place(expr, expr_ty, None, Box::new([lhs, index]), expr.span)
|
||||
self.overloaded_place(
|
||||
expr,
|
||||
expr_ty,
|
||||
None,
|
||||
Box::new([lhs, index]),
|
||||
brackets_span,
|
||||
)
|
||||
} else {
|
||||
ExprKind::Index { lhs: self.mirror_expr(lhs), index: self.mirror_expr(index) }
|
||||
}
|
||||
@ -1072,6 +1078,9 @@ impl<'tcx> Cx<'tcx> {
|
||||
variant_index,
|
||||
name: field,
|
||||
},
|
||||
HirProjectionKind::OpaqueCast => {
|
||||
ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) }
|
||||
}
|
||||
HirProjectionKind::Index | HirProjectionKind::Subslice => {
|
||||
// We don't capture these projections, so we can ignore them here
|
||||
continue;
|
||||
|
@ -857,7 +857,7 @@ impl<'a> Parser<'a> {
|
||||
let msg = format!(
|
||||
"cast cannot be followed by {}",
|
||||
match with_postfix.kind {
|
||||
ExprKind::Index(_, _) => "indexing",
|
||||
ExprKind::Index(..) => "indexing",
|
||||
ExprKind::Try(_) => "`?`",
|
||||
ExprKind::Field(_, _) => "a field access",
|
||||
ExprKind::MethodCall(_) => "a method call",
|
||||
@ -1304,7 +1304,10 @@ impl<'a> Parser<'a> {
|
||||
let index = self.parse_expr()?;
|
||||
self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?;
|
||||
self.expect(&token::CloseDelim(Delimiter::Bracket))?;
|
||||
Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index)))
|
||||
Ok(self.mk_expr(
|
||||
lo.to(self.prev_token.span),
|
||||
self.mk_index(base, index, open_delim_span.to(self.prev_token.span)),
|
||||
))
|
||||
}
|
||||
|
||||
/// Assuming we have just parsed `.`, continue parsing into an expression.
|
||||
@ -3366,8 +3369,8 @@ impl<'a> Parser<'a> {
|
||||
ExprKind::Binary(binop, lhs, rhs)
|
||||
}
|
||||
|
||||
fn mk_index(&self, expr: P<Expr>, idx: P<Expr>) -> ExprKind {
|
||||
ExprKind::Index(expr, idx)
|
||||
fn mk_index(&self, expr: P<Expr>, idx: P<Expr>, brackets_span: Span) -> ExprKind {
|
||||
ExprKind::Index(expr, idx, brackets_span)
|
||||
}
|
||||
|
||||
fn mk_call(&self, f: P<Expr>, args: ThinVec<P<Expr>>) -> ExprKind {
|
||||
|
@ -1061,7 +1061,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
self.propagate_through_expr(&l, ln)
|
||||
}
|
||||
|
||||
hir::ExprKind::Index(ref l, ref r) | hir::ExprKind::Binary(_, ref l, ref r) => {
|
||||
hir::ExprKind::Index(ref l, ref r, _) | hir::ExprKind::Binary(_, ref l, ref r) => {
|
||||
let r_succ = self.propagate_through_expr(&r, succ);
|
||||
self.propagate_through_expr(&l, r_succ)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ edition = "2021"
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
parking_lot = "0.11"
|
||||
parking_lot = "0.12"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
@ -1,4 +1,3 @@
|
||||
use parking_lot::Mutex;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerRef};
|
||||
@ -88,7 +87,7 @@ pub struct DepGraphData<K: DepKind> {
|
||||
|
||||
colors: DepNodeColorMap,
|
||||
|
||||
processed_side_effects: Mutex<FxHashSet<DepNodeIndex>>,
|
||||
processed_side_effects: Lock<FxHashSet<DepNodeIndex>>,
|
||||
|
||||
/// When we load, there may be `.o` files, cached MIR, or other such
|
||||
/// things available to us. If we find that they are not dirty, we
|
||||
|
@ -21,12 +21,11 @@ use {
|
||||
parking_lot::{Condvar, Mutex},
|
||||
rayon_core,
|
||||
rustc_data_structures::fx::FxHashSet,
|
||||
rustc_data_structures::sync::Lock,
|
||||
rustc_data_structures::sync::Lrc,
|
||||
rustc_data_structures::{defer, jobserver},
|
||||
rustc_span::DUMMY_SP,
|
||||
std::iter,
|
||||
std::process,
|
||||
std::sync::Arc,
|
||||
};
|
||||
|
||||
/// Represents a span and a query key.
|
||||
@ -191,7 +190,7 @@ struct QueryWaiter<D: DepKind> {
|
||||
query: Option<QueryJobId>,
|
||||
condvar: Condvar,
|
||||
span: Span,
|
||||
cycle: Lock<Option<CycleError<D>>>,
|
||||
cycle: Mutex<Option<CycleError<D>>>,
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
@ -205,20 +204,20 @@ impl<D: DepKind> QueryWaiter<D> {
|
||||
#[cfg(parallel_compiler)]
|
||||
struct QueryLatchInfo<D: DepKind> {
|
||||
complete: bool,
|
||||
waiters: Vec<Lrc<QueryWaiter<D>>>,
|
||||
waiters: Vec<Arc<QueryWaiter<D>>>,
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
#[derive(Clone)]
|
||||
pub(super) struct QueryLatch<D: DepKind> {
|
||||
info: Lrc<Mutex<QueryLatchInfo<D>>>,
|
||||
info: Arc<Mutex<QueryLatchInfo<D>>>,
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
impl<D: DepKind> QueryLatch<D> {
|
||||
fn new() -> Self {
|
||||
QueryLatch {
|
||||
info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
|
||||
info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,11 +228,11 @@ impl<D: DepKind> QueryLatch<D> {
|
||||
span: Span,
|
||||
) -> Result<(), CycleError<D>> {
|
||||
let waiter =
|
||||
Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() });
|
||||
Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
|
||||
self.wait_on_inner(&waiter);
|
||||
// FIXME: Get rid of this lock. We have ownership of the QueryWaiter
|
||||
// although another thread may still have a Lrc reference so we cannot
|
||||
// use Lrc::get_mut
|
||||
// although another thread may still have a Arc reference so we cannot
|
||||
// use Arc::get_mut
|
||||
let mut cycle = waiter.cycle.lock();
|
||||
match cycle.take() {
|
||||
None => Ok(()),
|
||||
@ -242,7 +241,7 @@ impl<D: DepKind> QueryLatch<D> {
|
||||
}
|
||||
|
||||
/// Awaits the caller on this latch by blocking the current thread.
|
||||
fn wait_on_inner(&self, waiter: &Lrc<QueryWaiter<D>>) {
|
||||
fn wait_on_inner(&self, waiter: &Arc<QueryWaiter<D>>) {
|
||||
let mut info = self.info.lock();
|
||||
if !info.complete {
|
||||
// We push the waiter on to the `waiters` list. It can be accessed inside
|
||||
@ -276,7 +275,7 @@ impl<D: DepKind> QueryLatch<D> {
|
||||
|
||||
/// Removes a single waiter from the list of waiters.
|
||||
/// This is used to break query cycles.
|
||||
fn extract_waiter(&self, waiter: usize) -> Lrc<QueryWaiter<D>> {
|
||||
fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<D>> {
|
||||
let mut info = self.info.lock();
|
||||
debug_assert!(!info.complete);
|
||||
// Remove the waiter from the list of waiters
|
||||
@ -428,7 +427,7 @@ where
|
||||
fn remove_cycle<D: DepKind>(
|
||||
query_map: &QueryMap<D>,
|
||||
jobs: &mut Vec<QueryJobId>,
|
||||
wakelist: &mut Vec<Lrc<QueryWaiter<D>>>,
|
||||
wakelist: &mut Vec<Arc<QueryWaiter<D>>>,
|
||||
) -> bool {
|
||||
let mut visited = FxHashSet::default();
|
||||
let mut stack = Vec::new();
|
||||
|
@ -4269,7 +4269,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
ExprKind::ConstBlock(ref ct) => {
|
||||
self.resolve_anon_const(ct, AnonConstKind::InlineConst);
|
||||
}
|
||||
ExprKind::Index(ref elem, ref idx) => {
|
||||
ExprKind::Index(ref elem, ref idx, _) => {
|
||||
self.resolve_expr(elem, Some(expr));
|
||||
self.visit_expr(idx);
|
||||
}
|
||||
|
@ -1350,18 +1350,15 @@ fn default_emitter(
|
||||
);
|
||||
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
|
||||
} else {
|
||||
let emitter = EmitterWriter::stderr(
|
||||
color_config,
|
||||
Some(source_map),
|
||||
bundle,
|
||||
fallback_bundle,
|
||||
short,
|
||||
sopts.unstable_opts.teach,
|
||||
sopts.diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
);
|
||||
let emitter = EmitterWriter::stderr(color_config, fallback_bundle)
|
||||
.fluent_bundle(bundle)
|
||||
.sm(Some(source_map))
|
||||
.short_message(short)
|
||||
.teach(sopts.unstable_opts.teach)
|
||||
.diagnostic_width(sopts.diagnostic_width)
|
||||
.macro_backtrace(macro_backtrace)
|
||||
.track_diagnostics(track_diagnostics)
|
||||
.terminal_url(terminal_url);
|
||||
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
|
||||
}
|
||||
}
|
||||
@ -1794,18 +1791,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<dyn Emitter + sync::Send + 'static
|
||||
let emitter: Box<dyn Emitter + sync::Send> = match output {
|
||||
config::ErrorOutputType::HumanReadable(kind) => {
|
||||
let (short, color_config) = kind.unzip();
|
||||
Box::new(EmitterWriter::stderr(
|
||||
color_config,
|
||||
None,
|
||||
None,
|
||||
fallback_bundle,
|
||||
short,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
))
|
||||
Box::new(EmitterWriter::stderr(color_config, fallback_bundle).short_message(short))
|
||||
}
|
||||
config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic(
|
||||
pretty,
|
||||
|
@ -248,9 +248,9 @@ fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option<Sym
|
||||
})
|
||||
}
|
||||
|
||||
fn sort_by_words(name: &str) -> String {
|
||||
fn sort_by_words(name: &str) -> Vec<&str> {
|
||||
let mut split_words: Vec<&str> = name.split('_').collect();
|
||||
// We are sorting primitive &strs and can use unstable sort here.
|
||||
split_words.sort_unstable();
|
||||
split_words.join("_")
|
||||
split_words
|
||||
}
|
||||
|
@ -400,6 +400,7 @@ symbols! {
|
||||
async_await,
|
||||
async_closure,
|
||||
async_fn_in_trait,
|
||||
async_fn_track_caller,
|
||||
atomic,
|
||||
atomic_mod,
|
||||
atomics,
|
||||
|
@ -11,7 +11,7 @@ pub fn target() -> Target {
|
||||
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
||||
linker: Some("rust-lld".into()),
|
||||
cpu: "generic-rv32".into(),
|
||||
max_atomic_width: Some(0),
|
||||
max_atomic_width: Some(32),
|
||||
atomic_cas: false,
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
relocation_model: RelocModel::Static,
|
||||
|
@ -11,7 +11,7 @@ pub fn target() -> Target {
|
||||
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
||||
linker: Some("rust-lld".into()),
|
||||
cpu: "generic-rv32".into(),
|
||||
max_atomic_width: Some(0),
|
||||
max_atomic_width: Some(32),
|
||||
atomic_cas: false,
|
||||
features: "+m".into(),
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
|
@ -11,7 +11,7 @@ pub fn target() -> Target {
|
||||
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
||||
linker: Some("rust-lld".into()),
|
||||
cpu: "generic-rv32".into(),
|
||||
max_atomic_width: Some(0),
|
||||
max_atomic_width: Some(32),
|
||||
atomic_cas: false,
|
||||
features: "+m,+c".into(),
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
|
@ -162,7 +162,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
self.add_goal(Goal::new(
|
||||
self.tcx(),
|
||||
param_env,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }),
|
||||
ty::ProjectionPredicate { projection_ty: alias, term: other },
|
||||
));
|
||||
|
||||
Ok(())
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! Code shared by trait and projection goals for candidate assembly.
|
||||
|
||||
use super::search_graph::OverflowHandler;
|
||||
use super::{EvalCtxt, SolverMode};
|
||||
use crate::traits::coherence;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -315,7 +314,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
return ambig;
|
||||
}
|
||||
|
||||
let mut candidates = self.assemble_candidates_via_self_ty(goal);
|
||||
let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
|
||||
|
||||
self.assemble_blanket_impl_candidates(goal, &mut candidates);
|
||||
|
||||
@ -351,6 +350,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>(
|
||||
&mut self,
|
||||
goal: Goal<'tcx, G>,
|
||||
num_steps: usize,
|
||||
) -> Vec<Candidate<'tcx>> {
|
||||
debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
|
||||
if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
|
||||
@ -369,7 +369,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
|
||||
self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
|
||||
|
||||
self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
|
||||
self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps);
|
||||
|
||||
candidates
|
||||
}
|
||||
@ -393,49 +393,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
&mut self,
|
||||
goal: Goal<'tcx, G>,
|
||||
candidates: &mut Vec<Candidate<'tcx>>,
|
||||
num_steps: usize,
|
||||
) {
|
||||
let tcx = self.tcx();
|
||||
let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return };
|
||||
|
||||
let normalized_self_candidates: Result<_, NoSolution> =
|
||||
self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| {
|
||||
ecx.with_incremented_depth(
|
||||
|ecx| {
|
||||
let result = ecx.evaluate_added_goals_and_make_canonical_response(
|
||||
Certainty::OVERFLOW,
|
||||
)?;
|
||||
Ok(vec![Candidate {
|
||||
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
result,
|
||||
}])
|
||||
},
|
||||
|ecx| {
|
||||
let normalized_ty = ecx.next_ty_infer();
|
||||
let normalizes_to_goal = goal.with(
|
||||
tcx,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
projection_ty,
|
||||
term: normalized_ty.into(),
|
||||
}),
|
||||
);
|
||||
ecx.add_goal(normalizes_to_goal);
|
||||
let _ = ecx.try_evaluate_added_goals().inspect_err(|_| {
|
||||
debug!("self type normalization failed");
|
||||
})?;
|
||||
let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
|
||||
debug!(?normalized_ty, "self type normalized");
|
||||
// NOTE: Alternatively we could call `evaluate_goal` here and only
|
||||
// have a `Normalized` candidate. This doesn't work as long as we
|
||||
// use `CandidateSource` in winnowing.
|
||||
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
|
||||
Ok(ecx.assemble_candidates_via_self_ty(goal))
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
if let Ok(normalized_self_candidates) = normalized_self_candidates {
|
||||
candidates.extend(normalized_self_candidates);
|
||||
}
|
||||
candidates.extend(self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| {
|
||||
if num_steps < ecx.local_overflow_limit() {
|
||||
let normalized_ty = ecx.next_ty_infer();
|
||||
let normalizes_to_goal = goal.with(
|
||||
tcx,
|
||||
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
|
||||
);
|
||||
ecx.add_goal(normalizes_to_goal);
|
||||
if let Err(NoSolution) = ecx.try_evaluate_added_goals() {
|
||||
debug!("self type normalization failed");
|
||||
return vec![];
|
||||
}
|
||||
let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
|
||||
debug!(?normalized_ty, "self type normalized");
|
||||
// NOTE: Alternatively we could call `evaluate_goal` here and only
|
||||
// have a `Normalized` candidate. This doesn't work as long as we
|
||||
// use `CandidateSource` in winnowing.
|
||||
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
|
||||
ecx.assemble_candidates_via_self_ty(goal, num_steps + 1)
|
||||
} else {
|
||||
match ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) {
|
||||
Ok(result) => vec![Candidate {
|
||||
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
result,
|
||||
}],
|
||||
Err(NoSolution) => vec![],
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
@ -533,7 +524,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (),
|
||||
|
||||
// FIXME: These should ideally not exist as a self type. It would be nice for
|
||||
// the builtin auto trait impls of generators should instead directly recurse
|
||||
// the builtin auto trait impls of generators to instead directly recurse
|
||||
// into the witness.
|
||||
ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (),
|
||||
|
||||
|
@ -207,23 +207,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
|
||||
t
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
match self.canonicalize_mode {
|
||||
CanonicalizeMode::Input => {
|
||||
// Don't resolve infer vars in input, since it affects
|
||||
// caching and may cause trait selection bugs which rely
|
||||
// on regions to be equal.
|
||||
}
|
||||
CanonicalizeMode::Response { .. } => {
|
||||
if let ty::ReVar(vid) = *r {
|
||||
r = self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.unwrap_region_constraints()
|
||||
.opportunistic_resolve_var(self.infcx.tcx, vid);
|
||||
}
|
||||
}
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
if let ty::ReVar(vid) = *r {
|
||||
let resolved_region = self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.unwrap_region_constraints()
|
||||
.opportunistic_resolve_var(self.infcx.tcx, vid);
|
||||
assert_eq!(
|
||||
r, resolved_region,
|
||||
"region var should have been resolved, {r} -> {resolved_region}"
|
||||
);
|
||||
}
|
||||
|
||||
let kind = match *r {
|
||||
@ -278,38 +273,22 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
|
||||
ty::Region::new_late_bound(self.interner(), self.binder_index, br)
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let kind = match *t.kind() {
|
||||
ty::Infer(ty::TyVar(mut vid)) => {
|
||||
// We need to canonicalize the *root* of our ty var.
|
||||
// This is so that our canonical response correctly reflects
|
||||
// any equated inference vars correctly!
|
||||
let root_vid = self.infcx.root_var(vid);
|
||||
if root_vid != vid {
|
||||
t = Ty::new_var(self.infcx.tcx, root_vid);
|
||||
vid = root_vid;
|
||||
}
|
||||
|
||||
match self.infcx.probe_ty_var(vid) {
|
||||
Ok(t) => return self.fold_ty(t),
|
||||
Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
|
||||
}
|
||||
ty::Infer(ty::TyVar(vid)) => {
|
||||
assert_eq!(self.infcx.root_var(vid), vid, "ty vid should have been resolved");
|
||||
let Err(ui) = self.infcx.probe_ty_var(vid) else {
|
||||
bug!("ty var should have been resolved: {t}");
|
||||
};
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
|
||||
}
|
||||
ty::Infer(ty::IntVar(vid)) => {
|
||||
let nt = self.infcx.opportunistic_resolve_int_var(vid);
|
||||
if nt != t {
|
||||
return self.fold_ty(nt);
|
||||
} else {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
|
||||
}
|
||||
assert_eq!(self.infcx.opportunistic_resolve_int_var(vid), t);
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
|
||||
}
|
||||
ty::Infer(ty::FloatVar(vid)) => {
|
||||
let nt = self.infcx.opportunistic_resolve_float_var(vid);
|
||||
if nt != t {
|
||||
return self.fold_ty(nt);
|
||||
} else {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
|
||||
}
|
||||
assert_eq!(self.infcx.opportunistic_resolve_float_var(vid), t);
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
|
||||
}
|
||||
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
bug!("fresh var during canonicalization: {t:?}")
|
||||
@ -372,22 +351,19 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
|
||||
Ty::new_bound(self.infcx.tcx, self.binder_index, bt)
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
let kind = match c.kind() {
|
||||
ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => {
|
||||
// We need to canonicalize the *root* of our const var.
|
||||
// This is so that our canonical response correctly reflects
|
||||
// any equated inference vars correctly!
|
||||
let root_vid = self.infcx.root_const_var(vid);
|
||||
if root_vid != vid {
|
||||
c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty());
|
||||
vid = root_vid;
|
||||
}
|
||||
|
||||
match self.infcx.probe_const_var(vid) {
|
||||
Ok(c) => return self.fold_const(c),
|
||||
Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
|
||||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
|
||||
assert_eq!(
|
||||
self.infcx.root_const_var(vid),
|
||||
vid,
|
||||
"const var should have been resolved"
|
||||
);
|
||||
let Err(ui) = self.infcx.probe_const_var(vid) else {
|
||||
bug!("const var should have been resolved");
|
||||
};
|
||||
// FIXME: we should fold this ty eventually
|
||||
CanonicalVarKind::Const(ui, c.ty())
|
||||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => {
|
||||
bug!("fresh var during canonicalization: {c:?}")
|
||||
|
@ -1,20 +1,21 @@
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::infer::at::ToTrace;
|
||||
use rustc_infer::infer::canonical::CanonicalVarValues;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::{
|
||||
DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, RegionVariableOrigin,
|
||||
TyCtxtInferExt,
|
||||
DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt,
|
||||
};
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::infer::canonical::CanonicalVarInfos;
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_middle::traits::solve::inspect;
|
||||
use rustc_middle::traits::solve::{
|
||||
CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques,
|
||||
PredefinedOpaquesData, QueryResult,
|
||||
};
|
||||
use rustc_middle::traits::DefiningAnchor;
|
||||
use rustc_middle::traits::{specialization_graph, DefiningAnchor};
|
||||
use rustc_middle::ty::{
|
||||
self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt, TypeVisitor,
|
||||
@ -24,11 +25,10 @@ use rustc_span::DUMMY_SP;
|
||||
use std::io::Write;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::traits::specialization_graph;
|
||||
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
||||
|
||||
use super::inspect::ProofTreeBuilder;
|
||||
use super::search_graph::{self, OverflowHandler};
|
||||
use super::search_graph;
|
||||
use super::SolverMode;
|
||||
use super::{search_graph::SearchGraph, Goal};
|
||||
pub use select::InferCtxtSelectExt;
|
||||
@ -55,6 +55,9 @@ pub struct EvalCtxt<'a, 'tcx> {
|
||||
/// the job already.
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
|
||||
/// The variable info for the `var_values`, only used to make an ambiguous response
|
||||
/// with no constraints.
|
||||
variables: CanonicalVarInfos<'tcx>,
|
||||
pub(super) var_values: CanonicalVarValues<'tcx>,
|
||||
|
||||
predefined_opaques_in_body: PredefinedOpaques<'tcx>,
|
||||
@ -171,6 +174,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
self.search_graph.solver_mode()
|
||||
}
|
||||
|
||||
pub(super) fn local_overflow_limit(&self) -> usize {
|
||||
self.search_graph.local_overflow_limit()
|
||||
}
|
||||
|
||||
/// Creates a root evaluation context and search graph. This should only be
|
||||
/// used from outside of any evaluation, and other methods should be preferred
|
||||
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
|
||||
@ -184,18 +191,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
|
||||
let mut ecx = EvalCtxt {
|
||||
search_graph: &mut search_graph,
|
||||
infcx: infcx,
|
||||
infcx,
|
||||
nested_goals: NestedGoals::new(),
|
||||
inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
|
||||
|
||||
// Only relevant when canonicalizing the response,
|
||||
// which we don't do within this evaluation context.
|
||||
predefined_opaques_in_body: infcx
|
||||
.tcx
|
||||
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
|
||||
// Only relevant when canonicalizing the response.
|
||||
max_input_universe: ty::UniverseIndex::ROOT,
|
||||
variables: ty::List::empty(),
|
||||
var_values: CanonicalVarValues::dummy(),
|
||||
nested_goals: NestedGoals::new(),
|
||||
tainted: Ok(()),
|
||||
inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
|
||||
};
|
||||
let result = f(&mut ecx);
|
||||
|
||||
@ -245,6 +253,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
|
||||
let mut ecx = EvalCtxt {
|
||||
infcx,
|
||||
variables: canonical_input.variables,
|
||||
var_values,
|
||||
predefined_opaques_in_body: input.predefined_opaques_in_body,
|
||||
max_input_universe: canonical_input.max_universe,
|
||||
@ -300,24 +309,26 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
// Deal with overflow, caching, and coinduction.
|
||||
//
|
||||
// The actual solver logic happens in `ecx.compute_goal`.
|
||||
search_graph.with_new_goal(
|
||||
tcx,
|
||||
canonical_input,
|
||||
goal_evaluation,
|
||||
|search_graph, goal_evaluation| {
|
||||
EvalCtxt::enter_canonical(
|
||||
tcx,
|
||||
search_graph,
|
||||
canonical_input,
|
||||
goal_evaluation,
|
||||
|ecx, goal| {
|
||||
let result = ecx.compute_goal(goal);
|
||||
ecx.inspect.query_result(result);
|
||||
result
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
ensure_sufficient_stack(|| {
|
||||
search_graph.with_new_goal(
|
||||
tcx,
|
||||
canonical_input,
|
||||
goal_evaluation,
|
||||
|search_graph, goal_evaluation| {
|
||||
EvalCtxt::enter_canonical(
|
||||
tcx,
|
||||
search_graph,
|
||||
canonical_input,
|
||||
goal_evaluation,
|
||||
|ecx, goal| {
|
||||
let result = ecx.compute_goal(goal);
|
||||
ecx.inspect.query_result(result);
|
||||
result
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Recursively evaluates `goal`, returning whether any inference vars have
|
||||
@ -329,6 +340,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
|
||||
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
||||
let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
|
||||
let encountered_overflow = self.search_graph.encountered_overflow();
|
||||
let canonical_response = EvalCtxt::evaluate_canonical_goal(
|
||||
self.tcx(),
|
||||
self.search_graph,
|
||||
@ -377,6 +389,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
&& !self.search_graph.in_cycle()
|
||||
{
|
||||
debug!("rerunning goal to check result is stable");
|
||||
self.search_graph.reset_encountered_overflow(encountered_overflow);
|
||||
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
||||
let new_canonical_response = EvalCtxt::evaluate_canonical_goal(
|
||||
self.tcx(),
|
||||
@ -471,101 +484,22 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
let inspect = self.inspect.new_evaluate_added_goals();
|
||||
let inspect = core::mem::replace(&mut self.inspect, inspect);
|
||||
|
||||
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
|
||||
let mut new_goals = NestedGoals::new();
|
||||
|
||||
let response = self.repeat_while_none(
|
||||
|_| Ok(Certainty::OVERFLOW),
|
||||
|this| {
|
||||
this.inspect.evaluate_added_goals_loop_start();
|
||||
|
||||
let mut has_changed = Err(Certainty::Yes);
|
||||
|
||||
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
|
||||
// Replace the goal with an unconstrained infer var, so the
|
||||
// RHS does not affect projection candidate assembly.
|
||||
let unconstrained_rhs = this.next_term_infer_of_kind(goal.predicate.term);
|
||||
let unconstrained_goal = goal.with(
|
||||
this.tcx(),
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
projection_ty: goal.predicate.projection_ty,
|
||||
term: unconstrained_rhs,
|
||||
}),
|
||||
);
|
||||
|
||||
let (_, certainty, instantiate_goals) =
|
||||
match this.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal) {
|
||||
Ok(r) => r,
|
||||
Err(NoSolution) => return Some(Err(NoSolution)),
|
||||
};
|
||||
new_goals.goals.extend(instantiate_goals);
|
||||
|
||||
// Finally, equate the goal's RHS with the unconstrained var.
|
||||
// We put the nested goals from this into goals instead of
|
||||
// next_goals to avoid needing to process the loop one extra
|
||||
// time if this goal returns something -- I don't think this
|
||||
// matters in practice, though.
|
||||
match this.eq_and_get_goals(
|
||||
goal.param_env,
|
||||
goal.predicate.term,
|
||||
unconstrained_rhs,
|
||||
) {
|
||||
Ok(eq_goals) => {
|
||||
goals.goals.extend(eq_goals);
|
||||
}
|
||||
Err(NoSolution) => return Some(Err(NoSolution)),
|
||||
};
|
||||
|
||||
// We only look at the `projection_ty` part here rather than
|
||||
// looking at the "has changed" return from evaluate_goal,
|
||||
// because we expect the `unconstrained_rhs` part of the predicate
|
||||
// to have changed -- that means we actually normalized successfully!
|
||||
if goal.predicate.projection_ty
|
||||
!= this.resolve_vars_if_possible(goal.predicate.projection_ty)
|
||||
{
|
||||
has_changed = Ok(())
|
||||
}
|
||||
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
// We need to resolve vars here so that we correctly
|
||||
// deal with `has_changed` in the next iteration.
|
||||
new_goals.normalizes_to_hack_goal =
|
||||
Some(this.resolve_vars_if_possible(goal));
|
||||
has_changed = has_changed.map_err(|c| c.unify_with(certainty));
|
||||
}
|
||||
}
|
||||
let mut response = Ok(Certainty::OVERFLOW);
|
||||
for _ in 0..self.local_overflow_limit() {
|
||||
// FIXME: This match is a bit ugly, it might be nice to change the inspect
|
||||
// stuff to use a closure instead. which should hopefully simplify this a bit.
|
||||
match self.evaluate_added_goals_step() {
|
||||
Ok(Some(cert)) => {
|
||||
response = Ok(cert);
|
||||
break;
|
||||
}
|
||||
|
||||
for goal in goals.goals.drain(..) {
|
||||
let (changed, certainty, instantiate_goals) =
|
||||
match this.evaluate_goal(IsNormalizesToHack::No, goal) {
|
||||
Ok(result) => result,
|
||||
Err(NoSolution) => return Some(Err(NoSolution)),
|
||||
};
|
||||
new_goals.goals.extend(instantiate_goals);
|
||||
|
||||
if changed {
|
||||
has_changed = Ok(());
|
||||
}
|
||||
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
new_goals.goals.push(goal);
|
||||
has_changed = has_changed.map_err(|c| c.unify_with(certainty));
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(NoSolution) => {
|
||||
response = Err(NoSolution);
|
||||
break;
|
||||
}
|
||||
|
||||
core::mem::swap(&mut new_goals, &mut goals);
|
||||
match has_changed {
|
||||
Ok(()) => None,
|
||||
Err(certainty) => Some(Ok(certainty)),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.inspect.eval_added_goals_result(response);
|
||||
|
||||
@ -576,9 +510,84 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
let goal_evaluations = std::mem::replace(&mut self.inspect, inspect);
|
||||
self.inspect.added_goals_evaluation(goal_evaluations);
|
||||
|
||||
self.nested_goals = goals;
|
||||
response
|
||||
}
|
||||
|
||||
/// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning.
|
||||
///
|
||||
/// Goals for the next step get directly added the the nested goals of the `EvalCtxt`.
|
||||
fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
|
||||
let tcx = self.tcx();
|
||||
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
|
||||
|
||||
self.inspect.evaluate_added_goals_loop_start();
|
||||
// If this loop did not result in any progress, what's our final certainty.
|
||||
let mut unchanged_certainty = Some(Certainty::Yes);
|
||||
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
|
||||
// Replace the goal with an unconstrained infer var, so the
|
||||
// RHS does not affect projection candidate assembly.
|
||||
let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
|
||||
let unconstrained_goal = goal.with(
|
||||
tcx,
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: goal.predicate.projection_ty,
|
||||
term: unconstrained_rhs,
|
||||
},
|
||||
);
|
||||
|
||||
let (_, certainty, instantiate_goals) =
|
||||
self.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal)?;
|
||||
self.add_goals(instantiate_goals);
|
||||
|
||||
// Finally, equate the goal's RHS with the unconstrained var.
|
||||
// We put the nested goals from this into goals instead of
|
||||
// next_goals to avoid needing to process the loop one extra
|
||||
// time if this goal returns something -- I don't think this
|
||||
// matters in practice, though.
|
||||
let eq_goals =
|
||||
self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?;
|
||||
goals.goals.extend(eq_goals);
|
||||
|
||||
// We only look at the `projection_ty` part here rather than
|
||||
// looking at the "has changed" return from evaluate_goal,
|
||||
// because we expect the `unconstrained_rhs` part of the predicate
|
||||
// to have changed -- that means we actually normalized successfully!
|
||||
if goal.predicate.projection_ty
|
||||
!= self.resolve_vars_if_possible(goal.predicate.projection_ty)
|
||||
{
|
||||
unchanged_certainty = None;
|
||||
}
|
||||
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
// We need to resolve vars here so that we correctly
|
||||
// deal with `has_changed` in the next iteration.
|
||||
self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal));
|
||||
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for goal in goals.goals.drain(..) {
|
||||
let (has_changed, certainty, instantiate_goals) =
|
||||
self.evaluate_goal(IsNormalizesToHack::No, goal)?;
|
||||
self.add_goals(instantiate_goals);
|
||||
if has_changed {
|
||||
unchanged_certainty = None;
|
||||
}
|
||||
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
self.add_goal(goal);
|
||||
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(unchanged_certainty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
@ -593,10 +602,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn next_region_infer(&self) -> ty::Region<'tcx> {
|
||||
self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP))
|
||||
}
|
||||
|
||||
pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> {
|
||||
self.infcx.next_const_var(
|
||||
ty,
|
||||
|
@ -10,17 +10,21 @@
|
||||
//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
|
||||
use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
|
||||
use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
|
||||
use crate::solve::{CanonicalResponse, QueryResult, Response};
|
||||
use crate::solve::{response_no_constraints_raw, CanonicalResponse, QueryResult, Response};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
|
||||
use rustc_infer::infer::canonical::CanonicalVarValues;
|
||||
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{
|
||||
ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
|
||||
ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
TypeVisitableExt,
|
||||
};
|
||||
use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
@ -32,6 +36,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
&self,
|
||||
goal: Goal<'tcx, T>,
|
||||
) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
|
||||
let opaque_types = self.infcx.clone_opaque_types_for_query_response();
|
||||
let (goal, opaque_types) =
|
||||
(goal, opaque_types).fold_with(&mut EagerResolver { infcx: self.infcx });
|
||||
|
||||
let mut orig_values = Default::default();
|
||||
let canonical_goal = Canonicalizer::canonicalize(
|
||||
self.infcx,
|
||||
@ -40,11 +48,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
QueryInput {
|
||||
goal,
|
||||
anchor: self.infcx.defining_use_anchor,
|
||||
predefined_opaques_in_body: self.tcx().mk_predefined_opaques_in_body(
|
||||
PredefinedOpaquesData {
|
||||
opaque_types: self.infcx.clone_opaque_types_for_query_response(),
|
||||
},
|
||||
),
|
||||
predefined_opaques_in_body: self
|
||||
.tcx()
|
||||
.mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }),
|
||||
},
|
||||
);
|
||||
(orig_values, canonical_goal)
|
||||
@ -70,34 +76,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
);
|
||||
|
||||
let certainty = certainty.unify_with(goals_certainty);
|
||||
if let Certainty::OVERFLOW = certainty {
|
||||
// If we have overflow, it's probable that we're substituting a type
|
||||
// into itself infinitely and any partial substitutions in the query
|
||||
// response are probably not useful anyways, so just return an empty
|
||||
// query response.
|
||||
//
|
||||
// This may prevent us from potentially useful inference, e.g.
|
||||
// 2 candidates, one ambiguous and one overflow, which both
|
||||
// have the same inference constraints.
|
||||
//
|
||||
// Changing this to retain some constraints in the future
|
||||
// won't be a breaking change, so this is good enough for now.
|
||||
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow));
|
||||
}
|
||||
|
||||
let response = match certainty {
|
||||
Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => {
|
||||
let external_constraints = self.compute_external_query_constraints()?;
|
||||
Response { var_values: self.var_values, external_constraints, certainty }
|
||||
}
|
||||
Certainty::Maybe(MaybeCause::Overflow) => {
|
||||
// If we have overflow, it's probable that we're substituting a type
|
||||
// into itself infinitely and any partial substitutions in the query
|
||||
// response are probably not useful anyways, so just return an empty
|
||||
// query response.
|
||||
//
|
||||
// This may prevent us from potentially useful inference, e.g.
|
||||
// 2 candidates, one ambiguous and one overflow, which both
|
||||
// have the same inference constraints.
|
||||
//
|
||||
// Changing this to retain some constraints in the future
|
||||
// won't be a breaking change, so this is good enough for now.
|
||||
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow));
|
||||
}
|
||||
};
|
||||
let var_values = self.var_values;
|
||||
let external_constraints = self.compute_external_query_constraints()?;
|
||||
|
||||
let (var_values, mut external_constraints) =
|
||||
(var_values, external_constraints).fold_with(&mut EagerResolver { infcx: self.infcx });
|
||||
// Remove any trivial region constraints once we've resolved regions
|
||||
external_constraints
|
||||
.region_constraints
|
||||
.outlives
|
||||
.retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1));
|
||||
|
||||
let canonical = Canonicalizer::canonicalize(
|
||||
self.infcx,
|
||||
CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
|
||||
&mut Default::default(),
|
||||
response,
|
||||
Response {
|
||||
var_values,
|
||||
certainty,
|
||||
external_constraints: self.tcx().mk_external_constraints(external_constraints),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(canonical)
|
||||
}
|
||||
|
||||
@ -109,29 +124,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
&self,
|
||||
maybe_cause: MaybeCause,
|
||||
) -> CanonicalResponse<'tcx> {
|
||||
let unconstrained_response = Response {
|
||||
var_values: CanonicalVarValues {
|
||||
var_values: self.tcx().mk_args_from_iter(self.var_values.var_values.iter().map(
|
||||
|arg| -> ty::GenericArg<'tcx> {
|
||||
match arg.unpack() {
|
||||
GenericArgKind::Lifetime(_) => self.next_region_infer().into(),
|
||||
GenericArgKind::Type(_) => self.next_ty_infer().into(),
|
||||
GenericArgKind::Const(ct) => self.next_const_infer(ct.ty()).into(),
|
||||
}
|
||||
},
|
||||
)),
|
||||
},
|
||||
external_constraints: self
|
||||
.tcx()
|
||||
.mk_external_constraints(ExternalConstraintsData::default()),
|
||||
certainty: Certainty::Maybe(maybe_cause),
|
||||
};
|
||||
|
||||
Canonicalizer::canonicalize(
|
||||
self.infcx,
|
||||
CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
|
||||
&mut Default::default(),
|
||||
unconstrained_response,
|
||||
response_no_constraints_raw(
|
||||
self.tcx(),
|
||||
self.max_input_universe,
|
||||
self.variables,
|
||||
Certainty::Maybe(maybe_cause),
|
||||
)
|
||||
}
|
||||
|
||||
@ -143,7 +140,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
/// further constrained by inference, that will be passed back in the var
|
||||
/// values.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
|
||||
fn compute_external_query_constraints(
|
||||
&self,
|
||||
) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
|
||||
// We only check for leaks from universes which were entered inside
|
||||
// of the query.
|
||||
self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
|
||||
@ -173,9 +172,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
|
||||
});
|
||||
|
||||
Ok(self
|
||||
.tcx()
|
||||
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
|
||||
Ok(ExternalConstraintsData { region_constraints, opaque_types })
|
||||
}
|
||||
|
||||
/// After calling a canonical query, we apply the constraints returned
|
||||
@ -333,3 +330,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves ty, region, and const vars to their inferred values or their root vars.
|
||||
struct EagerResolver<'a, 'tcx> {
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> {
|
||||
fn interner(&self) -> TyCtxt<'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match *t.kind() {
|
||||
ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) {
|
||||
Ok(t) => t.fold_with(self),
|
||||
Err(_) => Ty::new_var(self.infcx.tcx, self.infcx.root_var(vid)),
|
||||
},
|
||||
ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid),
|
||||
ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid),
|
||||
_ => {
|
||||
if t.has_infer() {
|
||||
t.super_fold_with(self)
|
||||
} else {
|
||||
t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
match *r {
|
||||
ty::ReVar(vid) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.unwrap_region_constraints()
|
||||
.opportunistic_resolve_var(self.infcx.tcx, vid),
|
||||
_ => r,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
match c.kind() {
|
||||
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
|
||||
// FIXME: we need to fold the ty too, I think.
|
||||
match self.infcx.probe_const_var(vid) {
|
||||
Ok(c) => c.fold_with(self),
|
||||
Err(_) => {
|
||||
ty::Const::new_var(self.infcx.tcx, self.infcx.root_const_var(vid), c.ty())
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if c.has_infer() {
|
||||
c.super_fold_with(self)
|
||||
} else {
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ where
|
||||
|
||||
let mut nested_ecx = EvalCtxt {
|
||||
infcx: outer_ecx.infcx,
|
||||
variables: outer_ecx.variables,
|
||||
var_values: outer_ecx.var_values,
|
||||
predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
|
||||
max_input_universe: outer_ecx.max_input_universe,
|
||||
|
@ -14,7 +14,6 @@ use rustc_span::DUMMY_SP;
|
||||
use crate::solve::assembly::{Candidate, CandidateSource};
|
||||
use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
|
||||
use crate::solve::inspect::ProofTreeBuilder;
|
||||
use crate::solve::search_graph::OverflowHandler;
|
||||
use crate::traits::StructurallyNormalizeExt;
|
||||
use crate::traits::TraitEngineExt;
|
||||
|
||||
@ -143,7 +142,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
// the cycle anyways one step later.
|
||||
EvalCtxt::enter_canonical(
|
||||
self.tcx(),
|
||||
self.search_graph(),
|
||||
self.search_graph,
|
||||
canonical_input,
|
||||
// FIXME: This is wrong, idk if we even want to track stuff here.
|
||||
&mut ProofTreeBuilder::new_noop(),
|
||||
@ -269,7 +268,7 @@ fn rematch_unsize<'tcx>(
|
||||
infcx.tcx,
|
||||
ObligationCause::dummy(),
|
||||
goal.param_env,
|
||||
ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
|
||||
ty::OutlivesPredicate(a_ty, region),
|
||||
));
|
||||
|
||||
Ok(Some(ImplSource::Builtin(source, nested)))
|
||||
|
@ -17,10 +17,11 @@
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::infer::canonical::CanonicalVarInfos;
|
||||
use rustc_middle::traits::solve::{
|
||||
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
|
||||
use rustc_middle::ty::{
|
||||
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
|
||||
};
|
||||
@ -284,20 +285,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn response_no_constraints<'tcx>(
|
||||
fn response_no_constraints_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal: Canonical<'tcx, impl Sized>,
|
||||
max_universe: UniverseIndex,
|
||||
variables: CanonicalVarInfos<'tcx>,
|
||||
certainty: Certainty,
|
||||
) -> QueryResult<'tcx> {
|
||||
Ok(Canonical {
|
||||
max_universe: goal.max_universe,
|
||||
variables: goal.variables,
|
||||
) -> CanonicalResponse<'tcx> {
|
||||
Canonical {
|
||||
max_universe,
|
||||
variables,
|
||||
value: Response {
|
||||
var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
|
||||
var_values: CanonicalVarValues::make_identity(tcx, variables),
|
||||
// FIXME: maybe we should store the "no response" version in tcx, like
|
||||
// we do for tcx.types and stuff.
|
||||
external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
|
||||
certainty,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -75,10 +75,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
|
||||
tcx,
|
||||
self.at.cause.clone(),
|
||||
self.at.param_env,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
projection_ty: alias,
|
||||
term: new_infer_ty.into(),
|
||||
}),
|
||||
ty::ProjectionPredicate { projection_ty: alias, term: new_infer_ty.into() },
|
||||
);
|
||||
|
||||
// Do not emit an error if normalization is known to fail but instead
|
||||
@ -131,10 +128,10 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
|
||||
tcx,
|
||||
self.at.cause.clone(),
|
||||
self.at.param_env,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: tcx.mk_alias_ty(uv.def, uv.args),
|
||||
term: new_infer_ct.into(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
let result = if infcx.predicate_may_hold(&obligation) {
|
||||
|
@ -393,10 +393,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
None => tcx.types.unit,
|
||||
Some(field_def) => {
|
||||
let self_ty = field_def.ty(tcx, args);
|
||||
ecx.add_goal(goal.with(
|
||||
tcx,
|
||||
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
|
||||
));
|
||||
ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)));
|
||||
return ecx
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
|
||||
}
|
||||
@ -406,10 +403,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
ty::Tuple(elements) => match elements.last() {
|
||||
None => tcx.types.unit,
|
||||
Some(&self_ty) => {
|
||||
ecx.add_goal(goal.with(
|
||||
tcx,
|
||||
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
|
||||
));
|
||||
ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)));
|
||||
return ecx
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
|
||||
}
|
||||
@ -450,10 +444,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
Self::consider_implied_clause(
|
||||
ecx,
|
||||
goal,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
|
||||
term,
|
||||
})
|
||||
}
|
||||
.to_predicate(tcx),
|
||||
// Technically, we need to check that the future type is Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
@ -490,12 +484,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
Self::consider_implied_clause(
|
||||
ecx,
|
||||
goal,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ecx
|
||||
.tcx()
|
||||
.mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
|
||||
term,
|
||||
})
|
||||
}
|
||||
.to_predicate(tcx),
|
||||
// Technically, we need to check that the future type is Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
|
@ -1,37 +1,51 @@
|
||||
mod cache;
|
||||
mod overflow;
|
||||
|
||||
pub(super) use overflow::OverflowHandler;
|
||||
use rustc_middle::traits::solve::inspect::CacheHit;
|
||||
|
||||
use self::cache::ProvisionalEntry;
|
||||
use cache::ProvisionalCache;
|
||||
use overflow::OverflowData;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::dep_graph::DepKind;
|
||||
use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use std::{collections::hash_map::Entry, mem};
|
||||
|
||||
use super::inspect::ProofTreeBuilder;
|
||||
use super::SolverMode;
|
||||
use cache::ProvisionalCache;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::dep_graph::DepKind;
|
||||
use rustc_middle::traits::solve::inspect::CacheHit;
|
||||
use rustc_middle::traits::solve::CacheData;
|
||||
use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Limit;
|
||||
use std::{collections::hash_map::Entry, mem};
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
pub struct StackDepth {}
|
||||
}
|
||||
|
||||
struct StackElem<'tcx> {
|
||||
#[derive(Debug)]
|
||||
struct StackEntry<'tcx> {
|
||||
input: CanonicalInput<'tcx>,
|
||||
available_depth: Limit,
|
||||
// The maximum depth reached by this stack entry, only up-to date
|
||||
// for the top of the stack and lazily updated for the rest.
|
||||
reached_depth: StackDepth,
|
||||
encountered_overflow: bool,
|
||||
has_been_used: bool,
|
||||
|
||||
/// We put only the root goal of a coinductive cycle into the global cache.
|
||||
///
|
||||
/// If we were to use that result when later trying to prove another cycle
|
||||
/// participant, we can end up with unstable query results.
|
||||
///
|
||||
/// See tests/ui/new-solver/coinduction/incompleteness-unstable-result.rs for
|
||||
/// an example of where this is needed.
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
}
|
||||
|
||||
pub(super) struct SearchGraph<'tcx> {
|
||||
mode: SolverMode,
|
||||
local_overflow_limit: usize,
|
||||
/// The stack of goals currently being computed.
|
||||
///
|
||||
/// An element is *deeper* in the stack if its index is *lower*.
|
||||
stack: IndexVec<StackDepth, StackElem<'tcx>>,
|
||||
overflow_data: OverflowData,
|
||||
stack: IndexVec<StackDepth, StackEntry<'tcx>>,
|
||||
provisional_cache: ProvisionalCache<'tcx>,
|
||||
}
|
||||
|
||||
@ -39,8 +53,8 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
|
||||
Self {
|
||||
mode,
|
||||
local_overflow_limit: tcx.recursion_limit().0.ilog2() as usize,
|
||||
stack: Default::default(),
|
||||
overflow_data: OverflowData::new(tcx),
|
||||
provisional_cache: ProvisionalCache::empty(),
|
||||
}
|
||||
}
|
||||
@ -49,15 +63,38 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// We do not use the global cache during coherence.
|
||||
pub(super) fn local_overflow_limit(&self) -> usize {
|
||||
self.local_overflow_limit
|
||||
}
|
||||
|
||||
/// Update the stack and reached depths on cache hits.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) {
|
||||
let reached_depth = self.stack.next_index().plus(additional_depth);
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
last.reached_depth = last.reached_depth.max(reached_depth);
|
||||
last.encountered_overflow |= encountered_overflow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops the highest goal from the stack, lazily updating the
|
||||
/// the next goal in the stack.
|
||||
///
|
||||
/// Directly popping from the stack instead of using this method
|
||||
/// would cause us to not track overflow and recursion depth correctly.
|
||||
fn pop_stack(&mut self) -> StackEntry<'tcx> {
|
||||
let elem = self.stack.pop().unwrap();
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
last.reached_depth = last.reached_depth.max(elem.reached_depth);
|
||||
last.encountered_overflow |= elem.encountered_overflow;
|
||||
}
|
||||
elem
|
||||
}
|
||||
|
||||
/// The trait solver behavior is different for coherence
|
||||
/// so we would have to add the solver mode to the cache key.
|
||||
/// This is probably not worth it as trait solving during
|
||||
/// coherence tends to already be incredibly fast.
|
||||
///
|
||||
/// We could add another global cache for coherence instead,
|
||||
/// but that's effort so let's only do it if necessary.
|
||||
/// so we use a separate cache. Alternatively we could use
|
||||
/// a single cache and share it between coherence and ordinary
|
||||
/// trait solving.
|
||||
pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> {
|
||||
match self.mode {
|
||||
SolverMode::Normal => &tcx.new_solver_evaluation_cache,
|
||||
@ -87,36 +124,107 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries putting the new goal on the stack, returning an error if it is already cached.
|
||||
/// Fetches whether the current goal encountered overflow.
|
||||
///
|
||||
/// This correctly updates the provisional cache if there is a cycle.
|
||||
#[instrument(level = "debug", skip(self, tcx, inspect), ret)]
|
||||
fn try_push_stack(
|
||||
/// This should only be used for the check in `evaluate_goal`.
|
||||
pub(super) fn encountered_overflow(&self) -> bool {
|
||||
if let Some(last) = self.stack.raw.last() { last.encountered_overflow } else { false }
|
||||
}
|
||||
|
||||
/// Resets `encountered_overflow` of the current goal.
|
||||
///
|
||||
/// This should only be used for the check in `evaluate_goal`.
|
||||
pub(super) fn reset_encountered_overflow(&mut self, encountered_overflow: bool) {
|
||||
if encountered_overflow {
|
||||
self.stack.raw.last_mut().unwrap().encountered_overflow = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the remaining depth allowed for nested goals.
|
||||
///
|
||||
/// This is generally simply one less than the current depth.
|
||||
/// However, if we encountered overflow, we significantly reduce
|
||||
/// the remaining depth of all nested goals to prevent hangs
|
||||
/// in case there is exponential blowup.
|
||||
fn allowed_depth_for_nested(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
stack: &IndexVec<StackDepth, StackEntry<'tcx>>,
|
||||
) -> Option<Limit> {
|
||||
if let Some(last) = stack.raw.last() {
|
||||
if last.available_depth.0 == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(if last.encountered_overflow {
|
||||
Limit(last.available_depth.0 / 4)
|
||||
} else {
|
||||
Limit(last.available_depth.0 - 1)
|
||||
})
|
||||
} else {
|
||||
Some(tcx.recursion_limit())
|
||||
}
|
||||
}
|
||||
|
||||
/// Probably the most involved method of the whole solver.
|
||||
///
|
||||
/// Given some goal which is proven via the `prove_goal` closure, this
|
||||
/// handles caching, overflow, and coinductive cycles.
|
||||
pub(super) fn with_new_goal(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
input: CanonicalInput<'tcx>,
|
||||
inspect: &mut ProofTreeBuilder<'tcx>,
|
||||
) -> Result<(), QueryResult<'tcx>> {
|
||||
// Look at the provisional cache to check for cycles.
|
||||
mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
// Check for overflow.
|
||||
let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
last.encountered_overflow = true;
|
||||
}
|
||||
return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
|
||||
};
|
||||
|
||||
// Try to fetch the goal from the global cache.
|
||||
if inspect.use_global_cache() {
|
||||
if let Some(CacheData { result, reached_depth, encountered_overflow }) =
|
||||
self.global_cache(tcx).get(
|
||||
tcx,
|
||||
input,
|
||||
|cycle_participants| {
|
||||
self.stack.iter().any(|entry| cycle_participants.contains(&entry.input))
|
||||
},
|
||||
available_depth,
|
||||
)
|
||||
{
|
||||
self.on_cache_hit(reached_depth, encountered_overflow);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Look at the provisional cache to detect cycles.
|
||||
let cache = &mut self.provisional_cache;
|
||||
match cache.lookup_table.entry(input) {
|
||||
// No entry, simply push this goal on the stack after dealing with overflow.
|
||||
// No entry, we push this goal on the stack and try to prove it.
|
||||
Entry::Vacant(v) => {
|
||||
if self.overflow_data.has_overflow(self.stack.len()) {
|
||||
return Err(self.deal_with_overflow(tcx, input));
|
||||
}
|
||||
|
||||
let depth = self.stack.push(StackElem { input, has_been_used: false });
|
||||
let response = super::response_no_constraints(tcx, input, Certainty::Yes);
|
||||
let depth = self.stack.next_index();
|
||||
let entry = StackEntry {
|
||||
input,
|
||||
available_depth,
|
||||
reached_depth: depth,
|
||||
encountered_overflow: false,
|
||||
has_been_used: false,
|
||||
cycle_participants: Default::default(),
|
||||
};
|
||||
assert_eq!(self.stack.push(entry), depth);
|
||||
let response = Self::response_no_constraints(tcx, input, Certainty::Yes);
|
||||
let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input });
|
||||
v.insert(entry_index);
|
||||
Ok(())
|
||||
}
|
||||
// We have a nested goal which relies on a goal `root` deeper in the stack.
|
||||
//
|
||||
// We first store that we may have to rerun `evaluate_goal` for `root` in case the
|
||||
// provisional response is not equal to the final response. We also update the depth
|
||||
// of all goals which recursively depend on our current goal to depend on `root`
|
||||
// We first store that we may have to reprove `root` in case the provisional
|
||||
// response is not equal to the final response. We also update the depth of all
|
||||
// goals which recursively depend on our current goal to depend on `root`
|
||||
// instead.
|
||||
//
|
||||
// Finally we can return either the provisional response for that goal if we have a
|
||||
@ -125,13 +233,16 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
inspect.cache_hit(CacheHit::Provisional);
|
||||
|
||||
let entry_index = *entry_index.get();
|
||||
|
||||
let stack_depth = cache.depth(entry_index);
|
||||
debug!("encountered cycle with depth {stack_depth:?}");
|
||||
|
||||
cache.add_dependency_of_leaf_on(entry_index);
|
||||
let mut iter = self.stack.iter_mut();
|
||||
let root = iter.nth(stack_depth.as_usize()).unwrap();
|
||||
for e in iter {
|
||||
root.cycle_participants.insert(e.input);
|
||||
}
|
||||
|
||||
self.stack[stack_depth].has_been_used = true;
|
||||
// NOTE: The goals on the stack aren't the only goals involved in this cycle.
|
||||
// We can also depend on goals which aren't part of the stack but coinductively
|
||||
// depend on the stack themselves. We already checked whether all the goals
|
||||
@ -142,145 +253,111 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
.iter()
|
||||
.all(|g| g.input.value.goal.predicate.is_coinductive(tcx))
|
||||
{
|
||||
Err(cache.provisional_result(entry_index))
|
||||
// If we're in a coinductive cycle, we have to retry proving the current goal
|
||||
// until we reach a fixpoint.
|
||||
self.stack[stack_depth].has_been_used = true;
|
||||
return cache.provisional_result(entry_index);
|
||||
} else {
|
||||
Err(super::response_no_constraints(tcx, input, Certainty::OVERFLOW))
|
||||
return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We cannot simply store the result of [super::EvalCtxt::compute_goal] as we have to deal with
|
||||
/// coinductive cycles.
|
||||
///
|
||||
/// When we encounter a coinductive cycle, we have to prove the final result of that cycle
|
||||
/// while we are still computing that result. Because of this we continuously recompute the
|
||||
/// cycle until the result of the previous iteration is equal to the final result, at which
|
||||
/// point we are done.
|
||||
///
|
||||
/// This function returns `true` if we were able to finalize the goal and `false` if it has
|
||||
/// updated the provisional cache and we have to recompute the current goal.
|
||||
///
|
||||
/// FIXME: Refer to the rustc-dev-guide entry once it exists.
|
||||
#[instrument(level = "debug", skip(self, actual_input), ret)]
|
||||
fn try_finalize_goal(
|
||||
&mut self,
|
||||
actual_input: CanonicalInput<'tcx>,
|
||||
response: QueryResult<'tcx>,
|
||||
) -> bool {
|
||||
let stack_elem = self.stack.pop().unwrap();
|
||||
let StackElem { input, has_been_used } = stack_elem;
|
||||
assert_eq!(input, actual_input);
|
||||
// This is for global caching, so we properly track query dependencies.
|
||||
// Everything that affects the `result` should be performed within this
|
||||
// `with_anon_task` closure.
|
||||
let ((final_entry, result), dep_node) =
|
||||
tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
|
||||
// When we encounter a coinductive cycle, we have to fetch the
|
||||
// result of that cycle while we are still computing it. Because
|
||||
// of this we continuously recompute the cycle until the result
|
||||
// of the previous iteration is equal to the final result, at which
|
||||
// point we are done.
|
||||
for _ in 0..self.local_overflow_limit() {
|
||||
let response = prove_goal(self, inspect);
|
||||
|
||||
// Check whether the current goal is the root of a cycle and whether
|
||||
// we have to rerun because its provisional result differed from the
|
||||
// final result.
|
||||
//
|
||||
// Also update the response for this goal stored in the provisional
|
||||
// cache.
|
||||
let stack_entry = self.pop_stack();
|
||||
debug_assert_eq!(stack_entry.input, input);
|
||||
let cache = &mut self.provisional_cache;
|
||||
let provisional_entry_index =
|
||||
*cache.lookup_table.get(&stack_entry.input).unwrap();
|
||||
let provisional_entry = &mut cache.entries[provisional_entry_index];
|
||||
let prev_response = mem::replace(&mut provisional_entry.response, response);
|
||||
if stack_entry.has_been_used && prev_response != response {
|
||||
// If so, remove all entries whose result depends on this goal
|
||||
// from the provisional cache...
|
||||
//
|
||||
// That's not completely correct, as a nested goal can also
|
||||
// depend on a goal which is lower in the stack so it doesn't
|
||||
// actually depend on the current goal. This should be fairly
|
||||
// rare and is hopefully not relevant for performance.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index);
|
||||
cache.entries.truncate(provisional_entry_index.index() + 1);
|
||||
|
||||
// ...and finally push our goal back on the stack and reevaluate it.
|
||||
self.stack.push(StackEntry { has_been_used: false, ..stack_entry });
|
||||
} else {
|
||||
return (stack_entry, response);
|
||||
}
|
||||
}
|
||||
|
||||
debug!("canonical cycle overflow");
|
||||
let current_entry = self.pop_stack();
|
||||
let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
|
||||
(current_entry, result)
|
||||
});
|
||||
|
||||
// We're now done with this goal. In case this goal is involved in a larger cycle
|
||||
// do not remove it from the provisional cache and do not add it to the global
|
||||
// cache.
|
||||
//
|
||||
// It is not possible for any nested goal to depend on something deeper on the
|
||||
// stack, as this would have also updated the depth of the current goal.
|
||||
let cache = &mut self.provisional_cache;
|
||||
let provisional_entry_index = *cache.lookup_table.get(&input).unwrap();
|
||||
let provisional_entry = &mut cache.entries[provisional_entry_index];
|
||||
// We eagerly update the response in the cache here. If we have to reevaluate
|
||||
// this goal we use the new response when hitting a cycle, and we definitely
|
||||
// want to access the final response whenever we look at the cache.
|
||||
let prev_response = mem::replace(&mut provisional_entry.response, response);
|
||||
|
||||
// Was the current goal the root of a cycle and was the provisional response
|
||||
// different from the final one.
|
||||
if has_been_used && prev_response != response {
|
||||
// If so, remove all entries whose result depends on this goal
|
||||
// from the provisional cache...
|
||||
//
|
||||
// That's not completely correct, as a nested goal can also
|
||||
// depend on a goal which is lower in the stack so it doesn't
|
||||
// actually depend on the current goal. This should be fairly
|
||||
// rare and is hopefully not relevant for performance.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index);
|
||||
cache.entries.truncate(provisional_entry_index.index() + 1);
|
||||
|
||||
// ...and finally push our goal back on the stack and reevaluate it.
|
||||
self.stack.push(StackElem { input, has_been_used: false });
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn with_new_goal(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
canonical_input: CanonicalInput<'tcx>,
|
||||
inspect: &mut ProofTreeBuilder<'tcx>,
|
||||
mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if inspect.use_global_cache() {
|
||||
if let Some(result) = self.global_cache(tcx).get(&canonical_input, tcx) {
|
||||
debug!(?canonical_input, ?result, "cache hit");
|
||||
inspect.cache_hit(CacheHit::Global);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
match self.try_push_stack(tcx, canonical_input, inspect) {
|
||||
Ok(()) => {}
|
||||
// Our goal is already on the stack, eager return.
|
||||
Err(response) => return response,
|
||||
}
|
||||
|
||||
// This is for global caching, so we properly track query dependencies.
|
||||
// Everything that affects the `Result` should be performed within this
|
||||
// `with_anon_task` closure.
|
||||
let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
|
||||
self.repeat_while_none(
|
||||
|this| {
|
||||
let result = this.deal_with_overflow(tcx, canonical_input);
|
||||
let _ = this.stack.pop().unwrap();
|
||||
result
|
||||
},
|
||||
|this| {
|
||||
let result = loop_body(this, inspect);
|
||||
this.try_finalize_goal(canonical_input, result).then(|| result)
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
let cache = &mut self.provisional_cache;
|
||||
let provisional_entry_index = *cache.lookup_table.get(&canonical_input).unwrap();
|
||||
let provisional_entry = &mut cache.entries[provisional_entry_index];
|
||||
let depth = provisional_entry.depth;
|
||||
|
||||
// If not, we're done with this goal.
|
||||
//
|
||||
// Check whether that this goal doesn't depend on a goal deeper on the stack
|
||||
// and if so, move it to the global cache.
|
||||
//
|
||||
// Note that if any nested goal were to depend on something deeper on the stack,
|
||||
// this would have also updated the depth of the current goal.
|
||||
if depth == self.stack.next_index() {
|
||||
// If the current goal is the head of a cycle, we drop all other
|
||||
// cycle participants without moving them to the global cache.
|
||||
let other_cycle_participants = provisional_entry_index.index() + 1;
|
||||
for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) {
|
||||
for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) {
|
||||
let actual_index = cache.lookup_table.remove(&entry.input);
|
||||
debug_assert_eq!(Some(i), actual_index);
|
||||
debug_assert!(entry.depth == depth);
|
||||
}
|
||||
|
||||
let current_goal = cache.entries.pop().unwrap();
|
||||
let actual_index = cache.lookup_table.remove(¤t_goal.input);
|
||||
debug_assert_eq!(Some(provisional_entry_index), actual_index);
|
||||
debug_assert!(current_goal.depth == depth);
|
||||
|
||||
// We move the root goal to the global cache if we either did not hit an overflow or if it's
|
||||
// the root goal as that will now always hit the same overflow limit.
|
||||
// When encountering a cycle, both inductive and coinductive, we only
|
||||
// move the root into the global cache. We also store all other cycle
|
||||
// participants involved.
|
||||
//
|
||||
// NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's
|
||||
// dependencies, our non-root goal may no longer appear as child of the root goal.
|
||||
//
|
||||
// See https://github.com/rust-lang/rust/pull/108071 for some additional context.
|
||||
let can_cache = inspect.use_global_cache()
|
||||
&& (!self.overflow_data.did_overflow() || self.stack.is_empty());
|
||||
if can_cache {
|
||||
self.global_cache(tcx).insert(current_goal.input, dep_node, current_goal.response)
|
||||
}
|
||||
// We disable the global cache entry of the root goal if a cycle
|
||||
// participant is on the stack. This is necessary to prevent unstable
|
||||
// results. See the comment of `StackEntry::cycle_participants` for
|
||||
// more details.
|
||||
let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len();
|
||||
self.global_cache(tcx).insert(
|
||||
input,
|
||||
reached_depth,
|
||||
final_entry.encountered_overflow,
|
||||
final_entry.cycle_participants,
|
||||
dep_node,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn response_no_constraints(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal: CanonicalInput<'tcx>,
|
||||
certainty: Certainty,
|
||||
) -> QueryResult<'tcx> {
|
||||
Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
|
||||
}
|
||||
}
|
||||
|
@ -1,120 +0,0 @@
|
||||
use rustc_infer::infer::canonical::Canonical;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{Certainty, QueryResult};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Limit;
|
||||
|
||||
use super::SearchGraph;
|
||||
use crate::solve::{response_no_constraints, EvalCtxt};
|
||||
|
||||
/// When detecting a solver overflow, we return ambiguity. Overflow can be
|
||||
/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**.
|
||||
///
|
||||
/// This is in issue in case of exponential blowup, e.g. if each goal on the stack
|
||||
/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit
|
||||
/// used by the solver when hitting the default limit for the first time.
|
||||
///
|
||||
/// FIXME: Get tests where always using the `default_limit` results in a hang and refer
|
||||
/// to them here. We can also improve the overflow strategy if necessary.
|
||||
pub(super) struct OverflowData {
|
||||
default_limit: Limit,
|
||||
current_limit: Limit,
|
||||
/// When proving an **AND** we have to repeatedly iterate over the yet unproven goals.
|
||||
///
|
||||
/// Because of this each iteration also increases the depth in addition to the stack
|
||||
/// depth.
|
||||
additional_depth: usize,
|
||||
}
|
||||
|
||||
impl OverflowData {
|
||||
pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData {
|
||||
let default_limit = tcx.recursion_limit();
|
||||
OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn did_overflow(&self) -> bool {
|
||||
self.default_limit.0 != self.current_limit.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn has_overflow(&self, depth: usize) -> bool {
|
||||
!self.current_limit.value_within_limit(depth + self.additional_depth)
|
||||
}
|
||||
|
||||
/// Updating the current limit when hitting overflow.
|
||||
fn deal_with_overflow(&mut self) {
|
||||
// When first hitting overflow we reduce the overflow limit
|
||||
// for all future goals to prevent hangs if there's an exponential
|
||||
// blowup.
|
||||
self.current_limit.0 = self.default_limit.0 / 8;
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::solve) trait OverflowHandler<'tcx> {
|
||||
fn search_graph(&mut self) -> &mut SearchGraph<'tcx>;
|
||||
|
||||
fn repeat_while_none<T>(
|
||||
&mut self,
|
||||
on_overflow: impl FnOnce(&mut Self) -> Result<T, NoSolution>,
|
||||
mut loop_body: impl FnMut(&mut Self) -> Option<Result<T, NoSolution>>,
|
||||
) -> Result<T, NoSolution> {
|
||||
let start_depth = self.search_graph().overflow_data.additional_depth;
|
||||
let depth = self.search_graph().stack.len();
|
||||
while !self.search_graph().overflow_data.has_overflow(depth) {
|
||||
if let Some(result) = loop_body(self) {
|
||||
self.search_graph().overflow_data.additional_depth = start_depth;
|
||||
return result;
|
||||
}
|
||||
|
||||
self.search_graph().overflow_data.additional_depth += 1;
|
||||
}
|
||||
self.search_graph().overflow_data.additional_depth = start_depth;
|
||||
self.search_graph().overflow_data.deal_with_overflow();
|
||||
on_overflow(self)
|
||||
}
|
||||
|
||||
// Increment the `additional_depth` by one and evaluate `body`, or `on_overflow`
|
||||
// if the depth is overflown.
|
||||
fn with_incremented_depth<T>(
|
||||
&mut self,
|
||||
on_overflow: impl FnOnce(&mut Self) -> T,
|
||||
body: impl FnOnce(&mut Self) -> T,
|
||||
) -> T {
|
||||
let depth = self.search_graph().stack.len();
|
||||
self.search_graph().overflow_data.additional_depth += 1;
|
||||
|
||||
let result = if self.search_graph().overflow_data.has_overflow(depth) {
|
||||
self.search_graph().overflow_data.deal_with_overflow();
|
||||
on_overflow(self)
|
||||
} else {
|
||||
body(self)
|
||||
};
|
||||
|
||||
self.search_graph().overflow_data.additional_depth -= 1;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> {
|
||||
fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
|
||||
&mut self.search_graph
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> OverflowHandler<'tcx> for SearchGraph<'tcx> {
|
||||
fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> SearchGraph<'tcx> {
|
||||
pub fn deal_with_overflow(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal: Canonical<'tcx, impl Sized>,
|
||||
) -> QueryResult<'tcx> {
|
||||
self.overflow_data.deal_with_overflow();
|
||||
response_no_constraints(tcx, goal, Certainty::OVERFLOW)
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
|
||||
|
||||
use super::assembly::{self, structural_traits};
|
||||
use super::search_graph::OverflowHandler;
|
||||
use super::{EvalCtxt, SolverMode};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{LangItem, Movability};
|
||||
@ -444,7 +443,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
Err(NoSolution) => vec![],
|
||||
};
|
||||
|
||||
ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| {
|
||||
ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
|
||||
let a_ty = goal.predicate.self_ty();
|
||||
// We need to normalize the b_ty since it's matched structurally
|
||||
// in the other functions below.
|
||||
@ -526,7 +525,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
b_region: ty::Region<'tcx>,
|
||||
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
|
||||
let tcx = self.tcx();
|
||||
let Goal { predicate: (a_ty, b_ty), .. } = goal;
|
||||
let Goal { predicate: (a_ty, _b_ty), .. } = goal;
|
||||
|
||||
// All of a's auto traits need to be in b's auto traits.
|
||||
let auto_traits_compatible =
|
||||
@ -535,51 +534,30 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with
|
||||
// the supertrait principal and subtyping the types.
|
||||
let unsize_dyn_to_principal =
|
||||
|ecx: &mut Self, principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
|
||||
ecx.probe_candidate("upcast dyn to principle").enter(
|
||||
|ecx| -> Result<_, NoSolution> {
|
||||
// Require that all of the trait predicates from A match B, except for
|
||||
// the auto traits. We do this by constructing a new A type with B's
|
||||
// auto traits, and equating these types.
|
||||
let new_a_data = principal
|
||||
.into_iter()
|
||||
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
|
||||
.chain(a_data.iter().filter(|a| {
|
||||
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
|
||||
}))
|
||||
.chain(
|
||||
b_data
|
||||
.auto_traits()
|
||||
.map(ty::ExistentialPredicate::AutoTrait)
|
||||
.map(ty::Binder::dummy),
|
||||
);
|
||||
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
|
||||
let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
|
||||
|
||||
// We also require that A's lifetime outlives B's lifetime.
|
||||
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
|
||||
ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_region, b_region)));
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
let mut responses = vec![];
|
||||
// If the principal def ids match (or are both none), then we're not doing
|
||||
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
|
||||
if a_data.principal_def_id() == b_data.principal_def_id() {
|
||||
if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) {
|
||||
if let Ok(resp) = self.consider_builtin_upcast_to_principal(
|
||||
goal,
|
||||
a_data,
|
||||
a_region,
|
||||
b_data,
|
||||
b_region,
|
||||
a_data.principal(),
|
||||
) {
|
||||
responses.push((resp, BuiltinImplSource::Misc));
|
||||
}
|
||||
} else if let Some(a_principal) = a_data.principal() {
|
||||
self.walk_vtable(
|
||||
a_principal.with_self_ty(tcx, a_ty),
|
||||
|ecx, new_a_principal, _, vtable_vptr_slot| {
|
||||
if let Ok(resp) = unsize_dyn_to_principal(
|
||||
ecx,
|
||||
if let Ok(resp) = ecx.consider_builtin_upcast_to_principal(
|
||||
goal,
|
||||
a_data,
|
||||
a_region,
|
||||
b_data,
|
||||
b_region,
|
||||
Some(new_a_principal.map_bound(|trait_ref| {
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
||||
})),
|
||||
@ -631,6 +609,83 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
fn consider_builtin_upcast_to_principal(
|
||||
&mut self,
|
||||
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
||||
a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
a_region: ty::Region<'tcx>,
|
||||
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
b_region: ty::Region<'tcx>,
|
||||
upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
||||
) -> QueryResult<'tcx> {
|
||||
let param_env = goal.param_env;
|
||||
|
||||
// More than one projection in a_ty's bounds may match the projection
|
||||
// in b_ty's bound. Use this to first determine *which* apply without
|
||||
// having any inference side-effects. We process obligations because
|
||||
// unification may initially succeed due to deferred projection equality.
|
||||
let projection_may_match =
|
||||
|ecx: &mut Self,
|
||||
source_projection: ty::PolyExistentialProjection<'tcx>,
|
||||
target_projection: ty::PolyExistentialProjection<'tcx>| {
|
||||
source_projection.item_def_id() == target_projection.item_def_id()
|
||||
&& ecx
|
||||
.probe(|_| CandidateKind::UpcastProbe)
|
||||
.enter(|ecx| -> Result<(), NoSolution> {
|
||||
ecx.eq(param_env, source_projection, target_projection)?;
|
||||
let _ = ecx.try_evaluate_added_goals()?;
|
||||
Ok(())
|
||||
})
|
||||
.is_ok()
|
||||
};
|
||||
|
||||
for bound in b_data {
|
||||
match bound.skip_binder() {
|
||||
// Check that a's supertrait (upcast_principal) is compatible
|
||||
// with the target (b_ty).
|
||||
ty::ExistentialPredicate::Trait(target_principal) => {
|
||||
self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?;
|
||||
}
|
||||
// Check that b_ty's projection is satisfied by exactly one of
|
||||
// a_ty's projections. First, we look through the list to see if
|
||||
// any match. If not, error. Then, if *more* than one matches, we
|
||||
// return ambiguity. Otherwise, if exactly one matches, equate
|
||||
// it with b_ty's projection.
|
||||
ty::ExistentialPredicate::Projection(target_projection) => {
|
||||
let target_projection = bound.rebind(target_projection);
|
||||
let mut matching_projections =
|
||||
a_data.projection_bounds().filter(|source_projection| {
|
||||
projection_may_match(self, *source_projection, target_projection)
|
||||
});
|
||||
let Some(source_projection) = matching_projections.next() else {
|
||||
return Err(NoSolution);
|
||||
};
|
||||
if matching_projections.next().is_some() {
|
||||
return self.evaluate_added_goals_and_make_canonical_response(
|
||||
Certainty::AMBIGUOUS,
|
||||
);
|
||||
}
|
||||
self.eq(param_env, source_projection, target_projection)?;
|
||||
}
|
||||
// Check that b_ty's auto traits are present in a_ty's bounds.
|
||||
ty::ExistentialPredicate::AutoTrait(def_id) => {
|
||||
if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also require that a_ty's lifetime outlives b_ty's lifetime.
|
||||
self.add_goal(Goal::new(
|
||||
self.tcx(),
|
||||
param_env,
|
||||
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
|
||||
));
|
||||
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
/// We have the following builtin impls for arrays:
|
||||
/// ```ignore (builtin impl example)
|
||||
/// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {}
|
||||
@ -857,12 +912,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
ecx.add_goals(
|
||||
constituent_tys(ecx, goal.predicate.self_ty())?
|
||||
.into_iter()
|
||||
.map(|ty| {
|
||||
goal.with(
|
||||
ecx.tcx(),
|
||||
ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)),
|
||||
)
|
||||
})
|
||||
.map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty)))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
@ -879,7 +929,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
|
||||
/// Normalize a non-self type when it is structually matched on when solving
|
||||
/// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty`
|
||||
/// a built-in goal.
|
||||
///
|
||||
/// This is handled already through `assemble_candidates_after_normalizing_self_ty`
|
||||
/// for the self type, but for other goals, additional normalization of other
|
||||
/// arguments may be needed to completely implement the semantics of the trait.
|
||||
///
|
||||
@ -894,30 +946,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
return Ok(Some(ty));
|
||||
}
|
||||
|
||||
self.repeat_while_none(
|
||||
|_| Ok(None),
|
||||
|ecx| {
|
||||
let ty::Alias(_, projection_ty) = *ty.kind() else {
|
||||
return Some(Ok(Some(ty)));
|
||||
};
|
||||
for _ in 0..self.local_overflow_limit() {
|
||||
let ty::Alias(_, projection_ty) = *ty.kind() else {
|
||||
return Ok(Some(ty));
|
||||
};
|
||||
|
||||
let normalized_ty = ecx.next_ty_infer();
|
||||
let normalizes_to_goal = Goal::new(
|
||||
ecx.tcx(),
|
||||
param_env,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
projection_ty,
|
||||
term: normalized_ty.into(),
|
||||
}),
|
||||
);
|
||||
ecx.add_goal(normalizes_to_goal);
|
||||
if let Err(err) = ecx.try_evaluate_added_goals() {
|
||||
return Some(Err(err));
|
||||
}
|
||||
let normalized_ty = self.next_ty_infer();
|
||||
let normalizes_to_goal = Goal::new(
|
||||
self.tcx(),
|
||||
param_env,
|
||||
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
|
||||
);
|
||||
self.add_goal(normalizes_to_goal);
|
||||
self.try_evaluate_added_goals()?;
|
||||
ty = self.resolve_vars_if_possible(normalized_ty);
|
||||
}
|
||||
|
||||
ty = ecx.resolve_vars_if_possible(normalized_ty);
|
||||
None
|
||||
},
|
||||
)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
@ -2987,6 +2987,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
unsatisfied_const: bool,
|
||||
) {
|
||||
let body_def_id = obligation.cause.body_id;
|
||||
let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } =
|
||||
obligation.cause.code()
|
||||
{
|
||||
*rhs_span
|
||||
} else {
|
||||
span
|
||||
};
|
||||
|
||||
// Try to report a help message
|
||||
if is_fn_trait
|
||||
&& let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user