Skip to content

Commit 2ea1c88

Browse files
Record inferred const for type or const placeholders
This extends InferenceResult with a new const_of_const_placeholders map to record which const a given const or type placeholder gets resolved to. This is basically the same as type_of_type_placeholder, but for consts. Note that while surprising, TypeRef::Placeholder may indeed resolve to a const because except for arrays the parser doesn't know ahead of type if a _ will resolve to a type or a const. In the case of an array, we have an Expr::Underscore: ``` let a: [_; _] = [4, 5, 6]; //^ TypeRef::Placeholder that resolves to the type i32 //^ Expr::Underscore that resolves to the const 3 ``` But for a struct we have a TypeRef::Placeholder: ``` struct S<T, const N: usize>([T; N]); let s: S<_, _> = S([1, 2, 3]); //^ TypeRef::Placeholder that resolves to the type i32 //^ TypeRef::Placeholder that resolves to the const 3 ```
1 parent cdfe408 commit 2ea1c88

8 files changed

Lines changed: 149 additions & 16 deletions

File tree

crates/hir-def/src/hir.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use crate::{
3232
HygieneId,
3333
path::{GenericArgs, Path},
3434
},
35-
type_ref::{Mutability, Rawness},
35+
type_ref::{ConstRef, Mutability, Rawness},
3636
};
3737

3838
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
@@ -76,6 +76,38 @@ impl ExprOrPatId {
7676
}
7777
stdx::impl_from!(ExprId, PatId for ExprOrPatId);
7878

