Skip to content

Commit 78d6b88

Browse files
committed
Add more tests, refactor array lengths/consteval work
Fix #2922: add unknown length as a condition for a type having unknown. Incorporate reviews: * Extract some of the const evaluation workings into functions * Add fixmes on the hacks * Add tests for impls on specific array lengths (these work!!! 😁) * Add tests for const generics (indeed we don't support it yet)
1 parent 32c6006 commit 78d6b88

File tree

8 files changed

+223
-35
lines changed

8 files changed

+223
-35
lines changed

crates/hir/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ use hir_def::{
5252
};
5353
use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
5454
use hir_ty::{
55-
autoderef, could_unify,
55+
autoderef,
56+
consteval::ConstExtension,
57+
could_unify,
5658
method_resolution::{self, def_crates, TyFingerprint},
5759
primitive::UintTy,
5860
subst_prefix,
@@ -1910,6 +1912,7 @@ impl Type {
19101912
substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go)
19111913
}
19121914

1915+
TyKind::Array(_ty, len) if len.is_unknown() => true,
19131916
TyKind::Array(ty, _)
19141917
| TyKind::Slice(ty)
19151918
| TyKind::Raw(_, ty)

crates/hir_def/src/type_ref.rs

+12
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ pub enum TypeRef {
8080
Path(Path),
8181
RawPtr(Box<TypeRef>, Mutability),
8282
Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
83+
// FIXME: for full const generics, the latter element (length) here is going to have to be an
84+
// expression that is further lowered later in hir_ty.
8385
Array(Box<TypeRef>, ConstScalar),
8486
Slice(Box<TypeRef>),
8587
/// A fn pointer. Last element of the vector is the return type.
@@ -141,6 +143,10 @@ impl TypeRef {
141143
TypeRef::RawPtr(Box::new(inner_ty), mutability)
142144
}
143145
ast::Type::ArrayType(inner) => {
146+
// FIXME: This is a hack. We should probably reuse the machinery of
147+
// `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
148+
// `hir_ty` level, which would allow knowing the type of:
149+
// let v: [u8; 2 + 2] = [0u8; 4];
144150
let len = inner
145151
.expr()
146152
.map(ConstScalar::usize_from_literal_expr)
@@ -313,6 +319,10 @@ pub enum ConstScalar {
313319
Usize(u64),
314320

315321
/// Case of an unknown value that rustc might know but we don't
322+
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
323+
// constants
324+
// https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
325+
// https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
316326
Unknown,
317327
}
318328

@@ -326,6 +336,8 @@ impl std::fmt::Display for ConstScalar {
326336
}
327337

328338
impl ConstScalar {
339+
// FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
340+
// parse stage.
329341
fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
330342
match expr {
331343
ast::Expr::Literal(lit) => {

crates/hir_ty/src/consteval.rs

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//! Constant evaluation details
2+
3+
use std::convert::TryInto;
4+
5+
use hir_def::{
6+
builtin_type::BuiltinUint,
7+
expr::{Expr, Literal},
8+
type_ref::ConstScalar,
9+
};
10+
11+
use crate::{Const, ConstData, ConstValue, Interner, TyKind};
12+
13+
/// Extension trait for [`Const`]
14+
pub trait ConstExtension {
15+
/// Is a [`Const`] unknown?
16+
fn is_unknown(&self) -> bool;
17+
}
18+
19+
impl ConstExtension for Const {
20+
fn is_unknown(&self) -> bool {
21+
match self.data(&Interner).value {
22+
// interned Unknown
23+
chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
24+
interned: ConstScalar::Unknown,
25+
}) => true,
26+
27+
// interned concrete anything else
28+
chalk_ir::ConstValue::Concrete(..) => false,
29+
30+
_ => {
31+
log::error!("is_unknown was called on a non-concrete constant value! {:?}", self);
32+
true
33+
}
34+
}
35+
}
36+
}
37+
38+
/// Extension trait for [`Expr`]
39+
pub trait ExprEval {
40+
/// Attempts to evaluate the expression as a target usize.
41+
fn eval_usize(&self) -> Option<u64>;
42+
}
43+
44+
impl ExprEval for Expr {
45+
// FIXME: support more than just evaluating literals
46+
fn eval_usize(&self) -> Option<u64> {
47+
match self {
48+
Expr::Literal(Literal::Uint(v, None))
49+
| Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(),
50+
_ => None,
51+
}
52+
}
53+
}
54+
55+
/// Interns a possibly-unknown target usize
56+
pub fn usize_const(value: Option<u64>) -> Const {
57+
ConstData {
58+
ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner),
59+
value: ConstValue::Concrete(chalk_ir::ConcreteConst {
60+
interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown),
61+
}),
62+
}
63+
.intern(&Interner)
64+
}

crates/hir_ty/src/infer/expr.rs

+8-33
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
//! Type inference for expressions.
22
3-
use std::{
4-
convert::TryInto,
5-
iter::{repeat, repeat_with},
6-
};
3+
use std::iter::{repeat, repeat_with};
74
use std::{mem, sync::Arc};
85

9-
use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind};
6+
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
107
use hir_def::{
11-
builtin_type::BuiltinUint,
128
expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
139
path::{GenericArg, GenericArgs},
1410
resolver::resolver_for_expr,
15-
type_ref::ConstScalar,
1611
AssocContainerId, FieldId, Lookup,
1712
};
1813
use hir_expand::name::{name, Name};
@@ -21,16 +16,16 @@ use syntax::ast::RangeOp;
2116

2217
use crate::{
2318
autoderef,
19+
consteval::{self, ExprEval},
2420
lower::lower_to_chalk_mutability,
2521
mapping::from_chalk,
2622
method_resolution, op,
2723
primitive::{self, UintTy},
2824
static_lifetime, to_chalk_trait_id,
2925
traits::FnTrait,
3026
utils::{generics, Generics},
31-
AdtId, Binders, CallableDefId, ConcreteConst, ConstValue, FnPointer, FnSig, FnSubst,
32-
InEnvironment, Interner, ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty,
33-
TyBuilder, TyExt, TyKind,
27+
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
28+
ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
3429
};
3530

3631
use super::{
@@ -743,25 +738,11 @@ impl<'a> InferenceContext<'a> {
743738
);
744739

745740
let repeat_expr = &self.body.exprs[*repeat];
746-
match repeat_expr {
747-
Expr::Literal(Literal::Uint(v, None))
748-
| Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => {
749-
(*v).try_into().ok()
750-
}
751-
_ => None,
752-
}
741+
repeat_expr.eval_usize()
753742
}
754743
};
755744

756-
let cd = ConstData {
757-
ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
758-
value: ConstValue::Concrete(chalk_ir::ConcreteConst {
759-
interned: len
760-
.map(|len| ConstScalar::Usize(len))
761-
.unwrap_or(ConstScalar::Unknown),
762-
}),
763-
};
764-
TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner)
745+
TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner)
765746
}
766747
Expr::Literal(lit) => match lit {
767748
Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
@@ -772,13 +753,7 @@ impl<'a> InferenceContext<'a> {
772753
Literal::ByteString(bs) => {
773754
let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
774755

775-
let len = ConstData {
776-
ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
777-
value: ConstValue::Concrete(ConcreteConst {
778-
interned: ConstScalar::Usize(bs.len() as u64),
779-
}),
780-
}
781-
.intern(&Interner);
756+
let len = consteval::usize_const(Some(bs.len() as u64));
782757

783758
let array_type = TyKind::Array(byte_type, len).intern(&Interner);
784759
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)

crates/hir_ty/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod autoderef;
1010
mod builder;
1111
mod chalk_db;
1212
mod chalk_ext;
13+
pub mod consteval;
1314
mod infer;
1415
mod interner;
1516
mod lower;

crates/hir_ty/src/tests/simple.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1271,12 +1271,14 @@ fn infer_array() {
12711271
12721272
let b = [a, ["b"]];
12731273
let x: [u8; 0] = [];
1274+
// FIXME: requires const evaluation/taking type from rhs somehow
1275+
let y: [u8; 2+2] = [1,2,3,4];
12741276
}
12751277
"#,
12761278
expect![[r#"
12771279
8..9 'x': &str
12781280
17..18 'y': isize
1279-
27..292 '{ ... []; }': ()
1281+
27..395 '{ ...,4]; }': ()
12801282
37..38 'a': [&str; 1]
12811283
41..44 '[x]': [&str; 1]
12821284
42..43 'x': &str
@@ -1326,6 +1328,12 @@ fn infer_array() {
13261328
259..262 '"b"': &str
13271329
274..275 'x': [u8; 0]
13281330
287..289 '[]': [u8; 0]
1331+
368..369 'y': [u8; _]
1332+
383..392 '[1,2,3,4]': [u8; 4]
1333+
384..385 '1': u8
1334+
386..387 '2': u8
1335+
388..389 '3': u8
1336+
390..391 '4': u8
13291337
"#]],
13301338
);
13311339
}

crates/hir_ty/src/tests/traits.rs

+97
Original file line numberDiff line numberDiff line change
@@ -3474,3 +3474,100 @@ fn main(){
34743474
"#]],
34753475
)
34763476
}
3477+
3478+
#[test]
3479+
fn array_length() {
3480+
check_infer(
3481+
r#"
3482+
trait T {
3483+
type Output;
3484+
fn do_thing(&self) -> Self::Output;
3485+
}
3486+
3487+
impl T for [u8; 4] {
3488+
type Output = usize;
3489+
fn do_thing(&self) -> Self::Output {
3490+
2
3491+
}
3492+
}
3493+
3494+
impl T for [u8; 2] {
3495+
type Output = u8;
3496+
fn do_thing(&self) -> Self::Output {
3497+
2
3498+
}
3499+
}
3500+
3501+
fn main() {
3502+
let v = [0u8; 2];
3503+
let v2 = v.do_thing();
3504+
let v3 = [0u8; 4];
3505+
let v4 = v3.do_thing();
3506+
}
3507+
"#,
3508+
expect![[r#"
3509+
44..48 'self': &Self
3510+
133..137 'self': &[u8; 4]
3511+
155..172 '{ ... }': usize
3512+
165..166 '2': usize
3513+
236..240 'self': &[u8; 2]
3514+
258..275 '{ ... }': u8
3515+
268..269 '2': u8
3516+
289..392 '{ ...g(); }': ()
3517+
299..300 'v': [u8; 2]
3518+
303..311 '[0u8; 2]': [u8; 2]
3519+
304..307 '0u8': u8
3520+
309..310 '2': usize
3521+
321..323 'v2': u8
3522+
326..327 'v': [u8; 2]
3523+
326..338 'v.do_thing()': u8
3524+
348..350 'v3': [u8; 4]
3525+
353..361 '[0u8; 4]': [u8; 4]
3526+
354..357 '0u8': u8
3527+
359..360 '4': usize
3528+
371..373 'v4': usize
3529+
376..378 'v3': [u8; 4]
3530+
376..389 'v3.do_thing()': usize
3531+
"#]],
3532+
)
3533+
}
3534+
3535+
// FIXME: We should infer the length of the returned array :)
3536+
#[test]
3537+
fn const_generics() {
3538+
check_infer(
3539+
r#"
3540+
trait T {
3541+
type Output;
3542+
fn do_thing(&self) -> Self::Output;
3543+
}
3544+
3545+
impl<const L: usize> T for [u8; L] {
3546+
type Output = [u8; L];
3547+
fn do_thing(&self) -> Self::Output {
3548+
*self
3549+
}
3550+
}
3551+
3552+
fn main() {
3553+
let v = [0u8; 2];
3554+
let v2 = v.do_thing();
3555+
}
3556+
"#,
3557+
expect![[r#"
3558+
44..48 'self': &Self
3559+
151..155 'self': &[u8; _]
3560+
173..194 '{ ... }': [u8; _]
3561+
183..188 '*self': [u8; _]
3562+
184..188 'self': &[u8; _]
3563+
208..260 '{ ...g(); }': ()
3564+
218..219 'v': [u8; 2]
3565+
222..230 '[0u8; 2]': [u8; 2]
3566+
223..226 '0u8': u8
3567+
228..229 '2': usize
3568+
240..242 'v2': [u8; _]
3569+
245..246 'v': [u8; 2]
3570+
245..257 'v.do_thing()': [u8; _]
3571+
"#]],
3572+
)
3573+
}

crates/ide_assists/src/handlers/add_explicit_type.rs

+28
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,34 @@ fn main() {
198198
)
199199
}
200200

201+
/// https://github.com/rust-analyzer/rust-analyzer/issues/2922
202+
#[test]
203+
fn regression_issue_2922() {
204+
check_assist(
205+
add_explicit_type,
206+
r#"
207+
fn main() {
208+
let $0v = [0.0; 2];
209+
}
210+
"#,
211+
r#"
212+
fn main() {
213+
let v: [f64; 2] = [0.0; 2];
214+
}
215+
"#,
216+
);
217+
// note: this may break later if we add more consteval. it just needs to be something that our
218+
// consteval engine doesn't understand
219+
check_assist_not_applicable(
220+
add_explicit_type,
221+
r#"
222+
fn main() {
223+
let $0l = [0.0; 2+2];
224+
}
225+
"#,
226+
);
227+
}
228+
201229
#[test]
202230
fn default_generics_should_not_be_added() {
203231
check_assist(

0 commit comments

Comments
 (0)