Skip to content
Draft
14 changes: 9 additions & 5 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use rustc_ast as ast;
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::{NodeId, PatKind, attr, token};
use rustc_errors::E0001;
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
use rustc_session::Session;
use rustc_session::parse::{feature_err, feature_warn};
Expand Down Expand Up @@ -650,10 +651,13 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
.map(|feat| feat.attr_sp)
{
#[allow(rustc::symbol_intern_string_literal)]
sess.dcx().emit_err(errors::IncompatibleFeatures {
spans: vec![gce_span],
f1: Symbol::intern("-Znext-solver=globally"),
f2: sym::generic_const_exprs,
});
sess.dcx()
.create_fatal(errors::IncompatibleFeatures {
spans: vec![gce_span],
f1: Symbol::intern("-Znext-solver=globally"),
f2: sym::generic_const_exprs,
})
.with_code(E0001)
.emit();
}
}
120 changes: 74 additions & 46 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult};
use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
use rustc_infer::traits::{ObligationCause, ObligationCauseCode, PredicateObligations};
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::span_bug;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{
self, ClosureKind, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor,
self, ClosureKind, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_span::def_id::LocalDefId;
use rustc_span::{DUMMY_SP, Span};
use rustc_trait_selection::error_reporting::traits::ArgKind;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use tracing::{debug, instrument, trace};

use super::{CoroutineTypes, Expectation, FnCtxt, check_fn};
Expand Down Expand Up @@ -384,56 +385,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Make sure that we didn't infer a signature that mentions itself.
// This can happen when we elaborate certain supertrait bounds that
// mention projections containing the `Self` type. See #105401.
struct MentionsTy<'tcx> {
expected_ty: Ty<'tcx>,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MentionsTy<'tcx> {
type Result = ControlFlow<()>;

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
if t == self.expected_ty {
ControlFlow::Break(())
} else {
t.super_visit_with(self)
}
}
}

// Don't infer a closure signature from a goal that names the closure type as this will
// (almost always) lead to occurs check errors later in type checking.
//
// Doing so will (almost always) lead to occurs check errors later in
// type checking.
if self.next_trait_solver()
&& let Some(inferred_sig) = inferred_sig
{
// In the new solver it is difficult to explicitly normalize the inferred signature as we
// would have to manually handle universes and rewriting bound vars and placeholders back
// and forth.
//
// Instead we take advantage of the fact that we relating an inference variable with an alias
// will only instantiate the variable if the alias is rigid(*not quite). Concretely we:
// - Create some new variable `?sig`
// - Equate `?sig` with the unnormalized signature, e.g. `fn(<Foo<?x> as Trait>::Assoc)`
// - Depending on whether `<Foo<?x> as Trait>::Assoc` is rigid, ambiguous or normalizeable,
// we will either wind up with `?sig=<Foo<?x> as Trait>::Assoc/?y/ConcreteTy` respectively.
//
// *: In cases where there are ambiguous aliases in the signature that make use of bound vars
// they will wind up present in `?sig` even though they are non-rigid.
// If we've got `F: FnOnce(<u32 as Id<F>>::This)` we want to
// use this to infer the signature `FnOnce(u32)` for the closure.
//
// This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty`
// even though the normalized form may not name `expected_ty`. However, this matches the existing
// behaviour of the old solver and would be technically a breaking change to fix.
// We handle self-referential aliases here by relying on generalization
// which replaces such aliases with inference variables. This is currently
// a bit too weak, see trait-system-refactor-initiative#191.
struct ReplaceTy<'tcx> {
tcx: TyCtxt<'tcx>,
expected_ty: Ty<'tcx>,
with_ty: Ty<'tcx>,
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceTy<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if t == self.expected_ty {
self.with_ty
} else {
t.super_fold_with(self)
}
}
}
let generalized_fnptr_sig = self.next_ty_var(span);
let inferred_fnptr_sig = Ty::new_fn_ptr(self.tcx, inferred_sig.sig);
self.demand_eqtype(span, inferred_fnptr_sig, generalized_fnptr_sig);

let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig);

if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
expected_sig = Some(ExpectedSig {
cause_span: inferred_sig.cause_span,
sig: resolved_sig.fn_sig(self.tcx),
});
let inferred_fnptr_sig = inferred_fnptr_sig.fold_with(&mut ReplaceTy {
tcx: self.tcx,
expected_ty,
with_ty: generalized_fnptr_sig,
});
let resolved_sig = self.commit_if_ok(|snapshot| {
let outer_universe = self.universe();
let ocx = ObligationCtxt::new(self);
ocx.eq(
&ObligationCause::dummy(),
self.param_env,
generalized_fnptr_sig,
inferred_fnptr_sig,
)?;
if ocx.select_where_possible().is_empty() {
self.leak_check(outer_universe, Some(snapshot))?;
Ok(self.resolve_vars_if_possible(generalized_fnptr_sig))
} else {
Err(TypeError::Mismatch)
}
});
match resolved_sig {
Ok(resolved_sig) => {
expected_sig = Some(ExpectedSig {
cause_span: inferred_sig.cause_span,
sig: resolved_sig.fn_sig(self.tcx),
})
}
Err(_) => {}
}
} else {
struct MentionsTy<'tcx> {
expected_ty: Ty<'tcx>,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MentionsTy<'tcx> {
type Result = ControlFlow<()>;

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
if t == self.expected_ty {
ControlFlow::Break(())
} else {
t.super_visit_with(self)
}
}
}
if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
expected_sig = inferred_sig;
}
Expand Down
41 changes: 21 additions & 20 deletions compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! A utility module to inspect currently ambiguous obligations in the current context.

