Skip to content

Commit 12cc9b4

Browse files
authored
Rollup merge of #135044 - compiler-errors:better-infer-suggestions-in-const, r=oli-obk
Improve infer (`_`) suggestions in `const`s and `static`s Fixes #135010. This PR does a few things to (imo) greatly improve the error message when users write something like `static FOO: [i32; _] = [1, 2, 3]`. Firstly, it adapts the recovery code for when we encounter `_` in a const/static to work a bit more like `fn foo() -> _`, and removes the somewhat redundant query `diagnostic_only_typeck`. Secondly, it changes the lowering for `[T; _]` to always lower under the `feature(generic_arg_infer)` logic to `ConstArgKind::Infer`. We still issue the feature error, so it's not doing anything *observable* on the good path, but it does mean that we no longer erroneously interpret `[T; _]`'s array length as a `_` **wildcard expression** (à la destructuring assignment, like `(_, y) = expr`). Lastly it makes the suggestions verbose and fixes (well, suppresses) a bug with stashing and suggestions. r? oli-obk
2 parents f0c03f6 + 0fd64ef commit 12cc9b4

33 files changed

+388
-316
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -2031,20 +2031,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
20312031
fn lower_array_length_to_const_arg(&mut self, c: &AnonConst) -> &'hir hir::ConstArg<'hir> {
20322032
match c.value.kind {
20332033
ExprKind::Underscore => {
2034-
if self.tcx.features().generic_arg_infer() {
2035-
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
2036-
self.arena
2037-
.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
2038-
} else {
2034+
if !self.tcx.features().generic_arg_infer() {
20392035
feature_err(
20402036
&self.tcx.sess,
20412037
sym::generic_arg_infer,
20422038
c.value.span,
20432039
fluent_generated::ast_lowering_underscore_array_length_unstable,
20442040
)
20452041
.stash(c.value.span, StashKey::UnderscoreForArrayLengths);
2046-
self.lower_anon_const_to_const_arg(c)
20472042
}
2043+
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
2044+
self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
20482045
}
20492046
_ => self.lower_anon_const_to_const_arg(c),
20502047
}

compiler/rustc_hir/src/hir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3387,7 +3387,7 @@ impl<'hir> FnRetTy<'hir> {
33873387
}
33883388
}
33893389

3390-
pub fn get_infer_ret_ty(&self) -> Option<&'hir Ty<'hir>> {
3390+
pub fn is_suggestable_infer_ty(&self) -> Option<&'hir Ty<'hir>> {
33913391
if let Self::Return(ty) = self {
33923392
if ty.is_suggestable_infer_ty() {
33933393
return Some(*ty);

compiler/rustc_hir_analysis/src/collect.rs

+46-18
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,25 @@ pub struct ItemCtxt<'tcx> {
131131
///////////////////////////////////////////////////////////////////////////
132132

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

136141
impl<'v> Visitor<'v> for HirPlaceholderCollector {
137142
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
138143
if let hir::TyKind::Infer = t.kind {
139-
self.0.push(t.span);
144+
self.spans.push(t.span);
140145
}
141146
intravisit::walk_ty(self, t)
142147
}
143148
fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) {
144149
match generic_arg {
145150
hir::GenericArg::Infer(inf) => {
146-
self.0.push(inf.span);
151+
self.spans.push(inf.span);
152+
self.may_contain_const_infer = true;
147153
intravisit::walk_inf(self, inf);
148154
}
149155
hir::GenericArg::Type(t) => self.visit_ty(t),
@@ -152,7 +158,8 @@ impl<'v> Visitor<'v> for HirPlaceholderCollector {
152158
}
153159
fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) {
154160
if let hir::ConstArgKind::Infer(span) = const_arg.kind {
155-
self.0.push(span);
161+
self.may_contain_const_infer = true;
162+
self.spans.push(span);
156163
}
157164
intravisit::walk_const_arg(self, const_arg)
158165
}
@@ -277,8 +284,8 @@ fn reject_placeholder_type_signatures_in_item<'tcx>(
277284
placeholder_type_error(
278285
icx.lowerer(),
279286
Some(generics),
280-
visitor.0,
281-
suggest,
287+
visitor.spans,
288+
suggest && !visitor.may_contain_const_infer,
282289
None,
283290
item.kind.descr(),
284291
);
@@ -607,16 +614,16 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
607614
hir::FnRetTy::DefaultReturn(..) => tcx.types.unit,
608615
};
609616

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

614621
let mut diag = crate::collect::placeholder_type_error_diag(
615622
self,
616623
generics,
617-
visitor.0,
624+
visitor.spans,
618625
infer_replacements.iter().map(|(s, _)| *s).collect(),
619-
true,
626+
!visitor.may_contain_const_infer,
620627
hir_ty,
621628
"function",
622629
);
@@ -712,7 +719,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
712719
placeholder_type_error(
713720
icx.lowerer(),
714721
None,
715-
visitor.0,
722+
visitor.spans,
716723
false,
717724
None,
718725
"static variable",
@@ -780,7 +787,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
780787
placeholder_type_error(
781788
icx.lowerer(),
782789
None,
783-
visitor.0,
790+
visitor.spans,
784791
false,
785792
None,
786793
it.kind.descr(),
@@ -822,7 +829,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
822829
placeholder_type_error(
823830
icx.lowerer(),
824831
None,
825-
visitor.0,
832+
visitor.spans,
826833
false,
827834
None,
828835
"associated constant",
@@ -837,7 +844,14 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
837844
// Account for `type T = _;`.
838845
let mut visitor = HirPlaceholderCollector::default();
839846
visitor.visit_trait_item(trait_item);
840-
placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
847+
placeholder_type_error(
848+
icx.lowerer(),
849+
None,
850+
visitor.spans,
851+
false,
852+
None,
853+
"associated type",
854+
);
841855
}
842856

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

851-
placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
865+
placeholder_type_error(
866+
icx.lowerer(),
867+
None,
868+
visitor.spans,
869+
false,
870+
None,
871+
"associated type",
872+
);
852873
}
853874
};
854875

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

875-
placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
896+
placeholder_type_error(
897+
icx.lowerer(),
898+
None,
899+
visitor.spans,
900+
false,
901+
None,
902+
"associated type",
903+
);
876904
}
877905
hir::ImplItemKind::Const(ty, _) => {
878906
// Account for `const T: _ = ..;`
@@ -882,7 +910,7 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
882910
placeholder_type_error(
883911
icx.lowerer(),
884912
None,
885-
visitor.0,
913+
visitor.spans,
886914
false,
887915
None,
888916
"associated constant",
@@ -1371,7 +1399,7 @@ fn lower_fn_sig_recovering_infer_ret_ty<'tcx>(
13711399
generics: &'tcx hir::Generics<'tcx>,
13721400
def_id: LocalDefId,
13731401
) -> ty::PolyFnSig<'tcx> {
1374-
if let Some(infer_ret_ty) = sig.decl.output.get_infer_ret_ty() {
1402+
if let Some(infer_ret_ty) = sig.decl.output.is_suggestable_infer_ty() {
13751403
return recover_infer_ret_ty(icx, infer_ret_ty, generics, def_id);
13761404
}
13771405

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

1425-
let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
1453+
let mut diag = bad_placeholder(icx.lowerer(), visitor.spans, "return type");
14261454
let ret_ty = fn_sig.output();
14271455

14281456
// Don't leak types into signatures unless they're nameable!

compiler/rustc_hir_analysis/src/collect/type_of.rs

+42-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_errors::{Applicability, StashKey, Suggestions};
44
use rustc_hir as hir;
55
use rustc_hir::HirId;
66
use rustc_hir::def_id::{DefId, LocalDefId};
7+
use rustc_hir::intravisit::Visitor;
78
use rustc_middle::query::plumbing::CyclePlaceholder;
89
use rustc_middle::ty::fold::fold_regions;
910
use rustc_middle::ty::print::with_forced_trimmed_paths;
@@ -12,7 +13,7 @@ use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableEx
1213
use rustc_middle::{bug, span_bug};
1314
use rustc_span::{DUMMY_SP, Ident, Span};
1415

15-
use super::{ItemCtxt, bad_placeholder};
16+
use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder};
1617
use crate::errors::TypeofReservedKeywordUsed;
1718
use crate::hir_ty_lowering::HirTyLowerer;
1819

@@ -412,7 +413,7 @@ fn infer_placeholder_type<'tcx>(
412413
kind: &'static str,
413414
) -> Ty<'tcx> {
414415
let tcx = cx.tcx();
415-
let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id);
416+
let ty = tcx.typeck(def_id).node_type(body_id.hir_id);
416417

417418
// If this came from a free `const` or `static mut?` item,
418419
// then the user may have written e.g. `const A = 42;`.
@@ -447,13 +448,37 @@ fn infer_placeholder_type<'tcx>(
447448
}
448449
})
449450
.unwrap_or_else(|| {
450-
let mut diag = bad_placeholder(cx, vec![span], kind);
451-
452-
if !ty.references_error() {
451+
let mut visitor = HirPlaceholderCollector::default();
452+
let node = tcx.hir_node_by_def_id(def_id);
453+
if let Some(ty) = node.ty() {
454+
visitor.visit_ty(ty);
455+
}
456+
// If we have just one span, let's try to steal a const `_` feature error.
457+
let try_steal_span = if !tcx.features().generic_arg_infer() && visitor.spans.len() == 1
458+
{
459+
visitor.spans.first().copied()
460+
} else {
461+
None
462+
};
463+
// If we didn't find any infer tys, then just fallback to `span`.
464+
if visitor.spans.is_empty() {
465+
visitor.spans.push(span);
466+
}
467+
let mut diag = bad_placeholder(cx, visitor.spans, kind);
468+
469+
// HACK(#69396): Stashing and stealing diagnostics does not interact
470+
// well with macros which may delay more than one diagnostic on the
471+
// same span. If this happens, we will fall through to this arm, so
472+
// we need to suppress the suggestion since it's invalid. Ideally we
473+
// would suppress the duplicated error too, but that's really hard.
474+
if span.is_empty() && span.from_expansion() {
475+
// An approximately better primary message + no suggestion...
476+
diag.primary_message("missing type for item");
477+
} else if !ty.references_error() {
453478
if let Some(ty) = ty.make_suggestable(tcx, false, None) {
454-
diag.span_suggestion(
479+
diag.span_suggestion_verbose(
455480
span,
456-
"replace with the correct type",
481+
"replace this with a fully-specified type",
457482
ty,
458483
Applicability::MachineApplicable,
459484
);
@@ -464,7 +489,16 @@ fn infer_placeholder_type<'tcx>(
464489
));
465490
}
466491
}
467-
diag.emit()
492+
493+
if let Some(try_steal_span) = try_steal_span {
494+
cx.dcx().try_steal_replace_and_emit_err(
495+
try_steal_span,
496+
StashKey::UnderscoreForArrayLengths,
497+
diag,
498+
)
499+
} else {
500+
diag.emit()
501+
}
468502
});
469503
Ty::new_error(tcx, guar)
470504
}

compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ impl TaitConstraintLocator<'_> {
187187
"foreign items cannot constrain opaque types",
188188
);
189189
if let Some(hir_sig) = hir_node.fn_sig()
190-
&& hir_sig.decl.output.get_infer_ret_ty().is_some()
190+
&& hir_sig.decl.output.is_suggestable_infer_ty().is_some()
191191
{
192192
let guar = self.tcx.dcx().span_delayed_bug(
193193
hir_sig.decl.output.span(),

compiler/rustc_hir_typeck/src/expr.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -1771,12 +1771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17711771
let parent_node = self.tcx.hir().parent_iter(expr.hir_id).find(|(_, node)| {
17721772
!matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. }))
17731773
});
1774-
let Some((
1775-
_,
1776-
hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. })
1777-
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }),
1778-
)) = parent_node
1779-
else {
1774+
let Some((_, hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }))) = parent_node else {
17801775
return;
17811776
};
17821777
if let hir::TyKind::Array(_, ct) = ty.peel_refs().kind {

compiler/rustc_hir_typeck/src/lib.rs

+22-23
Original file line numberDiff line numberDiff line change
@@ -87,21 +87,7 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet<LocalDef
8787
}
8888

8989
fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> {
90-
let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity();
91-
typeck_with_fallback(tcx, def_id, fallback, None)
92-
}
93-
94-
/// Used only to get `TypeckResults` for type inference during error recovery.
95-
/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors.
96-
fn diagnostic_only_typeck<'tcx>(
97-
tcx: TyCtxt<'tcx>,
98-
def_id: LocalDefId,
99-
) -> &'tcx ty::TypeckResults<'tcx> {
100-
let fallback = move || {
101-
let span = tcx.hir().span(tcx.local_def_id_to_hir_id(def_id));
102-
Ty::new_error_with_message(tcx, span, "diagnostic only typeck table used")
103-
};
104-
typeck_with_fallback(tcx, def_id, fallback, None)
90+
typeck_with_fallback(tcx, def_id, None)
10591
}
10692

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

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

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

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

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

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

507506
pub fn provide(providers: &mut Providers) {
508507
method::provide(providers);
509-
*providers = Providers { typeck, diagnostic_only_typeck, used_trait_imports, ..*providers };
508+
*providers = Providers { typeck, used_trait_imports, ..*providers };
510509
}

0 commit comments

Comments
 (0)