1
Fork 0

add TypingMode::Borrowck

This commit is contained in:
lcnr 2025-04-01 23:48:41 +02:00
parent 990201cb78
commit 509a144eed
131 changed files with 888 additions and 747 deletions

View file

@ -61,6 +61,7 @@ pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
type_of: type_of::type_of,
type_of_opaque: type_of::type_of_opaque,
type_of_opaque_hir_typeck: type_of::type_of_opaque_hir_typeck,
type_alias_is_lazy: type_of::type_alias_is_lazy,
item_bounds: item_bounds::item_bounds,
explicit_item_bounds: item_bounds::explicit_item_bounds,

View file

@ -7,7 +7,9 @@ use rustc_hir::{self as hir, AmbigArg, HirId};
use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, fold_regions};
use rustc_middle::ty::{
self, DefiningScopeKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span};
@ -324,10 +326,18 @@ pub(super) fn type_of_opaque(
if let Some(def_id) = def_id.as_local() {
Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
opaque::find_opaque_ty_constraints_for_tait(tcx, def_id)
opaque::find_opaque_ty_constraints_for_tait(
tcx,
def_id,
DefiningScopeKind::MirBorrowck,
)
}
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id)
opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
tcx,
def_id,
DefiningScopeKind::MirBorrowck,
)
}
// Opaque types desugared from `impl Trait`.
hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
@ -340,7 +350,12 @@ pub(super) fn type_of_opaque(
"tried to get type of this RPITIT with no definition"
);
}
opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
opaque::find_opaque_ty_constraints_for_rpit(
tcx,
def_id,
owner,
DefiningScopeKind::MirBorrowck,
)
}
}))
} else {
@ -350,6 +365,42 @@ pub(super) fn type_of_opaque(
}
}
pub(super) fn type_of_opaque_hir_typeck(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
) -> ty::EarlyBinder<'_, Ty<'_>> {
ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
opaque::find_opaque_ty_constraints_for_tait(tcx, def_id, DefiningScopeKind::HirTypeck)
}
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
tcx,
def_id,
DefiningScopeKind::HirTypeck,
)
}
// Opaque types desugared from `impl Trait`.
hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
| hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => {
if in_trait_or_impl == Some(hir::RpitContext::Trait)
&& !tcx.defaultness(owner).has_value()
{
span_bug!(
tcx.def_span(def_id),
"tried to get type of this RPITIT with no definition"
);
}
opaque::find_opaque_ty_constraints_for_rpit(
tcx,
def_id,
owner,
DefiningScopeKind::HirTypeck,
)
}
})
}
fn infer_placeholder_type<'tcx>(
cx: &dyn HirTyLowerer<'tcx>,
def_id: LocalDefId,

View file

