Skip to content

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

Merged
merged 8 commits into from
Jan 16, 2019
Merged

Implement array inference #524

merged 8 commits into from
Jan 16, 2019

Conversation

h-michael
Copy link
Contributor

related #394

Copy link
Member

@flodiebold flodiebold left a 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 :)

@@ -216,6 +216,9 @@ pub enum Ty {
/// A tuple type. For example, `(i32, bool)`.
Tuple(Arc<[Ty]>),

/// A array type. For example, `[i32]`.
Copy link
Member

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?

@@ -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() {
Copy link
Member

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.

@h-michael
Copy link
Contributor Author

@flodiebold Thanks. How is this?
But I also think that there may be better way of unifying.

/// 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>),
Copy link
Contributor Author

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)?

Copy link
Member

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>),
Copy link
Member

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.

}

Ty::Tuple(Arc::from(ty_vec))
}
Expr::Array { exprs } => {
let mut ty = Ty::Unknown;
Copy link
Member

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.

match infered {
Ty::Unknown => (),
_ => ty = infered,
}
Copy link
Member

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];

Copy link
Member

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.

Copy link
Member

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).

Copy link
Contributor Author

@h-michael h-michael left a 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]]
Copy link
Contributor Author

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.

Copy link
Member

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.

let mut ty = self.insert_type_vars(expected.ty.clone());

for (i, expr) in exprs.iter().enumerate() {
if i == 0 {
Copy link
Contributor Author

@h-michael h-michael Jan 13, 2019

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.

Copy link
Member

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 :)

let mut ty = self.insert_type_vars(expected.ty.clone());

for (i, expr) in exprs.iter().enumerate() {
if i == 0 {
Copy link
Member

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 :)

}

Ty::Tuple(Arc::from(ty_vec))
}
Expr::Array { exprs } => {
let mut ty = self.insert_type_vars(expected.ty.clone());
Copy link
Member

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())

Copy link
Contributor Author

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.

Copy link
Contributor Author

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?

Copy link
Member

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()
}

Copy link
Member

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]]
Copy link
Member

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.

@h-michael
Copy link
Contributor Author

@flodiebold Fixed.
Thank you for your very very detailed explanation.
I understand implementation of ra_hir crate.

Copy link
Member

@flodiebold flodiebold left a 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 :)

let b = [a, a];
let c = [b, b];

let d = [y, 1, 2, 3]
Copy link
Member

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 :)

};

for expr in exprs.iter() {
elem_ty = self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone()))?;
Copy link
Member

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:

Suggested change
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).

@h-michael
Copy link
Contributor Author

@flodiebold Thanks! Fixed it.

Copy link
Member

@flodiebold flodiebold left a 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': [_]
Copy link
Member

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)?

@h-michael
Copy link
Contributor Author

@flodiebold I fixed and rebased.

@flodiebold
Copy link
Member

Great, thanks!

bors r+

bors bot added a commit that referenced this pull request Jan 16, 2019
524:  Implement array inference r=flodiebold a=h-michael

related #394

Co-authored-by: Hirokazu Hata <[email protected]>
@bors
Copy link
Contributor

bors bot commented Jan 16, 2019

Build succeeded

@bors bors bot merged commit 0aedd4f into rust-lang:master Jan 16, 2019
@h-michael h-michael deleted the array branch January 16, 2019 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants