mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
Auto merge of #3669 - rust-lang:rustup-2024-06-13, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
c5e94246a3
@ -3452,7 +3452,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "run_make_support"
|
||||
version = "0.0.0"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gimli 0.28.1",
|
||||
"object 0.34.0",
|
||||
|
@ -147,7 +147,7 @@ Language
|
||||
- [Split `refining_impl_trait` lint into `_reachable`, `_internal` variants](https://github.com/rust-lang/rust/pull/121720/)
|
||||
- [Remove unnecessary type inference when using associated types inside of higher ranked `where`-bounds](https://github.com/rust-lang/rust/pull/119849)
|
||||
- [Weaken eager detection of cyclic types during type inference](https://github.com/rust-lang/rust/pull/119989)
|
||||
- [`trait Trait: Auto {}`: allow upcasting from `dyn Trait` to `dyn Auto`](https://github.com/rust-lang/rust/pull/119338)
|
||||
- [`trait Trait: Auto {}`: allow upcasting from `dyn Trait` to `dyn Trait + Auto`](https://github.com/rust-lang/rust/pull/119338)
|
||||
|
||||
<a id="1.78.0-Compiler"></a>
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", doc(rust_logo))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
|
||||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::fmt;
|
||||
use std::num::{NonZeroUsize, ParseIntError};
|
||||
|
@ -7,23 +7,25 @@
|
||||
//!
|
||||
//! This crate implements several kinds of arena.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(clippy::mut_from_ref)] // Arena allocators are one place where this pattern is fine.
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![doc(
|
||||
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
|
||||
test(no_crate_inject, attr(deny(warnings)))
|
||||
)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(new_uninit)]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(new_uninit)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![allow(internal_features)]
|
||||
#![allow(clippy::mut_from_ref)] // Arena allocators are one of the places where this pattern is fine.
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -4,20 +4,22 @@
|
||||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![doc(
|
||||
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
|
||||
test(attr(deny(warnings)))
|
||||
)]
|
||||
#![doc(rust_logo)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod util {
|
||||
pub mod case;
|
||||
|
@ -1,6 +1,8 @@
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", feature(never_type))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_NoContext};
|
||||
|
@ -67,7 +67,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
return false;
|
||||
};
|
||||
if let Some(local_sig_id) = sig_id.as_local() {
|
||||
self.resolver.delegation_fn_sigs[&local_sig_id].has_self
|
||||
// The value may be missing due to recursive delegation.
|
||||
// Error will be emmited later during HIR ty lowering.
|
||||
self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self)
|
||||
} else {
|
||||
match self.tcx.def_kind(sig_id) {
|
||||
DefKind::Fn => false,
|
||||
|
@ -30,12 +30,14 @@
|
||||
//! get confused if the spans from leaf AST nodes occur in multiple places
|
||||
//! in the HIR, especially for multiple identifiers.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait};
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
|
@ -4,13 +4,15 @@
|
||||
//!
|
||||
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_is_partitioned)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod ast_validation;
|
||||
mod errors;
|
||||
|
@ -1,7 +1,9 @@
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod helpers;
|
||||
pub mod pp;
|
||||
|
@ -877,18 +877,11 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
}
|
||||
|
||||
fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
|
||||
match nt {
|
||||
token::NtExpr(e) => self.expr_to_string(e),
|
||||
token::NtMeta(e) => self.attr_item_to_string(e),
|
||||
token::NtTy(e) => self.ty_to_string(e),
|
||||
token::NtPath(e) => self.path_to_string(e),
|
||||
token::NtItem(e) => self.item_to_string(e),
|
||||
token::NtBlock(e) => self.block_to_string(e),
|
||||
token::NtStmt(e) => self.stmt_to_string(e),
|
||||
token::NtPat(e) => self.pat_to_string(e),
|
||||
token::NtLiteral(e) => self.expr_to_string(e),
|
||||
token::NtVis(e) => self.vis_to_string(e),
|
||||
}
|
||||
// We extract the token stream from the AST fragment and pretty print
|
||||
// it, rather than using AST pretty printing, because `Nonterminal` is
|
||||
// slated for removal in #124141. (This method will also then be
|
||||
// removed.)
|
||||
self.tts_to_string(&TokenStream::from_nonterminal_ast(nt))
|
||||
}
|
||||
|
||||
/// Print the token kind precisely, without converting `$crate` into its respective crate name.
|
||||
@ -1022,6 +1015,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
Self::to_string(|s| s.print_attr_item(ai, ai.path.span))
|
||||
}
|
||||
|
||||
fn tts_to_string(&self, tokens: &TokenStream) -> String {
|
||||
Self::to_string(|s| s.print_tts(tokens, false))
|
||||
}
|
||||
|
||||
fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
|
||||
let mut printer = State::new();
|
||||
f(&mut printer);
|
||||
@ -2068,10 +2065,6 @@ impl<'a> State<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn tts_to_string(&self, tokens: &TokenStream) -> String {
|
||||
Self::to_string(|s| s.print_tts(tokens, false))
|
||||
}
|
||||
|
||||
pub(crate) fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
|
||||
Self::to_string(|s| s.print_path_segment(p, false))
|
||||
}
|
||||
|
@ -4,10 +4,12 @@
|
||||
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
|
||||
//! to this crate.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod builtin;
|
||||
mod session_diagnostics;
|
||||
|
@ -20,10 +20,12 @@
|
||||
//! --cldr-tag latest --icuexport-tag latest -o src/data
|
||||
//! ```
|
||||
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
// tidy-alphabetical-start
|
||||
#![allow(elided_lifetimes_in_paths)]
|
||||
#![allow(internal_features)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod data {
|
||||
include!("data/mod.rs");
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rustc_data_structures::graph::scc::Sccs;
|
||||
use crate::type_check::Locations;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
|
||||
@ -6,8 +6,6 @@ use rustc_span::Span;
|
||||
use std::fmt;
|
||||
use std::ops::Index;
|
||||
|
||||
use crate::type_check::Locations;
|
||||
|
||||
pub(crate) mod graph;
|
||||
|
||||
/// A set of NLL region constraints. These include "outlives"
|
||||
@ -45,18 +43,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
|
||||
graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars)
|
||||
}
|
||||
|
||||
/// Computes cycles (SCCs) in the graph of regions. In particular,
|
||||
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
|
||||
/// them into an SCC, and find the relationships between SCCs.
|
||||
pub(crate) fn compute_sccs(
|
||||
&self,
|
||||
constraint_graph: &graph::NormalConstraintGraph,
|
||||
static_region: RegionVid,
|
||||
) -> Sccs<RegionVid, ConstraintSccIndex> {
|
||||
let region_graph = &constraint_graph.region_graph(self, static_region);
|
||||
Sccs::new(region_graph)
|
||||
}
|
||||
|
||||
pub(crate) fn outlives(
|
||||
&self,
|
||||
) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
|
||||
|
@ -557,8 +557,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// for the branching codepaths that aren't covered, to point at them.
|
||||
let map = self.infcx.tcx.hir();
|
||||
let body = map.body_owned_by(self.mir_def_id());
|
||||
|
||||
let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
|
||||
let mut visitor =
|
||||
ConditionVisitor { tcx: self.infcx.tcx, spans: &spans, name: &name, errors: vec![] };
|
||||
visitor.visit_body(&body);
|
||||
|
||||
let mut show_assign_sugg = false;
|
||||
@ -4372,13 +4372,14 @@ impl<'hir> Visitor<'hir> for BreakFinder {
|
||||
|
||||
/// Given a set of spans representing statements initializing the relevant binding, visit all the
|
||||
/// function expressions looking for branching code paths that *do not* initialize the binding.
|
||||
struct ConditionVisitor<'b> {
|
||||
struct ConditionVisitor<'b, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
spans: &'b [Span],
|
||||
name: &'b str,
|
||||
errors: Vec<(Span, String)>,
|
||||
}
|
||||
|
||||
impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
|
||||
impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> {
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||
match ex.kind {
|
||||
hir::ExprKind::If(cond, body, None) => {
|
||||
@ -4464,6 +4465,12 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
|
||||
),
|
||||
));
|
||||
} else if let Some(guard) = &arm.guard {
|
||||
if matches!(
|
||||
self.tcx.hir_node(arm.body.hir_id),
|
||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
self.errors.push((
|
||||
arm.pat.span.to(guard.span),
|
||||
format!(
|
||||
@ -4473,6 +4480,12 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
|
||||
),
|
||||
));
|
||||
} else {
|
||||
if matches!(
|
||||
self.tcx.hir_node(arm.body.hir_id),
|
||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
self.errors.push((
|
||||
arm.pat.span,
|
||||
format!(
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! This query borrow-checks the MIR to (further) ensure it is not broken.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
@ -10,8 +10,10 @@
|
||||
#![feature(min_specialization)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(try_blocks)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
@ -4,10 +4,10 @@ use std::rc::Rc;
|
||||
use rustc_data_structures::binary_search_util;
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::scc::Sccs;
|
||||
use rustc_data_structures::graph::scc::{self, Sccs};
|
||||
use rustc_errors::Diag;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::outlives::test_type_match;
|
||||
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
|
||||
@ -19,7 +19,7 @@ use rustc_middle::mir::{
|
||||
};
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex};
|
||||
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -46,6 +46,97 @@ mod reverse_sccs;
|
||||
|
||||
pub mod values;
|
||||
|
||||
pub type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex, RegionTracker>;
|
||||
|
||||
/// An annotation for region graph SCCs that tracks
|
||||
/// the values of its elements.
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub struct RegionTracker {
|
||||
/// The largest universe of a placeholder reached from this SCC.
|
||||
/// This includes placeholders within this SCC.
|
||||
max_placeholder_universe_reached: UniverseIndex,
|
||||
|
||||
/// The smallest universe index reachable form the nodes of this SCC.
|
||||
min_reachable_universe: UniverseIndex,
|
||||
|
||||
/// The representative Region Variable Id for this SCC. We prefer
|
||||
/// placeholders over existentially quantified variables, otherwise
|
||||
/// it's the one with the smallest Region Variable ID.
|
||||
representative: RegionVid,
|
||||
|
||||
/// Is the current representative a placeholder?
|
||||
representative_is_placeholder: bool,
|
||||
|
||||
/// Is the current representative existentially quantified?
|
||||
representative_is_existential: bool,
|
||||
}
|
||||
|
||||
impl scc::Annotation for RegionTracker {
|
||||
fn merge_scc(mut self, mut other: Self) -> Self {
|
||||
// Prefer any placeholder over any existential
|
||||
if other.representative_is_placeholder && self.representative_is_existential {
|
||||
other.merge_min_max_seen(&self);
|
||||
return other;
|
||||
}
|
||||
|
||||
if self.representative_is_placeholder && other.representative_is_existential
|
||||
|| (self.representative <= other.representative)
|
||||
{
|
||||
self.merge_min_max_seen(&other);
|
||||
return self;
|
||||
}
|
||||
other.merge_min_max_seen(&self);
|
||||
other
|
||||
}
|
||||
|
||||
fn merge_reached(mut self, other: Self) -> Self {
|
||||
// No update to in-component values, only add seen values.
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RegionTracker {
|
||||
fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
let (representative_is_placeholder, representative_is_existential) = match definition.origin
|
||||
{
|
||||
rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false),
|
||||
rustc_infer::infer::NllRegionVariableOrigin::Placeholder(_) => (true, false),
|
||||
rustc_infer::infer::NllRegionVariableOrigin::Existential { .. } => (false, true),
|
||||
};
|
||||
|
||||
let placeholder_universe =
|
||||
if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
|
||||
|
||||
Self {
|
||||
max_placeholder_universe_reached: placeholder_universe,
|
||||
min_reachable_universe: definition.universe,
|
||||
representative: rvid,
|
||||
representative_is_placeholder,
|
||||
representative_is_existential,
|
||||
}
|
||||
}
|
||||
fn universe(self) -> UniverseIndex {
|
||||
self.min_reachable_universe
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
other.max_placeholder_universe_reached,
|
||||
);
|
||||
|
||||
self.min_reachable_universe =
|
||||
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
|
||||
}
|
||||
|
||||
/// Returns `true` if during the annotated SCC reaches a placeholder
|
||||
/// with a universe larger than the smallest reachable one, `false` otherwise.
|
||||
pub fn has_incompatible_universes(&self) -> bool {
|
||||
self.universe().cannot_name(self.max_placeholder_universe_reached)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegionInferenceContext<'tcx> {
|
||||
pub var_infos: VarInfos,
|
||||
|
||||
@ -72,7 +163,7 @@ pub struct RegionInferenceContext<'tcx> {
|
||||
/// The SCC computed from `constraints` and the constraint
|
||||
/// graph. We have an edge from SCC A to SCC B if `A: B`. Used to
|
||||
/// compute the values of each region.
|
||||
constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
|
||||
constraint_sccs: Rc<ConstraintSccs>,
|
||||
|
||||
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
|
||||
/// `B: A`. This is used to compute the universal regions that are required
|
||||
@ -91,22 +182,6 @@ pub struct RegionInferenceContext<'tcx> {
|
||||
/// Map universe indexes to information on why we created it.
|
||||
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
|
||||
|
||||
/// Contains the minimum universe of any variable within the same
|
||||
/// SCC. We will ensure that no SCC contains values that are not
|
||||
/// visible from this index.
|
||||
scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
|
||||
|
||||
/// Contains the "representative" region of each SCC.
|
||||
/// It is defined as the one with the minimal RegionVid, favoring
|
||||
/// free regions, then placeholders, then existential regions.
|
||||
///
|
||||
/// It is a hacky way to manage checking regions for equality,
|
||||
/// since we can 'canonicalize' each region to the representative
|
||||
/// of its SCC and be sure that -- if they have the same repr --
|
||||
/// they *must* be equal (though not having the same repr does not
|
||||
/// mean they are unequal).
|
||||
scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
|
||||
|
||||
/// The final inferred values of the region variables; we compute
|
||||
/// one value per SCC. To get the value for any given *region*,
|
||||
/// you first find which scc it is a part of.
|
||||
@ -151,7 +226,7 @@ pub(crate) struct AppliedMemberConstraint {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RegionDefinition<'tcx> {
|
||||
pub struct RegionDefinition<'tcx> {
|
||||
/// What kind of variable is this -- a free region? existential
|
||||
/// variable? etc. (See the `NllRegionVariableOrigin` for more
|
||||
/// info.)
|
||||
@ -250,7 +325,7 @@ pub enum ExtraConstraintInfo {
|
||||
}
|
||||
|
||||
#[instrument(skip(infcx, sccs), level = "debug")]
|
||||
fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>) {
|
||||
fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
|
||||
use crate::renumber::RegionCtxt;
|
||||
|
||||
let var_to_origin = infcx.reg_var_to_origin.borrow();
|
||||
@ -264,7 +339,7 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, Con
|
||||
}
|
||||
debug!("{}", reg_vars_to_origins_str);
|
||||
|
||||
let num_components = sccs.scc_data().ranges().len();
|
||||
let num_components = sccs.num_sccs();
|
||||
let mut components = vec![FxIndexSet::default(); num_components];
|
||||
|
||||
for (reg_var_idx, scc_idx) in sccs.scc_indices().iter().enumerate() {
|
||||
@ -301,10 +376,11 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, Con
|
||||
|
||||
let mut scc_node_to_edges = FxIndexMap::default();
|
||||
for (scc_idx, repr) in components_representatives.iter() {
|
||||
let edges_range = sccs.scc_data().ranges()[*scc_idx].clone();
|
||||
let edges = &sccs.scc_data().all_successors()[edges_range];
|
||||
let edge_representatives =
|
||||
edges.iter().map(|scc_idx| components_representatives[scc_idx]).collect::<Vec<_>>();
|
||||
let edge_representatives = sccs
|
||||
.successors(*scc_idx)
|
||||
.iter()
|
||||
.map(|scc_idx| components_representatives[scc_idx])
|
||||
.collect::<Vec<_>>();
|
||||
scc_node_to_edges.insert((scc_idx, repr), edge_representatives);
|
||||
}
|
||||
|
||||
@ -320,7 +396,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// The `outlives_constraints` and `type_tests` are an initial set
|
||||
/// of constraints produced by the MIR type check.
|
||||
pub(crate) fn new(
|
||||
_infcx: &BorrowckInferCtxt<'tcx>,
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
var_infos: VarInfos,
|
||||
universal_regions: Rc<UniversalRegions<'tcx>>,
|
||||
placeholder_indices: Rc<PlaceholderIndices>,
|
||||
@ -343,13 +419,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
.map(|info| RegionDefinition::new(info.universe, info.origin))
|
||||
.collect();
|
||||
|
||||
let fr_static = universal_regions.fr_static;
|
||||
let constraints = Frozen::freeze(outlives_constraints);
|
||||
let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
|
||||
let fr_static = universal_regions.fr_static;
|
||||
let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static));
|
||||
let constraint_sccs = {
|
||||
let constraint_graph = constraints.graph(definitions.len());
|
||||
let region_graph = &constraint_graph.region_graph(&constraints, fr_static);
|
||||
let sccs = ConstraintSccs::new_with_annotation(®ion_graph, |r| {
|
||||
RegionTracker::new(r, &definitions[r])
|
||||
});
|
||||
Rc::new(sccs)
|
||||
};
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
sccs_info(_infcx, constraint_sccs.clone());
|
||||
sccs_info(infcx, &constraint_sccs);
|
||||
}
|
||||
|
||||
let mut scc_values =
|
||||
@ -360,10 +443,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
scc_values.merge_liveness(scc, region, &liveness_constraints);
|
||||
}
|
||||
|
||||
let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
|
||||
|
||||
let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
|
||||
|
||||
let member_constraints =
|
||||
Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
|
||||
|
||||
@ -378,8 +457,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
member_constraints,
|
||||
member_constraints_applied: Vec::new(),
|
||||
universe_causes,
|
||||
scc_universes,
|
||||
scc_representatives,
|
||||
scc_values,
|
||||
type_tests,
|
||||
universal_regions,
|
||||
@ -391,123 +468,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
result
|
||||
}
|
||||
|
||||
/// Each SCC is the combination of many region variables which
|
||||
/// have been equated. Therefore, we can associate a universe with
|
||||
/// each SCC which is minimum of all the universes of its
|
||||
/// constituent regions -- this is because whatever value the SCC
|
||||
/// takes on must be a value that each of the regions within the
|
||||
/// SCC could have as well. This implies that the SCC must have
|
||||
/// the minimum, or narrowest, universe.
|
||||
fn compute_scc_universes(
|
||||
constraint_sccs: &Sccs<RegionVid, ConstraintSccIndex>,
|
||||
definitions: &IndexSlice<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> IndexVec<ConstraintSccIndex, ty::UniverseIndex> {
|
||||
let num_sccs = constraint_sccs.num_sccs();
|
||||
let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs);
|
||||
|
||||
debug!("compute_scc_universes()");
|
||||
|
||||
// For each region R in universe U, ensure that the universe for the SCC
|
||||
// that contains R is "no bigger" than U. This effectively sets the universe
|
||||
// for each SCC to be the minimum of the regions within.
|
||||
for (region_vid, region_definition) in definitions.iter_enumerated() {
|
||||
let scc = constraint_sccs.scc(region_vid);
|
||||
let scc_universe = &mut scc_universes[scc];
|
||||
let scc_min = std::cmp::min(region_definition.universe, *scc_universe);
|
||||
if scc_min != *scc_universe {
|
||||
*scc_universe = scc_min;
|
||||
debug!(
|
||||
"compute_scc_universes: lowered universe of {scc:?} to {scc_min:?} \
|
||||
because it contains {region_vid:?} in {region_universe:?}",
|
||||
scc = scc,
|
||||
scc_min = scc_min,
|
||||
region_vid = region_vid,
|
||||
region_universe = region_definition.universe,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk each SCC `A` and `B` such that `A: B`
|
||||
// and ensure that universe(A) can see universe(B).
|
||||
//
|
||||
// This serves to enforce the 'empty/placeholder' hierarchy
|
||||
// (described in more detail on `RegionKind`):
|
||||
//
|
||||
// ```
|
||||
// static -----+
|
||||
// | |
|
||||
// empty(U0) placeholder(U1)
|
||||
// | /
|
||||
// empty(U1)
|
||||
// ```
|
||||
//
|
||||
// In particular, imagine we have variables R0 in U0 and R1
|
||||
// created in U1, and constraints like this;
|
||||
//
|
||||
// ```
|
||||
// R1: !1 // R1 outlives the placeholder in U1
|
||||
// R1: R0 // R1 outlives R0
|
||||
// ```
|
||||
//
|
||||
// Here, we wish for R1 to be `'static`, because it
|
||||
// cannot outlive `placeholder(U1)` and `empty(U0)` any other way.
|
||||
//
|
||||
// Thanks to this loop, what happens is that the `R1: R0`
|
||||
// constraint lowers the universe of `R1` to `U0`, which in turn
|
||||
// means that the `R1: !1` constraint will (later) cause
|
||||
// `R1` to become `'static`.
|
||||
for scc_a in constraint_sccs.all_sccs() {
|
||||
for &scc_b in constraint_sccs.successors(scc_a) {
|
||||
let scc_universe_a = scc_universes[scc_a];
|
||||
let scc_universe_b = scc_universes[scc_b];
|
||||
let scc_universe_min = std::cmp::min(scc_universe_a, scc_universe_b);
|
||||
if scc_universe_a != scc_universe_min {
|
||||
scc_universes[scc_a] = scc_universe_min;
|
||||
|
||||
debug!(
|
||||
"compute_scc_universes: lowered universe of {scc_a:?} to {scc_universe_min:?} \
|
||||
because {scc_a:?}: {scc_b:?} and {scc_b:?} is in universe {scc_universe_b:?}",
|
||||
scc_a = scc_a,
|
||||
scc_b = scc_b,
|
||||
scc_universe_min = scc_universe_min,
|
||||
scc_universe_b = scc_universe_b
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes);
|
||||
|
||||
scc_universes
|
||||
}
|
||||
|
||||
/// For each SCC, we compute a unique `RegionVid`. See the
|
||||
/// `scc_representatives` field of `RegionInferenceContext` for
|
||||
/// more details.
|
||||
fn compute_scc_representatives(
|
||||
constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
|
||||
definitions: &IndexSlice<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> IndexVec<ConstraintSccIndex, ty::RegionVid> {
|
||||
let num_sccs = constraints_scc.num_sccs();
|
||||
let mut scc_representatives = IndexVec::from_elem_n(RegionVid::MAX, num_sccs);
|
||||
|
||||
// Iterate over all RegionVids *in-order* and pick the least RegionVid as the
|
||||
// representative of its SCC. This naturally prefers free regions over others.
|
||||
for (vid, def) in definitions.iter_enumerated() {
|
||||
let repr = &mut scc_representatives[constraints_scc.scc(vid)];
|
||||
if *repr == ty::RegionVid::MAX {
|
||||
*repr = vid;
|
||||
} else if matches!(def.origin, NllRegionVariableOrigin::Placeholder(_))
|
||||
&& matches!(definitions[*repr].origin, NllRegionVariableOrigin::Existential { .. })
|
||||
{
|
||||
// Pick placeholders over existentials even if they have a greater RegionVid.
|
||||
*repr = vid;
|
||||
}
|
||||
}
|
||||
|
||||
scc_representatives
|
||||
}
|
||||
|
||||
/// Initializes the region variables for each universally
|
||||
/// quantified region (lifetime parameter). The first N variables
|
||||
/// always correspond to the regions appearing in the function
|
||||
@ -528,12 +488,45 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// and (b) any universally quantified regions that it outlives,
|
||||
/// which in this case is just itself. R1 (`'b`) in contrast also
|
||||
/// outlives `'a` and hence contains R0 and R1.
|
||||
///
|
||||
/// This bit of logic also handles invalid universe relations
|
||||
/// for higher-kinded types.
|
||||
///
|
||||
/// We Walk each SCC `A` and `B` such that `A: B`
|
||||
/// and ensure that universe(A) can see universe(B).
|
||||
///
|
||||
/// This serves to enforce the 'empty/placeholder' hierarchy
|
||||
/// (described in more detail on `RegionKind`):
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// static -----+
|
||||
/// | |
|
||||
/// empty(U0) placeholder(U1)
|
||||
/// | /
|
||||
/// empty(U1)
|
||||
/// ```
|
||||
///
|
||||
/// In particular, imagine we have variables R0 in U0 and R1
|
||||
/// created in U1, and constraints like this;
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// R1: !1 // R1 outlives the placeholder in U1
|
||||
/// R1: R0 // R1 outlives R0
|
||||
/// ```
|
||||
///
|
||||
/// Here, we wish for R1 to be `'static`, because it
|
||||
/// cannot outlive `placeholder(U1)` and `empty(U0)` any other way.
|
||||
///
|
||||
/// Thanks to this loop, what happens is that the `R1: R0`
|
||||
/// constraint has lowered the universe of `R1` to `U0`, which in turn
|
||||
/// means that the `R1: !1` constraint here will cause
|
||||
/// `R1` to become `'static`.
|
||||
fn init_free_and_bound_regions(&mut self) {
|
||||
// Update the names (if any)
|
||||
// This iterator has unstable order but we collect it all into an IndexVec
|
||||
for (external_name, variable) in self.universal_regions.named_universal_regions() {
|
||||
debug!(
|
||||
"init_universal_regions: region {:?} has external name {:?}",
|
||||
"init_free_and_bound_regions: region {:?} has external name {:?}",
|
||||
variable, external_name
|
||||
);
|
||||
self.definitions[variable].external_name = Some(external_name);
|
||||
@ -559,7 +552,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// its universe `ui` and its extensions. So we
|
||||
// can't just add it into `scc` unless the
|
||||
// universe of the scc can name this region.
|
||||
let scc_universe = self.scc_universes[scc];
|
||||
let scc_universe = self.scc_universe(scc);
|
||||
if scc_universe.can_name(placeholder.universe) {
|
||||
self.scc_values.add_element(scc, placeholder);
|
||||
} else {
|
||||
@ -640,8 +633,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
/// Returns access to the value of `r` for debugging purposes.
|
||||
pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
|
||||
let scc = self.constraint_sccs.scc(r);
|
||||
self.scc_universes[scc]
|
||||
self.scc_universe(self.constraint_sccs.scc(r))
|
||||
}
|
||||
|
||||
/// Once region solving has completed, this function will return the member constraints that
|
||||
@ -737,8 +729,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// SCC. For each SCC, we visit its successors and compute
|
||||
// their values, then we union all those values to get our
|
||||
// own.
|
||||
let constraint_sccs = self.constraint_sccs.clone();
|
||||
for scc in constraint_sccs.all_sccs() {
|
||||
for scc in self.constraint_sccs.all_sccs() {
|
||||
self.compute_value_for_scc(scc);
|
||||
}
|
||||
|
||||
@ -817,20 +808,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// if one exists.
|
||||
for c_r in &mut choice_regions {
|
||||
let scc = self.constraint_sccs.scc(*c_r);
|
||||
*c_r = self.scc_representatives[scc];
|
||||
*c_r = self.scc_representative(scc);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
if !self.constraint_sccs().annotation(scc).universe().is_root() {
|
||||
return;
|
||||
}
|
||||
debug_assert!(
|
||||
self.scc_values.placeholders_contained_in(scc).next().is_none(),
|
||||
"scc {:?} in a member constraint has placeholder value: {:?}",
|
||||
scc,
|
||||
self.scc_values.region_value_str(scc),
|
||||
);
|
||||
|
||||
// The existing value for `scc` is a lower-bound. This will
|
||||
// consist of some set `{P} + {LB}` of points `{P}` and
|
||||
@ -900,12 +886,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// in `scc_a`. Used during constraint propagation, and only once
|
||||
/// the value of `scc_b` has been computed.
|
||||
fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
|
||||
let universe_a = self.scc_universes[scc_a];
|
||||
let universe_a = self.constraint_sccs().annotation(scc_a).universe();
|
||||
let universe_b = self.constraint_sccs().annotation(scc_b).universe();
|
||||
|
||||
// Quick check: if scc_b's declared universe is a subset of
|
||||
// scc_a's declared universe (typically, both are ROOT), then
|
||||
// it cannot contain any problematic universe elements.
|
||||
if universe_a.can_name(self.scc_universes[scc_b]) {
|
||||
if universe_a.can_name(universe_b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1033,7 +1020,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
debug!(
|
||||
"lower_bound = {:?} r_scc={:?} universe={:?}",
|
||||
lower_bound, r_scc, self.scc_universes[r_scc]
|
||||
lower_bound,
|
||||
r_scc,
|
||||
self.constraint_sccs.annotation(r_scc).universe()
|
||||
);
|
||||
|
||||
// If the type test requires that `T: 'a` where `'a` is a
|
||||
@ -1321,7 +1310,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
tcx.fold_regions(value, |r, _db| {
|
||||
let vid = self.to_region_vid(r);
|
||||
let scc = self.constraint_sccs.scc(vid);
|
||||
let repr = self.scc_representatives[scc];
|
||||
let repr = self.scc_representative(scc);
|
||||
ty::Region::new_var(tcx, repr)
|
||||
})
|
||||
}
|
||||
@ -1547,6 +1536,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The minimum universe of any variable reachable from this
|
||||
/// SCC, inside or outside of it.
|
||||
fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
|
||||
self.constraint_sccs().annotation(scc).universe()
|
||||
}
|
||||
/// Checks the final value for the free region `fr` to see if it
|
||||
/// grew too large. In particular, examine what `end(X)` points
|
||||
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
|
||||
@ -1566,8 +1560,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
// Because this free region must be in the ROOT universe, we
|
||||
// know it cannot contain any bound universes.
|
||||
assert!(self.scc_universes[longer_fr_scc].is_root());
|
||||
debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none());
|
||||
assert!(self.scc_universe(longer_fr_scc).is_root());
|
||||
|
||||
// Only check all of the relations for the main representative of each
|
||||
// SCC, otherwise just check that we outlive said representative. This
|
||||
@ -1575,7 +1568,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// closures.
|
||||
// Note that the representative will be a universal region if there is
|
||||
// one in this SCC, so we will always check the representative here.
|
||||
let representative = self.scc_representatives[longer_fr_scc];
|
||||
let representative = self.scc_representative(longer_fr_scc);
|
||||
if representative != longer_fr {
|
||||
if let RegionRelationCheckResult::Error = self.check_universal_region_relation(
|
||||
longer_fr,
|
||||
@ -1796,16 +1789,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// `true` if `r1` cannot name that placeholder in its
|
||||
/// value; otherwise, returns `false`.
|
||||
pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
|
||||
debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
|
||||
|
||||
match self.definitions[r2].origin {
|
||||
NllRegionVariableOrigin::Placeholder(placeholder) => {
|
||||
let universe1 = self.definitions[r1].universe;
|
||||
let r1_universe = self.definitions[r1].universe;
|
||||
debug!(
|
||||
"cannot_name_value_of: universe1={:?} placeholder={:?}",
|
||||
universe1, placeholder
|
||||
"cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}",
|
||||
placeholder
|
||||
);
|
||||
universe1.cannot_name(placeholder.universe)
|
||||
r1_universe.cannot_name(placeholder.universe)
|
||||
}
|
||||
|
||||
NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => {
|
||||
@ -1835,6 +1826,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
///
|
||||
/// Returns: a series of constraints as well as the region `R`
|
||||
/// that passed the target test.
|
||||
#[instrument(skip(self, target_test), ret)]
|
||||
pub(crate) fn find_constraint_paths_between_regions(
|
||||
&self,
|
||||
from_region: RegionVid,
|
||||
@ -1932,7 +1924,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
#[instrument(skip(self), level = "trace", ret)]
|
||||
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
|
||||
trace!(scc = ?self.constraint_sccs.scc(fr1));
|
||||
trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]);
|
||||
trace!(universe = ?self.region_universe(fr1));
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `location`
|
||||
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
|
||||
@ -2252,8 +2244,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// This can be used to quickly under-approximate the regions which are equal to each other
|
||||
/// and their relative orderings.
|
||||
// This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`.
|
||||
pub fn constraint_sccs(&self) -> &Sccs<RegionVid, ConstraintSccIndex> {
|
||||
self.constraint_sccs.as_ref()
|
||||
pub fn constraint_sccs(&self) -> &ConstraintSccs {
|
||||
&self.constraint_sccs
|
||||
}
|
||||
|
||||
/// Access to the region graph, built from the outlives constraints.
|
||||
@ -2282,6 +2274,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
let point = self.liveness_constraints.point_from_location(location);
|
||||
self.liveness_constraints.is_loan_live_at(loan_idx, point)
|
||||
}
|
||||
|
||||
/// Returns the representative `RegionVid` for a given SCC.
|
||||
/// See `RegionTracker` for how a region variable ID is chosen.
|
||||
///
|
||||
/// It is a hacky way to manage checking regions for equality,
|
||||
/// since we can 'canonicalize' each region to the representative
|
||||
/// of its SCC and be sure that -- if they have the same repr --
|
||||
/// they *must* be equal (though not having the same repr does not
|
||||
/// mean they are unequal).
|
||||
fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
|
||||
self.constraint_sccs.annotation(scc).representative
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionDefinition<'tcx> {
|
||||
|
@ -85,7 +85,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// Use the SCC representative instead of directly using `region`.
|
||||
// See [rustc-dev-guide chapter] § "Strict lifetime equality".
|
||||
let scc = self.constraint_sccs.scc(region.as_var());
|
||||
let vid = self.scc_representatives[scc];
|
||||
let vid = self.scc_representative(scc);
|
||||
let named = match self.definitions[vid].origin {
|
||||
// Iterate over all universal regions in a consistent order and find the
|
||||
// *first* equal region. This makes sure that equal lifetimes will have
|
||||
@ -213,7 +213,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
let scc = self.constraint_sccs.scc(vid);
|
||||
|
||||
// Special handling of higher-ranked regions.
|
||||
if !self.scc_universes[scc].is_root() {
|
||||
if !self.scc_universe(scc).is_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)) => {
|
||||
|
@ -1,9 +1,10 @@
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_infer::infer::relate::{ObligationEmittingRelation, StructurallyRelateAliases};
|
||||
use rustc_infer::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
|
||||
use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_infer::infer::NllRegionVariableOrigin;
|
||||
use rustc_infer::traits::{Obligation, PredicateObligations};
|
||||
use rustc_infer::traits::solve::Goal;
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
@ -153,9 +154,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
|
||||
"expected at least one opaque type in `relate_opaques`, got {a} and {b}."
|
||||
),
|
||||
};
|
||||
let cause = ObligationCause::dummy_with_span(self.span());
|
||||
let obligations = infcx.handle_opaque_type(a, b, &cause, self.param_env())?.obligations;
|
||||
self.register_obligations(obligations);
|
||||
self.register_goals(infcx.handle_opaque_type(a, b, self.span(), self.param_env())?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -533,7 +532,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
|
||||
impl<'bccx, 'tcx> PredicateEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
|
||||
fn span(&self) -> Span {
|
||||
self.locations.span(self.type_checker.body)
|
||||
}
|
||||
@ -550,22 +549,32 @@ impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item: ty::Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
self.register_obligations(
|
||||
obligations
|
||||
.into_iter()
|
||||
.map(|to_pred| {
|
||||
Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred)
|
||||
})
|
||||
.collect(),
|
||||
let tcx = self.tcx();
|
||||
let param_env = self.param_env();
|
||||
self.register_goals(
|
||||
obligations.into_iter().map(|to_pred| Goal::new(tcx, param_env, to_pred)),
|
||||
);
|
||||
}
|
||||
|
||||
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
|
||||
fn register_goals(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
let _: Result<_, ErrorGuaranteed> = self.type_checker.fully_perform_op(
|
||||
self.locations,
|
||||
self.category,
|
||||
InstantiateOpaqueType {
|
||||
obligations,
|
||||
obligations: obligations
|
||||
.into_iter()
|
||||
.map(|goal| {
|
||||
Obligation::new(
|
||||
self.tcx(),
|
||||
ObligationCause::dummy_with_span(self.span()),
|
||||
goal.param_env,
|
||||
goal.predicate,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
// These fields are filled in during execution of the operation
|
||||
base_universe: None,
|
||||
region_constraints: None,
|
||||
@ -573,7 +582,7 @@ impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx
|
||||
);
|
||||
}
|
||||
|
||||
fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
|
||||
ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
|
||||
a.into(),
|
||||
|
@ -1,12 +1,12 @@
|
||||
//! This crate contains implementations of built-in macros and other code generating facilities
|
||||
//! injecting code into the crate before it is lowered to HIR.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(decl_macro)]
|
||||
@ -15,7 +15,9 @@
|
||||
#![feature(lint_reasons)]
|
||||
#![feature(proc_macro_internals)]
|
||||
#![feature(proc_macro_quote)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(try_blocks)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
|
@ -2,20 +2,23 @@
|
||||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
trait T {
|
||||
type Item;
|
||||
}
|
||||
mod helper {
|
||||
pub trait T {
|
||||
type Item;
|
||||
}
|
||||
|
||||
type Alias<'a> = impl T<Item = &'a ()>;
|
||||
pub type Alias<'a> = impl T<Item = &'a ()>;
|
||||
|
||||
struct S;
|
||||
impl<'a> T for &'a S {
|
||||
type Item = &'a ();
|
||||
}
|
||||
struct S;
|
||||
impl<'a> T for &'a S {
|
||||
type Item = &'a ();
|
||||
}
|
||||
|
||||
fn filter_positive<'a>() -> Alias<'a> {
|
||||
&S
|
||||
pub fn filter_positive<'a>() -> Alias<'a> {
|
||||
&S
|
||||
}
|
||||
}
|
||||
use helper::*;
|
||||
|
||||
fn with_positive(fun: impl Fn(Alias<'_>)) {
|
||||
fun(filter_positive());
|
||||
|
@ -12,7 +12,7 @@ diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/wind
|
||||
index ad8e01bfa9b..9ca8e4c16ce 100644
|
||||
--- a/library/std/src/sys/pal/windows/c.rs
|
||||
+++ b/library/std/src/sys/pal/windows/c.rs
|
||||
@@ -323,7 +323,7 @@ pub unsafe fn NtWriteFile(
|
||||
@@ -312,7 +312,7 @@ pub unsafe fn NtWriteFile(
|
||||
|
||||
// Use raw-dylib to import ProcessPrng as we can't rely on there being an import library.
|
||||
cfg_if::cfg_if! {
|
||||
@ -26,8 +26,8 @@ index e427546222a..f2fe42a4d51 100644
|
||||
--- a/library/std/src/sys/pal/windows/rand.rs
|
||||
+++ b/library/std/src/sys/pal/windows/rand.rs
|
||||
@@ -2,7 +2,7 @@
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
|
||||
use crate::sys::c;
|
||||
|
||||
-#[cfg(not(target_vendor = "win7"))]
|
||||
+#[cfg(any())]
|
||||
|
@ -1,13 +1,16 @@
|
||||
#![cfg_attr(doc, allow(internal_features))]
|
||||
#![cfg_attr(doc, feature(rustdoc_internals))]
|
||||
#![cfg_attr(doc, doc(rust_logo))]
|
||||
#![feature(rustc_private)]
|
||||
// Note: please avoid adding other feature gates where possible
|
||||
// tidy-alphabetical-start
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(doc, allow(internal_features))]
|
||||
#![cfg_attr(doc, doc(rust_logo))]
|
||||
#![cfg_attr(doc, feature(rustdoc_internals))]
|
||||
// Note: please avoid adding other feature gates where possible
|
||||
#![feature(rustc_private)]
|
||||
// Note: please avoid adding other feature gates where possible
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![warn(unused_lifetimes)]
|
||||
#![warn(unreachable_pub)]
|
||||
#![warn(unused_lifetimes)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate jobserver;
|
||||
#[macro_use]
|
||||
|
@ -142,6 +142,14 @@ pub unsafe fn create_module<'ll>(
|
||||
}
|
||||
}
|
||||
|
||||
if llvm_version < (19, 0, 0) {
|
||||
if sess.target.arch == "loongarch64" {
|
||||
// LLVM 19 updates the LoongArch64 data layout.
|
||||
// See https://github.com/llvm/llvm-project/pull/93814
|
||||
target_data_layout = target_data_layout.replace("-n32:64", "-n64");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the data-layout values hardcoded remain the defaults.
|
||||
{
|
||||
let tm = crate::back::write::create_informational_target_machine(tcx.sess);
|
||||
|
@ -4,16 +4,18 @@
|
||||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(extern_types)]
|
||||
#![feature(hash_raw_entry)]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use back::owned_target_machine::OwnedTargetMachine;
|
||||
use back::write::{create_informational_target_machine, create_target_machine};
|
||||
|
@ -1,15 +1,17 @@
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(try_blocks)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
//! This crate contains codegen code that is used by all codegen backends (LLVM and others).
|
||||
//! The backend-agnostic functions of this crate use functions defined in various traits that
|
||||
|
@ -274,9 +274,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
// Let's make sure v is sign-extended *if* it has a signed type.
|
||||
let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`.
|
||||
|
||||
let v = scalar.to_bits(src_layout.size)?;
|
||||
let v = if signed { self.sign_extend(v, src_layout) } else { v };
|
||||
trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty);
|
||||
let v = match src_layout.ty.kind() {
|
||||
Uint(_) | RawPtr(..) | FnPtr(..) => scalar.to_uint(src_layout.size)?,
|
||||
Int(_) => scalar.to_int(src_layout.size)? as u128, // we will cast back to `i128` below if the sign matters
|
||||
Bool => scalar.to_bool()?.into(),
|
||||
Char => scalar.to_char()?.into(),
|
||||
_ => span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty),
|
||||
};
|
||||
|
||||
Ok(match *cast_ty.kind() {
|
||||
// int -> int
|
||||
@ -383,7 +387,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
|
||||
(&ty::Array(_, length), &ty::Slice(_)) => {
|
||||
let ptr = self.read_pointer(src)?;
|
||||
// u64 cast is from usize to u64, which is always good
|
||||
let val = Immediate::new_slice(
|
||||
ptr,
|
||||
length.eval_target_usize(*self.tcx, self.param_env),
|
||||
@ -401,13 +404,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
let (old_data, old_vptr) = val.to_scalar_pair();
|
||||
let old_data = old_data.to_pointer(self)?;
|
||||
let old_vptr = old_vptr.to_pointer(self)?;
|
||||
let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
|
||||
if old_trait != data_a.principal() {
|
||||
throw_ub!(InvalidVTableTrait {
|
||||
expected_trait: data_a,
|
||||
vtable_trait: old_trait,
|
||||
});
|
||||
}
|
||||
let ty = self.get_ptr_vtable_ty(old_vptr, Some(data_a))?;
|
||||
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
|
||||
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
|
||||
}
|
||||
|
@ -765,10 +765,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
}
|
||||
Ok(Some((full_size, full_align)))
|
||||
}
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
ty::Dynamic(expected_trait, _, ty::Dyn) => {
|
||||
let vtable = metadata.unwrap_meta().to_pointer(self)?;
|
||||
// Read size and align from vtable (already checks size).
|
||||
Ok(Some(self.get_vtable_size_and_align(vtable)?))
|
||||
Ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?))
|
||||
}
|
||||
|
||||
ty::Slice(_) | ty::Str => {
|
||||
|
@ -197,7 +197,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
|
||||
let layout_val = self.layout_of(instance_args.type_at(0))?;
|
||||
let val = self.read_scalar(&args[0])?;
|
||||
let val_bits = val.to_bits(layout_val.size)?;
|
||||
let val_bits = val.to_bits(layout_val.size)?; // sign is ignored here
|
||||
|
||||
let layout_raw_shift = self.layout_of(self.tcx.types.u32)?;
|
||||
let raw_shift = self.read_scalar(&args[1])?;
|
||||
@ -432,12 +432,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
||||
sym::vtable_size => {
|
||||
let ptr = self.read_pointer(&args[0])?;
|
||||
let (size, _align) = self.get_vtable_size_and_align(ptr)?;
|
||||
// `None` because we don't know which trait to expect here; any vtable is okay.
|
||||
let (size, _align) = self.get_vtable_size_and_align(ptr, None)?;
|
||||
self.write_scalar(Scalar::from_target_usize(size.bytes(), self), dest)?;
|
||||
}
|
||||
sym::vtable_align => {
|
||||
let ptr = self.read_pointer(&args[0])?;
|
||||
let (_size, align) = self.get_vtable_size_and_align(ptr)?;
|
||||
// `None` because we don't know which trait to expect here; any vtable is okay.
|
||||
let (_size, align) = self.get_vtable_size_and_align(ptr, None)?;
|
||||
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
|
||||
}
|
||||
|
||||
@ -484,7 +486,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
ret_layout: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
assert!(layout.ty.is_integral(), "invalid type for numeric intrinsic: {}", layout.ty);
|
||||
let bits = val.to_bits(layout.size)?;
|
||||
let bits = val.to_bits(layout.size)?; // these operations all ignore the sign
|
||||
let extra = 128 - u128::from(layout.size.bits());
|
||||
let bits_out = match name {
|
||||
sym::ctpop => u128::from(bits.count_ones()),
|
||||
@ -519,6 +521,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
|
||||
// First, check x % y != 0 (or if that computation overflows).
|
||||
let rem = self.binary_op(BinOp::Rem, a, b)?;
|
||||
// sign does not matter for 0 test, so `to_bits` is fine
|
||||
if rem.to_scalar().to_bits(a.layout.size)? != 0 {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_exact_div_has_remainder,
|
||||
@ -545,22 +548,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair();
|
||||
Ok(if overflowed.to_bool()? {
|
||||
let size = l.layout.size;
|
||||
let num_bits = size.bits();
|
||||
if l.layout.abi.is_signed() {
|
||||
// For signed ints the saturated value depends on the sign of the first
|
||||
// term since the sign of the second term can be inferred from this and
|
||||
// the fact that the operation has overflowed (if either is 0 no
|
||||
// overflow can occur)
|
||||
let first_term: u128 = l.to_scalar().to_bits(l.layout.size)?;
|
||||
let first_term_positive = first_term & (1 << (num_bits - 1)) == 0;
|
||||
if first_term_positive {
|
||||
let first_term: i128 = l.to_scalar().to_int(l.layout.size)?;
|
||||
if first_term >= 0 {
|
||||
// Negative overflow not possible since the positive first term
|
||||
// can only increase an (in range) negative term for addition
|
||||
// or corresponding negated positive term for subtraction
|
||||
// or corresponding negated positive term for subtraction.
|
||||
Scalar::from_int(size.signed_int_max(), size)
|
||||
} else {
|
||||
// Positive overflow not possible for similar reason
|
||||
// max negative
|
||||
// Positive overflow not possible for similar reason.
|
||||
Scalar::from_int(size.signed_int_min(), size)
|
||||
}
|
||||
} else {
|
||||
|
@ -867,19 +867,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
.ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into())
|
||||
}
|
||||
|
||||
pub fn get_ptr_vtable(
|
||||
/// Get the dynamic type of the given vtable pointer.
|
||||
/// If `expected_trait` is `Some`, it must be a vtable for the given trait.
|
||||
pub fn get_ptr_vtable_ty(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)> {
|
||||
expected_trait: Option<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>,
|
||||
) -> InterpResult<'tcx, Ty<'tcx>> {
|
||||
trace!("get_ptr_vtable({:?})", ptr);
|
||||
let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
|
||||
if offset.bytes() != 0 {
|
||||
throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
|
||||
}
|
||||
match self.tcx.try_get_global_alloc(alloc_id) {
|
||||
Some(GlobalAlloc::VTable(ty, trait_ref)) => Ok((ty, trait_ref)),
|
||||
_ => throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))),
|
||||
let Some(GlobalAlloc::VTable(ty, vtable_trait)) = self.tcx.try_get_global_alloc(alloc_id)
|
||||
else {
|
||||
throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
|
||||
};
|
||||
if let Some(expected_trait) = expected_trait {
|
||||
self.check_vtable_for_type(vtable_trait, expected_trait)?;
|
||||
}
|
||||
Ok(ty)
|
||||
}
|
||||
|
||||
pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
|
||||
|
@ -437,23 +437,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
};
|
||||
Ok(ImmTy::from_scalar(res, layout))
|
||||
}
|
||||
_ if layout.ty.is_integral() => {
|
||||
let val = val.to_scalar();
|
||||
let val = val.to_bits(layout.size)?;
|
||||
ty::Int(..) => {
|
||||
let val = val.to_scalar().to_int(layout.size)?;
|
||||
let res = match un_op {
|
||||
Not => self.truncate(!val, layout), // bitwise negation, then truncate
|
||||
Neg => {
|
||||
// arithmetic negation
|
||||
assert!(layout.abi.is_signed());
|
||||
let val = self.sign_extend(val, layout) as i128;
|
||||
let res = val.wrapping_neg();
|
||||
let res = res as u128;
|
||||
// Truncate to target type.
|
||||
self.truncate(res, layout)
|
||||
}
|
||||
Not => !val,
|
||||
Neg => val.wrapping_neg(),
|
||||
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
|
||||
};
|
||||
Ok(ImmTy::from_uint(res, layout))
|
||||
let res = ScalarInt::truncate_from_int(res, layout.size).0;
|
||||
Ok(ImmTy::from_scalar(res.into(), layout))
|
||||
}
|
||||
ty::Uint(..) => {
|
||||
let val = val.to_scalar().to_uint(layout.size)?;
|
||||
let res = match un_op {
|
||||
Not => !val,
|
||||
_ => span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op),
|
||||
};
|
||||
let res = ScalarInt::truncate_from_uint(res, layout.size).0;
|
||||
Ok(ImmTy::from_scalar(res.into(), layout))
|
||||
}
|
||||
ty::RawPtr(..) => {
|
||||
assert_eq!(un_op, PtrMetadata);
|
||||
|
@ -9,7 +9,6 @@ use tracing::{instrument, trace};
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::{bug, span_bug};
|
||||
@ -1018,54 +1017,6 @@ where
|
||||
let layout = self.layout_of(raw.ty)?;
|
||||
Ok(self.ptr_to_mplace(ptr.into(), layout))
|
||||
}
|
||||
|
||||
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
|
||||
/// Aso returns the vtable.
|
||||
pub(super) fn unpack_dyn_trait(
|
||||
&self,
|
||||
mplace: &MPlaceTy<'tcx, M::Provenance>,
|
||||
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer<Option<M::Provenance>>)> {
|
||||
assert!(
|
||||
matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)),
|
||||
"`unpack_dyn_trait` only makes sense on `dyn*` types"
|
||||
);
|
||||
let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
|
||||
let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?;
|
||||
if expected_trait.principal() != vtable_trait {
|
||||
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
|
||||
}
|
||||
// This is a kind of transmute, from a place with unsized type and metadata to
|
||||
// a place with sized type and no metadata.
|
||||
let layout = self.layout_of(ty)?;
|
||||
let mplace =
|
||||
MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout };
|
||||
Ok((mplace, vtable))
|
||||
}
|
||||
|
||||
/// Turn a `dyn* Trait` type into an value with the actual dynamic type.
|
||||
/// Also returns the vtable.
|
||||
pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
val: &P,
|
||||
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> InterpResult<'tcx, (P, Pointer<Option<M::Provenance>>)> {
|
||||
assert!(
|
||||
matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
|
||||
"`unpack_dyn_star` only makes sense on `dyn*` types"
|
||||
);
|
||||
let data = self.project_field(val, 0)?;
|
||||
let vtable = self.project_field(val, 1)?;
|
||||
let vtable = self.read_pointer(&vtable.to_op(self)?)?;
|
||||
let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?;
|
||||
if expected_trait.principal() != vtable_trait {
|
||||
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
|
||||
}
|
||||
// `data` is already the right thing but has the wrong type. So we transmute it.
|
||||
let layout = self.layout_of(ty)?;
|
||||
let data = data.transmute(layout, self)?;
|
||||
Ok((data, vtable))
|
||||
}
|
||||
}
|
||||
|
||||
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use either::Either;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use tracing::trace;
|
||||
|
||||
use rustc_middle::span_bug;
|
||||
@ -827,20 +828,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
};
|
||||
|
||||
// Obtain the underlying trait we are working on, and the adjusted receiver argument.
|
||||
let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) =
|
||||
let (dyn_trait, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) =
|
||||
receiver_place.layout.ty.kind()
|
||||
{
|
||||
let (recv, vptr) = self.unpack_dyn_star(&receiver_place, data)?;
|
||||
let (dyn_ty, _dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
let recv = self.unpack_dyn_star(&receiver_place, data)?;
|
||||
|
||||
(vptr, dyn_ty, recv.ptr())
|
||||
(data.principal(), recv.layout.ty, recv.ptr())
|
||||
} else {
|
||||
// Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`.
|
||||
// (For that reason we also cannot use `unpack_dyn_trait`.)
|
||||
let receiver_tail = self
|
||||
.tcx
|
||||
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
|
||||
let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else {
|
||||
let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"dynamic call on non-`dyn` type {}",
|
||||
@ -851,25 +851,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?;
|
||||
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
if dyn_trait != data.principal() {
|
||||
throw_ub!(InvalidVTableTrait {
|
||||
expected_trait: data,
|
||||
vtable_trait: dyn_trait,
|
||||
});
|
||||
}
|
||||
let dyn_ty = self.get_ptr_vtable_ty(vptr, Some(receiver_trait))?;
|
||||
|
||||
// It might be surprising that we use a pointer as the receiver even if this
|
||||
// is a by-val case; this works because by-val passing of an unsized `dyn
|
||||
// Trait` to a function is actually desugared to a pointer.
|
||||
(vptr, dyn_ty, receiver_place.ptr())
|
||||
(receiver_trait.principal(), dyn_ty, receiver_place.ptr())
|
||||
};
|
||||
|
||||
// Now determine the actual method to call. We can do that in two different ways and
|
||||
// compare them to ensure everything fits.
|
||||
let Some(ty::VtblEntry::Method(fn_inst)) =
|
||||
self.get_vtable_entries(vptr)?.get(idx).copied()
|
||||
else {
|
||||
let vtable_entries = if let Some(dyn_trait) = dyn_trait {
|
||||
let trait_ref = dyn_trait.with_self_ty(*self.tcx, dyn_ty);
|
||||
let trait_ref = self.tcx.erase_regions(trait_ref);
|
||||
self.tcx.vtable_entries(trait_ref)
|
||||
} else {
|
||||
TyCtxt::COMMON_VTABLE_ENTRIES
|
||||
};
|
||||
let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else {
|
||||
// FIXME(fee1-dead) these could be variants of the UB info enum instead of this
|
||||
throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method);
|
||||
};
|
||||
@ -898,7 +897,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty);
|
||||
args[0] = FnArg::Copy(
|
||||
ImmTy::from_immediate(
|
||||
Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
|
||||
Scalar::from_maybe_pointer(adjusted_recv, self).into(),
|
||||
self.layout_of(receiver_ty)?,
|
||||
)
|
||||
.into(),
|
||||
@ -974,11 +973,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
let place = match place.layout.ty.kind() {
|
||||
ty::Dynamic(data, _, ty::Dyn) => {
|
||||
// Dropping a trait object. Need to find actual drop fn.
|
||||
self.unpack_dyn_trait(&place, data)?.0
|
||||
self.unpack_dyn_trait(&place, data)?
|
||||
}
|
||||
ty::Dynamic(data, _, ty::DynStar) => {
|
||||
// Dropping a `dyn*`. Need to find actual drop fn.
|
||||
self.unpack_dyn_star(&place, data)?.0
|
||||
self.unpack_dyn_star(&place, data)?
|
||||
}
|
||||
_ => {
|
||||
debug_assert_eq!(
|
||||
|
@ -1,11 +1,14 @@
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::mir::interpret::{InterpResult, Pointer};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use tracing::trace;
|
||||
|
||||
use super::util::ensure_monomorphic_enough;
|
||||
use super::{InterpCx, Machine};
|
||||
use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable};
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
|
||||
@ -33,28 +36,90 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
Ok(vtable_ptr.into())
|
||||
}
|
||||
|
||||
/// Returns a high-level representation of the entries of the given vtable.
|
||||
pub fn get_vtable_entries(
|
||||
&self,
|
||||
vtable: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> {
|
||||
let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?;
|
||||
Ok(if let Some(poly_trait_ref) = poly_trait_ref {
|
||||
let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
|
||||
let trait_ref = self.tcx.erase_regions(trait_ref);
|
||||
self.tcx.vtable_entries(trait_ref)
|
||||
} else {
|
||||
TyCtxt::COMMON_VTABLE_ENTRIES
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_vtable_size_and_align(
|
||||
&self,
|
||||
vtable: Pointer<Option<M::Provenance>>,
|
||||
expected_trait: Option<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>,
|
||||
) -> InterpResult<'tcx, (Size, Align)> {
|
||||
let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?;
|
||||
let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
assert!(layout.is_sized(), "there are no vtables for unsized types");
|
||||
Ok((layout.size, layout.align.abi))
|
||||
}
|
||||
|
||||
/// Check that the given vtable trait is valid for a pointer/reference/place with the given
|
||||
/// expected trait type.
|
||||
pub(super) fn check_vtable_for_type(
|
||||
&self,
|
||||
vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
||||
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Fast path: if they are equal, it's all fine.
|
||||
if expected_trait.principal() == vtable_trait {
|
||||
return Ok(());
|
||||
}
|
||||
if let (Some(expected_trait), Some(vtable_trait)) =
|
||||
(expected_trait.principal(), vtable_trait)
|
||||
{
|
||||
// Slow path: spin up an inference context to check if these traits are sufficiently equal.
|
||||
let infcx = self.tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let cause = ObligationCause::dummy_with_span(self.cur_span());
|
||||
// equate the two trait refs after normalization
|
||||
let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait);
|
||||
let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait);
|
||||
if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() {
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
// All good.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
|
||||
}
|
||||
|
||||
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
|
||||
pub(super) fn unpack_dyn_trait(
|
||||
&self,
|
||||
mplace: &MPlaceTy<'tcx, M::Provenance>,
|
||||
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
assert!(
|
||||
matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)),
|
||||
"`unpack_dyn_trait` only makes sense on `dyn*` types"
|
||||
);
|
||||
let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
|
||||
let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?;
|
||||
// This is a kind of transmute, from a place with unsized type and metadata to
|
||||
// a place with sized type and no metadata.
|
||||
let layout = self.layout_of(ty)?;
|
||||
let mplace = mplace.offset_with_meta(
|
||||
Size::ZERO,
|
||||
OffsetMode::Wrapping,
|
||||
MemPlaceMeta::None,
|
||||
layout,
|
||||
self,
|
||||
)?;
|
||||
Ok(mplace)
|
||||
}
|
||||
|
||||
/// Turn a `dyn* Trait` type into an value with the actual dynamic type.
|
||||
pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
val: &P,
|
||||
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> InterpResult<'tcx, P> {
|
||||
assert!(
|
||||
matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
|
||||
"`unpack_dyn_star` only makes sense on `dyn*` types"
|
||||
);
|
||||
let data = self.project_field(val, 0)?;
|
||||
let vtable = self.project_field(val, 1)?;
|
||||
let vtable = self.read_pointer(&vtable.to_op(self)?)?;
|
||||
let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?;
|
||||
// `data` is already the right thing but has the wrong type. So we transmute it.
|
||||
let layout = self.layout_of(ty)?;
|
||||
let data = data.transmute(layout, self)?;
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
@ -343,20 +343,16 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
match tail.kind() {
|
||||
ty::Dynamic(data, _, ty::Dyn) => {
|
||||
let vtable = meta.unwrap_meta().to_pointer(self.ecx)?;
|
||||
// Make sure it is a genuine vtable pointer.
|
||||
let (_dyn_ty, dyn_trait) = try_validation!(
|
||||
self.ecx.get_ptr_vtable(vtable),
|
||||
// Make sure it is a genuine vtable pointer for the right trait.
|
||||
try_validation!(
|
||||
self.ecx.get_ptr_vtable_ty(vtable, Some(data)),
|
||||
self.path,
|
||||
Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) =>
|
||||
InvalidVTablePtr { value: format!("{vtable}") }
|
||||
InvalidVTablePtr { value: format!("{vtable}") },
|
||||
Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => {
|
||||
InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait }
|
||||
},
|
||||
);
|
||||
// Make sure it is for the right trait.
|
||||
if dyn_trait != data.principal() {
|
||||
throw_validation_failure!(
|
||||
self.path,
|
||||
InvalidMetaWrongTrait { expected_trait: data, vtable_trait: dyn_trait }
|
||||
);
|
||||
}
|
||||
}
|
||||
ty::Slice(..) | ty::Str => {
|
||||
let _len = meta.unwrap_meta().to_target_usize(self.ecx)?;
|
||||
|
@ -95,7 +95,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
|
||||
// unsized values are never immediate, so we can assert_mem_place
|
||||
let op = v.to_op(self.ecx())?;
|
||||
let dest = op.assert_mem_place();
|
||||
let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?.0;
|
||||
let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?;
|
||||
trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
|
||||
// recurse with the inner type
|
||||
return self.visit_field(v, 0, &inner_mplace.into());
|
||||
@ -104,7 +104,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
|
||||
// DynStar types. Very different from a dyn type (but strangely part of the
|
||||
// same variant in `TyKind`): These are pairs where the 2nd component is the
|
||||
// vtable, and the first component is the data (which must be ptr-sized).
|
||||
let data = self.ecx().unpack_dyn_star(v, data)?.0;
|
||||
let data = self.ecx().unpack_dyn_star(v, data)?;
|
||||
return self.visit_field(v, 0, &data);
|
||||
}
|
||||
// Slices do not need special handling here: they have `Array` field
|
||||
|
@ -1,18 +1,20 @@
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(never_type)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(yeet_expr)]
|
||||
#![feature(if_let_guard)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod check_consts;
|
||||
pub mod const_eval;
|
||||
|
@ -4,54 +4,121 @@
|
||||
//! node in the graph. This uses [Tarjan's algorithm](
|
||||
//! https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
|
||||
//! that completes in *O*(*n*) time.
|
||||
//! Optionally, also annotate the SCC nodes with some commutative data.
|
||||
//! Typical examples would include: minimum element in SCC, maximum element
|
||||
//! reachable from it, etc.
|
||||
|
||||
use crate::fx::FxHashSet;
|
||||
use crate::graph::vec_graph::VecGraph;
|
||||
use crate::graph::{DirectedGraph, NumEdges, Successors};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Range;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// An annotation for an SCC. This can be a representative,
|
||||
/// the max/min element of the SCC, or all of the above.
|
||||
///
|
||||
/// Concretely, the both merge operations must commute, e.g. where `merge`
|
||||
/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)`
|
||||
///
|
||||
/// In general, what you want is probably always min/max according
|
||||
/// to some ordering, potentially with side constraints (min x such
|
||||
/// that P holds).
|
||||
pub trait Annotation: Debug + Copy {
|
||||
/// Merge two existing annotations into one during
|
||||
/// path compression.o
|
||||
fn merge_scc(self, other: Self) -> Self;
|
||||
|
||||
/// Merge a successor into this annotation.
|
||||
fn merge_reached(self, other: Self) -> Self;
|
||||
|
||||
fn update_scc(&mut self, other: Self) {
|
||||
*self = self.merge_scc(other)
|
||||
}
|
||||
|
||||
fn update_reachable(&mut self, other: Self) {
|
||||
*self = self.merge_reached(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// The empty annotation, which does nothing.
|
||||
impl Annotation for () {
|
||||
fn merge_reached(self, _other: Self) -> Self {
|
||||
()
|
||||
}
|
||||
fn merge_scc(self, _other: Self) -> Self {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
/// Strongly connected components (SCC) of a graph. The type `N` is
|
||||
/// the index type for the graph nodes and `S` is the index type for
|
||||
/// the SCCs. We can map from each node to the SCC that it
|
||||
/// participates in, and we also have the successors of each SCC.
|
||||
pub struct Sccs<N: Idx, S: Idx> {
|
||||
pub struct Sccs<N: Idx, S: Idx, A: Annotation = ()> {
|
||||
/// For each node, what is the SCC index of the SCC to which it
|
||||
/// belongs.
|
||||
scc_indices: IndexVec<N, S>,
|
||||
|
||||
/// Data about each SCC.
|
||||
scc_data: SccData<S>,
|
||||
/// Data about all the SCCs.
|
||||
scc_data: SccData<S, A>,
|
||||
}
|
||||
|
||||
pub struct SccData<S: Idx> {
|
||||
/// For each SCC, the range of `all_successors` where its
|
||||
/// Information about an invidividual SCC node.
|
||||
struct SccDetails<A: Annotation> {
|
||||
/// For this SCC, the range of `all_successors` where its
|
||||
/// successors can be found.
|
||||
ranges: IndexVec<S, Range<usize>>,
|
||||
range: Range<usize>,
|
||||
|
||||
/// User-specified metadata about the SCC.
|
||||
annotation: A,
|
||||
}
|
||||
|
||||
// The name of this struct should discourage you from making it public and leaking
|
||||
// its representation. This message was left here by one who came before you,
|
||||
// who learnt the hard way that making even small changes in representation
|
||||
// is difficult when it's publicly inspectable.
|
||||
//
|
||||
// Obey the law of Demeter!
|
||||
struct SccData<S: Idx, A: Annotation> {
|
||||
/// Maps SCC indices to their metadata, including
|
||||
/// offsets into `all_successors`.
|
||||
scc_details: IndexVec<S, SccDetails<A>>,
|
||||
|
||||
/// Contains the successors for all the Sccs, concatenated. The
|
||||
/// range of indices corresponding to a given SCC is found in its
|
||||
/// SccData.
|
||||
/// `scc_details.range`.
|
||||
all_successors: Vec<S>,
|
||||
}
|
||||
|
||||
impl<N: Idx, S: Idx + Ord> Sccs<N, S> {
|
||||
impl<N: Idx, S: Idx + Ord> Sccs<N, S, ()> {
|
||||
/// Compute SCCs without annotations.
|
||||
pub fn new(graph: &impl Successors<Node = N>) -> Self {
|
||||
SccsConstruction::construct(graph)
|
||||
Self::new_with_annotation(graph, |_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> {
|
||||
/// Compute SCCs and annotate them with a user-supplied annotation
|
||||
pub fn new_with_annotation<F: Fn(N) -> A>(
|
||||
graph: &impl Successors<Node = N>,
|
||||
to_annotation: F,
|
||||
) -> Self {
|
||||
SccsConstruction::construct(graph, to_annotation)
|
||||
}
|
||||
|
||||
pub fn annotation(&self, scc: S) -> A {
|
||||
self.scc_data.annotation(scc)
|
||||
}
|
||||
|
||||
pub fn scc_indices(&self) -> &IndexSlice<N, S> {
|
||||
&self.scc_indices
|
||||
}
|
||||
|
||||
pub fn scc_data(&self) -> &SccData<S> {
|
||||
&self.scc_data
|
||||
}
|
||||
|
||||
/// Returns the number of SCCs in the graph.
|
||||
pub fn num_sccs(&self) -> usize {
|
||||
self.scc_data.len()
|
||||
@ -90,7 +157,7 @@ impl<N: Idx, S: Idx + Ord> Sccs<N, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Idx, S: Idx + Ord> DirectedGraph for Sccs<N, S> {
|
||||
impl<N: Idx, S: Idx + Ord, A: Annotation> DirectedGraph for Sccs<N, S, A> {
|
||||
type Node = S;
|
||||
|
||||
fn num_nodes(&self) -> usize {
|
||||
@ -98,43 +165,33 @@ impl<N: Idx, S: Idx + Ord> DirectedGraph for Sccs<N, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Idx, S: Idx + Ord> NumEdges for Sccs<N, S> {
|
||||
impl<N: Idx, S: Idx + Ord, A: Annotation> NumEdges for Sccs<N, S, A> {
|
||||
fn num_edges(&self) -> usize {
|
||||
self.scc_data.all_successors.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Idx, S: Idx + Ord> Successors for Sccs<N, S> {
|
||||
impl<N: Idx, S: Idx + Ord, A: Annotation> Successors for Sccs<N, S, A> {
|
||||
fn successors(&self, node: S) -> impl Iterator<Item = Self::Node> {
|
||||
self.successors(node).iter().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Idx> SccData<S> {
|
||||
impl<S: Idx, A: Annotation> SccData<S, A> {
|
||||
/// Number of SCCs,
|
||||
fn len(&self) -> usize {
|
||||
self.ranges.len()
|
||||
}
|
||||
|
||||
pub fn ranges(&self) -> &IndexSlice<S, Range<usize>> {
|
||||
&self.ranges
|
||||
}
|
||||
|
||||
pub fn all_successors(&self) -> &Vec<S> {
|
||||
&self.all_successors
|
||||
self.scc_details.len()
|
||||
}
|
||||
|
||||
/// Returns the successors of the given SCC.
|
||||
fn successors(&self, scc: S) -> &[S] {
|
||||
// Annoyingly, `range` does not implement `Copy`, so we have
|
||||
// to do `range.start..range.end`:
|
||||
let range = &self.ranges[scc];
|
||||
&self.all_successors[range.start..range.end]
|
||||
&self.all_successors[self.scc_details[scc].range.clone()]
|
||||
}
|
||||
|
||||
/// Creates a new SCC with `successors` as its successors and
|
||||
/// the maximum weight of its internal nodes `scc_max_weight` and
|
||||
/// returns the resulting index.
|
||||
fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
|
||||
fn create_scc(&mut self, successors: impl IntoIterator<Item = S>, annotation: A) -> S {
|
||||
// Store the successors on `scc_successors_vec`, remembering
|
||||
// the range of indices.
|
||||
let all_successors_start = self.all_successors.len();
|
||||
@ -142,22 +199,35 @@ impl<S: Idx> SccData<S> {
|
||||
let all_successors_end = self.all_successors.len();
|
||||
|
||||
debug!(
|
||||
"create_scc({:?}) successors={:?}",
|
||||
self.ranges.len(),
|
||||
"create_scc({:?}) successors={:?}, annotation={:?}",
|
||||
self.len(),
|
||||
&self.all_successors[all_successors_start..all_successors_end],
|
||||
annotation
|
||||
);
|
||||
|
||||
self.ranges.push(all_successors_start..all_successors_end)
|
||||
let range = all_successors_start..all_successors_end;
|
||||
let metadata = SccDetails { range, annotation };
|
||||
self.scc_details.push(metadata)
|
||||
}
|
||||
|
||||
fn annotation(&self, scc: S) -> A {
|
||||
self.scc_details[scc].annotation
|
||||
}
|
||||
}
|
||||
|
||||
struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> {
|
||||
struct SccsConstruction<'c, G, S, A, F>
|
||||
where
|
||||
G: DirectedGraph + Successors,
|
||||
S: Idx,
|
||||
A: Annotation,
|
||||
F: Fn(G::Node) -> A,
|
||||
{
|
||||
graph: &'c G,
|
||||
|
||||
/// The state of each node; used during walk to record the stack
|
||||
/// and after walk to record what cycle each node ended up being
|
||||
/// in.
|
||||
node_states: IndexVec<G::Node, NodeState<G::Node, S>>,
|
||||
node_states: IndexVec<G::Node, NodeState<G::Node, S, A>>,
|
||||
|
||||
/// The stack of nodes that we are visiting as part of the DFS.
|
||||
node_stack: Vec<G::Node>,
|
||||
@ -174,26 +244,34 @@ struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> {
|
||||
/// around between successors to amortize memory allocation costs.
|
||||
duplicate_set: FxHashSet<S>,
|
||||
|
||||
scc_data: SccData<S>,
|
||||
scc_data: SccData<S, A>,
|
||||
|
||||
/// A function that constructs an initial SCC annotation
|
||||
/// out of a single node.
|
||||
to_annotation: F,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum NodeState<N, S> {
|
||||
enum NodeState<N, S, A> {
|
||||
/// This node has not yet been visited as part of the DFS.
|
||||
///
|
||||
/// After SCC construction is complete, this state ought to be
|
||||
/// impossible.
|
||||
NotVisited,
|
||||
|
||||
/// This node is currently being walk as part of our DFS. It is on
|
||||
/// the stack at the depth `depth`.
|
||||
/// This node is currently being walked as part of our DFS. It is on
|
||||
/// the stack at the depth `depth` and its current annotation is
|
||||
/// `annotation`.
|
||||
///
|
||||
/// After SCC construction is complete, this state ought to be
|
||||
/// impossible.
|
||||
BeingVisited { depth: usize },
|
||||
BeingVisited { depth: usize, annotation: A },
|
||||
|
||||
/// Indicates that this node is a member of the given cycle.
|
||||
InCycle { scc_index: S },
|
||||
/// Indicates that this node is a member of the given cycle where
|
||||
/// the merged annotation is `annotation`.
|
||||
/// Note that an SCC can have several cycles, so its final annotation
|
||||
/// is the merged value of all its member annotations.
|
||||
InCycle { scc_index: S, annotation: A },
|
||||
|
||||
/// Indicates that this node is a member of whatever cycle
|
||||
/// `parent` is a member of. This state is transient: whenever we
|
||||
@ -203,16 +281,27 @@ enum NodeState<N, S> {
|
||||
InCycleWith { parent: N },
|
||||
}
|
||||
|
||||
/// The state of walking a given node.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum WalkReturn<S> {
|
||||
Cycle { min_depth: usize },
|
||||
Complete { scc_index: S },
|
||||
enum WalkReturn<S, A> {
|
||||
/// The walk found a cycle, but the entire component is not known to have
|
||||
/// been fully walked yet. We only know the minimum depth of this
|
||||
/// component in a minimum spanning tree of the graph. This component
|
||||
/// is tentatively represented by the state of the first node of this
|
||||
/// cycle we met, which is at `min_depth`.
|
||||
Cycle { min_depth: usize, annotation: A },
|
||||
/// The SCC and everything reachable from it have been fully walked.
|
||||
/// At this point we know what is inside the SCC as we have visited every
|
||||
/// node reachable from it. The SCC can now be fully represented by its ID.
|
||||
Complete { scc_index: S, annotation: A },
|
||||
}
|
||||
|
||||
impl<'c, G, S> SccsConstruction<'c, G, S>
|
||||
impl<'c, G, S, A, F> SccsConstruction<'c, G, S, A, F>
|
||||
where
|
||||
G: DirectedGraph + Successors,
|
||||
S: Idx,
|
||||
F: Fn(G::Node) -> A,
|
||||
A: Annotation,
|
||||
{
|
||||
/// Identifies SCCs in the graph `G` and computes the resulting
|
||||
/// DAG. This uses a variant of [Tarjan's
|
||||
@ -225,8 +314,10 @@ where
|
||||
/// D' (i.e., D' < D), we know that N, N', and all nodes in
|
||||
/// between them on the stack are part of an SCC.
|
||||
///
|
||||
/// Additionally, we keep track of a current annotation of the SCC.
|
||||
///
|
||||
/// [wikipedia]: https://bit.ly/2EZIx84
|
||||
fn construct(graph: &'c G) -> Sccs<G::Node, S> {
|
||||
fn construct(graph: &'c G, to_annotation: F) -> Sccs<G::Node, S, A> {
|
||||
let num_nodes = graph.num_nodes();
|
||||
|
||||
let mut this = Self {
|
||||
@ -234,15 +325,16 @@ where
|
||||
node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
|
||||
node_stack: Vec::with_capacity(num_nodes),
|
||||
successors_stack: Vec::new(),
|
||||
scc_data: SccData { ranges: IndexVec::new(), all_successors: Vec::new() },
|
||||
scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() },
|
||||
duplicate_set: FxHashSet::default(),
|
||||
to_annotation,
|
||||
};
|
||||
|
||||
let scc_indices = (0..num_nodes)
|
||||
.map(G::Node::new)
|
||||
.map(|node| match this.start_walk_from(node) {
|
||||
WalkReturn::Complete { scc_index } => scc_index,
|
||||
WalkReturn::Cycle { min_depth } => {
|
||||
WalkReturn::Complete { scc_index, .. } => scc_index,
|
||||
WalkReturn::Cycle { min_depth, .. } => {
|
||||
panic!("`start_walk_node({node:?})` returned cycle with depth {min_depth:?}")
|
||||
}
|
||||
})
|
||||
@ -251,12 +343,8 @@ where
|
||||
Sccs { scc_indices, scc_data: this.scc_data }
|
||||
}
|
||||
|
||||
fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<S> {
|
||||
if let Some(result) = self.inspect_node(node) {
|
||||
result
|
||||
} else {
|
||||
self.walk_unvisited_node(node)
|
||||
}
|
||||
fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<S, A> {
|
||||
self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node))
|
||||
}
|
||||
|
||||
/// Inspect a node during the DFS. We first examine its current
|
||||
@ -271,11 +359,15 @@ where
|
||||
/// Otherwise, we are looking at a node that has already been
|
||||
/// completely visited. We therefore return `WalkReturn::Complete`
|
||||
/// with its associated SCC index.
|
||||
fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<S>> {
|
||||
fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<S, A>> {
|
||||
Some(match self.find_state(node) {
|
||||
NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index },
|
||||
NodeState::InCycle { scc_index, annotation } => {
|
||||
WalkReturn::Complete { scc_index, annotation }
|
||||
}
|
||||
|
||||
NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth },
|
||||
NodeState::BeingVisited { depth: min_depth, annotation } => {
|
||||
WalkReturn::Cycle { min_depth, annotation }
|
||||
}
|
||||
|
||||
NodeState::NotVisited => return None,
|
||||
|
||||
@ -290,7 +382,7 @@ where
|
||||
/// of `r2` (and updates `r` to reflect current result). This is
|
||||
/// basically the "find" part of a standard union-find algorithm
|
||||
/// (with path compression).
|
||||
fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S> {
|
||||
fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S, A> {
|
||||
// To avoid recursion we temporarily reuse the `parent` of each
|
||||
// InCycleWith link to encode a downwards link while compressing
|
||||
// the path. After we have found the root or deepest node being
|
||||
@ -306,24 +398,40 @@ where
|
||||
// found the initial self-loop.
|
||||
let mut previous_node = node;
|
||||
|
||||
// Ultimately assigned by the parent when following
|
||||
// Ultimately propagated to all the transitive parents when following
|
||||
// `InCycleWith` upwards.
|
||||
let node_state = loop {
|
||||
debug!("find_state(r = {:?} in state {:?})", node, self.node_states[node]);
|
||||
match self.node_states[node] {
|
||||
NodeState::InCycle { scc_index } => break NodeState::InCycle { scc_index },
|
||||
NodeState::BeingVisited { depth } => break NodeState::BeingVisited { depth },
|
||||
NodeState::NotVisited => break NodeState::NotVisited,
|
||||
NodeState::InCycleWith { parent } => {
|
||||
// We test this, to be extremely sure that we never
|
||||
// ever break our termination condition for the
|
||||
// reverse iteration loop.
|
||||
assert!(node != parent, "Node can not be in cycle with itself");
|
||||
// Store the previous node as an inverted list link
|
||||
self.node_states[node] = NodeState::InCycleWith { parent: previous_node };
|
||||
// Update to parent node.
|
||||
previous_node = node;
|
||||
node = parent;
|
||||
// This loop performs the downward link encoding mentioned above. Details below!
|
||||
// Note that there are two different states being assigned: the root state, and
|
||||
// a potentially derived version of the root state for non-root nodes in the chain.
|
||||
let (root_state, assigned_state) = {
|
||||
loop {
|
||||
debug!("find_state(r = {node:?} in state {:?})", self.node_states[node]);
|
||||
match self.node_states[node] {
|
||||
// This must have been the first and only state since it is unexplored*;
|
||||
// no update needed! * Unless there is a bug :')
|
||||
s @ NodeState::NotVisited => return s,
|
||||
// We are in a completely discovered SCC; every node on our path is in that SCC:
|
||||
s @ NodeState::InCycle { .. } => break (s, s),
|
||||
// The Interesting Third Base Case: we are a path back to a root node
|
||||
// still being explored. Now we need that node to keep its state and
|
||||
// every other node to be recorded as being in whatever component that
|
||||
// ends up in.
|
||||
s @ NodeState::BeingVisited { depth, .. } => {
|
||||
break (s, NodeState::InCycleWith { parent: self.node_stack[depth] });
|
||||
}
|
||||
// We are not at the head of a path; keep compressing it!
|
||||
NodeState::InCycleWith { parent } => {
|
||||
// We test this, to be extremely sure that we never
|
||||
// ever break our termination condition for the
|
||||
// reverse iteration loop.
|
||||
assert!(node != parent, "Node can not be in cycle with itself");
|
||||
|
||||
// Store the previous node as an inverted list link
|
||||
self.node_states[node] = NodeState::InCycleWith { parent: previous_node };
|
||||
// Update to parent node.
|
||||
previous_node = node;
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -365,10 +473,14 @@ where
|
||||
// Move backwards until we found the node where we started. We
|
||||
// will know when we hit the state where previous_node == node.
|
||||
loop {
|
||||
// Back at the beginning, we can return.
|
||||
// Back at the beginning, we can return. Note that we return the root state.
|
||||
// This is becuse for components being explored, we would otherwise get a
|
||||
// `node_state[n] = InCycleWith{ parent: n }` and that's wrong.
|
||||
if previous_node == node {
|
||||
return node_state;
|
||||
return root_state;
|
||||
}
|
||||
debug!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}");
|
||||
|
||||
// Update to previous node in the link.
|
||||
match self.node_states[previous_node] {
|
||||
NodeState::InCycleWith { parent: previous } => {
|
||||
@ -376,34 +488,14 @@ where
|
||||
previous_node = previous;
|
||||
}
|
||||
// Only InCycleWith nodes were added to the reverse linked list.
|
||||
other => panic!("Invalid previous link while compressing cycle: {other:?}"),
|
||||
other => unreachable!("Invalid previous link while compressing cycle: {other:?}"),
|
||||
}
|
||||
|
||||
debug!("find_state: parent_state = {:?}", node_state);
|
||||
|
||||
// Update the node state from the parent state. The assigned
|
||||
// state is actually a loop invariant but it will only be
|
||||
// evaluated if there is at least one backlink to follow.
|
||||
// Fully trusting llvm here to find this loop optimization.
|
||||
match node_state {
|
||||
// Path compression, make current node point to the same root.
|
||||
NodeState::InCycle { .. } => {
|
||||
self.node_states[node] = node_state;
|
||||
}
|
||||
// Still visiting nodes, compress to cycle to the node
|
||||
// at that depth.
|
||||
NodeState::BeingVisited { depth } => {
|
||||
self.node_states[node] =
|
||||
NodeState::InCycleWith { parent: self.node_stack[depth] };
|
||||
}
|
||||
// These are never allowed as parent nodes. InCycleWith
|
||||
// should have been followed to a real parent and
|
||||
// NotVisited can not be part of a cycle since it should
|
||||
// have instead gotten explored.
|
||||
NodeState::NotVisited | NodeState::InCycleWith { .. } => {
|
||||
panic!("invalid parent state: {node_state:?}")
|
||||
}
|
||||
}
|
||||
// Update the node state to the (potentially derived) state.
|
||||
// If the root is still being explored, this is
|
||||
// `InCycleWith{ parent: <root node>}`, otherwise
|
||||
// `assigned_state == root_state`.
|
||||
self.node_states[node] = assigned_state;
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,30 +505,36 @@ where
|
||||
/// caller decide avoids mutual recursion between the two methods and allows
|
||||
/// us to maintain an allocated stack for nodes on the path between calls.
|
||||
#[instrument(skip(self, initial), level = "debug")]
|
||||
fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S> {
|
||||
struct VisitingNodeFrame<G: DirectedGraph, Successors> {
|
||||
fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S, A> {
|
||||
debug!("Walk unvisited node: {initial:?}");
|
||||
struct VisitingNodeFrame<G: DirectedGraph, Successors, A> {
|
||||
node: G::Node,
|
||||
iter: Option<Successors>,
|
||||
successors: Option<Successors>,
|
||||
depth: usize,
|
||||
min_depth: usize,
|
||||
successors_len: usize,
|
||||
min_cycle_root: G::Node,
|
||||
successor_node: G::Node,
|
||||
/// The annotation for the SCC starting in `node`. It may or may
|
||||
/// not contain other nodes.
|
||||
current_component_annotation: A,
|
||||
}
|
||||
|
||||
// Move the stack to a local variable. We want to utilize the existing allocation and
|
||||
// mutably borrow it without borrowing self at the same time.
|
||||
let mut successors_stack = core::mem::take(&mut self.successors_stack);
|
||||
|
||||
debug_assert_eq!(successors_stack.len(), 0);
|
||||
|
||||
let mut stack: Vec<VisitingNodeFrame<G, _>> = vec![VisitingNodeFrame {
|
||||
let mut stack: Vec<VisitingNodeFrame<G, _, _>> = vec![VisitingNodeFrame {
|
||||
node: initial,
|
||||
depth: 0,
|
||||
min_depth: 0,
|
||||
iter: None,
|
||||
successors: None,
|
||||
successors_len: 0,
|
||||
min_cycle_root: initial,
|
||||
successor_node: initial,
|
||||
current_component_annotation: (self.to_annotation)(initial),
|
||||
}];
|
||||
|
||||
let mut return_value = None;
|
||||
@ -445,18 +543,26 @@ where
|
||||
let VisitingNodeFrame {
|
||||
node,
|
||||
depth,
|
||||
iter,
|
||||
successors,
|
||||
successors_len,
|
||||
min_depth,
|
||||
min_cycle_root,
|
||||
successor_node,
|
||||
current_component_annotation,
|
||||
} = frame;
|
||||
|
||||
let node = *node;
|
||||
let depth = *depth;
|
||||
|
||||
let successors = match iter {
|
||||
Some(iter) => iter,
|
||||
// node is definitely in the current component, add it to the annotation.
|
||||
if node != initial {
|
||||
current_component_annotation.update_scc((self.to_annotation)(node));
|
||||
}
|
||||
debug!(
|
||||
"Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}"
|
||||
);
|
||||
|
||||
let successors = match successors {
|
||||
Some(successors) => successors,
|
||||
None => {
|
||||
// This None marks that we still have the initialize this node's frame.
|
||||
debug!(?depth, ?node);
|
||||
@ -464,7 +570,10 @@ where
|
||||
debug_assert!(matches!(self.node_states[node], NodeState::NotVisited));
|
||||
|
||||
// Push `node` onto the stack.
|
||||
self.node_states[node] = NodeState::BeingVisited { depth };
|
||||
self.node_states[node] = NodeState::BeingVisited {
|
||||
depth,
|
||||
annotation: *current_component_annotation,
|
||||
};
|
||||
self.node_stack.push(node);
|
||||
|
||||
// Walk each successor of the node, looking to see if any of
|
||||
@ -472,11 +581,11 @@ where
|
||||
// so, that means they can also reach us.
|
||||
*successors_len = successors_stack.len();
|
||||
// Set and return a reference, this is currently empty.
|
||||
iter.get_or_insert(self.graph.successors(node))
|
||||
successors.get_or_insert(self.graph.successors(node))
|
||||
}
|
||||
};
|
||||
|
||||
// Now that iter is initialized, this is a constant for this frame.
|
||||
// Now that the successors iterator is initialized, this is a constant for this frame.
|
||||
let successors_len = *successors_len;
|
||||
|
||||
// Construct iterators for the nodes and walk results. There are two cases:
|
||||
@ -489,10 +598,17 @@ where
|
||||
debug!(?node, ?successor_node);
|
||||
(successor_node, self.inspect_node(successor_node))
|
||||
});
|
||||
|
||||
for (successor_node, walk) in returned_walk.chain(successor_walk) {
|
||||
match walk {
|
||||
Some(WalkReturn::Cycle { min_depth: successor_min_depth }) => {
|
||||
// The starting node `node` leads to a cycle whose earliest node,
|
||||
// `successor_node`, is at `min_depth`. There may be more cycles.
|
||||
Some(WalkReturn::Cycle {
|
||||
min_depth: successor_min_depth,
|
||||
annotation: successor_annotation,
|
||||
}) => {
|
||||
debug!(
|
||||
"Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}"
|
||||
);
|
||||
// Track the minimum depth we can reach.
|
||||
assert!(successor_min_depth <= depth);
|
||||
if successor_min_depth < *min_depth {
|
||||
@ -500,41 +616,56 @@ where
|
||||
*min_depth = successor_min_depth;
|
||||
*min_cycle_root = successor_node;
|
||||
}
|
||||
current_component_annotation.update_scc(successor_annotation);
|
||||
}
|
||||
|
||||
Some(WalkReturn::Complete { scc_index: successor_scc_index }) => {
|
||||
// The starting node `node` is succeeded by a fully identified SCC
|
||||
// which is now added to the set under `scc_index`.
|
||||
Some(WalkReturn::Complete {
|
||||
scc_index: successor_scc_index,
|
||||
annotation: successor_annotation,
|
||||
}) => {
|
||||
debug!(
|
||||
"Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}"
|
||||
);
|
||||
// Push the completed SCC indices onto
|
||||
// the `successors_stack` for later.
|
||||
debug!(?node, ?successor_scc_index);
|
||||
successors_stack.push(successor_scc_index);
|
||||
current_component_annotation.update_reachable(successor_annotation);
|
||||
}
|
||||
|
||||
// `node` has no more (direct) successors; search recursively.
|
||||
None => {
|
||||
let depth = depth + 1;
|
||||
debug!("Recursing down into {successor_node:?} at depth {depth:?}");
|
||||
debug!(?depth, ?successor_node);
|
||||
// Remember which node the return value will come from.
|
||||
frame.successor_node = successor_node;
|
||||
// Start a new stack frame the step into it.
|
||||
// Start a new stack frame, then step into it.
|
||||
stack.push(VisitingNodeFrame {
|
||||
node: successor_node,
|
||||
depth,
|
||||
iter: None,
|
||||
successors: None,
|
||||
successors_len: 0,
|
||||
min_depth: depth,
|
||||
min_cycle_root: successor_node,
|
||||
successor_node,
|
||||
current_component_annotation: (self.to_annotation)(successor_node),
|
||||
});
|
||||
continue 'recurse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Finished walk from {node:?} with annotation: {current_component_annotation:?}");
|
||||
|
||||
// Completed walk, remove `node` from the stack.
|
||||
let r = self.node_stack.pop();
|
||||
debug_assert_eq!(r, Some(node));
|
||||
|
||||
// Remove the frame, it's done.
|
||||
let frame = stack.pop().unwrap();
|
||||
let current_component_annotation = frame.current_component_annotation;
|
||||
debug_assert_eq!(frame.node, node);
|
||||
|
||||
// If `min_depth == depth`, then we are the root of the
|
||||
// cycle: we can't reach anyone further down the stack.
|
||||
@ -543,6 +674,8 @@ where
|
||||
// We return one frame at a time so there can't be another return value.
|
||||
debug_assert!(return_value.is_none());
|
||||
return_value = Some(if frame.min_depth == depth {
|
||||
// We are at the head of the component.
|
||||
|
||||
// Note that successor stack may have duplicates, so we
|
||||
// want to remove those:
|
||||
let deduplicated_successors = {
|
||||
@ -552,15 +685,25 @@ where
|
||||
.drain(successors_len..)
|
||||
.filter(move |&i| duplicate_set.insert(i))
|
||||
};
|
||||
let scc_index = self.scc_data.create_scc(deduplicated_successors);
|
||||
self.node_states[node] = NodeState::InCycle { scc_index };
|
||||
WalkReturn::Complete { scc_index }
|
||||
|
||||
debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node);
|
||||
|
||||
let scc_index =
|
||||
self.scc_data.create_scc(deduplicated_successors, current_component_annotation);
|
||||
|
||||
self.node_states[node] =
|
||||
NodeState::InCycle { scc_index, annotation: current_component_annotation };
|
||||
|
||||
WalkReturn::Complete { scc_index, annotation: current_component_annotation }
|
||||
} else {
|
||||
// We are not the head of the cycle. Return back to our
|
||||
// caller. They will take ownership of the
|
||||
// `self.successors` data that we pushed.
|
||||
self.node_states[node] = NodeState::InCycleWith { parent: frame.min_cycle_root };
|
||||
WalkReturn::Cycle { min_depth: frame.min_depth }
|
||||
WalkReturn::Cycle {
|
||||
min_depth: frame.min_depth,
|
||||
annotation: current_component_annotation,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,53 @@ extern crate test;
|
||||
use super::*;
|
||||
use crate::graph::tests::TestGraph;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct MaxReached(usize);
|
||||
type UsizeSccs = Sccs<usize, usize, ()>;
|
||||
type MaxReachedSccs = Sccs<usize, usize, MaxReached>;
|
||||
|
||||
impl Annotation for MaxReached {
|
||||
fn merge_scc(self, other: Self) -> Self {
|
||||
Self(std::cmp::max(other.0, self.0))
|
||||
}
|
||||
|
||||
fn merge_reached(self, other: Self) -> Self {
|
||||
self.merge_scc(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<usize> for MaxReached {
|
||||
fn eq(&self, other: &usize) -> bool {
|
||||
&self.0 == other
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxReached {
|
||||
fn from_usize(nr: usize) -> Self {
|
||||
Self(nr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct MinMaxIn {
|
||||
min: usize,
|
||||
max: usize,
|
||||
}
|
||||
|
||||
impl Annotation for MinMaxIn {
|
||||
fn merge_scc(self, other: Self) -> Self {
|
||||
Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) }
|
||||
}
|
||||
|
||||
fn merge_reached(self, _other: Self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diamond() {
|
||||
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
|
||||
let sccs: Sccs<_, usize> = Sccs::new(&graph);
|
||||
let sccs: UsizeSccs = Sccs::new(&graph);
|
||||
assert_eq!(sccs.num_sccs(), 4);
|
||||
assert_eq!(sccs.num_sccs(), 4);
|
||||
}
|
||||
@ -34,7 +77,7 @@ fn test_big_scc() {
|
||||
+-- 2 <--+
|
||||
*/
|
||||
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]);
|
||||
let sccs: Sccs<_, usize> = Sccs::new(&graph);
|
||||
let sccs: UsizeSccs = Sccs::new(&graph);
|
||||
assert_eq!(sccs.num_sccs(), 1);
|
||||
}
|
||||
|
||||
@ -50,7 +93,7 @@ fn test_three_sccs() {
|
||||
+-- 2 <--+
|
||||
*/
|
||||
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]);
|
||||
let sccs: Sccs<_, usize> = Sccs::new(&graph);
|
||||
let sccs: UsizeSccs = Sccs::new(&graph);
|
||||
assert_eq!(sccs.num_sccs(), 3);
|
||||
assert_eq!(sccs.scc(0), 1);
|
||||
assert_eq!(sccs.scc(1), 0);
|
||||
@ -106,7 +149,7 @@ fn test_find_state_2() {
|
||||
// 2 InCycleWith { 1 }
|
||||
// 3 InCycleWith { 0 }
|
||||
|
||||
let sccs: Sccs<_, usize> = Sccs::new(&graph);
|
||||
let sccs: UsizeSccs = Sccs::new(&graph);
|
||||
assert_eq!(sccs.num_sccs(), 1);
|
||||
assert_eq!(sccs.scc(0), 0);
|
||||
assert_eq!(sccs.scc(1), 0);
|
||||
@ -130,7 +173,7 @@ fn test_find_state_3() {
|
||||
*/
|
||||
let graph =
|
||||
TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]);
|
||||
let sccs: Sccs<_, usize> = Sccs::new(&graph);
|
||||
let sccs: UsizeSccs = Sccs::new(&graph);
|
||||
assert_eq!(sccs.num_sccs(), 2);
|
||||
assert_eq!(sccs.scc(0), 0);
|
||||
assert_eq!(sccs.scc(1), 0);
|
||||
@ -165,7 +208,7 @@ fn test_deep_linear() {
|
||||
nodes.push((i - 1, i));
|
||||
}
|
||||
let graph = TestGraph::new(0, nodes.as_slice());
|
||||
let sccs: Sccs<_, usize> = Sccs::new(&graph);
|
||||
let sccs: UsizeSccs = Sccs::new(&graph);
|
||||
assert_eq!(sccs.num_sccs(), NR_NODES);
|
||||
assert_eq!(sccs.scc(0), NR_NODES - 1);
|
||||
assert_eq!(sccs.scc(NR_NODES - 1), 0);
|
||||
@ -210,7 +253,164 @@ fn bench_sccc(b: &mut test::Bencher) {
|
||||
graph[21] = (7, 4);
|
||||
let graph = TestGraph::new(0, &graph[..]);
|
||||
b.iter(|| {
|
||||
let sccs: Sccs<_, usize> = Sccs::new(&graph);
|
||||
let sccs: UsizeSccs = Sccs::new(&graph);
|
||||
assert_eq!(sccs.num_sccs(), 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_self_loop() {
|
||||
let graph = TestGraph::new(0, &[(0, 0)]);
|
||||
let sccs: MaxReachedSccs =
|
||||
Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) });
|
||||
assert_eq!(sccs.annotation(0), 17);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_branch() {
|
||||
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]);
|
||||
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)), 4);
|
||||
assert_eq!(sccs.annotation(sccs.scc(1)), 3);
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)), 4);
|
||||
}
|
||||
#[test]
|
||||
fn test_single_cycle_max() {
|
||||
let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]);
|
||||
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)), 4);
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_cycle_max() {
|
||||
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]);
|
||||
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
|
||||
assert_eq!(sccs.num_sccs(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_cycle_max() {
|
||||
let graph =
|
||||
TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]);
|
||||
let sccs: MaxReachedSccs =
|
||||
Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) });
|
||||
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)).0, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bug_minimised() {
|
||||
let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]);
|
||||
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n {
|
||||
3 => MaxReached(1),
|
||||
_ => MaxReached(0),
|
||||
});
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)), 1);
|
||||
assert_eq!(sccs.annotation(sccs.scc(1)), 0);
|
||||
assert_eq!(sccs.annotation(sccs.scc(4)), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bug_max_leak_minimised() {
|
||||
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]);
|
||||
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
|
||||
4 => MaxReached(1),
|
||||
_ => MaxReached(0),
|
||||
});
|
||||
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)), 0);
|
||||
assert_eq!(sccs.annotation(sccs.scc(3)), 1);
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bug_max_leak() {
|
||||
let graph = TestGraph::new(
|
||||
8,
|
||||
&[
|
||||
(0, 0),
|
||||
(0, 18),
|
||||
(0, 19),
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(0, 7),
|
||||
(0, 8),
|
||||
(0, 23),
|
||||
(18, 0),
|
||||
(18, 12),
|
||||
(19, 0),
|
||||
(19, 25),
|
||||
(12, 18),
|
||||
(12, 3),
|
||||
(12, 5),
|
||||
(3, 12),
|
||||
(3, 21),
|
||||
(3, 22),
|
||||
(5, 13),
|
||||
(21, 3),
|
||||
(22, 3),
|
||||
(13, 5),
|
||||
(13, 4),
|
||||
(4, 13),
|
||||
(4, 0),
|
||||
(2, 11),
|
||||
(7, 6),
|
||||
(6, 20),
|
||||
(20, 6),
|
||||
(8, 17),
|
||||
(17, 9),
|
||||
(9, 16),
|
||||
(16, 26),
|
||||
(26, 15),
|
||||
(15, 10),
|
||||
(10, 14),
|
||||
(14, 27),
|
||||
(23, 24),
|
||||
],
|
||||
);
|
||||
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
|
||||
22 => MaxReached(1),
|
||||
24 => MaxReached(2),
|
||||
27 => MaxReached(2),
|
||||
_ => MaxReached(0),
|
||||
});
|
||||
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)), 0);
|
||||
assert_eq!(sccs.annotation(sccs.scc(7)), 0);
|
||||
assert_eq!(sccs.annotation(sccs.scc(8)), 2);
|
||||
assert_eq!(sccs.annotation(sccs.scc(23)), 2);
|
||||
assert_eq!(sccs.annotation(sccs.scc(3)), 2);
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bug_max_zero_stick_shape() {
|
||||
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]);
|
||||
|
||||
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
|
||||
4 => MaxReached(1),
|
||||
_ => MaxReached(0),
|
||||
});
|
||||
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)), 1);
|
||||
assert_eq!(sccs.annotation(sccs.scc(1)), 1);
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)), 1);
|
||||
assert_eq!(sccs.annotation(sccs.scc(3)), 1);
|
||||
assert_eq!(sccs.annotation(sccs.scc(4)), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_max_in() {
|
||||
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]);
|
||||
let sccs: Sccs<usize, usize, MinMaxIn> =
|
||||
Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w });
|
||||
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)).min, 2);
|
||||
assert_eq!(sccs.annotation(sccs.scc(2)).max, 2);
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)).min, 0);
|
||||
assert_eq!(sccs.annotation(sccs.scc(0)).max, 4);
|
||||
assert_eq!(sccs.annotation(sccs.scc(3)).min, 0);
|
||||
assert_eq!(sccs.annotation(sccs.scc(3)).max, 4);
|
||||
assert_eq!(sccs.annotation(sccs.scc(5)).min, 5);
|
||||
}
|
||||
|
@ -146,8 +146,6 @@ pub enum ProcessResult<O, E> {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
struct ObligationTreeId(usize);
|
||||
|
||||
type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
|
||||
|
||||
pub struct ObligationForest<O: ForestObligation> {
|
||||
/// The list of obligations. In between calls to [Self::process_obligations],
|
||||
/// this list only contains nodes in the `Pending` or `Waiting` state.
|
||||
@ -310,18 +308,25 @@ pub struct Error<O, E> {
|
||||
pub backtrace: Vec<O>,
|
||||
}
|
||||
|
||||
impl<O: ForestObligation> ObligationForest<O> {
|
||||
pub fn new() -> ObligationForest<O> {
|
||||
ObligationForest {
|
||||
nodes: vec![],
|
||||
done_cache: Default::default(),
|
||||
active_cache: Default::default(),
|
||||
reused_node_vec: vec![],
|
||||
obligation_tree_id_generator: (0..).map(ObligationTreeId),
|
||||
error_cache: Default::default(),
|
||||
mod helper {
|
||||
use super::*;
|
||||
pub type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
|
||||
impl<O: ForestObligation> ObligationForest<O> {
|
||||
pub fn new() -> ObligationForest<O> {
|
||||
ObligationForest {
|
||||
nodes: vec![],
|
||||
done_cache: Default::default(),
|
||||
active_cache: Default::default(),
|
||||
reused_node_vec: vec![],
|
||||
obligation_tree_id_generator: (0..).map(ObligationTreeId),
|
||||
error_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use helper::*;
|
||||
|
||||
impl<O: ForestObligation> ObligationForest<O> {
|
||||
/// Returns the total number of nodes in the forest that have not
|
||||
/// yet been fully resolved.
|
||||
pub fn len(&self) -> usize {
|
||||
|
@ -1,8 +1,10 @@
|
||||
// This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in
|
||||
// `rustc_driver_impl` to be compiled in parallel with other crates.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub use rustc_driver_impl::*;
|
||||
|
@ -4,16 +4,18 @@
|
||||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(panic_backtrace_config)]
|
||||
#![feature(panic_update_hook)]
|
||||
#![feature(result_flattening)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
|
||||
@ -52,7 +54,7 @@ use std::ffi::OsString;
|
||||
use std::fmt::Write as _;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, IsTerminal, Read, Write};
|
||||
use std::panic::{self, catch_unwind, PanicInfo};
|
||||
use std::panic::{self, catch_unwind, PanicHookInfo};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command, Stdio};
|
||||
use std::str;
|
||||
@ -1366,11 +1368,10 @@ pub fn install_ice_hook(
|
||||
let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default());
|
||||
let using_internal_features_hook = using_internal_features.clone();
|
||||
panic::update_hook(Box::new(
|
||||
move |default_hook: &(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static),
|
||||
info: &PanicInfo<'_>| {
|
||||
move |default_hook: &(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static),
|
||||
info: &PanicHookInfo<'_>| {
|
||||
// Lock stderr to prevent interleaving of concurrent panics.
|
||||
let _guard = io::stderr().lock();
|
||||
|
||||
// If the error was caused by a broken pipe then this is not a bug.
|
||||
// Write the error and return immediately. See #98700.
|
||||
#[cfg(windows)]
|
||||
@ -1431,7 +1432,7 @@ pub fn install_ice_hook(
|
||||
/// When `install_ice_hook` is called, this function will be called as the panic
|
||||
/// hook.
|
||||
fn report_ice(
|
||||
info: &panic::PanicInfo<'_>,
|
||||
info: &panic::PanicHookInfo<'_>,
|
||||
bug_report_url: &str,
|
||||
extra_info: fn(&DiagCtxt),
|
||||
using_internal_features: &AtomicBool,
|
||||
|
@ -39,6 +39,8 @@ type Foo<T> = impl std::fmt::Debug;
|
||||
fn foo<U>() -> Foo<U> {
|
||||
5u32
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
This means that no matter the generic parameter to `foo`,
|
||||
@ -57,4 +59,6 @@ type Foo<T: Debug> = impl Debug;
|
||||
fn foo<U: Debug>() -> Foo<U> {
|
||||
Vec::<U>::new()
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
@ -1,10 +1,12 @@
|
||||
//! This library is used to gather all error codes into one place, to make
|
||||
//! their maintenance easier.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![deny(rustdoc::invalid_codeblock_attributes)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
// This higher-order macro defines the error codes that are in use. It is used
|
||||
// in the `rustc_errors` crate. Removed error codes are listed in the comment
|
||||
|
@ -1,8 +1,10 @@
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use fluent_bundle::FluentResource;
|
||||
use fluent_syntax::parser::ParserError;
|
||||
|
@ -157,7 +157,7 @@ expand_unsupported_key_value =
|
||||
key-value macro attributes are not supported
|
||||
|
||||
expand_var_still_repeating =
|
||||
variable '{$ident}' is still repeating at this depth
|
||||
variable `{$ident}` is still repeating at this depth
|
||||
|
||||
expand_wrong_fragment_kind =
|
||||
non-{$kind} macro in {$kind} position: {$name}
|
||||
|
@ -139,7 +139,7 @@ declare_features! (
|
||||
/// Allows `crate` in paths.
|
||||
(accepted, crate_in_paths, "1.30.0", Some(45477)),
|
||||
/// Allows users to provide classes for fenced code block using `class:classname`.
|
||||
(accepted, custom_code_classes_in_docs, "CURRENT_RUSTC_VERSION", Some(79483)),
|
||||
(accepted, custom_code_classes_in_docs, "1.80.0", Some(79483)),
|
||||
/// Allows using `#[debugger_visualizer]` attribute.
|
||||
(accepted, debugger_visualizer, "1.71.0", Some(95939)),
|
||||
/// Allows rustc to inject a default alloc_error_handler
|
||||
@ -165,7 +165,7 @@ declare_features! (
|
||||
/// Allows using `dyn Trait` as a syntax for trait objects.
|
||||
(accepted, dyn_trait, "1.27.0", Some(44662)),
|
||||
/// Allows `X..Y` patterns.
|
||||
(accepted, exclusive_range_pattern, "CURRENT_RUSTC_VERSION", Some(37854)),
|
||||
(accepted, exclusive_range_pattern, "1.80.0", Some(37854)),
|
||||
/// Allows integer match exhaustiveness checking (RFC 2591).
|
||||
(accepted, exhaustive_integer_patterns, "1.33.0", Some(50907)),
|
||||
/// Allows explicit generic arguments specification with `impl Trait` present.
|
||||
|
@ -11,9 +11,11 @@
|
||||
//! even if it is stabilized or removed, *do not remove it*. Instead, move the
|
||||
//! symbol to the `accepted` or `removed` modules respectively.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod accepted;
|
||||
mod builtin_attrs;
|
||||
|
@ -457,7 +457,7 @@ declare_features! (
|
||||
/// Allows explicit tail calls via `become` expression.
|
||||
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
|
||||
/// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment.
|
||||
(incomplete, expr_fragment_specifier_2024, "CURRENT_RUSTC_VERSION", Some(123742)),
|
||||
(incomplete, expr_fragment_specifier_2024, "1.80.0", Some(123742)),
|
||||
/// Allows using `efiapi`, `sysv64` and `win64` as calling convention
|
||||
/// for functions with varargs.
|
||||
(unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),
|
||||
@ -488,7 +488,7 @@ declare_features! (
|
||||
/// Allows generic parameters and where-clauses on free & associated const items.
|
||||
(incomplete, generic_const_items, "1.73.0", Some(113521)),
|
||||
/// Allows registering static items globally, possibly across crates, to iterate over at runtime.
|
||||
(unstable, global_registration, "CURRENT_RUSTC_VERSION", Some(125119)),
|
||||
(unstable, global_registration, "1.80.0", Some(125119)),
|
||||
/// Allows using `..=X` as a patterns in slices.
|
||||
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
|
||||
/// Allows `if let` guard in match arms.
|
||||
@ -581,7 +581,7 @@ declare_features! (
|
||||
(unstable, repr_simd, "1.4.0", Some(27731)),
|
||||
/// Allows enums like Result<T, E> to be used across FFI, if T's niche value can
|
||||
/// be used to describe E or vise-versa.
|
||||
(unstable, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)),
|
||||
(unstable, result_ffi_guarantees, "1.80.0", Some(110503)),
|
||||
/// Allows bounding the return type of AFIT/RPITIT.
|
||||
(incomplete, return_type_notation, "1.70.0", Some(109417)),
|
||||
/// Allows `extern "rust-cold"`.
|
||||
@ -623,9 +623,9 @@ declare_features! (
|
||||
/// Allows unnamed fields of struct and union type
|
||||
(incomplete, unnamed_fields, "1.74.0", Some(49804)),
|
||||
/// Allows unsafe attributes.
|
||||
(unstable, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)),
|
||||
(unstable, unsafe_attributes, "1.80.0", Some(123757)),
|
||||
/// Allows unsafe on extern declarations and safety qualifiers over internal items.
|
||||
(unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)),
|
||||
(unstable, unsafe_extern_blocks, "1.80.0", Some(123743)),
|
||||
/// Allows unsized fn parameters.
|
||||
(unstable, unsized_fn_params, "1.49.0", Some(48055)),
|
||||
/// Allows unsized rvalues at arguments and parameters.
|
||||
|
@ -1,10 +1,12 @@
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::default_hash_types)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(proc_macro_diagnostic)]
|
||||
#![feature(proc_macro_span)]
|
||||
#![allow(rustc::default_hash_types)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
// tidy-alphabetical-start
|
||||
use std::ffi::CString;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{absolute, Path, PathBuf};
|
||||
// tidy-alphabetical-end
|
||||
|
||||
// Unfortunately, on windows, it looks like msvcrt.dll is silently translating
|
||||
// verbatim paths under the hood to non-verbatim paths! This manifests itself as
|
||||
|
@ -269,13 +269,15 @@
|
||||
//!
|
||||
//! * [DOT language](https://www.graphviz.org/doc/info/lang.html)
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![doc(
|
||||
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
|
||||
test(attr(allow(unused_variables), deny(warnings)))
|
||||
)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use LabelText::*;
|
||||
|
||||
|
@ -1038,6 +1038,7 @@ pub struct Block<'hir> {
|
||||
pub hir_id: HirId,
|
||||
/// Distinguishes between `unsafe { ... }` and `{ ... }`.
|
||||
pub rules: BlockCheckMode,
|
||||
/// The span includes the curly braces `{` and `}` around the block.
|
||||
pub span: Span,
|
||||
/// If true, then there may exist `break 'a` values that aim to
|
||||
/// break out of this block early.
|
||||
|
@ -2,13 +2,15 @@
|
||||
//!
|
||||
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(closure_track_caller)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(variant_count)]
|
||||
#![allow(internal_features)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate self as rustc_hir;
|
||||
|
||||
|
@ -8,6 +8,10 @@ hir_analysis_assoc_item_constraints_not_allowed_here =
|
||||
associated item constraints are not allowed here
|
||||
.label = associated item constraint not allowed here
|
||||
|
||||
hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private
|
||||
.label = private {$kind}
|
||||
.defined_here_label = the {$kind} is defined here
|
||||
|
||||
hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}`
|
||||
|
||||
hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
|
||||
@ -460,6 +464,10 @@ hir_analysis_static_specialize = cannot specialize on `'static` lifetime
|
||||
hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
|
||||
.note = this item must mention the opaque type in its signature in order to be able to register hidden types
|
||||
|
||||
hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`, but has it in its signature
|
||||
.note = consider moving the opaque type's declaration and defining uses into a separate module
|
||||
.opaque = this opaque type is in the signature
|
||||
|
||||
hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]`
|
||||
|
||||
hir_analysis_too_large_static = extern static is too large for the current architecture
|
||||
|
@ -11,6 +11,7 @@ use rustc_session::lint;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
use rustc_hir::*;
|
||||
|
||||
@ -66,7 +67,22 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
// FIXME(#43408) always enable this once `lazy_normalization` is
|
||||
// stable enough and does not need a feature gate anymore.
|
||||
Node::AnonConst(_) => {
|
||||
let parent_def_id = tcx.hir().get_parent_item(hir_id);
|
||||
let parent_did = tcx.parent(def_id.to_def_id());
|
||||
|
||||
// We don't do this unconditionally because the `DefId` parent of an anon const
|
||||
// might be an implicitly created closure during `async fn` desugaring. This would
|
||||
// have the wrong generics.
|
||||
//
|
||||
// i.e. `async fn foo<'a>() { let a = [(); { 1 + 2 }]; bar().await() }`
|
||||
// would implicitly have a closure in its body that would be the parent of
|
||||
// the `{ 1 + 2 }` anon const. This closure's generics is simply a witness
|
||||
// instead of `['a]`.
|
||||
let parent_did = if let DefKind::AnonConst = tcx.def_kind(parent_did) {
|
||||
parent_did
|
||||
} else {
|
||||
tcx.hir().get_parent_item(hir_id).to_def_id()
|
||||
};
|
||||
debug!(?parent_did);
|
||||
|
||||
let mut in_param_ty = false;
|
||||
for (_parent, node) in tcx.hir().parent_iter(hir_id) {
|
||||
@ -121,7 +137,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
//
|
||||
// This has some implications for how we get the predicates available to the anon const
|
||||
// see `explicit_predicates_of` for more information on this
|
||||
let generics = tcx.generics_of(parent_def_id.to_def_id());
|
||||
let generics = tcx.generics_of(parent_did);
|
||||
let param_def_idx = generics.param_def_id_to_index[¶m_id.to_def_id()];
|
||||
// In the above example this would be .params[..N#0]
|
||||
let own_params = generics.params_to(param_def_idx as usize, tcx).to_owned();
|
||||
@ -147,7 +163,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
//
|
||||
// Note that we do not supply the parent generics when using
|
||||
// `min_const_generics`.
|
||||
Some(parent_def_id.to_def_id())
|
||||
Some(parent_did)
|
||||
}
|
||||
} else {
|
||||
let parent_node = tcx.parent_hir_node(hir_id);
|
||||
@ -159,7 +175,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
Node::Expr(Expr { kind: ExprKind::Repeat(_, constant), .. })
|
||||
if constant.hir_id() == hir_id =>
|
||||
{
|
||||
Some(parent_def_id.to_def_id())
|
||||
Some(parent_did)
|
||||
}
|
||||
// Exclude `GlobalAsm` here which cannot have generics.
|
||||
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
|
||||
@ -171,7 +187,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
Some(parent_def_id.to_def_id())
|
||||
Some(parent_did)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -169,12 +169,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
||||
predicates.insert((trait_ref.upcast(tcx), tcx.def_span(def_id)));
|
||||
}
|
||||
|
||||
// Collect the predicates that were written inline by the user on each
|
||||
// type parameter (e.g., `<T: Foo>`). Also add `ConstArgHasType` predicates
|
||||
// for each const parameter.
|
||||
// Add implicit predicates that should be treated as if the user has written them,
|
||||
// including the implicit `T: Sized` for all generic parameters, and `ConstArgHasType`
|
||||
// for const params.
|
||||
for param in hir_generics.params {
|
||||
match param.kind {
|
||||
// We already dealt with early bound lifetimes above.
|
||||
GenericParamKind::Lifetime { .. } => (),
|
||||
GenericParamKind::Type { .. } => {
|
||||
let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
|
||||
@ -204,7 +203,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
||||
}
|
||||
|
||||
trace!(?predicates);
|
||||
// Add in the bounds that appear in the where-clause.
|
||||
// Add inline `<T: Foo>` bounds and bounds in the where clause.
|
||||
for predicate in hir_generics.predicates {
|
||||
match predicate {
|
||||
hir::WherePredicate::BoundPredicate(bound_pred) => {
|
||||
|
@ -8,7 +8,7 @@ use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
|
||||
|
||||
use crate::errors::{TaitForwardCompat, TypeOf, UnconstrainedOpaqueType};
|
||||
use crate::errors::{TaitForwardCompat, TaitForwardCompat2, TypeOf, UnconstrainedOpaqueType};
|
||||
|
||||
pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let mut res = Ok(());
|
||||
@ -229,13 +229,14 @@ impl TaitConstraintLocator<'_> {
|
||||
return;
|
||||
}
|
||||
|
||||
let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
|
||||
|
||||
let mut constrained = false;
|
||||
for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
|
||||
if opaque_type_key.def_id != self.def_id {
|
||||
continue;
|
||||
}
|
||||
constrained = true;
|
||||
let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
|
||||
|
||||
if !opaque_types_defined_by.contains(&self.def_id) {
|
||||
self.tcx.dcx().emit_err(TaitForwardCompat {
|
||||
@ -259,6 +260,16 @@ impl TaitConstraintLocator<'_> {
|
||||
|
||||
if !constrained {
|
||||
debug!("no constraints in typeck results");
|
||||
if opaque_types_defined_by.contains(&self.def_id) {
|
||||
self.tcx.dcx().emit_err(TaitForwardCompat2 {
|
||||
span: self
|
||||
.tcx
|
||||
.def_ident_span(item_def_id)
|
||||
.unwrap_or_else(|| self.tcx.def_span(item_def_id)),
|
||||
opaque_type_span: self.tcx.def_span(self.def_id),
|
||||
opaque_type: self.tcx.def_path_str(self.def_id),
|
||||
});
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -55,6 +55,18 @@ pub struct AssocKindMismatchWrapInBracesSugg {
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_assoc_item_is_private, code = E0624)]
|
||||
pub struct AssocItemIsPrivate {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub kind: &'static str,
|
||||
pub name: Ident,
|
||||
#[label(hir_analysis_defined_here_label)]
|
||||
pub defined_here_label: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_assoc_item_not_found, code = E0220)]
|
||||
pub struct AssocItemNotFound<'a> {
|
||||
@ -390,6 +402,17 @@ pub struct TaitForwardCompat {
|
||||
pub item_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_tait_forward_compat2)]
|
||||
#[note]
|
||||
pub struct TaitForwardCompat2 {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[note(hir_analysis_opaque)]
|
||||
pub opaque_type_span: Span,
|
||||
pub opaque_type: String,
|
||||
}
|
||||
|
||||
pub struct MissingTypeParams {
|
||||
pub span: Span,
|
||||
pub def_span: Span,
|
||||
|
@ -294,30 +294,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
)?
|
||||
};
|
||||
|
||||
let (assoc_ident, def_scope) =
|
||||
tcx.adjust_ident_and_get_scope(constraint.ident, candidate.def_id(), hir_ref_id);
|
||||
|
||||
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
|
||||
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
|
||||
// `assoc_ident` again and again.
|
||||
let assoc_item = tcx
|
||||
.associated_items(candidate.def_id())
|
||||
.filter_by_name_unhygienic(assoc_ident.name)
|
||||
.find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
|
||||
.expect("missing associated item");
|
||||
|
||||
if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
|
||||
let reported = tcx
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
constraint.span,
|
||||
format!("{} `{}` is private", assoc_item.kind, constraint.ident),
|
||||
)
|
||||
.with_span_label(constraint.span, format!("private {}", assoc_item.kind))
|
||||
.emit();
|
||||
self.set_tainted_by_errors(reported);
|
||||
}
|
||||
tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), constraint.span, None);
|
||||
let assoc_item = self
|
||||
.probe_assoc_item(
|
||||
constraint.ident,
|
||||
assoc_kind,
|
||||
hir_ref_id,
|
||||
constraint.span,
|
||||
candidate.def_id(),
|
||||
)
|
||||
.expect("failed to find associated item");
|
||||
|
||||
duplicates
|
||||
.entry(assoc_item.def_id)
|
||||
@ -404,10 +389,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
// Create the generic arguments for the associated type or constant by joining the
|
||||
// parent arguments (the arguments of the trait) and the own arguments (the ones of
|
||||
// the associated item itself) and construct an alias type using them.
|
||||
let alias_ty = candidate.map_bound(|trait_ref| {
|
||||
let ident = Ident::new(assoc_item.name, constraint.ident.span);
|
||||
let alias_term = candidate.map_bound(|trait_ref| {
|
||||
let item_segment = hir::PathSegment {
|
||||
ident,
|
||||
ident: constraint.ident,
|
||||
hir_id: constraint.hir_id,
|
||||
res: Res::Err,
|
||||
args: Some(constraint.gen_args),
|
||||
@ -426,15 +410,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
});
|
||||
|
||||
// Provide the resolved type of the associated constant to `type_of(AnonConst)`.
|
||||
if let hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(anon_const) } =
|
||||
constraint.kind
|
||||
{
|
||||
let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args));
|
||||
let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, constraint.hir_id);
|
||||
if let Some(anon_const) = constraint.ct() {
|
||||
let ty = alias_term
|
||||
.map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args));
|
||||
let ty =
|
||||
check_assoc_const_binding_type(tcx, constraint.ident, ty, constraint.hir_id);
|
||||
tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
|
||||
}
|
||||
|
||||
alias_ty
|
||||
alias_term
|
||||
};
|
||||
|
||||
match constraint.kind {
|
||||
|
@ -1151,8 +1151,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
};
|
||||
|
||||
let trait_did = bound.def_id();
|
||||
let assoc_ty_did = self.probe_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap();
|
||||
let ty = self.lower_assoc_ty(span, assoc_ty_did, assoc_segment, bound);
|
||||
let assoc_ty = self
|
||||
.probe_assoc_item(assoc_ident, ty::AssocKind::Type, hir_ref_id, span, trait_did)
|
||||
.expect("failed to find associated type");
|
||||
let ty = self.lower_assoc_ty(span, assoc_ty.def_id, assoc_segment, bound);
|
||||
|
||||
if let Some(variant_def_id) = variant_resolution {
|
||||
tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
|
||||
@ -1168,7 +1170,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
};
|
||||
|
||||
could_refer_to(DefKind::Variant, variant_def_id, "");
|
||||
could_refer_to(DefKind::AssocTy, assoc_ty_did, " also");
|
||||
could_refer_to(DefKind::AssocTy, assoc_ty.def_id, " also");
|
||||
|
||||
lint.span_suggestion(
|
||||
span,
|
||||
@ -1178,7 +1180,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
);
|
||||
});
|
||||
}
|
||||
Ok((ty, DefKind::AssocTy, assoc_ty_did))
|
||||
Ok((ty, DefKind::AssocTy, assoc_ty.def_id))
|
||||
}
|
||||
|
||||
fn probe_inherent_assoc_ty(
|
||||
@ -1205,7 +1207,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
let candidates: Vec<_> = tcx
|
||||
.inherent_impls(adt_did)?
|
||||
.iter()
|
||||
.filter_map(|&impl_| Some((impl_, self.probe_assoc_ty_unchecked(name, block, impl_)?)))
|
||||
.filter_map(|&impl_| {
|
||||
let (item, scope) =
|
||||
self.probe_assoc_item_unchecked(name, ty::AssocKind::Type, block, impl_)?;
|
||||
Some((impl_, (item.def_id, scope)))
|
||||
})
|
||||
.collect();
|
||||
|
||||
if candidates.is_empty() {
|
||||
@ -1249,7 +1255,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
},
|
||||
)?;
|
||||
|
||||
self.check_assoc_ty(assoc_item, name, def_scope, block, span);
|
||||
self.check_assoc_item(assoc_item, name, def_scope, block, span);
|
||||
|
||||
// FIXME(fmease): Currently creating throwaway `parent_args` to please
|
||||
// `lower_generic_args_of_assoc_item`. Modify the latter instead (or sth. similar) to
|
||||
@ -1336,50 +1342,69 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
}
|
||||
}
|
||||
|
||||
fn probe_assoc_ty(&self, name: Ident, block: HirId, span: Span, scope: DefId) -> Option<DefId> {
|
||||
let (item, def_scope) = self.probe_assoc_ty_unchecked(name, block, scope)?;
|
||||
self.check_assoc_ty(item, name, def_scope, block, span);
|
||||
/// Given name and kind search for the assoc item in the provided scope and check if it's accessible[^1].
|
||||
///
|
||||
/// [^1]: I.e., accessible in the provided scope wrt. visibility and stability.
|
||||
fn probe_assoc_item(
|
||||
&self,
|
||||
ident: Ident,
|
||||
kind: ty::AssocKind,
|
||||
block: HirId,
|
||||
span: Span,
|
||||
scope: DefId,
|
||||
) -> Option<ty::AssocItem> {
|
||||
let (item, scope) = self.probe_assoc_item_unchecked(ident, kind, block, scope)?;
|
||||
self.check_assoc_item(item.def_id, ident, scope, block, span);
|
||||
Some(item)
|
||||
}
|
||||
|
||||
fn probe_assoc_ty_unchecked(
|
||||
/// Given name and kind search for the assoc item in the provided scope
|
||||
/// *without* checking if it's accessible[^1].
|
||||
///
|
||||
/// [^1]: I.e., accessible in the provided scope wrt. visibility and stability.
|
||||
fn probe_assoc_item_unchecked(
|
||||
&self,
|
||||
name: Ident,
|
||||
ident: Ident,
|
||||
kind: ty::AssocKind,
|
||||
block: HirId,
|
||||
scope: DefId,
|
||||
) -> Option<(DefId, DefId)> {
|
||||
) -> Option<(ty::AssocItem, /*scope*/ DefId)> {
|
||||
let tcx = self.tcx();
|
||||
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block);
|
||||
|
||||
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block);
|
||||
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
|
||||
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
|
||||
// `ident` again and again.
|
||||
let item = tcx.associated_items(scope).in_definition_order().find(|i| {
|
||||
i.kind.namespace() == Namespace::TypeNS
|
||||
&& i.ident(tcx).normalize_to_macros_2_0() == ident
|
||||
})?;
|
||||
let item = tcx
|
||||
.associated_items(scope)
|
||||
.filter_by_name_unhygienic(ident.name)
|
||||
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == ident)?;
|
||||
|
||||
Some((item.def_id, def_scope))
|
||||
Some((*item, def_scope))
|
||||
}
|
||||
|
||||
fn check_assoc_ty(&self, item: DefId, name: Ident, def_scope: DefId, block: HirId, span: Span) {
|
||||
/// Check if the given assoc item is accessible in the provided scope wrt. visibility and stability.
|
||||
fn check_assoc_item(
|
||||
&self,
|
||||
item_def_id: DefId,
|
||||
ident: Ident,
|
||||
scope: DefId,
|
||||
block: HirId,
|
||||
span: Span,
|
||||
) {
|
||||
let tcx = self.tcx();
|
||||
let kind = DefKind::AssocTy;
|
||||
|
||||
if !tcx.visibility(item).is_accessible_from(def_scope, tcx) {
|
||||
let kind = tcx.def_kind_descr(kind, item);
|
||||
let msg = format!("{kind} `{name}` is private");
|
||||
let def_span = tcx.def_span(item);
|
||||
let reported = tcx
|
||||
.dcx()
|
||||
.struct_span_err(span, msg)
|
||||
.with_code(E0624)
|
||||
.with_span_label(span, format!("private {kind}"))
|
||||
.with_span_label(def_span, format!("{kind} defined here"))
|
||||
.emit();
|
||||
if !tcx.visibility(item_def_id).is_accessible_from(scope, tcx) {
|
||||
let reported = tcx.dcx().emit_err(crate::errors::AssocItemIsPrivate {
|
||||
span,
|
||||
kind: tcx.def_descr(item_def_id),
|
||||
name: ident,
|
||||
defined_here_label: tcx.def_span(item_def_id),
|
||||
});
|
||||
self.set_tainted_by_errors(reported);
|
||||
}
|
||||
tcx.check_stability(item, Some(block), span, None);
|
||||
|
||||
tcx.check_stability(item_def_id, Some(block), span, None);
|
||||
}
|
||||
|
||||
fn probe_traits_that_match_assoc_ty(
|
||||
|
@ -55,21 +55,23 @@ This API is completely unstable and subject to change.
|
||||
|
||||
*/
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(is_sorted)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(slice_partition_dedup)]
|
||||
#![feature(try_blocks)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
@ -1,7 +1,9 @@
|
||||
//! HIR pretty-printing is layered on top of AST pretty-printing. A number of
|
||||
//! the definitions in this file have equivalents in `rustc_ast_pretty`.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::util::parser::{self, AssocOp, Fixity};
|
||||
|
@ -113,7 +113,7 @@ fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'
|
||||
fn success<'tcx>(
|
||||
adj: Vec<Adjustment<'tcx>>,
|
||||
target: Ty<'tcx>,
|
||||
obligations: traits::PredicateObligations<'tcx>,
|
||||
obligations: Vec<traits::PredicateObligation<'tcx>>,
|
||||
) -> CoerceResult<'tcx> {
|
||||
Ok(InferOk { value: (adj, target), obligations })
|
||||
}
|
||||
|
@ -1342,14 +1342,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
Ok(method)
|
||||
}
|
||||
Err(error) => {
|
||||
if segment.ident.name != kw::Empty {
|
||||
if let Some(err) =
|
||||
self.report_method_error(expr.hir_id, rcvr_t, error, expected, false)
|
||||
{
|
||||
err.emit();
|
||||
}
|
||||
if segment.ident.name == kw::Empty {
|
||||
span_bug!(rcvr.span, "empty method name")
|
||||
} else {
|
||||
Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false))
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -18,12 +18,12 @@ use rustc_span::{def_id::LocalDefId, Span};
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DivergingFallbackBehavior {
|
||||
/// Always fallback to `()` (aka "always spontaneous decay")
|
||||
FallbackToUnit,
|
||||
ToUnit,
|
||||
/// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
|
||||
FallbackToNiko,
|
||||
ContextDependent,
|
||||
/// Always fallback to `!` (which should be equivalent to never falling back + not making
|
||||
/// never-to-any coercions unless necessary)
|
||||
FallbackToNever,
|
||||
ToNever,
|
||||
/// Don't fallback at all
|
||||
NoFallback,
|
||||
}
|
||||
@ -373,13 +373,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
diverging_fallback.insert(diverging_ty, ty);
|
||||
};
|
||||
|
||||
use DivergingFallbackBehavior::*;
|
||||
match behavior {
|
||||
FallbackToUnit => {
|
||||
DivergingFallbackBehavior::ToUnit => {
|
||||
debug!("fallback to () - legacy: {:?}", diverging_vid);
|
||||
fallback_to(self.tcx.types.unit);
|
||||
}
|
||||
FallbackToNiko => {
|
||||
DivergingFallbackBehavior::ContextDependent => {
|
||||
if found_infer_var_info.self_in_trait && found_infer_var_info.output {
|
||||
// This case falls back to () to ensure that the code pattern in
|
||||
// tests/ui/never_type/fallback-closure-ret.rs continues to
|
||||
@ -415,14 +414,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
fallback_to(self.tcx.types.never);
|
||||
}
|
||||
}
|
||||
FallbackToNever => {
|
||||
DivergingFallbackBehavior::ToNever => {
|
||||
debug!(
|
||||
"fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}",
|
||||
diverging_vid
|
||||
);
|
||||
fallback_to(self.tcx.types.never);
|
||||
}
|
||||
NoFallback => {
|
||||
DivergingFallbackBehavior::NoFallback => {
|
||||
debug!(
|
||||
"no fallback - `rustc_never_type_mode = \"no_fallback\"`: {:?}",
|
||||
diverging_vid
|
||||
|
@ -660,8 +660,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
|
||||
let ty_error = Ty::new_misc_error(self.tcx);
|
||||
pub(crate) fn err_args(&self, len: usize, guar: ErrorGuaranteed) -> Vec<Ty<'tcx>> {
|
||||
let ty_error = Ty::new_error(self.tcx, guar);
|
||||
vec![ty_error; len]
|
||||
}
|
||||
|
||||
@ -846,15 +846,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
if item_name.name != kw::Empty {
|
||||
if let Some(e) = self.report_method_error(
|
||||
self.report_method_error(
|
||||
hir_id,
|
||||
ty.normalized,
|
||||
error,
|
||||
Expectation::NoExpectation,
|
||||
trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021
|
||||
) {
|
||||
e.emit();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -113,17 +113,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
sp: Span,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
method: Result<MethodCallee<'tcx>, ()>,
|
||||
method: Result<MethodCallee<'tcx>, ErrorGuaranteed>,
|
||||
args_no_rcvr: &'tcx [hir::Expr<'tcx>],
|
||||
tuple_arguments: TupleArgumentsFlag,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let has_error = match method {
|
||||
Ok(method) => method.args.references_error() || method.sig.references_error(),
|
||||
Err(_) => true,
|
||||
Ok(method) => method.args.error_reported().and(method.sig.error_reported()),
|
||||
Err(guar) => Err(guar),
|
||||
};
|
||||
if has_error {
|
||||
let err_inputs = self.err_args(args_no_rcvr.len());
|
||||
if let Err(guar) = has_error {
|
||||
let err_inputs = self.err_args(args_no_rcvr.len(), guar);
|
||||
|
||||
let err_inputs = match tuple_arguments {
|
||||
DontTupleArguments => err_inputs,
|
||||
@ -140,7 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
tuple_arguments,
|
||||
method.ok().map(|method| method.def_id),
|
||||
);
|
||||
return Ty::new_misc_error(self.tcx);
|
||||
return Ty::new_error(self.tcx, guar);
|
||||
}
|
||||
|
||||
let method = method.unwrap();
|
||||
@ -237,7 +237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
_ => {
|
||||
// Otherwise, there's a mismatch, so clear out what we're expecting, and set
|
||||
// our input types to err_args so we don't blow up the error messages
|
||||
struct_span_code_err!(
|
||||
let guar = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
call_span,
|
||||
E0059,
|
||||
@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
for the function trait is neither a tuple nor unit"
|
||||
)
|
||||
.emit();
|
||||
(self.err_args(provided_args.len()), None)
|
||||
(self.err_args(provided_args.len(), guar), None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -124,7 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
body_id: LocalDefId,
|
||||
) -> FnCtxt<'a, 'tcx> {
|
||||
let (diverging_fallback_behavior, diverging_block_behavior) =
|
||||
parse_never_type_options_attr(root_ctxt.tcx);
|
||||
never_type_behavior(root_ctxt.tcx);
|
||||
FnCtxt {
|
||||
body_id,
|
||||
param_env,
|
||||
@ -387,11 +387,33 @@ impl<'tcx> LoweredTy<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn never_type_behavior(tcx: TyCtxt<'_>) -> (DivergingFallbackBehavior, DivergingBlockBehavior) {
|
||||
let (fallback, block) = parse_never_type_options_attr(tcx);
|
||||
let fallback = fallback.unwrap_or_else(|| default_fallback(tcx));
|
||||
let block = block.unwrap_or_default();
|
||||
|
||||
(fallback, block)
|
||||
}
|
||||
|
||||
/// Returns the default fallback which is used when there is no explicit override via `#![never_type_options(...)]`.
|
||||
fn default_fallback(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
|
||||
// Edition 2024: fallback to `!`
|
||||
if tcx.sess.edition().at_least_rust_2024() {
|
||||
return DivergingFallbackBehavior::ToNever;
|
||||
}
|
||||
|
||||
// `feature(never_type_fallback)`: fallback to `!` or `()` trying to not break stuff
|
||||
if tcx.features().never_type_fallback {
|
||||
return DivergingFallbackBehavior::ContextDependent;
|
||||
}
|
||||
|
||||
// Otherwise: fallback to `()`
|
||||
DivergingFallbackBehavior::ToUnit
|
||||
}
|
||||
|
||||
fn parse_never_type_options_attr(
|
||||
tcx: TyCtxt<'_>,
|
||||
) -> (DivergingFallbackBehavior, DivergingBlockBehavior) {
|
||||
use DivergingFallbackBehavior::*;
|
||||
|
||||
) -> (Option<DivergingFallbackBehavior>, Option<DivergingBlockBehavior>) {
|
||||
// Error handling is dubious here (unwraps), but that's probably fine for an internal attribute.
|
||||
// Just don't write incorrect attributes <3
|
||||
|
||||
@ -407,10 +429,10 @@ fn parse_never_type_options_attr(
|
||||
if item.has_name(sym::fallback) && fallback.is_none() {
|
||||
let mode = item.value_str().unwrap();
|
||||
match mode {
|
||||
sym::unit => fallback = Some(FallbackToUnit),
|
||||
sym::niko => fallback = Some(FallbackToNiko),
|
||||
sym::never => fallback = Some(FallbackToNever),
|
||||
sym::no => fallback = Some(NoFallback),
|
||||
sym::unit => fallback = Some(DivergingFallbackBehavior::ToUnit),
|
||||
sym::niko => fallback = Some(DivergingFallbackBehavior::ContextDependent),
|
||||
sym::never => fallback = Some(DivergingFallbackBehavior::ToNever),
|
||||
sym::no => fallback = Some(DivergingFallbackBehavior::NoFallback),
|
||||
_ => {
|
||||
tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)"));
|
||||
}
|
||||
@ -439,11 +461,5 @@ fn parse_never_type_options_attr(
|
||||
);
|
||||
}
|
||||
|
||||
let fallback = fallback.unwrap_or_else(|| {
|
||||
if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit }
|
||||
});
|
||||
|
||||
let block = block.unwrap_or_default();
|
||||
|
||||
(fallback, block)
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
// tidy-alphabetical-start
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(never_type)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(try_blocks)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
@ -33,7 +33,7 @@ use rustc_middle::ty::IsSuggestable;
|
||||
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::def_id::DefIdSet;
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span};
|
||||
use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span};
|
||||
use rustc_span::{Symbol, DUMMY_SP};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
|
||||
@ -192,7 +192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
error: MethodError<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
trait_missing_method: bool,
|
||||
) -> Option<Diag<'_>> {
|
||||
) -> ErrorGuaranteed {
|
||||
let (span, sugg_span, source, item_name, args) = match self.tcx.hir_node(call_id) {
|
||||
hir::Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::MethodCall(segment, rcvr, args, _),
|
||||
@ -226,8 +226,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
// Avoid suggestions when we don't know what's going on.
|
||||
if rcvr_ty.references_error() {
|
||||
return None;
|
||||
if let Err(guar) = rcvr_ty.error_reported() {
|
||||
return guar;
|
||||
}
|
||||
|
||||
match error {
|
||||
@ -265,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&mut sources,
|
||||
Some(sugg_span),
|
||||
);
|
||||
err.emit();
|
||||
return err.emit();
|
||||
}
|
||||
|
||||
MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => {
|
||||
@ -286,7 +286,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.unwrap_or_else(|| self.tcx.def_span(def_id));
|
||||
err.span_label(sp, format!("private {kind} defined here"));
|
||||
self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true);
|
||||
err.emit();
|
||||
return err.emit();
|
||||
}
|
||||
|
||||
MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => {
|
||||
@ -343,12 +343,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
return err.emit();
|
||||
}
|
||||
|
||||
MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"),
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn suggest_missing_writer(&self, rcvr_ty: Ty<'tcx>, rcvr_expr: &hir::Expr<'tcx>) -> Diag<'_> {
|
||||
@ -564,7 +563,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_no_match_method_error(
|
||||
fn report_no_match_method_error(
|
||||
&self,
|
||||
mut span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
@ -576,7 +575,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
no_match_data: &mut NoMatchData<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
trait_missing_method: bool,
|
||||
) -> Option<Diag<'_>> {
|
||||
) -> ErrorGuaranteed {
|
||||
let mode = no_match_data.mode;
|
||||
let tcx = self.tcx;
|
||||
let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
|
||||
@ -608,14 +607,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
// We could pass the file for long types into these two, but it isn't strictly necessary
|
||||
// given how targeted they are.
|
||||
if self.suggest_wrapping_range_with_parens(
|
||||
if let Err(guar) = self.report_failed_method_call_on_range_end(
|
||||
tcx,
|
||||
rcvr_ty,
|
||||
source,
|
||||
span,
|
||||
item_name,
|
||||
&short_ty_str,
|
||||
) || self.suggest_constraining_numerical_ty(
|
||||
) {
|
||||
return guar;
|
||||
}
|
||||
if let Err(guar) = self.report_failed_method_call_on_numerical_infer_var(
|
||||
tcx,
|
||||
rcvr_ty,
|
||||
source,
|
||||
@ -624,7 +626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
item_name,
|
||||
&short_ty_str,
|
||||
) {
|
||||
return None;
|
||||
return guar;
|
||||
}
|
||||
span = item_name.span;
|
||||
|
||||
@ -881,7 +883,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
vec![(span.shrink_to_lo(), format!("into_iter()."))],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return Some(err);
|
||||
return err.emit();
|
||||
} else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
|
||||
// We special case the situation where we are looking for `_` in
|
||||
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
|
||||
@ -1606,7 +1608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
|
||||
Some(err)
|
||||
err.emit()
|
||||
}
|
||||
|
||||
/// If an appropriate error source is not found, check method chain for possible candidates
|
||||
@ -2251,7 +2253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
/// Suggest possible range with adding parentheses, for example:
|
||||
/// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`.
|
||||
fn suggest_wrapping_range_with_parens(
|
||||
fn report_failed_method_call_on_range_end(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
actual: Ty<'tcx>,
|
||||
@ -2259,7 +2261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: Span,
|
||||
item_name: Ident,
|
||||
ty_str: &str,
|
||||
) -> bool {
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if let SelfSource::MethodCall(expr) = source {
|
||||
for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) {
|
||||
if let Node::Expr(parent_expr) = parent {
|
||||
@ -2316,7 +2318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
if pick.is_ok() {
|
||||
let range_span = parent_expr.span.with_hi(expr.span.hi());
|
||||
tcx.dcx().emit_err(errors::MissingParenthesesInRange {
|
||||
return Err(tcx.dcx().emit_err(errors::MissingParenthesesInRange {
|
||||
span,
|
||||
ty_str: ty_str.to_string(),
|
||||
method_name: item_name.as_str().to_string(),
|
||||
@ -2325,16 +2327,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
left: range_span.shrink_to_lo(),
|
||||
right: range_span.shrink_to_hi(),
|
||||
}),
|
||||
});
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn suggest_constraining_numerical_ty(
|
||||
fn report_failed_method_call_on_numerical_infer_var(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
actual: Ty<'tcx>,
|
||||
@ -2343,7 +2344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
item_kind: &str,
|
||||
item_name: Ident,
|
||||
ty_str: &str,
|
||||
) -> bool {
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let found_candidate = all_traits(self.tcx)
|
||||
.into_iter()
|
||||
.any(|info| self.associated_value(info.def_id, item_name).is_some());
|
||||
@ -2447,10 +2448,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
err.emit();
|
||||
return true;
|
||||
return Err(err.emit());
|
||||
}
|
||||
false
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// For code `rect::area(...)`,
|
||||
@ -3360,14 +3360,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.source_map()
|
||||
.indentation_before(rcvr.span)
|
||||
.unwrap_or_else(|| " ".to_string());
|
||||
err.multipart_suggestion(
|
||||
"consider pinning the expression",
|
||||
vec![
|
||||
(rcvr.span.shrink_to_lo(), format!("let mut pinned = std::pin::pin!(")),
|
||||
(rcvr.span.shrink_to_hi(), format!(");\n{indent}pinned.{pin_call}()")),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let mut expr = rcvr;
|
||||
while let Node::Expr(call_expr) = self.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let hir::ExprKind::MethodCall(hir::PathSegment { .. }, ..) =
|
||||
call_expr.kind
|
||||
{
|
||||
expr = call_expr;
|
||||
}
|
||||
match self.tcx.parent_hir_node(expr.hir_id) {
|
||||
Node::LetStmt(stmt)
|
||||
if let Some(init) = stmt.init
|
||||
&& let Ok(code) =
|
||||
self.tcx.sess.source_map().span_to_snippet(rcvr.span) =>
|
||||
{
|
||||
// We need to take care to account for the existing binding when we
|
||||
// suggest the code.
|
||||
err.multipart_suggestion(
|
||||
"consider pinning the expression",
|
||||
vec![
|
||||
(
|
||||
stmt.span.shrink_to_lo(),
|
||||
format!(
|
||||
"let mut pinned = std::pin::pin!({code});\n{indent}"
|
||||
),
|
||||
),
|
||||
(
|
||||
init.span.until(rcvr.span.shrink_to_hi()),
|
||||
format!("pinned.{pin_call}()"),
|
||||
),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
Node::Block(_) | Node::Stmt(_) => {
|
||||
// There's no binding, so we can provide a slightly nicer looking
|
||||
// suggestion.
|
||||
err.multipart_suggestion(
|
||||
"consider pinning the expression",
|
||||
vec![
|
||||
(
|
||||
rcvr.span.shrink_to_lo(),
|
||||
format!("let mut pinned = std::pin::pin!("),
|
||||
),
|
||||
(
|
||||
rcvr.span.shrink_to_hi(),
|
||||
format!(");\n{indent}pinned.{pin_call}()"),
|
||||
),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// We don't quite know what the users' code looks like, so we don't
|
||||
// provide a pinning suggestion.
|
||||
err.span_help(
|
||||
rcvr.span,
|
||||
"consider pinning the expression with `std::pin::pin!()` and \
|
||||
assigning that to a new binding",
|
||||
);
|
||||
}
|
||||
}
|
||||
// We don't care about the other suggestions.
|
||||
alt_rcvr_sugg = true;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
//! Support for serializing the dep-graph and reloading it.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![deny(missing_docs)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![allow(internal_features)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod assert_dep_graph;
|
||||
mod errors;
|
||||
|
@ -1,9 +1,11 @@
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))]
|
||||
#![cfg_attr(
|
||||
feature = "nightly",
|
||||
feature(extend_one, min_specialization, new_uninit, step_trait, test)
|
||||
)]
|
||||
#![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod bit_set;
|
||||
#[cfg(feature = "nightly")]
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![cfg_attr(feature = "nightly", feature(allow_internal_unstable))]
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", feature(allow_internal_unstable))]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
|
@ -31,6 +31,8 @@ use crate::infer::relate::{Relate, StructurallyRelateAliases, TypeRelation};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{Const, ImplSubject};
|
||||
|
||||
use crate::traits::Obligation;
|
||||
|
||||
/// Whether we should define opaque types or just treat them opaquely.
|
||||
///
|
||||
/// Currently only used to prevent predicate matching from matching anything
|
||||
@ -119,10 +121,8 @@ impl<'a, 'tcx> At<'a, 'tcx> {
|
||||
self.param_env,
|
||||
define_opaque_types,
|
||||
);
|
||||
fields
|
||||
.sup()
|
||||
.relate(expected, actual)
|
||||
.map(|_| InferOk { value: (), obligations: fields.obligations })
|
||||
fields.sup().relate(expected, actual)?;
|
||||
Ok(InferOk { value: (), obligations: fields.into_obligations() })
|
||||
}
|
||||
|
||||
/// Makes `expected <: actual`.
|
||||
@ -141,10 +141,8 @@ impl<'a, 'tcx> At<'a, 'tcx> {
|
||||
self.param_env,
|
||||
define_opaque_types,
|
||||
);
|
||||
fields
|
||||
.sub()
|
||||
.relate(expected, actual)
|
||||
.map(|_| InferOk { value: (), obligations: fields.obligations })
|
||||
fields.sub().relate(expected, actual)?;
|
||||
Ok(InferOk { value: (), obligations: fields.into_obligations() })
|
||||
}
|
||||
|
||||
/// Makes `expected == actual`.
|
||||
@ -163,10 +161,22 @@ impl<'a, 'tcx> At<'a, 'tcx> {
|
||||
self.param_env,
|
||||
define_opaque_types,
|
||||
);
|
||||
fields
|
||||
.equate(StructurallyRelateAliases::No)
|
||||
.relate(expected, actual)
|
||||
.map(|_| InferOk { value: (), obligations: fields.obligations })
|
||||
fields.equate(StructurallyRelateAliases::No).relate(expected, actual)?;
|
||||
Ok(InferOk {
|
||||
value: (),
|
||||
obligations: fields
|
||||
.goals
|
||||
.into_iter()
|
||||
.map(|goal| {
|
||||
Obligation::new(
|
||||
self.infcx.tcx,
|
||||
fields.trace.cause.clone(),
|
||||
goal.param_env,
|
||||
goal.predicate,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Equates `expected` and `found` while structurally relating aliases.
|
||||
@ -187,10 +197,8 @@ impl<'a, 'tcx> At<'a, 'tcx> {
|
||||
self.param_env,
|
||||
DefineOpaqueTypes::Yes,
|
||||
);
|
||||
fields
|
||||
.equate(StructurallyRelateAliases::Yes)
|
||||
.relate(expected, actual)
|
||||
.map(|_| InferOk { value: (), obligations: fields.obligations })
|
||||
fields.equate(StructurallyRelateAliases::Yes).relate(expected, actual)?;
|
||||
Ok(InferOk { value: (), obligations: fields.into_obligations() })
|
||||
}
|
||||
|
||||
pub fn relate<T>(
|
||||
@ -237,10 +245,8 @@ impl<'a, 'tcx> At<'a, 'tcx> {
|
||||
self.param_env,
|
||||
define_opaque_types,
|
||||
);
|
||||
fields
|
||||
.lub()
|
||||
.relate(expected, actual)
|
||||
.map(|value| InferOk { value, obligations: fields.obligations })
|
||||
let value = fields.lub().relate(expected, actual)?;
|
||||
Ok(InferOk { value, obligations: fields.into_obligations() })
|
||||
}
|
||||
|
||||
/// Computes the greatest-lower-bound, or mutual subtype, of two
|
||||
@ -261,10 +267,8 @@ impl<'a, 'tcx> At<'a, 'tcx> {
|
||||
self.param_env,
|
||||
define_opaque_types,
|
||||
);
|
||||
fields
|
||||
.glb()
|
||||
.relate(expected, actual)
|
||||
.map(|value| InferOk { value, obligations: fields.obligations })
|
||||
let value = fields.glb().relate(expected, actual)?;
|
||||
Ok(InferOk { value, obligations: fields.into_obligations() })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
pub use at::DefineOpaqueTypes;
|
||||
pub use freshen::TypeFreshener;
|
||||
pub use lexical_region_resolve::RegionResolutionError;
|
||||
pub use relate::combine::CombineFields;
|
||||
pub use relate::combine::PredicateEmittingRelation;
|
||||
pub use relate::StructurallyRelateAliases;
|
||||
pub use rustc_macros::{TypeFoldable, TypeVisitable};
|
||||
pub use rustc_middle::ty::IntVarValue;
|
||||
pub use BoundRegionConversionTime::*;
|
||||
@ -8,10 +11,8 @@ pub use RegionVariableOrigin::*;
|
||||
pub use SubregionOrigin::*;
|
||||
pub use ValuePairs::*;
|
||||
|
||||
use crate::infer::relate::{CombineFields, RelateResult};
|
||||
use crate::traits::{
|
||||
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
|
||||
};
|
||||
use crate::infer::relate::RelateResult;
|
||||
use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine};
|
||||
use error_reporting::TypeErrCtxt;
|
||||
use free_regions::RegionRelations;
|
||||
use lexical_region_resolve::LexicalRegionResolutions;
|
||||
@ -68,7 +69,7 @@ pub mod type_variable;
|
||||
#[derive(Debug)]
|
||||
pub struct InferOk<'tcx, T> {
|
||||
pub value: T,
|
||||
pub obligations: PredicateObligations<'tcx>,
|
||||
pub obligations: Vec<PredicateObligation<'tcx>>,
|
||||
}
|
||||
pub type InferResult<'tcx, T> = Result<InferOk<'tcx, T>, TypeError<'tcx>>;
|
||||
|
||||
@ -748,7 +749,7 @@ impl<'tcx, T> InferOk<'tcx, T> {
|
||||
}
|
||||
|
||||
impl<'tcx> InferOk<'tcx, ()> {
|
||||
pub fn into_obligations(self) -> PredicateObligations<'tcx> {
|
||||
pub fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
|
||||
self.obligations
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use super::{DefineOpaqueTypes, InferResult};
|
||||
use crate::errors::OpaqueHiddenTypeDiag;
|
||||
use crate::infer::{InferCtxt, InferOk};
|
||||
use crate::traits::{self, PredicateObligation};
|
||||
use crate::traits::{self, Obligation};
|
||||
use hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::traits::solve::Goal;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
@ -21,6 +21,8 @@ mod table;
|
||||
pub type OpaqueTypeMap<'tcx> = FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>;
|
||||
pub use table::{OpaqueTypeStorage, OpaqueTypeTable};
|
||||
|
||||
use super::DefineOpaqueTypes;
|
||||
|
||||
/// Information about the opaque types whose values we
|
||||
/// are inferring in this function (these are the `impl Trait` that
|
||||
/// appear in the return type).
|
||||
@ -62,11 +64,23 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
{
|
||||
let def_span = self.tcx.def_span(def_id);
|
||||
let span = if span.contains(def_span) { def_span } else { span };
|
||||
let code = traits::ObligationCauseCode::OpaqueReturnType(None);
|
||||
let cause = ObligationCause::new(span, body_id, code);
|
||||
let ty_var = self.next_ty_var(span);
|
||||
obligations.extend(
|
||||
self.handle_opaque_type(ty, ty_var, &cause, param_env).unwrap().obligations,
|
||||
self.handle_opaque_type(ty, ty_var, span, param_env)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|goal| {
|
||||
Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::new(
|
||||
span,
|
||||
body_id,
|
||||
traits::ObligationCauseCode::OpaqueReturnType(None),
|
||||
),
|
||||
goal.param_env,
|
||||
goal.predicate,
|
||||
)
|
||||
}),
|
||||
);
|
||||
ty_var
|
||||
}
|
||||
@ -80,9 +94,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
&self,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
span: Span,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> InferResult<'tcx, ()> {
|
||||
) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, TypeError<'tcx>> {
|
||||
let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() {
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => {
|
||||
let def_id = def_id.expect_local();
|
||||
@ -90,7 +104,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
// See comment on `insert_hidden_type` for why this is sufficient in coherence
|
||||
return Some(self.register_hidden_type(
|
||||
OpaqueTypeKey { def_id, args },
|
||||
cause.clone(),
|
||||
span,
|
||||
param_env,
|
||||
b,
|
||||
));
|
||||
@ -143,18 +157,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
&& self.tcx.is_type_alias_impl_trait(b_def_id)
|
||||
{
|
||||
self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag {
|
||||
span: cause.span,
|
||||
span,
|
||||
hidden_type: self.tcx.def_span(b_def_id),
|
||||
opaque_type: self.tcx.def_span(def_id),
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(self.register_hidden_type(
|
||||
OpaqueTypeKey { def_id, args },
|
||||
cause.clone(),
|
||||
param_env,
|
||||
b,
|
||||
))
|
||||
Some(self.register_hidden_type(OpaqueTypeKey { def_id, args }, span, param_env, b))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
@ -464,24 +473,23 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
fn register_hidden_type(
|
||||
&self,
|
||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
span: Span,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
hidden_ty: Ty<'tcx>,
|
||||
) -> InferResult<'tcx, ()> {
|
||||
let mut obligations = Vec::new();
|
||||
) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, TypeError<'tcx>> {
|
||||
let mut goals = Vec::new();
|
||||
|
||||
self.insert_hidden_type(opaque_type_key, &cause, param_env, hidden_ty, &mut obligations)?;
|
||||
self.insert_hidden_type(opaque_type_key, span, param_env, hidden_ty, &mut goals)?;
|
||||
|
||||
self.add_item_bounds_for_hidden_type(
|
||||
opaque_type_key.def_id.to_def_id(),
|
||||
opaque_type_key.args,
|
||||
cause,
|
||||
param_env,
|
||||
hidden_ty,
|
||||
&mut obligations,
|
||||
&mut goals,
|
||||
);
|
||||
|
||||
Ok(InferOk { value: (), obligations })
|
||||
Ok(goals)
|
||||
}
|
||||
|
||||
/// Insert a hidden type into the opaque type storage, making sure
|
||||
@ -507,27 +515,21 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
pub fn insert_hidden_type(
|
||||
&self,
|
||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
span: Span,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
hidden_ty: Ty<'tcx>,
|
||||
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||
goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) -> Result<(), TypeError<'tcx>> {
|
||||
// Ideally, we'd get the span where *this specific `ty` came
|
||||
// from*, but right now we just use the span from the overall
|
||||
// value being folded. In simple cases like `-> impl Foo`,
|
||||
// these are the same span, but not in cases like `-> (impl
|
||||
// Foo, impl Bar)`.
|
||||
let span = cause.span;
|
||||
if self.intercrate {
|
||||
// During intercrate we do not define opaque types but instead always
|
||||
// force ambiguity unless the hidden type is known to not implement
|
||||
// our trait.
|
||||
obligations.push(traits::Obligation::new(
|
||||
self.tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
ty::PredicateKind::Ambiguous,
|
||||
))
|
||||
goals.push(Goal::new(self.tcx, param_env, ty::PredicateKind::Ambiguous))
|
||||
} else {
|
||||
let prev = self
|
||||
.inner
|
||||
@ -535,10 +537,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
.opaque_types()
|
||||
.register(opaque_type_key, OpaqueHiddenType { ty: hidden_ty, span });
|
||||
if let Some(prev) = prev {
|
||||
obligations.extend(
|
||||
self.at(cause, param_env)
|
||||
goals.extend(
|
||||
self.at(&ObligationCause::dummy_with_span(span), param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, prev, hidden_ty)?
|
||||
.obligations,
|
||||
.obligations
|
||||
.into_iter()
|
||||
// FIXME: Shuttling between obligations and goals is awkward.
|
||||
.map(Goal::from),
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -550,10 +555,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
&self,
|
||||
def_id: DefId,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
hidden_ty: Ty<'tcx>,
|
||||
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||
goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
// Require that the hidden type is well-formed. We have to
|
||||
@ -567,12 +571,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
// type during MIR borrowck, causing us to infer the wrong
|
||||
// lifetime for its member constraints which then results in
|
||||
// unexpected region errors.
|
||||
obligations.push(traits::Obligation::new(
|
||||
tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
ty::ClauseKind::WellFormed(hidden_ty.into()),
|
||||
));
|
||||
goals.push(Goal::new(tcx, param_env, ty::ClauseKind::WellFormed(hidden_ty.into())));
|
||||
|
||||
let item_bounds = tcx.explicit_item_bounds(def_id);
|
||||
for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) {
|
||||
@ -588,13 +587,18 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
&& !tcx.is_impl_trait_in_trait(projection_ty.def_id)
|
||||
&& !self.next_trait_solver() =>
|
||||
{
|
||||
self.projection_ty_to_infer(
|
||||
let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id));
|
||||
goals.push(Goal::new(
|
||||
self.tcx,
|
||||
param_env,
|
||||
projection_ty,
|
||||
cause.clone(),
|
||||
0,
|
||||
obligations,
|
||||
)
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Projection(
|
||||
ty::ProjectionPredicate {
|
||||
projection_term: projection_ty.into(),
|
||||
term: ty_var.into(),
|
||||
},
|
||||
)),
|
||||
));
|
||||
ty_var
|
||||
}
|
||||
// Replace all other mentions of the same opaque type with the hidden type,
|
||||
// as the bounds must hold on the hidden type after all.
|
||||
@ -611,12 +615,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
|
||||
// Require that the predicate holds for the concrete type.
|
||||
debug!(?predicate);
|
||||
obligations.push(traits::Obligation::new(
|
||||
self.tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
predicate,
|
||||
));
|
||||
goals.push(Goal::new(self.tcx, param_env, predicate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,12 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert!(!self.next_trait_solver());
|
||||
let def_id = projection_ty.def_id;
|
||||
let ty_var = self.next_ty_var(self.tcx.def_span(def_id));
|
||||
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Projection(
|
||||
ty::ProjectionPredicate { projection_term: projection_ty.into(), term: ty_var.into() },
|
||||
)));
|
||||
let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id));
|
||||
let projection =
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate {
|
||||
projection_term: projection_ty.into(),
|
||||
term: ty_var.into(),
|
||||
}));
|
||||
let obligation =
|
||||
Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
|
||||
obligations.push(obligation);
|
||||
|
@ -25,9 +25,10 @@ use super::StructurallyRelateAliases;
|
||||
use super::{RelateResult, TypeRelation};
|
||||
use crate::infer::relate;
|
||||
use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
|
||||
use crate::traits::{Obligation, PredicateObligations};
|
||||
use crate::traits::{Obligation, PredicateObligation};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::infer::unify_key::EffectVarValue;
|
||||
use rustc_middle::traits::solve::Goal;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast};
|
||||
use rustc_middle::ty::{IntType, UintType};
|
||||
@ -38,7 +39,7 @@ pub struct CombineFields<'infcx, 'tcx> {
|
||||
pub infcx: &'infcx InferCtxt<'tcx>,
|
||||
pub trace: TypeTrace<'tcx>,
|
||||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
pub obligations: PredicateObligations<'tcx>,
|
||||
pub goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
pub define_opaque_types: DefineOpaqueTypes,
|
||||
}
|
||||
|
||||
@ -49,7 +50,21 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
define_opaque_types: DefineOpaqueTypes,
|
||||
) -> Self {
|
||||
Self { infcx, trace, param_env, define_opaque_types, obligations: vec![] }
|
||||
Self { infcx, trace, param_env, define_opaque_types, goals: vec![] }
|
||||
}
|
||||
|
||||
pub(crate) fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
|
||||
self.goals
|
||||
.into_iter()
|
||||
.map(|goal| {
|
||||
Obligation::new(
|
||||
self.infcx.tcx,
|
||||
self.trace.cause.clone(),
|
||||
goal.param_env,
|
||||
goal.predicate,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +76,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
b: Ty<'tcx>,
|
||||
) -> RelateResult<'tcx, Ty<'tcx>>
|
||||
where
|
||||
R: ObligationEmittingRelation<'tcx>,
|
||||
R: PredicateEmittingRelation<'tcx>,
|
||||
{
|
||||
debug_assert!(!a.has_escaping_bound_vars());
|
||||
debug_assert!(!b.has_escaping_bound_vars());
|
||||
@ -125,7 +140,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
relate::structurally_relate_tys(relation, a, b)
|
||||
}
|
||||
StructurallyRelateAliases::No => {
|
||||
relation.register_type_relate_obligation(a, b);
|
||||
relation.register_alias_relate_predicate(a, b);
|
||||
Ok(a)
|
||||
}
|
||||
}
|
||||
@ -156,7 +171,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
b: ty::Const<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Const<'tcx>>
|
||||
where
|
||||
R: ObligationEmittingRelation<'tcx>,
|
||||
R: PredicateEmittingRelation<'tcx>,
|
||||
{
|
||||
debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
|
||||
debug_assert!(!a.has_escaping_bound_vars());
|
||||
@ -290,21 +305,26 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
Glb::new(self)
|
||||
}
|
||||
|
||||
pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
|
||||
self.obligations.extend(obligations);
|
||||
pub fn register_obligations(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
self.goals.extend(obligations);
|
||||
}
|
||||
|
||||
pub fn register_predicates(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
self.obligations.extend(obligations.into_iter().map(|to_pred| {
|
||||
Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, to_pred)
|
||||
}))
|
||||
self.goals.extend(
|
||||
obligations
|
||||
.into_iter()
|
||||
.map(|to_pred| Goal::new(self.infcx.tcx, self.param_env, to_pred)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ObligationEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> {
|
||||
pub trait PredicateEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> {
|
||||
fn span(&self) -> Span;
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx>;
|
||||
@ -315,16 +335,18 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> {
|
||||
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
|
||||
|
||||
/// Register obligations that must hold in order for this relation to hold
|
||||
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>);
|
||||
fn register_goals(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
);
|
||||
|
||||
/// Register predicates that must hold in order for this relation to hold. Uses
|
||||
/// a default obligation cause, [`ObligationEmittingRelation::register_obligations`] should
|
||||
/// be used if control over the obligation causes is required.
|
||||
/// Register predicates that must hold in order for this relation to hold.
|
||||
/// This uses the default `param_env` of the obligation.
|
||||
fn register_predicates(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
|
||||
);
|
||||
|
||||
/// Register `AliasRelate` obligation(s) that both types must be related to each other.
|
||||
fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>);
|
||||
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::mem;
|
||||
|
||||
use super::StructurallyRelateAliases;
|
||||
use super::{ObligationEmittingRelation, Relate, RelateResult, TypeRelation};
|
||||
use super::{PredicateEmittingRelation, Relate, RelateResult, TypeRelation};
|
||||
use crate::infer::relate;
|
||||
use crate::infer::type_variable::TypeVariableValue;
|
||||
use crate::infer::{InferCtxt, RegionVariableOrigin};
|
||||
@ -30,7 +30,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
/// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
|
||||
/// other usecases (i.e. setting the value of a type var).
|
||||
#[instrument(level = "debug", skip(self, relation))]
|
||||
pub fn instantiate_ty_var<R: ObligationEmittingRelation<'tcx>>(
|
||||
pub fn instantiate_ty_var<R: PredicateEmittingRelation<'tcx>>(
|
||||
&self,
|
||||
relation: &mut R,
|
||||
target_is_expected: bool,
|
||||
@ -178,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
///
|
||||
/// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
|
||||
#[instrument(level = "debug", skip(self, relation))]
|
||||
pub(super) fn instantiate_const_var<R: ObligationEmittingRelation<'tcx>>(
|
||||
pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<'tcx>>(
|
||||
&self,
|
||||
relation: &mut R,
|
||||
target_is_expected: bool,
|
||||
|
@ -1,14 +1,15 @@
|
||||
//! Greatest lower bound. See [`lattice`].
|
||||
|
||||
use super::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::traits::solve::Goal;
|
||||
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::combine::{CombineFields, ObligationEmittingRelation};
|
||||
use super::combine::{CombineFields, PredicateEmittingRelation};
|
||||
use super::lattice::{self, LatticeDir};
|
||||
use super::StructurallyRelateAliases;
|
||||
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
|
||||
use crate::traits::{ObligationCause, PredicateObligations};
|
||||
use crate::traits::ObligationCause;
|
||||
|
||||
/// "Greatest lower bound" (common subtype)
|
||||
pub struct Glb<'combine, 'infcx, 'tcx> {
|
||||
@ -127,7 +128,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
|
||||
impl<'tcx> PredicateEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
|
||||
fn span(&self) -> Span {
|
||||
self.fields.trace.span()
|
||||
}
|
||||
@ -147,11 +148,14 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
|
||||
self.fields.register_predicates(obligations);
|
||||
}
|
||||
|
||||
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
|
||||
fn register_goals(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
self.fields.register_obligations(obligations);
|
||||
}
|
||||
|
||||
fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
|
||||
a.into(),
|
||||
b.into(),
|
||||
|
@ -17,11 +17,11 @@
|
||||
//!
|
||||
//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
|
||||
|
||||
use super::combine::ObligationEmittingRelation;
|
||||
use super::combine::PredicateEmittingRelation;
|
||||
use crate::infer::{DefineOpaqueTypes, InferCtxt};
|
||||
use crate::traits::ObligationCause;
|
||||
|
||||
use super::RelateResult;
|
||||
use rustc_middle::ty::relate::RelateResult;
|
||||
use rustc_middle::ty::TyVar;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Ty};
|
||||
///
|
||||
/// GLB moves "down" the lattice (to smaller values); LUB moves
|
||||
/// "up" the lattice (to bigger values).
|
||||
pub trait LatticeDir<'f, 'tcx>: ObligationEmittingRelation<'tcx> {
|
||||
pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<'tcx> {
|
||||
fn infcx(&self) -> &'f InferCtxt<'tcx>;
|
||||
|
||||
fn cause(&self) -> &ObligationCause<'tcx>;
|
||||
@ -108,9 +108,7 @@ where
|
||||
&& def_id.is_local()
|
||||
&& !this.infcx().next_trait_solver() =>
|
||||
{
|
||||
this.register_obligations(
|
||||
infcx.handle_opaque_type(a, b, this.cause(), this.param_env())?.obligations,
|
||||
);
|
||||
this.register_goals(infcx.handle_opaque_type(a, b, this.span(), this.param_env())?);
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
//! Least upper bound. See [`lattice`].
|
||||
|
||||
use super::combine::{CombineFields, ObligationEmittingRelation};
|
||||
use super::combine::{CombineFields, PredicateEmittingRelation};
|
||||
use super::lattice::{self, LatticeDir};
|
||||
use super::StructurallyRelateAliases;
|
||||
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
|
||||
use crate::traits::{ObligationCause, PredicateObligations};
|
||||
use crate::traits::ObligationCause;
|
||||
|
||||
use super::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::traits::solve::Goal;
|
||||
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -127,7 +128,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
|
||||
impl<'tcx> PredicateEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
|
||||
fn span(&self) -> Span {
|
||||
self.fields.trace.span()
|
||||
}
|
||||
@ -147,11 +148,14 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
|
||||
self.fields.register_predicates(obligations);
|
||||
}
|
||||
|
||||
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
|
||||
fn register_goals(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
self.fields.register_obligations(obligations)
|
||||
}
|
||||
|
||||
fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
|
||||
a.into(),
|
||||
b.into(),
|
||||
|
@ -6,7 +6,7 @@ pub use rustc_middle::ty::relate::*;
|
||||
|
||||
pub use self::_match::MatchAgainstFreshVars;
|
||||
pub use self::combine::CombineFields;
|
||||
pub use self::combine::ObligationEmittingRelation;
|
||||
pub use self::combine::PredicateEmittingRelation;
|
||||
|
||||
pub mod _match;
|
||||
pub(super) mod combine;
|
||||
|
@ -1,11 +1,10 @@
|
||||
use super::combine::CombineFields;
|
||||
use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
|
||||
use crate::infer::BoundRegionConversionTime::HigherRankedType;
|
||||
use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
|
||||
use crate::traits::{Obligation, PredicateObligations};
|
||||
|
||||
use super::{
|
||||
relate_args_invariantly, relate_args_with_variances, ObligationEmittingRelation, Relate,
|
||||
RelateResult, StructurallyRelateAliases, TypeRelation,
|
||||
use rustc_middle::traits::solve::Goal;
|
||||
use rustc_middle::ty::relate::{
|
||||
relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation,
|
||||
};
|
||||
use rustc_middle::ty::TyVar;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
@ -88,9 +87,8 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
|
||||
ty::Covariant => {
|
||||
// can't make progress on `A <: B` if both A and B are
|
||||
// type variables, so record an obligation.
|
||||
self.fields.obligations.push(Obligation::new(
|
||||
self.fields.goals.push(Goal::new(
|
||||
self.tcx(),
|
||||
self.fields.trace.cause.clone(),
|
||||
self.fields.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
|
||||
a_is_expected: true,
|
||||
@ -102,9 +100,8 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
|
||||
ty::Contravariant => {
|
||||
// can't make progress on `B <: A` if both A and B are
|
||||
// type variables, so record an obligation.
|
||||
self.fields.obligations.push(Obligation::new(
|
||||
self.fields.goals.push(Goal::new(
|
||||
self.tcx(),
|
||||
self.fields.trace.cause.clone(),
|
||||
self.fields.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
|
||||
a_is_expected: false,
|
||||
@ -153,11 +150,12 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
|
||||
&& def_id.is_local()
|
||||
&& !infcx.next_trait_solver() =>
|
||||
{
|
||||
self.fields.obligations.extend(
|
||||
infcx
|
||||
.handle_opaque_type(a, b, &self.fields.trace.cause, self.param_env())?
|
||||
.obligations,
|
||||
);
|
||||
self.fields.goals.extend(infcx.handle_opaque_type(
|
||||
a,
|
||||
b,
|
||||
self.fields.trace.cause.span,
|
||||
self.param_env(),
|
||||
)?);
|
||||
}
|
||||
|
||||
_ => {
|
||||
@ -298,7 +296,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
|
||||
impl<'tcx> PredicateEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
|
||||
fn span(&self) -> Span {
|
||||
self.fields.trace.span()
|
||||
}
|
||||
@ -318,11 +316,14 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
|
||||
self.fields.register_predicates(obligations);
|
||||
}
|
||||
|
||||
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
|
||||
fn register_goals(
|
||||
&mut self,
|
||||
obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) {
|
||||
self.fields.register_obligations(obligations);
|
||||
}
|
||||
|
||||
fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
|
||||
ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
|
||||
a.into(),
|
||||
|
@ -12,22 +12,24 @@
|
||||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(extend_one)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(iterator_try_collect)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(yeet_expr)]
|
||||
#![recursion_limit = "512"] // For rustdoc
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
@ -114,8 +114,6 @@ impl<'tcx> PolyTraitObligation<'tcx> {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
rustc_data_structures::static_assert_size!(PredicateObligation<'_>, 48);
|
||||
|
||||
pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>;
|
||||
|
||||
pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>;
|
||||
|
||||
/// A callback that can be provided to `inspect_typeck`. Invoked on evaluation
|
||||
|
@ -1,7 +1,9 @@
|
||||
// tidy-alphabetical-start
|
||||
#![feature(decl_macro)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(thread_spawn_unchecked)]
|
||||
#![feature(try_blocks)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod callbacks;
|
||||
mod errors;
|
||||
|
@ -19,9 +19,11 @@
|
||||
//!
|
||||
//! [`rustc_parse::lexer`]: ../rustc_parse/lexer/index.html
|
||||
|
||||
// tidy-alphabetical-start
|
||||
// We want to be able to build this crate with a stable compiler,
|
||||
// so no `#![feature]` attributes should be added.
|
||||
#![deny(unstable_features)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod cursor;
|
||||
pub mod unescape;
|
||||
|
@ -445,7 +445,8 @@ lint_macro_is_private = macro `{$ident}` is private
|
||||
lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used
|
||||
|
||||
lint_macro_use_deprecated =
|
||||
deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead
|
||||
applying the `#[macro_use]` attribute to an `extern crate` item is deprecated
|
||||
.help = remove it and import macros at use sites with a `use` item instead
|
||||
|
||||
lint_malformed_attribute = malformed lint attribute input
|
||||
|
||||
@ -456,7 +457,7 @@ lint_map_unit_fn = `Iterator::map` call that discard the iterator's values
|
||||
.map_label = after this call to map, the resulting iterator is `impl Iterator<Item = ()>`, which means the only information carried by the iterator is the number of items
|
||||
.suggestion = you might have meant to use `Iterator::for_each`
|
||||
|
||||
lint_metavariable_still_repeating = variable '{$name}' is still repeating at this depth
|
||||
lint_metavariable_still_repeating = variable `{$name}` is still repeating at this depth
|
||||
|
||||
lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator
|
||||
|
||||
@ -635,8 +636,8 @@ lint_pattern_in_bodiless = patterns aren't allowed in functions without bodies
|
||||
lint_pattern_in_foreign = patterns aren't allowed in foreign function declarations
|
||||
.label = pattern not allowed in foreign function
|
||||
|
||||
lint_private_extern_crate_reexport =
|
||||
extern crate `{$ident}` is private, and cannot be re-exported, consider declaring with `pub`
|
||||
lint_private_extern_crate_reexport = extern crate `{$ident}` is private and cannot be re-exported
|
||||
.suggestion = consider making the `extern crate` item publicly accessible
|
||||
|
||||
lint_proc_macro_derive_resolution_fallback = cannot find {$ns} `{$ident}` in this scope
|
||||
.label = names from parent modules are not accessible without an explicit import
|
||||
@ -847,7 +848,8 @@ lint_unused_coroutine =
|
||||
}{$post} that must be used
|
||||
.note = coroutines are lazy and do nothing unless resumed
|
||||
|
||||
lint_unused_crate_dependency = external crate `{$extern_crate}` unused in `{$local_crate}`: remove the dependency or add `use {$extern_crate} as _;`
|
||||
lint_unused_crate_dependency = extern crate `{$extern_crate}` is unused in crate `{$local_crate}`
|
||||
.help = remove the dependency or add `use {$extern_crate} as _;` to the crate root
|
||||
|
||||
lint_unused_def = unused {$pre}`{$def}`{$post} that must be used
|
||||
.suggestion = use `let _ = ...` to ignore the resulting value
|
||||
|
@ -142,7 +142,7 @@ declare_lint! {
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(box_pointers)]
|
||||
/// struct Foo {
|
||||
/// x: Box<isize>,
|
||||
/// x: Box<i32>,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
@ -340,8 +340,9 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
|
||||
lints::MacroUseDeprecated.decorate_lint(diag);
|
||||
}
|
||||
BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag),
|
||||
BuiltinLintDiag::PrivateExternCrateReexport(ident) => {
|
||||
lints::PrivateExternCrateReexport { ident }.decorate_lint(diag);
|
||||
BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => {
|
||||
lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() }
|
||||
.decorate_lint(diag);
|
||||
}
|
||||
BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag),
|
||||
BuiltinLintDiag::MacroIsPrivate(ident) => {
|
||||
|
@ -25,9 +25,10 @@
|
||||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(array_windows)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
@ -35,9 +36,10 @@
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_order_by)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(trait_upcasting)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(trait_upcasting)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod async_fn_in_trait;
|
||||
pub mod builtin;
|
||||
|
@ -2313,6 +2313,7 @@ pub mod unexpected_cfg_value {
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_macro_use_deprecated)]
|
||||
#[help]
|
||||
pub struct MacroUseDeprecated;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
@ -2323,6 +2324,8 @@ pub struct UnusedMacroUse;
|
||||
#[diag(lint_private_extern_crate_reexport, code = E0365)]
|
||||
pub struct PrivateExternCrateReexport {
|
||||
pub ident: Ident,
|
||||
#[suggestion(code = "pub ", style = "verbose", applicability = "maybe-incorrect")]
|
||||
pub sugg: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
@ -2416,6 +2419,7 @@ pub struct UnknownMacroVariable {
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_unused_crate_dependency)]
|
||||
#[help]
|
||||
pub struct UnusedCrateDependency {
|
||||
pub extern_crate: Symbol,
|
||||
pub local_crate: Symbol,
|
||||
|
@ -46,6 +46,8 @@ declare_lint! {
|
||||
/// fn test() -> impl Trait<Assoc = Tait> {
|
||||
/// 42
|
||||
/// }
|
||||
///
|
||||
/// fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
|
@ -49,10 +49,10 @@ declare_lint! {
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Since Rust CURRENT_RUSTC_VERSION, boxed slices implement `IntoIterator`. However, to avoid
|
||||
/// Since Rust 1.80.0, boxed slices implement `IntoIterator`. However, to avoid
|
||||
/// breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still
|
||||
/// behave as `(&boxed_slice).into_iter()`, returning an iterator over
|
||||
/// references, just like in Rust CURRENT_RUSTC_VERSION and earlier.
|
||||
/// references, just like in Rust 1.80.0 and earlier.
|
||||
/// This only applies to the method call syntax `boxed_slice.into_iter()`, not to
|
||||
/// any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`.
|
||||
pub BOXED_SLICE_INTO_ITER,
|
||||
|
@ -511,8 +511,9 @@ declare_lint! {
|
||||
/// This will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// error: external crate `regex` unused in `lint_example`: remove the dependency or add `use regex as _;`
|
||||
/// error: extern crate `regex` is unused in crate `lint_example`
|
||||
/// |
|
||||
/// = help: remove the dependency or add `use regex as _;` to the crate root
|
||||
/// note: the lint level is defined here
|
||||
/// --> src/lib.rs:1:9
|
||||
/// |
|
||||
@ -2160,8 +2161,7 @@ declare_lint! {
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `macro_use_extern_crate` lint detects the use of the
|
||||
/// [`macro_use` attribute].
|
||||
/// The `macro_use_extern_crate` lint detects the use of the [`macro_use` attribute].
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
@ -2179,12 +2179,13 @@ declare_lint! {
|
||||
/// This will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// error: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead
|
||||
/// error: applying the `#[macro_use]` attribute to an `extern crate` item is deprecated
|
||||
/// --> src/main.rs:3:1
|
||||
/// |
|
||||
/// 3 | #[macro_use]
|
||||
/// | ^^^^^^^^^^^^
|
||||
/// |
|
||||
/// = help: remove it and import macros at use sites with a `use` item instead
|
||||
/// note: the lint level is defined here
|
||||
/// --> src/main.rs:1:9
|
||||
/// |
|
||||
|
@ -1,3 +1,4 @@
|
||||
// tidy-alphabetical-start
|
||||
pub use self::Level::*;
|
||||
use rustc_ast::node_id::NodeId;
|
||||
use rustc_ast::{AttrId, Attribute};
|
||||
@ -14,6 +15,7 @@ use rustc_span::edition::Edition;
|
||||
use rustc_span::symbol::MacroRulesNormalizedIdent;
|
||||
use rustc_span::{sym, symbol::Ident, Span, Symbol};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -706,7 +708,10 @@ pub enum BuiltinLintDiag {
|
||||
},
|
||||
MacroUseDeprecated,
|
||||
UnusedMacroUse,
|
||||
PrivateExternCrateReexport(Ident),
|
||||
PrivateExternCrateReexport {
|
||||
source: Ident,
|
||||
extern_crate_span: Span,
|
||||
},
|
||||
UnusedLabel,
|
||||
MacroIsPrivate(Ident),
|
||||
UnusedMacroDefinition(Symbol),
|
||||
|
@ -23,6 +23,7 @@ const OPTIONAL_COMPONENTS: &[&str] = &[
|
||||
"nvptx",
|
||||
"hexagon",
|
||||
"riscv",
|
||||
"xtensa",
|
||||
"bpf",
|
||||
];
|
||||
|
||||
|
@ -155,6 +155,12 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char* FileName) {
|
||||
#define SUBTARGET_SPARC
|
||||
#endif
|
||||
|
||||
#ifdef LLVM_COMPONENT_XTENSA
|
||||
#define SUBTARGET_XTENSA SUBTARGET(XTENSA)
|
||||
#else
|
||||
#define SUBTARGET_XTENSA
|
||||
#endif
|
||||
|
||||
#ifdef LLVM_COMPONENT_HEXAGON
|
||||
#define SUBTARGET_HEXAGON SUBTARGET(Hexagon)
|
||||
#else
|
||||
@ -180,6 +186,7 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char* FileName) {
|
||||
SUBTARGET_MSP430 \
|
||||
SUBTARGET_SPARC \
|
||||
SUBTARGET_HEXAGON \
|
||||
SUBTARGET_XTENSA \
|
||||
SUBTARGET_RISCV \
|
||||
SUBTARGET_LOONGARCH \
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user