use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
use rustc_middle::traits::solve::GoalSource;
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use rustc_span::Span;
use rustc_trait_selection::solve::Certainty;
Expand Down Expand Up @@ -37,10 +36,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> bool {
match predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
self.type_matches_expected_vid(expected_vid, data.self_ty())
self.type_matches_expected_vid(data.self_ty(), expected_vid)
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty())
self.type_matches_expected_vid(data.projection_term.self_ty(), expected_vid)
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
Expand All @@ -60,7 +59,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

#[instrument(level = "debug", skip(self), ret)]
fn type_matches_expected_vid(&self, expected_vid: ty::TyVid, ty: Ty<'tcx>) -> bool {
fn type_matches_expected_vid(&self, ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool {
let ty = self.shallow_resolve(ty);
debug!(?ty);

Expand All @@ -76,7 +75,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
self_ty: ty::TyVid,
) -> PredicateObligations<'tcx> {
let obligations = self.fulfillment_cx.borrow().pending_obligations();
let sub_root_var = self.sub_unification_table_root_var(self_ty);
let obligations = self
.fulfillment_cx
.borrow()
.pending_obligations_potentially_referencing_sub_root(sub_root_var);
debug!(?obligations);
let mut obligations_for_self_ty = PredicateObligations::new();
for obligation in obligations {
Expand Down Expand Up @@ -125,23 +128,21 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
return;
}

// We don't care about any pending goals which don't actually
// use the self type.
if !inspect_goal
.orig_values()
.iter()
.filter_map(|arg| arg.as_type())
.any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty))
{
debug!(goal = ?inspect_goal.goal(), "goal does not mention self type");
return;
}

let tcx = self.fcx.tcx;
let goal = inspect_goal.goal();
if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty)
// We do not push the instantiated forms of goals as it would cause any
// aliases referencing bound vars to go from having escaping bound vars to
// being able to be normalized to an inference variable.
//
// This is mostly just a hack as arbitrary nested goals could still contain
// such aliases while having a different `GoalSource`. Closure signature inference
// however can't really handle *every* higher ranked `Fn` goal also being present
// in the form of `?c: Fn<(<?x as Trait<'!a>>::Assoc)`.
//
// This also just better matches the behaviour of the old solver where we do not
// encounter instantiated forms of goals, only nested goals that referred to bound
// vars from instantiated goals.
&& !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked)
{
if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) {
self.obligations_for_self_ty.push(traits::Obligation::new(
tcx,
self.root_cause.clone(),
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
57 changes: 50 additions & 7 deletions compiler/rustc_hir_typeck/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,29 @@ 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.
pub(super) fn try_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();
for entry in &mut opaque_types {
*entry = self.resolve_vars_if_possible(*entry);
}
debug!(?opaque_types);

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

/// 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
Expand All @@ -35,7 +58,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
}
debug!(?opaque_types);

self.compute_definition_site_hidden_types(&opaque_types);
self.compute_definition_site_hidden_types(&opaque_types, false);
self.apply_definition_site_hidden_types(&opaque_types);
}
}
Expand Down Expand Up @@ -74,6 +97,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
fn compute_definition_site_hidden_types(
&mut self,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
first_pass: bool,
) {
let tcx = self.tcx;
let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
Expand All @@ -94,12 +118,22 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
continue;
}

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

// If this the first pass (`try_handle_opaque_type_uses_next`),
// then do not report any errors.
if first_pass {
continue;
}

let guar = match usage_kind {
UsageKind::None => {
if let Some(guar) = self.tainted_by_errors() {
Expand Down Expand Up @@ -152,6 +186,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
&mut self,
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_type: OpaqueHiddenType<'tcx>,
first_pass: bool,
) -> UsageKind<'tcx> {
if let Err(err) = opaque_type_has_defining_use_args(
&self,
Expand Down Expand Up @@ -199,7 +234,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
.borrow_mut()
.hidden_types
.insert(opaque_type_key.def_id, hidden_type);
assert!(prev.is_none());

// We do want to insert opaque types the first pass, because we want to
// equate them. So, the second pass (where we report errors) will have
// a hidden type inserted.
if first_pass {
assert!(prev.is_none());
}
UsageKind::HasDefiningUse
}

Expand All @@ -209,10 +250,12 @@ impl<'tcx> FnCtxt<'_, '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);
// On the first pass to this function, some opaque types may not
// have a hidden type assigned.
if let Some(expected) = self.typeck_results.borrow_mut().hidden_types.get(&key.def_id) {
let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args);
self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
}
}
}

Expand Down
Loading
Loading