-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Implement array inference #524
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're reimplementing tuples here -- arrays should be homogenous :)
crates/ra_hir/src/ty.rs
Outdated
@@ -216,6 +216,9 @@ pub enum Ty { | |||
/// A tuple type. For example, `(i32, bool)`. | |||
Tuple(Arc<[Ty]>), | |||
|
|||
/// A array type. For example, `[i32]`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we already have Slice
above. The difference between Array
and Slice
would be that Array
has a known length (which we can leave out for now since we don't care about it), but then your example is wrong ;) Also, an array should only have one inner type -- all elements of the array need to have the same type.
Also can you move this up to be next to Slice
?
crates/ra_hir/src/ty.rs
Outdated
@@ -1048,6 +1061,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
|
|||
Ty::Tuple(Arc::from(ty_vec)) | |||
} | |||
Expr::Array { exprs } => { | |||
let mut ty_vec = Vec::with_capacity(exprs.len()); | |||
for arg in exprs.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to unify the types of all the elements here.
@flodiebold Thanks. How is this? |
/// The pointee of an array slice. Written as `[T]`. | ||
Slice(Arc<Ty>), | ||
|
||
// An array with the given length. Written as `[T; n]`. | ||
Array(Arc<Ty>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we define Array(Ty, ty::Const)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't currently have a ty::Const
(the commented-out lines are copied from rustc) :) And for our current use cases, we don't really need the array size -- if there was a mismatch in sizes somewhere, I think we would probably want to continue type inference anyway to still be able to provide completions etc.
/// The pointee of an array slice. Written as `[T]`. | ||
Slice(Arc<Ty>), | ||
|
||
// An array with the given length. Written as `[T; n]`. | ||
Array(Arc<Ty>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't currently have a ty::Const
(the commented-out lines are copied from rustc) :) And for our current use cases, we don't really need the array size -- if there was a mismatch in sizes somewhere, I think we would probably want to continue type inference anyway to still be able to provide completions etc.
crates/ra_hir/src/ty.rs
Outdated
} | ||
|
||
Ty::Tuple(Arc::from(ty_vec)) | ||
} | ||
Expr::Array { exprs } => { | ||
let mut ty = Ty::Unknown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a nicer way to do this: create a new type variable here (you could just write self.insert_type_vars(Ty::Unknown)
), and then pass that as the expected type to each of the sub expressions.
As a bonus, you can actually use the expected type you currently have. So instead of starting with Ty::Unknown
, you can look into expected.ty
and see if it's a slice or array type. If it is, you can use the element type from that.
crates/ra_hir/src/ty.rs
Outdated
match infered { | ||
Ty::Unknown => (), | ||
_ => ty = infered, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're not actually making sure all the elements have the same type anywhere here. The best way to do that would be to pass the element type as expected type (so Expectation::has_type(ty)
).
let d = [y]; | ||
let e = [d, d]; | ||
let f = [e, e]; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's an expression that checks whether the element types are actually unified:
[y, 1, 2, 3]
the 1, 2 and 3 should be inferred to isize
, and I think with your current code they won't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could also add the case where the array is empty:
let x: [u8; 0] = [];
we should still infer the element type on the right side. I think this should already work. This would also test array type references, which you implemented but didn't use in this test (the [u8; 0]
part).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It increases the inferring accuracy than before.
[226; 227) 'a': [&str] | ||
[229; 234) '["b"]': [[&str]] | ||
[230; 233) '"b"': [&str] | ||
[245; 246) 'x': [[u8]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be 'x': [u8]
.
But I don't know why is results above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because you're passing the expected type for the slice (which is [u8]
) as the expected type for the elements. You need to extract the u8
from the expected type and pass that down.
crates/ra_hir/src/ty.rs
Outdated
let mut ty = self.insert_type_vars(expected.ty.clone()); | ||
|
||
for (i, expr) in exprs.iter().enumerate() { | ||
if i == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, this is not good implementation.
If getting [1, y, 2, 3]
(let y: u32 = 1;
), it must inferes [_]
.
I'll fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to use the inferred type from the subexpressions. If you just pass Expectation::has_type(ty)
, all the element expressions will be unified to the same type. No need for special casing the first element either :)
crates/ra_hir/src/ty.rs
Outdated
let mut ty = self.insert_type_vars(expected.ty.clone()); | ||
|
||
for (i, expr) in exprs.iter().enumerate() { | ||
if i == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to use the inferred type from the subexpressions. If you just pass Expectation::has_type(ty)
, all the element expressions will be unified to the same type. No need for special casing the first element either :)
crates/ra_hir/src/ty.rs
Outdated
} | ||
|
||
Ty::Tuple(Arc::from(ty_vec)) | ||
} | ||
Expr::Array { exprs } => { | ||
let mut ty = self.insert_type_vars(expected.ty.clone()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe let's call this elem_ty
, since it should be the element type. So you can't just use expected.ty
, that's the expected type for the whole slice ;) You need to check whether the expected type is Slice
or Array
; if so, you can take the element type from that; otherwise, you can use a new type variable for the element type (self.new_type_var()
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to check whether the expected type is Slice or Array; if so, you can take the element type from that
How to check whether the expected type is Slice or Array?
I think that there is no information where this processing context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I defined wrong ArrayExpr.
ArrayExpr must contains SEMI and Number or Ref referring to usize.
My definitions is not for Array but for Slice, I think now.
Do I have the misconception something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to check whether the expected type is Slice or Array?
You have the expected type in expected.ty
here. You can match on that, basically:
let elem_ty = match expected.ty {
Ty::Slice(inner) | Ty::Array(inner) => inner.clone(),
_ => self.new_type_var()
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I defined wrong ArrayExpr.
ArrayExpr must contains SEMI and Number or Ref referring to usize.
My definitions is not for Array but for Slice, I think now.
Do I have the misconception something?
Both [a, b, c]
and [a; 99]
are array expressions resulting in an array type, not a slice, and if I'm not mistaken, they're both represented by ArrayExpr
. You could add support for the second form already here as well, but it'd be fine to ignore that for now and do in in a later PR.
(You get a slice by a coercion from an array, which we don't implement yet, but it will kind of work if we pass down the expected element type like I wrote: for example, in let x: &[u8] = [1, 2, 3]
, the expected type is a slice of u8
; then when typing the array expression, we take the expected element type, so u8
, and use that for the elements. Even though the resulting type of the expression, which is an array of u8
, doesn't unify with the expected type in the end.)
[226; 227) 'a': [&str] | ||
[229; 234) '["b"]': [[&str]] | ||
[230; 233) '"b"': [&str] | ||
[245; 246) 'x': [[u8]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because you're passing the expected type for the slice (which is [u8]
) as the expected type for the elements. You need to extract the u8
from the expected type and pass that down.
@flodiebold Fixed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, I just have two small comments, sorry :)
crates/ra_hir/src/ty/tests.rs
Outdated
let b = [a, a]; | ||
let c = [b, b]; | ||
|
||
let d = [y, 1, 2, 3] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there should be semicolons here and in the line below... it's a testament to the parser's error recovery that this still works :)
crates/ra_hir/src/ty.rs
Outdated
}; | ||
|
||
for expr in exprs.iter() { | ||
elem_ty = self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you have to assign elem_ty here. You should be able to keep using the elem_ty above:
elem_ty = self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone()))?; | |
self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone()))?; |
(and remove the mut
).
@flodiebold Thanks! Fixed it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, the latest change revealed another small problem ;) Apart from that, can you rebase on current master to get rid of the merge commit and make it mergable again?
[9; 10) 'x': &str | ||
[18; 19) 'y': isize | ||
[28; 293) '{ ... []; }': () | ||
[38; 39) 'a': [_] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the _
means that we have a type variable here, which shouldn't ever happen -- all type variables should be replaced by their known type or Ty::Unknown
after inference. The reason that this doesn't happen here is that Ty::Array
is missing in Ty::walk_mut
, which I missed so far. Can you add that there (should work exactly like the slice type)?
@flodiebold I fixed and rebased. |
Great, thanks! bors r+ |
524: Implement array inference r=flodiebold a=h-michael related #394 Co-authored-by: Hirokazu Hata <[email protected]>
Build succeeded |
related #394