Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve infer (_) suggestions in consts and statics #135044

Merged
Merged
9 changes: 3 additions & 6 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2031,20 +2031,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_array_length_to_const_arg(&mut self, c: &AnonConst) -> &'hir hir::ConstArg<'hir> {
match c.value.kind {
ExprKind::Underscore => {
if self.tcx.features().generic_arg_infer() {
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
self.arena
.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
} else {
if !self.tcx.features().generic_arg_infer() {
feature_err(
&self.tcx.sess,
sym::generic_arg_infer,
c.value.span,
fluent_generated::ast_lowering_underscore_array_length_unstable,
)
.stash(c.value.span, StashKey::UnderscoreForArrayLengths);
self.lower_anon_const_to_const_arg(c)
}
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
}
_ => self.lower_anon_const_to_const_arg(c),
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3387,7 +3387,7 @@ impl<'hir> FnRetTy<'hir> {
}
}

pub fn get_infer_ret_ty(&self) -> Option<&'hir Ty<'hir>> {
pub fn is_suggestable_infer_ty(&self) -> Option<&'hir Ty<'hir>> {
if let Self::Return(ty) = self {
if ty.is_suggestable_infer_ty() {
return Some(*ty);
Expand Down
64 changes: 46 additions & 18 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,25 @@ pub struct ItemCtxt<'tcx> {
///////////////////////////////////////////////////////////////////////////

#[derive(Default)]
pub(crate) struct HirPlaceholderCollector(pub(crate) Vec<Span>);
pub(crate) struct HirPlaceholderCollector {
pub spans: Vec<Span>,
// If any of the spans points to a const infer var, then suppress any messages
// that may try to turn that const infer into a type parameter.
pub may_contain_const_infer: bool,
}

impl<'v> Visitor<'v> for HirPlaceholderCollector {
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
if let hir::TyKind::Infer = t.kind {
self.0.push(t.span);
self.spans.push(t.span);
}
intravisit::walk_ty(self, t)
}
fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) {
match generic_arg {
hir::GenericArg::Infer(inf) => {
self.0.push(inf.span);
self.spans.push(inf.span);
self.may_contain_const_infer = true;
intravisit::walk_inf(self, inf);
}
hir::GenericArg::Type(t) => self.visit_ty(t),
Expand All @@ -152,7 +158,8 @@ impl<'v> Visitor<'v> for HirPlaceholderCollector {
}
fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) {
if let hir::ConstArgKind::Infer(span) = const_arg.kind {
self.0.push(span);
self.may_contain_const_infer = true;
self.spans.push(span);
}
intravisit::walk_const_arg(self, const_arg)
}
Expand Down Expand Up @@ -277,8 +284,8 @@ fn reject_placeholder_type_signatures_in_item<'tcx>(
placeholder_type_error(
icx.lowerer(),
Some(generics),
visitor.0,
suggest,
visitor.spans,
suggest && !visitor.may_contain_const_infer,
None,
item.kind.descr(),
);
Expand Down Expand Up @@ -607,16 +614,16 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
hir::FnRetTy::DefaultReturn(..) => tcx.types.unit,
};

if !(visitor.0.is_empty() && infer_replacements.is_empty()) {
if !(visitor.spans.is_empty() && infer_replacements.is_empty()) {
// We check for the presence of
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.

let mut diag = crate::collect::placeholder_type_error_diag(
self,
generics,
visitor.0,
visitor.spans,
infer_replacements.iter().map(|(s, _)| *s).collect(),
true,
!visitor.may_contain_const_infer,
hir_ty,
"function",
);
Expand Down Expand Up @@ -712,7 +719,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
placeholder_type_error(
icx.lowerer(),
None,
visitor.0,
visitor.spans,
false,
None,
"static variable",
Expand Down Expand Up @@ -780,7 +787,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
placeholder_type_error(
icx.lowerer(),
None,
visitor.0,
visitor.spans,
false,
None,
it.kind.descr(),
Expand Down Expand Up @@ -822,7 +829,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
placeholder_type_error(
icx.lowerer(),
None,
visitor.0,
visitor.spans,
false,
None,
"associated constant",
Expand All @@ -837,7 +844,14 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
// Account for `type T = _;`.
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);
placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
placeholder_type_error(
icx.lowerer(),
None,
visitor.spans,
false,
None,
"associated type",
);
}

hir::TraitItemKind::Type(_, None) => {
Expand All @@ -848,7 +862,14 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);

placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
placeholder_type_error(
icx.lowerer(),
None,
visitor.spans,
false,
None,
"associated type",
);
}
};

Expand All @@ -872,7 +893,14 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_impl_item(impl_item);

placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
placeholder_type_error(
icx.lowerer(),
None,
visitor.spans,
false,
None,
"associated type",
);
}
hir::ImplItemKind::Const(ty, _) => {
// Account for `const T: _ = ..;`
Expand All @@ -882,7 +910,7 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
placeholder_type_error(
icx.lowerer(),
None,
visitor.0,
visitor.spans,
false,
None,
"associated constant",
Expand Down Expand Up @@ -1371,7 +1399,7 @@ fn lower_fn_sig_recovering_infer_ret_ty<'tcx>(
generics: &'tcx hir::Generics<'tcx>,
def_id: LocalDefId,
) -> ty::PolyFnSig<'tcx> {
if let Some(infer_ret_ty) = sig.decl.output.get_infer_ret_ty() {
if let Some(infer_ret_ty) = sig.decl.output.is_suggestable_infer_ty() {
return recover_infer_ret_ty(icx, infer_ret_ty, generics, def_id);
}

Expand Down Expand Up @@ -1422,7 +1450,7 @@ fn recover_infer_ret_ty<'tcx>(
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_ty(infer_ret_ty);

let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
let mut diag = bad_placeholder(icx.lowerer(), visitor.spans, "return type");
let ret_ty = fn_sig.output();

// Don't leak types into signatures unless they're nameable!
Expand Down
50 changes: 42 additions & 8 deletions compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rustc_errors::{Applicability, StashKey, Suggestions};
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::print::with_forced_trimmed_paths;
Expand All @@ -12,7 +13,7 @@ use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableEx
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span};

use super::{ItemCtxt, bad_placeholder};
use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder};
use crate::errors::TypeofReservedKeywordUsed;
use crate::hir_ty_lowering::HirTyLowerer;

Expand Down Expand Up @@ -412,7 +413,7 @@ fn infer_placeholder_type<'tcx>(
kind: &'static str,
) -> Ty<'tcx> {
let tcx = cx.tcx();
let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id);
let ty = tcx.typeck(def_id).node_type(body_id.hir_id);

// If this came from a free `const` or `static mut?` item,
// then the user may have written e.g. `const A = 42;`.
Expand Down Expand Up @@ -447,13 +448,37 @@ fn infer_placeholder_type<'tcx>(
}
})
.unwrap_or_else(|| {
let mut diag = bad_placeholder(cx, vec![span], kind);

if !ty.references_error() {
let mut visitor = HirPlaceholderCollector::default();
let node = tcx.hir_node_by_def_id(def_id);
if let Some(ty) = node.ty() {
visitor.visit_ty(ty);
}
// If we have just one span, let's try to steal a const `_` feature error.
let try_steal_span = if !tcx.features().generic_arg_infer() && visitor.spans.len() == 1
{
visitor.spans.first().copied()
} else {
None
};
// If we didn't find any infer tys, then just fallback to `span`.
if visitor.spans.is_empty() {
visitor.spans.push(span);
}
let mut diag = bad_placeholder(cx, visitor.spans, kind);

// HACK(#69396): Stashing and stealing diagnostics does not interact
// well with macros which may delay more than one diagnostic on the
// same span. If this happens, we will fall through to this arm, so
// we need to suppress the suggestion since it's invalid. Ideally we
// would suppress the duplicated error too, but that's really hard.
if span.is_empty() && span.from_expansion() {
// An approximately better primary message + no suggestion...
diag.primary_message("missing type for item");
} else if !ty.references_error() {
if let Some(ty) = ty.make_suggestable(tcx, false, None) {
diag.span_suggestion(
diag.span_suggestion_verbose(
span,
"replace with the correct type",
"replace this with a fully-specified type",
ty,
Applicability::MachineApplicable,
);
Expand All @@ -464,7 +489,16 @@ fn infer_placeholder_type<'tcx>(
));
}
}
diag.emit()

if let Some(try_steal_span) = try_steal_span {
cx.dcx().try_steal_replace_and_emit_err(
try_steal_span,
StashKey::UnderscoreForArrayLengths,
diag,
)
} else {
diag.emit()
}
});
Ty::new_error(tcx, guar)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl TaitConstraintLocator<'_> {
"foreign items cannot constrain opaque types",
);
if let Some(hir_sig) = hir_node.fn_sig()
&& hir_sig.decl.output.get_infer_ret_ty().is_some()
&& hir_sig.decl.output.is_suggestable_infer_ty().is_some()
{
let guar = self.tcx.dcx().span_delayed_bug(
hir_sig.decl.output.span(),
Expand Down
7 changes: 1 addition & 6 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1771,12 +1771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let parent_node = self.tcx.hir().parent_iter(expr.hir_id).find(|(_, node)| {
!matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. }))
});
let Some((
_,
hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. })
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }),
)) = parent_node
else {
let Some((_, hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }))) = parent_node else {
return;
};
if let hir::TyKind::Array(_, ct) = ty.peel_refs().kind {
Expand Down
45 changes: 22 additions & 23 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,7 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet<LocalDef
}

fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> {
let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity();
typeck_with_fallback(tcx, def_id, fallback, None)
}

/// Used only to get `TypeckResults` for type inference during error recovery.
/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors.
fn diagnostic_only_typeck<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> &'tcx ty::TypeckResults<'tcx> {
let fallback = move || {
let span = tcx.hir().span(tcx.local_def_id_to_hir_id(def_id));
Ty::new_error_with_message(tcx, span, "diagnostic only typeck table used")
};
typeck_with_fallback(tcx, def_id, fallback, None)
typeck_with_fallback(tcx, def_id, None)
}

/// Same as `typeck` but `inspect` is invoked on evaluation of each root obligation.
Expand All @@ -113,15 +99,13 @@ pub fn inspect_typeck<'tcx>(
def_id: LocalDefId,
inspect: ObligationInspector<'tcx>,
) -> &'tcx ty::TypeckResults<'tcx> {
let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity();
typeck_with_fallback(tcx, def_id, fallback, Some(inspect))
typeck_with_fallback(tcx, def_id, Some(inspect))
}

#[instrument(level = "debug", skip(tcx, fallback, inspector), ret)]
#[instrument(level = "debug", skip(tcx, inspector), ret)]
fn typeck_with_fallback<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
fallback: impl Fn() -> Ty<'tcx> + 'tcx,
inspector: Option<ObligationInspector<'tcx>>,
) -> &'tcx ty::TypeckResults<'tcx> {
// Closures' typeck results come from their outermost function,
Expand Down Expand Up @@ -150,7 +134,11 @@ fn typeck_with_fallback<'tcx>(
let mut fcx = FnCtxt::new(&root_ctxt, param_env, def_id);

if let Some(hir::FnSig { header, decl, .. }) = node.fn_sig() {
let fn_sig = if decl.output.get_infer_ret_ty().is_some() {
let fn_sig = if decl.output.is_suggestable_infer_ty().is_some() {
// In the case that we're recovering `fn() -> W<_>` or some other return
// type that has an infer in it, lower the type directly so that it'll
// be correctly filled with infer. We'll use this inference to provide
// a suggestion later on.
fcx.lowerer().lower_fn_ty(id, header.safety, header.abi, decl, None, None)
} else {
tcx.fn_sig(def_id).instantiate_identity()
Expand All @@ -164,8 +152,19 @@ fn typeck_with_fallback<'tcx>(

check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params());
} else {
let expected_type = infer_type_if_missing(&fcx, node);
let expected_type = expected_type.unwrap_or_else(fallback);
let expected_type = if let Some(infer_ty) = infer_type_if_missing(&fcx, node) {
infer_ty
} else if let Some(ty) = node.ty()
&& ty.is_suggestable_infer_ty()
{
// In the case that we're recovering `const X: [T; _]` or some other
// type that has an infer in it, lower the type directly so that it'll
// be correctly filled with infer. We'll use this inference to provide
// a suggestion later on.
fcx.lowerer().lower_ty(ty)
} else {
tcx.type_of(def_id).instantiate_identity()
};

let expected_type = fcx.normalize(body.value.span, expected_type);

Expand Down Expand Up @@ -506,5 +505,5 @@ fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! {

pub fn provide(providers: &mut Providers) {
method::provide(providers);
*providers = Providers { typeck, diagnostic_only_typeck, used_trait_imports, ..*providers };
*providers = Providers { typeck, used_trait_imports, ..*providers };
}
Loading
Loading