auto merge of #12764 : Kimundi/rust/partial_typehint, r=nikomatsakis

# Summary

This patch introduces the `_` token into the type grammar, with the meaning "infer this type".
With this change, the following two lines become equivalent:
```
let x = foo();
let x: _ = foo();
```
But due to its composability, it enables partial type hints like this:
```
let x: Bar<_> = baz();
```

Using it on the item level is explicitly forbidden, as the Rust language does not enable global type inference by design.

This implements the feature requested in https://github.com/mozilla/rust/issues/9508.

# Things requiring clarification

- The change to enable it is very small, but I have only limited understanding of the related code, so the approach here might be wrong.
  - In particular, while this patch works, it does so in a way not originally intended according to the code comments.
- This probably needs more tests, or rather feedback for which tests are still missing.
- I'm unsure how this interacts with lifetime parameters, and whether it is correct in regard to them.
- Partial type hints on the right side of `as` like `&foo as *_` work in both a normal function contexts and in constexprs like `static foo: *int = &'static 123 as *_`. The question is whether this should be allowed in general.

# Todo for this PR

- The manual and tutorial still needs updating.

# Bugs I'm unsure how to fix

- Requesting inference for the top level of the right hand side of a `as` fails to infer correctly, even if all possible hints are given:

  ```
.../type_hole_1.rs:35:18: 35:22 error: the type of this value must be known in this context
.../type_hole_1.rs:35     let a: int = 1u32 as _;
                                           ^~~~
```
This commit is contained in:
bors 2014-03-14 08:01:28 -07:00
commit 2585803ec1
10 changed files with 236 additions and 11 deletions

View file

@ -640,13 +640,11 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
tcx.sess.span_bug(ast_ty.span, "typeof is reserved but unimplemented");
}
ast::TyInfer => {
// ty_infer should only appear as the type of arguments or return
// values in a fn_expr, or as the type of local variables. Both of
// these cases are handled specially and should not descend into this
// routine.
this.tcx().sess.span_bug(
ast_ty.span,
"found `ty_infer` in unexpected place");
// TyInfer also appears as the type of arguments or return
// values in a ExprFnBlock or ExprProc, or as the type of
// local variables. Both of these cases are handled specially
// and will not descend into this routine.
this.ty_infer(ast_ty.span)
}
});

View file

@ -132,7 +132,9 @@ impl AstConv for CrateCtxt {
}
fn ty_infer(&self, span: Span) -> ty::t {
self.tcx.sess.span_bug(span, "found `ty_infer` in unexpected place");
self.tcx.sess.span_err(span, "the type placeholder `_` is not \
allowed within types on item signatures.");
ty::mk_err()
}
}

View file

@ -830,8 +830,7 @@ pub enum Ty_ {
TyPath(Path, Option<OptVec<TyParamBound>>, NodeId), // for #7264; see above
TyTypeof(@Expr),
// TyInfer means the type should be inferred instead of it having been
// specified. This should only appear at the "top level" of a type and not
// nested in one.
// specified. This can appear anywhere in a type.
TyInfer,
}

View file

@ -1274,6 +1274,9 @@ impl Parser {
bounds
} = self.parse_path(LifetimeAndTypesAndBounds);
TyPath(path, bounds, ast::DUMMY_NODE_ID)
} else if self.eat(&token::UNDERSCORE) {
// TYPE TO BE INFERRED
TyInfer
} else {
let msg = format!("expected type, found token {:?}", self.token);
self.fatal(msg);

View file

@ -504,7 +504,7 @@ pub fn print_type(s: &mut State, ty: &ast::Ty) -> io::IoResult<()> {
try!(word(&mut s.s, ")"));
}
ast::TyInfer => {
fail!("print_type shouldn't see a ty_infer");
try!(word(&mut s.s, "_"));
}
}
end(s)

View file

@ -0,0 +1,119 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This test checks that it is not possible to enable global type
// inference by using the `_` type placeholder.
fn test() -> _ { 5 }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn test2() -> (_, _) { (5u, 5u) }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures.
static TEST3: _ = "test";
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
static TEST4: _ = 145u16;
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
static TEST5: (_, _) = (1, 2);
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn test6(_: _) { }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn test7(x: _) { let _x: uint = x; }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn test8(_f: fn() -> _) { }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
struct Test9;
impl Test9 {
fn test9(&self) -> _ { () }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn test10(&self, _x : _) { }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
}
impl Clone for Test9 {
fn clone(&self) -> _ { Test9 }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn clone_from(&mut self, other: _) { *self = Test9; }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
}
struct Test10 {
a: _,
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
b: (_, _),
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures.
}
pub fn main() {
fn fn_test() -> _ { 5 }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn fn_test2() -> (_, _) { (5u, 5u) }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures.
static FN_TEST3: _ = "test";
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
static FN_TEST4: _ = 145u16;
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
static FN_TEST5: (_, _) = (1, 2);
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn fn_test6(_: _) { }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn fn_test7(x: _) { let _x: uint = x; }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn fn_test8(_f: fn() -> _) { }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
struct FnTest9;
impl FnTest9 {
fn fn_test9(&self) -> _ { () }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn fn_test10(&self, _x : _) { }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
}
impl Clone for FnTest9 {
fn clone(&self) -> _ { FnTest9 }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
fn clone_from(&mut self, other: _) { *self = FnTest9; }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
}
struct FnTest10 {
a: _,
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
b: (_, _),
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures.
//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures.
}
}

View file

@ -0,0 +1,21 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This test checks that the `_` type placeholder does not react
// badly if put as a lifetime parameter.
struct Foo<'a, T> {
r: &'a T
}
pub fn main() {
let c: Foo<_, _> = Foo { r: &5u };
//~^ ERROR wrong number of type arguments: expected 1 but found 2
}

View file

@ -0,0 +1,21 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This test checks that the `_` type placeholder does not react
// badly if put as a lifetime parameter.
struct Foo<'a, T> {
r: &'a T
}
pub fn main() {
let c: Foo<_, uint> = Foo { r: &5 };
//~^ ERROR wrong number of type arguments: expected 1 but found 2
}

View file

@ -0,0 +1,30 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This test checks that genuine type errors with partial
// type hints are understandable.
struct Foo<T>;
struct Bar<U>;
pub fn main() {
}
fn test1() {
let x: Foo<_> = Bar::<uint>;
//~^ ERROR mismatched types: expected `Foo<<generic #0>>` but found `Bar<uint>`
let y: Foo<uint> = x;
}
fn test2() {
let x: Foo<_> = Bar::<uint>;
//~^ ERROR mismatched types: expected `Foo<<generic #0>>` but found `Bar<uint>`
//~^^ ERROR cannot determine a type for this local variable: unconstrained type
}

View file

@ -0,0 +1,32 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This test checks that the `_` type placeholder works
// correctly for enabling type inference.
static CONSTEXPR: *int = &'static 413 as *_;
pub fn main() {
use std::vec_ng::Vec;
let x: Vec<_> = range(0u, 5).collect();
assert_eq!(x.as_slice(), &[0u,1,2,3,4]);
let x = range(0u, 5).collect::<Vec<_>>();
assert_eq!(x.as_slice(), &[0u,1,2,3,4]);
let y: _ = "hello";
assert_eq!(y.len(), 5);
let ptr = &5u;
let ptr2 = ptr as *_;
assert_eq!(ptr as *uint as uint, ptr2 as uint);
}