-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Beginnings of type inference #327
Conversation
- move dir_tests to test_utils for that.
Ok(Ty::FnPtr(Arc::new(sig))) | ||
} | ||
|
||
// TODO this should probably be per namespace (i.e. types vs. values), since for |
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.
An altrnative would be to make two DefIds per source tuple struct: one DefId for type, and one DefId for Function. That's an important tricky question which I don't know how to answer.
If we go via de sugaring (creating two def ids), then intrnal compiler phases like type inference become easier, but the IDE bits become harder: you'll need to apply reverse-desugaring to go from internal compiler data structures to the surface syntax.
Going in the opposite direction of special-casing stuff in the compiler makes compiler harder, but IDE easier.
crates/ra_hir/src/ty.rs
Outdated
|
||
#[derive(Clone, PartialEq, Eq, Debug)] | ||
pub struct InferenceResult { | ||
type_for: FxHashMap<LocalSyntaxPtr, 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 need to figure out (not in this PR) a better way to refer to things inside a function. The minimal change would be to use LocalSyntaxPtrs
, with text offsets relative to the enclosing function. That way, we can reuse type-inference after most changes.
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.
Yeah, I was thinking about that as well, but I decided I'd just use LocalSyntaxPtr
to get started :)
Though if we want to not have to recompute the type inference itself, we'd also have to have a position-independent representation of the function bodies, right? Like the hir::Exprs
you mention maybe...
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.
Though if we want to not have to recompute the type inference itself, we'd also have to have a position-independent representation of the function bodies, right?
Hm, right, yeah. The current syntax tree is not even "position-based", it's "identity-based": if one parses the same file twice, the resulting trees won't be eq
, and this actually causes interesting behavior because salsa relies on Eq
a great deal.
In general, I hope to make syntax trees a very temporarily and short-lived thing in rust-analyzer: because they are immutable and store comments & whitespace explicitly, they have a pretty-high space overhead. Ideally, only syntax trees for files opened in the editor should be present in memory, syntax trees for other files should be recreated on demand and torn down liberally.
Using LocalSyntaxPtr
is defenitelly the best solution for now!
|
||
fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> { | ||
let ast_path = ctry!(expr.path()); | ||
let path = ctry!(Path::from_ast(ast_path)); |
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.
An alternative would be to make a hir::Path
from expr
as a first step, and then work from that.
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 problem there was that FnScopes::resolve_local_name
wants a NameRef
. Though a helper method that takes a node and a name separately to resolve could be added to FnScopes
...
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.
Makes sense! I'd say that we need to fix FnScopes
somehow as well (via hir::Expr probably).
😍 😍 😍 😍 Yessssss! |
r=me @flodiebold I've added you to bors reviewrs, could you |
Yeah, that's precisely the plan. However, @nikomatsakis was musing about rewriting |
Implementation-wise, I love everything I see :) The biggest open question is how do we identify an expression inside a function? I am not sure that So perhaps we should create a However, I perfer to merge this as is, an iterate on design in separate PRs :) |
crates/ra_hir/src/ty/tests.rs
Outdated
static INIT: Once = Once::new(); | ||
INIT.call_once(|| Logger::with_env().start().unwrap()); | ||
dir_tests(&test_data_dir(), &["."], |text, _path| infer_file(text)); | ||
} |
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 very much love such "data-driven" tests, where you just thrown two text-files at the thing and compare results.
However, for debugging, it's very useful to have a separate test for each dataset, so that one can easily launch just a single test and get useful logging, etc. We already have that feature :)
So, let's rewrite this as a bunch of functions like
#[test]
fn infers_types_for_primitives() {
check_inference(r#"
fn test(a: u32, b: isize, c: !, d: &str) {
a;
b;
c;
d;
1usize;
1isize;
"test";
1.0f32;
}
"#, r"
[33; 34) 'd': [unknown]
[88; 94) '1isize': [unknown]
[48; 49) 'a': u32
[55; 56) 'b': isize
[112; 118) '1.0f32': [unknown]
[76; 82) '1usize': [unknown]
[9; 10) 'a': u32
[27; 28) 'c': !
[62; 63) 'c': !
[17; 18) 'b': isize
[100; 106) '"test"': [unknown]
[42; 121) '{ ...f32; }': ()
[69; 70) 'd': [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.
True, though I like the ease of just deleting the file and rerunning the test to regenerate it, but admittedly it's just a matter of copy/pasting the expected result. Ideally, we would both be able to run single tests and to automatically update them...
@matklad #[test]
fn infers_types_for_primitives() {
check_inference(r#"
fn test(a: u32, b: isize, c: !, d: &str) {
a;
b;
c;
d;
1usize;
1isize;
"test";
1.0f32;
}
"#, "data/infers_types_for_primitives.txt");
} ? |
Excellent idea! I've missed the "auto-update" bit of the current impl |
Removing irrelevant comments copied from rustc etc.
Ok, did that and cleaned up a bit more, so I think we can merge now! I'll try: |
327: Beginnings of type inference r=flodiebold a=flodiebold I was a bit bored, so I thought I'd try to start implementing the type system and see how far I come 😉 This is obviously still extremely WIP, only very basic stuff working, but I thought I'd post this now to get some feedback as to whether this approach makes sense at all. There's no user-visible effect yet, but the type inference has tests similar to the ones for the parser. My next step will probably be to implement struct types, after which this could probably be used to complete fields. I realize this may all get thrown away when/if the compiler query system gets usable, but I feel like there are lots of IDE features that could be implemented with somewhat working type inference in the meantime 😄 Co-authored-by: Florian Diebold <flodiebold@gmail.com>
Build succeeded |
For the next steps, I think it makes sense to focus on two bits:
|
Yeah, I'm working on ADTs for now. |
I was a bit bored, so I thought I'd try to start implementing the type system and see how far I come 😉 This is obviously still extremely WIP, only very basic stuff working, but I thought I'd post this now to get some feedback as to whether this approach makes sense at all.
There's no user-visible effect yet, but the type inference has tests similar to the ones for the parser. My next step will probably be to implement struct types, after which this could probably be used to complete fields.
I realize this may all get thrown away when/if the compiler query system gets usable, but I feel like there are lots of IDE features that could be implemented with somewhat working type inference in the meantime 😄