Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
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