@ -3,8 +3,7 @@ use rustc_hir::def_id::LocalDefId;
use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravisit};
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::DUMMY_SP;
use rustc_middle::ty::{self, DefiningScopeKind, Ty, TyCtxt, TypeVisitableExt};
use tracing::{debug, instrument, trace};
use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
@ -15,6 +14,7 @@ use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
opaque_types_from: DefiningScopeKind,
) -> Ty<'_> {
let mut parent_def_id = def_id;
while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
@ -27,7 +27,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
other => bug!("invalid impl trait in assoc type parent: {other:?}"),
}
let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from };
for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
let assoc = tcx.associated_item(assoc_id);
@ -39,25 +39,14 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
}
if let Some(hidden) = locator.found {
// Only check against typeck if we didn't already error
if !hidden.ty.references_error() {
for concrete_type in locator.typeck_types {
if concrete_type.ty != tcx.erase_regions(hidden.ty) {
if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
d.emit();
}
}
}
}
hidden.ty
} else {
let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
span: tcx.def_span(def_id),
name: tcx.item_ident(parent_def_id.to_def_id()),
what: "impl",
});
Ty::new_error(tcx, reported)
Ty::new_error(tcx, guar)
}
}
@ -80,23 +69,16 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
/// fn b<T>() -> Foo<T, u32> { .. }
/// ```
#[instrument(skip(tcx), level = "debug")]
pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
pub(super) fn find_opaque_ty_constraints_for_tait(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
opaque_types_from: DefiningScopeKind,
) -> Ty<'_> {
let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from };
tcx.hir_walk_toplevel_module(&mut locator);
if let Some(hidden) = locator.found {
// Only check against typeck if we didn't already error
if !hidden.ty.references_error() {
for concrete_type in locator.typeck_types {
if concrete_type.ty != tcx.erase_regions(hidden.ty) {
if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
d.emit();
}
}
}
}
hidden.ty
} else {
let mut parent_def_id = def_id;
@ -104,12 +86,12 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
// Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
parent_def_id = tcx.local_parent(parent_def_id);
}
let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
span: tcx.def_span(def_id),
name: tcx.item_ident(parent_def_id.to_def_id()),
what: "crate",
});
Ty::new_error(tcx, reported)
Ty::new_error(tcx, guar)
}
}
@ -126,22 +108,44 @@ struct TaitConstraintLocator<'tcx> {
/// type).
found: Option<ty::OpaqueHiddenType<'tcx>>,
/// In the presence of dead code, typeck may figure out a hidden type
/// while borrowck will not. We collect these cases here and check at
/// the end that we actually found a type that matches (modulo regions).
typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
opaque_types_from: DefiningScopeKind,
}
impl TaitConstraintLocator<'_> {
impl<'tcx> TaitConstraintLocator<'tcx> {
fn insert_found(&mut self, hidden_ty: ty::OpaqueHiddenType<'tcx>) {
if let Some(prev) = &mut self.found {
if hidden_ty.ty != prev.ty {
let (Ok(guar) | Err(guar)) =
prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
prev.ty = Ty::new_error(self.tcx, guar);
}
} else {
self.found = Some(hidden_ty);
}
}
fn non_defining_use_in_defining_scope(&mut self, item_def_id: LocalDefId) {
let guar = 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),
});
self.insert_found(ty::OpaqueHiddenType::new_error(self.tcx, guar));
}
#[instrument(skip(self), level = "debug")]
fn check(&mut self, item_def_id: LocalDefId) {
// Don't try to check items that cannot possibly constrain the type.
if !self.tcx.has_typeck_results(item_def_id) {
let tcx = self.tcx;
if !tcx.has_typeck_results(item_def_id) {
debug!("no constraint: no typeck results");
return;
}
let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
let opaque_types_defined_by = tcx.opaque_types_defined_by(item_def_id);
// Don't try to check items that cannot possibly constrain the type.
if !opaque_types_defined_by.contains(&self.def_id) {
debug!("no constraint: no opaque types defined");
@ -152,7 +156,7 @@ impl TaitConstraintLocator<'_> {
// "non-defining use" errors for them.
// Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former
// excludes closures, which are allowed to have `_` in their return type.
let hir_node = self.tcx.hir_node_by_def_id(item_def_id);
let hir_node = tcx.hir_node_by_def_id(item_def_id);
debug_assert!(
!matches!(hir_node, Node::ForeignItem(..)),
"foreign items cannot constrain opaque types",
@ -164,88 +168,39 @@ impl TaitConstraintLocator<'_> {
hir_sig.decl.output.span(),
"inferring return types and opaque types do not mix well",
);
self.found =
Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
self.found = Some(ty::OpaqueHiddenType::new_error(tcx, guar));
return;
}
// Calling `mir_borrowck` can lead to cycle errors through
// const-checking, avoid calling it if we don't have to.
// ```rust
// type Foo = impl Fn() -> usize; // when computing type for this
// const fn bar() -> Foo {
// || 0usize
// }
// const BAZR: Foo = bar(); // we would mir-borrowck this, causing cycles
// // because we again need to reveal `Foo` so we can check whether the
// // constant does not contain interior mutability.
// ```
let tables = self.tcx.typeck(item_def_id);
if let Some(guar) = tables.tainted_by_errors {
self.found =
Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
return;
}
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 concrete_type =
self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
opaque_type_key,
self.tcx,
true,
));
if self.typeck_types.iter().all(|prev| prev.ty != concrete_type.ty) {
self.typeck_types.push(concrete_type);
}
}
if !constrained {
debug!("no constraints in typeck results");
if opaque_types_defined_by.contains(&self.def_id) {
let guar = 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),
});
// Avoid "opaque type not constrained" errors on the opaque itself.
self.found = Some(ty::OpaqueHiddenType {
span: DUMMY_SP,
ty: Ty::new_error(self.tcx, guar),
});
}
return;
};
// Use borrowck to get the type with unerased regions.
let borrowck_results = &self.tcx.mir_borrowck(item_def_id);
// If the body was tainted, then assume the opaque may have been constrained and just set it to error.
if let Some(guar) = borrowck_results.tainted_by_errors {
self.found =
Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
return;
}
debug!(?borrowck_results.concrete_opaque_types);
if let Some(&concrete_type) = borrowck_results.concrete_opaque_types.get(&self.def_id) {
debug!(?concrete_type, "found constraint");
if let Some(prev) = &mut self.found {
if concrete_type.ty != prev.ty {
let (Ok(guar) | Err(guar)) =
prev.build_mismatch_error(&concrete_type, self.tcx).map(|d| d.emit());
prev.ty = Ty::new_error(self.tcx, guar);
match self.opaque_types_from {
DefiningScopeKind::HirTypeck => {
let tables = tcx.typeck(item_def_id);
if let Some(guar) = tables.tainted_by_errors {
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else if let Some(&hidden_type) = tables.concrete_opaque_types.get(&self.def_id) {
self.insert_found(hidden_type);
} else {
self.non_defining_use_in_defining_scope(item_def_id);
}
}
DefiningScopeKind::MirBorrowck => {
let borrowck_result = tcx.mir_borrowck(item_def_id);
if let Some(guar) = borrowck_result.tainted_by_errors {
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else if let Some(&hidden_type) =
borrowck_result.concrete_opaque_types.get(&self.def_id)
{
debug!(?hidden_type, "found constraint");
self.insert_found(hidden_type);
} else if let Err(guar) = tcx
.type_of_opaque_hir_typeck(self.def_id)
.instantiate_identity()
.error_reported()
{
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else {
self.non_defining_use_in_defining_scope(item_def_id);
}
} else {
self.found = Some(concrete_type);
}
}
}
@ -287,54 +242,42 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
owner_def_id: LocalDefId,
opaque_types_from: DefiningScopeKind,
) -> Ty<'tcx> {
let tables = tcx.typeck(owner_def_id);
// Check that all of the opaques we inferred during HIR are compatible.
// FIXME: We explicitly don't check that the types inferred during HIR
// typeck are compatible with the one that we infer during borrowck,
// because that one actually sometimes has consts evaluated eagerly so
// using strict type equality will fail.
let mut hir_opaque_ty: Option<ty::OpaqueHiddenType<'tcx>> = None;
if tables.tainted_by_errors.is_none() {
for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
if opaque_type_key.def_id != def_id {
continue;
}
let concrete_type = tcx.erase_regions(
hidden_type.remap_generic_params_to_declaration_params(opaque_type_key, tcx, true),
);
if let Some(prev) = &mut hir_opaque_ty {
if concrete_type.ty != prev.ty {
if let Ok(d) = prev.build_mismatch_error(&concrete_type, tcx) {
d.emit();
}
}
match opaque_types_from {
DefiningScopeKind::HirTypeck => {
let tables = tcx.typeck(owner_def_id);
if let Some(guar) = tables.tainted_by_errors {
Ty::new_error(tcx, guar)
} else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) {
hidden_ty.ty
} else {
hir_opaque_ty = Some(concrete_type);
// FIXME(-Znext-solver): This should not be necessary and we should
// instead rely on inference variable fallback inside of typeck itself.
// We failed to resolve the opaque type or it
// resolves to itself. We interpret this as the
// no values of the hidden type ever being constructed,
// so we can just make the hidden type be `!`.
// For backwards compatibility reasons, we fall back to
// `()` until we the diverging default is changed.
Ty::new_diverging_default(tcx)
}
}
}
let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
if let Some(mir_opaque_ty) = mir_opaque_ty {
mir_opaque_ty.ty
} else if let Some(guar) = tables.tainted_by_errors {
// Some error in the owner fn prevented us from populating
// the `concrete_opaque_types` table.
Ty::new_error(tcx, guar)
} else {
// Fall back to the RPIT we inferred during HIR typeck
if let Some(hir_opaque_ty) = hir_opaque_ty {
hir_opaque_ty.ty
} else {
// We failed to resolve the opaque type or it
// resolves to itself. We interpret this as the
// no values of the hidden type ever being constructed,
// so we can just make the hidden type be `!`.
// For backwards compatibility reasons, we fall back to
// `()` until we the diverging default is changed.
Ty::new_diverging_default(tcx)
DefiningScopeKind::MirBorrowck => {
let borrowck_result = tcx.mir_borrowck(owner_def_id);
if let Some(guar) = borrowck_result.tainted_by_errors {
Ty::new_error(tcx, guar)
} else if let Some(hidden_ty) = borrowck_result.concrete_opaque_types.get(&def_id) {
hidden_ty.ty
} else {
let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
if let Err(guar) = hir_ty.error_reported() {
Ty::new_error(tcx, guar)
} else {
hir_ty
}
}
}
}
}