Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,6 @@ fn add_hidden_type<'tcx>(
}
}

fn get_hidden_type<'tcx>(
hidden_types: &DefinitionSiteHiddenTypes<'tcx>,
def_id: LocalDefId,
) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
}

#[derive(Debug)]
struct DefiningUse<'tcx> {
/// The opaque type using non NLL vars. This uses the actual
Expand Down Expand Up @@ -508,7 +501,8 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>(
let tcx = infcx.tcx;
let mut errors = Vec::new();
for &(key, hidden_type) in opaque_types {
let Some(expected) = get_hidden_type(hidden_types, key.def_id) else {
let Some(expected) = hidden_types.0.get(&key.def_id).map(|ty| EarlyBinder::bind(*ty))
else {
if !tcx.use_typing_mode_borrowck() {
if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
&& alias_ty.def_id == key.def_id.to_def_id()
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ fn typeck_with_inspect<'tcx>(
// the future.
fcx.check_repeat_exprs();

// We need to handle opaque types before emitting ambiguity errors as applying
// defining uses may guide type inference.
if fcx.next_trait_solver() {
fcx.try_handle_opaque_type_uses_next();
}

fcx.type_inference_fallback();

// Even though coercion casts provide type hints, we check casts after fallback for
Expand Down
107 changes: 66 additions & 41 deletions compiler/rustc_hir_typeck/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,50 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
/// inference variables.
///
/// It then uses these defining uses to guide inference for all other uses.
///
/// Unlike `handle_opaque_type_uses_next`, this does not report errors.
#[instrument(level = "debug", skip(self))]
pub(super) fn try_handle_opaque_type_uses_next(&mut self) {
// We clone the opaques instead of stealing them here as we still need
// to use them after fallback.
let opaque_types: Vec<_> = self.infcx.clone_opaque_types();

self.compute_definition_site_hidden_types(opaque_types, false);
}

/// This takes all the opaque type uses during HIR typeck. It first computes
/// the concrete hidden type by iterating over all defining uses.
///
/// A use during HIR typeck is defining if all non-lifetime arguments are
/// unique generic parameters and the hidden type does not reference any
/// inference variables.
///
/// It then uses these defining uses to guide inference for all other uses.
#[instrument(level = "debug", skip(self))]
pub(super) fn handle_opaque_type_uses_next(&mut self) {
// We clone the opaques instead of stealing them here as they are still used for
// normalization in the next generation trait solver.
let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types();
let opaque_types: Vec<_> = self.infcx.clone_opaque_types();
let num_entries = self.inner.borrow_mut().opaque_types().num_entries();
let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries));
debug_assert_eq!(prev, None);
for entry in &mut opaque_types {
*entry = self.resolve_vars_if_possible(*entry);
}
debug!(?opaque_types);

self.compute_definition_site_hidden_types(&opaque_types);
self.apply_definition_site_hidden_types(&opaque_types);
self.compute_definition_site_hidden_types(opaque_types, true);
}
}

#[derive(Copy, Clone, Debug)]
enum UsageKind<'tcx> {
None,
NonDefiningUse(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>),
UnconstrainedHiddenType(OpaqueHiddenType<'tcx>),
HasDefiningUse,
HasDefiningUse(OpaqueHiddenType<'tcx>),
}

impl<'tcx> UsageKind<'tcx> {
fn merge(&mut self, other: UsageKind<'tcx>) {
match (&*self, &other) {
(UsageKind::HasDefiningUse, _) | (_, UsageKind::None) => unreachable!(),
(UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(),
(UsageKind::None, _) => *self = other,
// When mergining non-defining uses, prefer earlier ones. This means
// the error happens as early as possible.
Expand All @@ -64,7 +79,7 @@ impl<'tcx> UsageKind<'tcx> {
// intended to be defining.
(
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse,
UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_),
) => *self = other,
}
}
Expand All @@ -73,8 +88,14 @@ impl<'tcx> UsageKind<'tcx> {
impl<'tcx> FnCtxt<'_, 'tcx> {
fn compute_definition_site_hidden_types(
&mut self,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
mut opaque_types: Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>,
error_on_missing_defining_use: bool,
) {
for entry in opaque_types.iter_mut() {
*entry = self.resolve_vars_if_possible(*entry);
}
debug!(?opaque_types);

let tcx = self.tcx;
let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
else {
Expand All @@ -88,19 +109,47 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
_ => unreachable!("not opaque or generator: {def_id:?}"),
}

// We do actually need to check this the second pass (we can't just
// store this), because we can go from `UnconstrainedHiddenType` to
// `HasDefiningUse` (because of fallback)
let mut usage_kind = UsageKind::None;
for &(opaque_type_key, hidden_type) in opaque_types {
for &(opaque_type_key, hidden_type) in &opaque_types {
if opaque_type_key.def_id != def_id {
continue;
}

usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type));
if let UsageKind::HasDefiningUse = usage_kind {

if let UsageKind::HasDefiningUse(..) = usage_kind {
break;
}
}

if let UsageKind::HasDefiningUse(ty) = usage_kind {
for &(opaque_type_key, hidden_type) in &opaque_types {
if opaque_type_key.def_id != def_id {
continue;
}

let expected = EarlyBinder::bind(ty.ty).instantiate(tcx, opaque_type_key.args);
self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
}

// Being explicit here: it may be possible that we in a
// previous call to this function we did an insert, but this
// should be just fine, since they all get equated anyways and
// we shouldn't ever go from `HasDefiningUse` to anyway else.
let _ = self.typeck_results.borrow_mut().hidden_types.insert(def_id, ty);
}

// If we're in `fn try_handle_opaque_type_uses_next` then do not
// report any errors.
if !error_on_missing_defining_use {
continue;
}

let guar = match usage_kind {
UsageKind::HasDefiningUse(_) => continue,
UsageKind::None => {
if let Some(guar) = self.tainted_by_errors() {
guar
Expand Down Expand Up @@ -137,7 +186,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
.emit()
}
}
UsageKind::HasDefiningUse => continue,
};

self.typeck_results
Expand All @@ -148,8 +196,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
}
}

#[tracing::instrument(skip(self), ret)]
fn consider_opaque_type_use(
&mut self,
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_type: OpaqueHiddenType<'tcx>,
) -> UsageKind<'tcx> {
Expand All @@ -161,11 +210,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
) {
match err {
NonDefiningUseReason::Tainted(guar) => {
self.typeck_results.borrow_mut().hidden_types.insert(
opaque_type_key.def_id,
OpaqueHiddenType::new_error(self.tcx, guar),
);
return UsageKind::HasDefiningUse;
return UsageKind::HasDefiningUse(OpaqueHiddenType::new_error(self.tcx, guar));
}
_ => return UsageKind::NonDefiningUse(opaque_type_key, hidden_type),
};
Expand Down Expand Up @@ -193,27 +238,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
self.tcx,
DefiningScopeKind::HirTypeck,
);

let prev = self
.typeck_results
.borrow_mut()
.hidden_types
.insert(opaque_type_key.def_id, hidden_type);
assert!(prev.is_none());
UsageKind::HasDefiningUse
}

fn apply_definition_site_hidden_types(
&mut self,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
) {
let tcx = self.tcx;
for &(key, hidden_type) in opaque_types {
let expected = *self.typeck_results.borrow_mut().hidden_types.get(&key.def_id).unwrap();

let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args);
self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
}
UsageKind::HasDefiningUse(hidden_type)
}

/// We may in theory add further uses of an opaque after cloning the opaque
Expand Down
14 changes: 9 additions & 5 deletions compiler/rustc_next_trait_solver/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,23 @@ where
response_no_constraints(cx, input, Certainty::overflow(false))
}

fn is_ambiguous_result(result: QueryResult<I>) -> bool {
result.is_ok_and(|response| {
has_no_inference_or_external_constraints(response)
fn is_ambiguous_result(result: QueryResult<I>) -> Option<Certainty> {
result.ok().and_then(|response| {
if has_no_inference_or_external_constraints(response)
&& matches!(response.value.certainty, Certainty::Maybe { .. })
{
Some(response.value.certainty)
} else {
None
}
})
}

fn propagate_ambiguity(
cx: I,
for_input: CanonicalInput<I>,
from_result: QueryResult<I>,
certainty: Certainty,
) -> QueryResult<I> {
let certainty = from_result.unwrap().value.certainty;
response_no_constraints(cx, for_input, certainty)
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::inherent::*;
use crate::ir_print::IrPrint;
use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
use crate::relate::Relate;
use crate::solve::{CanonicalInput, ExternalConstraintsData, QueryResult, inspect};
use crate::solve::{CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect};
use crate::visit::{Flags, TypeVisitable};
use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph};

Expand Down Expand Up @@ -550,6 +550,7 @@ impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
impl<I: Interner> search_graph::Cx for I {
type Input = CanonicalInput<I>;
type Result = QueryResult<I>;
type AmbiguityInfo = Certainty;

type DepNodeIndex = I::DepNodeIndex;
type Tracked<T: Debug + Clone> = I::Tracked<T>;
Expand Down
34 changes: 21 additions & 13 deletions compiler/rustc_type_ir/src/search_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub use global_cache::GlobalCache;
pub trait Cx: Copy {
type Input: Debug + Eq + Hash + Copy;
type Result: Debug + Eq + Hash + Copy;
type AmbiguityInfo: Debug + Eq + Hash + Copy;

type DepNodeIndex;
type Tracked<T: Debug + Clone>: Debug;
Expand Down Expand Up @@ -96,11 +97,13 @@ pub trait Delegate: Sized {
input: <Self::Cx as Cx>::Input,
) -> <Self::Cx as Cx>::Result;

fn is_ambiguous_result(result: <Self::Cx as Cx>::Result) -> bool;
fn is_ambiguous_result(
result: <Self::Cx as Cx>::Result,
) -> Option<<Self::Cx as Cx>::AmbiguityInfo>;
fn propagate_ambiguity(
cx: Self::Cx,
for_input: <Self::Cx as Cx>::Input,
from_result: <Self::Cx as Cx>::Result,
ambiguity_info: <Self::Cx as Cx>::AmbiguityInfo,
) -> <Self::Cx as Cx>::Result;

fn compute_goal(
Expand Down Expand Up @@ -913,9 +916,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
/// heads from the stack. This may not necessarily mean that we've actually
/// reached a fixpoint for that cycle head, which impacts the way we rebase
/// provisional cache entries.
enum RebaseReason {
enum RebaseReason<X: Cx> {
NoCycleUsages,
Ambiguity,
Ambiguity(X::AmbiguityInfo),
Overflow,
/// We've actually reached a fixpoint.
///
Expand Down Expand Up @@ -951,7 +954,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
&mut self,
cx: X,
stack_entry: &StackEntry<X>,
rebase_reason: RebaseReason,
rebase_reason: RebaseReason<X>,
) {
let popped_head_index = self.stack.next_index();
#[allow(rustc::potential_query_instability)]
Expand All @@ -969,10 +972,6 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
return true;
};

let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else {
return false;
};

// We're rebasing an entry `e` over a head `p`. This head
// has a number of own heads `h` it depends on.
//
Expand Down Expand Up @@ -1033,8 +1032,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
// is not actually equal to the final provisional result. We
// need to discard the provisional cache entry in this case.
RebaseReason::NoCycleUsages => return false,
RebaseReason::Ambiguity => {
*result = D::propagate_ambiguity(cx, input, *result);
RebaseReason::Ambiguity(info) => {
*result = D::propagate_ambiguity(cx, input, info);
}
RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input),
RebaseReason::ReachedFixpoint(None) => {}
Expand All @@ -1046,6 +1045,10 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
};
}

let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else {
return false;
};

// We now care about the path from the next highest cycle head to the
// provisional cache entry.
*path_from_head = path_from_head.extend(Self::cycle_path_kind(
Expand Down Expand Up @@ -1268,6 +1271,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
}

/// Whether we've reached a fixpoint when evaluating a cycle head.
#[instrument(level = "trace", skip(self, stack_entry), ret)]
fn reached_fixpoint(
&mut self,
stack_entry: &StackEntry<X>,
Expand Down Expand Up @@ -1355,8 +1359,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
// As we only get to this branch if we haven't yet reached a fixpoint,
// we also taint all provisional cache entries which depend on the
// current goal.
if D::is_ambiguous_result(result) {
self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Ambiguity);
if let Some(info) = D::is_ambiguous_result(result) {
self.rebase_provisional_cache_entries(
cx,
&stack_entry,
RebaseReason::Ambiguity(info),
);
return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
};

Expand Down
Loading
Loading