79+
// FIXME: Like ExprOrPatId above, eventually encode this as a single u32?
80+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, salsa::Update)]
81+
pub enum TypeRefIdOrConstRef {
82+
TypeRefId(TypeRefId),
83+
ConstRef(ConstRef),
84+
}
85+
86+
impl TypeRefIdOrConstRef {
87+
pub fn as_type_ref(self) -> Option<TypeRefId> {
88+
match self {
89+
Self::TypeRefId(v) => Some(v),
90+
_ => None,
91+
}
92+
}
93+
94+
pub fn is_type_ref(&self) -> bool {
95+
matches!(self, Self::TypeRefId(_))
96+
}
97+
98+
pub fn as_const_ref(self) -> Option<ConstRef> {
99+
match self {
100+
Self::ConstRef(v) => Some(v),
101+
_ => None,
102+
}
103+
}
104+
105+
pub fn is_expr(&self) -> bool {
106+
matches!(self, Self::ConstRef(_))
107+
}
108+
}
109+
stdx::impl_from!(TypeRefId, ConstRef for TypeRefIdOrConstRef);
110+
79111
#[derive(Debug, Clone, Eq, PartialEq)]
80112
pub struct Label {
81113
pub name: Name,

crates/hir-ty/src/infer.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use hir_def::{
4545
TupleFieldId, TupleId, VariantId,
4646
attrs::AttrFlags,
4747
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
48-
hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId},
48+
hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId, TypeRefIdOrConstRef},
4949
lang_item::LangItems,
5050
layout::Integer,
5151
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
@@ -95,7 +95,7 @@ use crate::{
9595
},
9696
method_resolution::CandidateId,
9797
next_solver::{
98-
AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region,
98+
AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredConst,
9999
StoredGenericArg, StoredGenericArgs, StoredTy, StoredTys, Term, Ty, TyKind, Tys,
100100
abi::Safety,
101101
infer::{InferCtxt, ObligationInspector, traits::ObligationCause},
@@ -705,6 +705,7 @@ pub struct InferenceResult {
705705
pub(crate) type_of_pat: ArenaMap<PatId, StoredTy>,
706706
pub(crate) type_of_binding: ArenaMap<BindingId, StoredTy>,
707707
pub(crate) type_of_type_placeholder: FxHashMap<TypeRefId, StoredTy>,
708+
pub(crate) const_of_const_placeholder: FxHashMap<TypeRefIdOrConstRef, StoredConst>,
708709
pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, StoredTy>,
709710

710711
/// Whether there are any type-mismatching errors in the result.
@@ -1007,6 +1008,7 @@ impl InferenceResult {
10071008
type_of_pat: Default::default(),
10081009
type_of_binding: Default::default(),
10091010
type_of_type_placeholder: Default::default(),
1011+
const_of_const_placeholder: Default::default(),
10101012
type_of_opaque: Default::default(),
10111013
skipped_ref_pats: Default::default(),
10121014
has_errors: Default::default(),
@@ -1082,6 +1084,16 @@ impl InferenceResult {
10821084
pub fn type_of_type_placeholder<'db>(&self, type_ref: TypeRefId) -> Option<Ty<'db>> {
10831085
self.type_of_type_placeholder.get(&type_ref).map(|ty| ty.as_ref())
10841086
}
1087+
pub fn placeholder_consts<'db>(
1088+
&self,
1089+
) -> impl Iterator<Item = (TypeRefIdOrConstRef, Const<'db>)> {
1090+
self.const_of_const_placeholder
1091+
.iter()
1092+
.map(|(&type_ref_or_const, const_)| (type_ref_or_const, const_.as_ref()))
1093+
}
1094+
pub fn const_of_const_placeholder<'db>(&self, expr: TypeRefIdOrConstRef) -> Option<Const<'db>> {
1095+
self.const_of_const_placeholder.get(&expr).map(|ty| ty.as_ref())
1096+
}
10851097
pub fn type_of_expr_or_pat<'db>(&self, id: ExprOrPatId) -> Option<Ty<'db>> {
10861098
match id {
10871099
ExprOrPatId::ExprId(id) => self.type_of_expr.get(id).map(|it| it.as_ref()),
@@ -1365,6 +1377,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
13651377
type_of_pat,
13661378
type_of_binding,
13671379
type_of_type_placeholder,
1380+
const_of_const_placeholder,
13681381
type_of_opaque,
13691382
has_errors: _,
13701383
diagnostics: _,
@@ -1396,6 +1409,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
13961409
merge_arena_maps(type_of_pat, &other.type_of_pat);
13971410
merge_arena_maps(type_of_binding, &other.type_of_binding);
13981411
merge_hash_maps(type_of_type_placeholder, &other.type_of_type_placeholder);
1412+
merge_hash_maps(const_of_const_placeholder, &other.const_of_const_placeholder);
13991413
merge_hash_maps(type_of_opaque, &other.type_of_opaque);
14001414
merge_hash_maps(expr_adjustments, &other.expr_adjustments);
14011415
merge_hash_maps(pat_adjustments, &other.pat_adjustments);
@@ -1531,6 +1545,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
15311545
type_of_pat,
15321546
type_of_binding,
15331547
type_of_type_placeholder,
1548+
const_of_const_placeholder,
15341549
type_of_opaque,
15351550
skipped_ref_pats,
15361551
closures_data,
@@ -1569,6 +1584,10 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
15691584
resolver.resolve_completely(ty);
15701585
}
15711586
type_of_type_placeholder.shrink_to_fit();
1587+
for const_ in const_of_const_placeholder.values_mut() {
1588+
resolver.resolve_completely(const_);
1589+
}
1590+
const_of_const_placeholder.shrink_to_fit();
15721591
type_of_opaque.shrink_to_fit();
15731592

15741593
if let Some(nodes_with_type_mismatches) = nodes_with_type_mismatches {
@@ -1868,6 +1887,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
18681887
InferenceTyDiagnosticSource::Body => Some(&mut InferenceTyLoweringVarsCtx {
18691888
table: &mut self.table,
18701889
type_of_type_placeholder: &mut self.result.type_of_type_placeholder,
1890+
const_of_const_placeholder: &mut self.result.const_of_const_placeholder,
18711891
} as _),
18721892
InferenceTyDiagnosticSource::Signature => None,
18731893
};
@@ -2203,6 +2223,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
22032223
let mut vars_ctx = InferenceTyLoweringVarsCtx {
22042224
table: &mut self.table,
22052225
type_of_type_placeholder: &mut self.result.type_of_type_placeholder,
2226+
const_of_const_placeholder: &mut self.result.const_of_const_placeholder,
22062227
};
22072228
let mut ctx = TyLoweringContext::new(
22082229
self.db,

crates/hir-ty/src/infer/diagnostics.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ use std::ops::{Deref, DerefMut};
77

88
use either::Either;
99
use hir_def::expr_store::path::Path;
10+
use hir_def::hir::TypeRefIdOrConstRef;
11+
use hir_def::type_ref::ConstRef;
1012
use hir_def::{ExpressionStoreOwnerId, GenericDefId};
1113
use hir_def::{expr_store::ExpressionStore, type_ref::TypeRefId};
1214
use hir_def::{hir::ExprOrPatId, resolver::Resolver};
1315
use la_arena::{Idx, RawIdx};
1416
use rustc_hash::FxHashMap;
1517
use thin_vec::ThinVec;
1618

19+
use crate::next_solver::StoredConst;
1720
use crate::{
1821
InferenceDiagnostic, InferenceTyDiagnosticSource, Span, TyLoweringDiagnostic,
1922
db::{AnonConstId, HirDatabase},
@@ -61,6 +64,7 @@ pub(crate) struct PathDiagnosticCallbackData<'a> {
6164
pub(super) struct InferenceTyLoweringVarsCtx<'a, 'db> {
6265
pub(super) table: &'a mut InferenceTable<'db>,
6366
pub(super) type_of_type_placeholder: &'a mut FxHashMap<TypeRefId, StoredTy>,
67+
pub(super) const_of_const_placeholder: &'a mut FxHashMap<TypeRefIdOrConstRef, StoredConst>,
6468
}
6569

6670
impl<'db> TyLoweringInferVarsCtx<'db> for InferenceTyLoweringVarsCtx<'_, 'db> {
@@ -74,7 +78,18 @@ impl<'db> TyLoweringInferVarsCtx<'db> for InferenceTyLoweringVarsCtx<'_, 'db> {
7478
ty
7579
}
7680
fn next_const_var(&mut self, span: Span) -> Const<'db> {
77-
self.table.infer_ctxt.next_const_var(span)
81+
let const_ = self.table.infer_ctxt.next_const_var(span);
82+
83+
let type_ref_id_or_const_ref = match span {
84+
Span::ExprId(expr) => Some(TypeRefIdOrConstRef::ConstRef(ConstRef { expr })),
85+
Span::TypeRefId(type_ref) => Some(type_ref.into()),
86+
_ => None,
87+
};
88+
if let Some(key) = type_ref_id_or_const_ref {
89+
self.const_of_const_placeholder.insert(key, const_.store());
90+
}
91+
92+
const_
7893
}
7994
fn next_region_var(&mut self, span: Span) -> Region<'db> {
8095
self.table.infer_ctxt.next_region_var(span)

crates/hir-ty/src/infer/path.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ impl<'db> InferenceContext<'_, 'db> {
152152
let mut vars_ctx = InferenceTyLoweringVarsCtx {
153153
table: &mut self.table,
154154
type_of_type_placeholder: &mut self.result.type_of_type_placeholder,
155+
const_of_const_placeholder: &mut self.result.const_of_const_placeholder,
155156
};
156157
let mut ctx = TyLoweringContext::new(
157158
self.db,

crates/hir-ty/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use hir_def::{
6767
TypeParamId,
6868
db::DefDatabase,
6969
expr_store::{Body, ExpressionStore},
70-
hir::{BindingId, ExprId, ExprOrPatId, PatId},
70+
hir::{BindingId, ExprId, ExprOrPatId, PatId, TypeRefIdOrConstRef},
7171
resolver::{HasResolver, Resolver, TypeNs},
7272
type_ref::{Rawness, TypeRefId},
7373
};
@@ -539,6 +539,15 @@ impl From<ExprOrPatId> for Span {
539539
}
540540
}
541541

542+
impl From<TypeRefIdOrConstRef> for Span {
543+
fn from(value: TypeRefIdOrConstRef) -> Self {
544+
match value {
545+
TypeRefIdOrConstRef::TypeRefId(idx) => idx.into(),
546+
TypeRefIdOrConstRef::ConstRef(idx) => idx.expr.into(),
547+
}
548+
}
549+
}
550+
542551
impl Span {
543552
pub(crate) fn pick_best(a: Span, b: Span) -> Span {
544553
// We prefer dummy spans to minimize the risk of false errors.

crates/hir-ty/src/next_solver/consts.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,15 @@ impl<'db> TypeVisitable<DbInterner<'db>> for Const<'db> {
217217
}
218218
}
219219

220+
impl<'db> TypeVisitable<DbInterner<'db>> for StoredConst {
221+
fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
222+
&self,
223+
visitor: &mut V,
224+
) -> V::Result {
225+
self.as_ref().visit_with(visitor)
226+
}
227+
}
228+
220229
impl<'db> TypeSuperVisitable<DbInterner<'db>> for Const<'db> {
221230
fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
222231
&self,
@@ -248,6 +257,18 @@ impl<'db> TypeFoldable<DbInterner<'db>> for Const<'db> {
248257
}
249258
}
250259

260+
impl<'db> TypeFoldable<DbInterner<'db>> for StoredConst {
261+
fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
262+
self,
263+
folder: &mut F,
264+
) -> Result<Self, F::Error> {
265+
Ok(self.as_ref().try_fold_with(folder)?.store())
266+
}
267+
fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
268+
self.as_ref().fold_with(folder).store()
269+
}
270+
}
271+
251272
impl<'db> TypeSuperFoldable<DbInterner<'db>> for Const<'db> {
252273
fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
253274
self,

crates/hir-ty/src/tests.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use hir_def::{
1919
AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId,
2020
SyntheticSyntax, VariantId,
2121
expr_store::{Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap},
22-
hir::{ExprId, Pat, PatId},
22+
hir::{ExprId, Pat, PatId, TypeRefIdOrConstRef},
2323
item_scope::ItemScope,
2424
nameres::DefMap,
2525
src::HasSource,
@@ -83,6 +83,7 @@ fn check_impl(
8383
let mut had_annotations = false;
8484
let mut mismatches = FxHashMap::default();
8585
let mut types = FxHashMap::default();
86+
let mut consts = FxHashMap::default();
8687
let mut adjustments = FxHashMap::default();
8788
for (file_id, annotations) in db.extract_annotations() {
8889
for (range, expected) in annotations {
@@ -91,6 +92,8 @@ fn check_impl(
9192
types.insert(file_range, expected);
9293
} else if let Some(ty) = expected.strip_prefix("type: ") {
9394
types.insert(file_range, ty.to_owned());
95+
} else if let Some(const_) = expected.strip_prefix("const: ") {
96+
consts.insert(file_range, const_.to_owned());
9497
} else if expected.starts_with("expected") {
9598
mismatches.insert(file_range, expected);
9699
} else if let Some(adjs) = expected.strip_prefix("adjustments:") {
@@ -243,6 +246,29 @@ fn check_impl(
243246
assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
244247
}
245248
}
249+
250+
for (type_ref_id_or_const_ref, const_) in inference_result.placeholder_consts() {
251+
let node = match type_ref_id_or_const_ref {
252+
TypeRefIdOrConstRef::TypeRefId(type_ref) => {
253+
type_node(body_source_map, type_ref, &db)
254+
}
255+
TypeRefIdOrConstRef::ConstRef(const_ref) => {
256+
expr_node(body_source_map, const_ref.expr, &db)
257+
}
258+
};
259+
let Some(node) = node else { continue };
260+
let range = node.as_ref().original_file_range_rooted(&db);
261+
if let Some(expected) = consts.remove(&range) {
262+
let actual = salsa::attach(&db, || {
263+
if display_source {
264+
const_.display_source_code(&db, def.module(&db), true).unwrap()
265+
} else {
266+
const_.display_test(&db, display_target).to_string()
267+
}
268+
});
269+
assert_eq!(actual, expected, "const annotation differs at {:#?}", range.range);
270+
}
271+
}
246272
}
247273

248274
let mut buf = String::new();
@@ -261,6 +287,12 @@ fn check_impl(
261287
format_to!(buf, "{:?}: type {}\n", t.0.range, t.1);
262288
}
263289
}
290+
if !consts.is_empty() {
291+
format_to!(buf, "Unchecked const annotations:\n");
292+
for c in consts {
293+
format_to!(buf, "{:?}: const {}\n", c.0.range, c.1);
294+
}
295+
}
264296
if !adjustments.is_empty() {
265297
format_to!(buf, "Unchecked adjustments annotations:\n");
266298
for t in adjustments {

crates/hir-ty/src/tests/display_source_code.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::check_types_source_code;
1+
use super::{check, check_types_source_code};
22

33
#[test]
44
fn qualify_path_to_submodule() {
@@ -248,19 +248,21 @@ fn test() {
248248
}
249249

250250
#[test]
251-
fn type_placeholder_type() {
252-
check_types_source_code(
251+
fn type_and_const_placeholders() {
252+
check(
253253
r#"
254-
struct S<T>(T);
254+
struct S<T, const N: usize>([T; N]);
255255
fn test() {
256-
let f: S<_> = S(3);
257-
//^ i32
256+
let f: S<_, _> = S([1, 2]);
257+
//^ type: i32
258+
//^ const: 2
258259
let f: [_; _] = [4_u32, 5, 6];
259-
//^ u32
260+
//^ type: u32
261+
//^ const: 3
260262
let f: (_, _, _) = (1_u32, 1_i32, false);
261-
//^ u32
262-
//^ i32
263-
//^ bool
263+
//^ type: u32
264+
//^ type: i32
265+
//^ type: bool
264266
}
265267
"#,
266268
);

0 commit comments

Comments
 (0)