Skip to content

Commit 7fbedc5

Browse files
committed
Auto merge of #24422 - pnkfelix:typeck-highlevel-before-bodies, r=nikomatsakis
typeck: Do high-level structural/signature checks before function body checks. This avoids various ICEs, e.g. premature calls to cat_expr that yield the dreaded "cat_expr Errd" ICE. However, it also means that some early error feedback is now not provided. This may be for the best, because the error feedback were were providing in some of those cases were false positives -- it was spurious feedback and a distraction from the real problem. So it is not 100% clear whether we actually want to put this change in or not. I think its a net win, but others might disagree. (Kudos to @arielb1 for suggesting this modification.)
2 parents a52182f + d82f912 commit 7fbedc5

19 files changed

+417
-98
lines changed

src/librustc_typeck/check/mod.rs

+71-44
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,11 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>)
441441
}
442442

443443
struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }
444+
struct CheckItemBodiesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }
444445

445446
impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
446447
fn visit_item(&mut self, i: &'tcx ast::Item) {
447-
check_item(self.ccx, i);
448+
check_item_type(self.ccx, i);
448449
visit::walk_item(self, i);
449450
}
450451

@@ -460,6 +461,13 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
460461
}
461462
}
462463

464+
impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> {
465+
fn visit_item(&mut self, i: &'tcx ast::Item) {
466+
check_item_body(self.ccx, i);
467+
visit::walk_item(self, i);
468+
}
469+
}
470+
463471
pub fn check_item_types(ccx: &CrateCtxt) {
464472
let krate = ccx.tcx.map.krate();
465473
let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx);
@@ -474,6 +482,11 @@ pub fn check_item_types(ccx: &CrateCtxt) {
474482

475483
ccx.tcx.sess.abort_if_errors();
476484

485+
let mut visit = CheckItemBodiesVisitor { ccx: ccx };
486+
visit::walk_crate(&mut visit, krate);
487+
488+
ccx.tcx.sess.abort_if_errors();
489+
477490
for drop_method_did in ccx.tcx.destructors.borrow().iter() {
478491
if drop_method_did.krate == ast::LOCAL_CRATE {
479492
let drop_impl_did = ccx.tcx.map.get_parent_did(drop_method_did.node);
@@ -713,13 +726,13 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
713726
}
714727
}
715728

716-
pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
717-
debug!("check_item(it.id={}, it.ident={})",
729+
pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
730+
debug!("check_item_type(it.id={}, it.ident={})",
718731
it.id,
719732
ty::item_path_str(ccx.tcx, local_def(it.id)));
720733
let _indenter = indenter();
721-
722734
match it.node {
735+
// Consts can play a role in type-checking, so they are included here.
723736
ast::ItemStatic(_, _, ref e) |
724737
ast::ItemConst(_, ref e) => check_const(ccx, it.span, &**e, it.id),
725738
ast::ItemEnum(ref enum_definition, _) => {
@@ -728,16 +741,9 @@ pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
728741
&enum_definition.variants,
729742
it.id);
730743
}
731-
ast::ItemFn(ref decl, _, _, _, ref body) => {
732-
let fn_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
733-
let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id);
734-
check_bare_fn(ccx, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env);
735-
}
744+
ast::ItemFn(_, _, _, _, _) => {} // entirely within check_item_body
736745
ast::ItemImpl(_, _, _, _, _, ref impl_items) => {
737-
debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
738-
739-
let impl_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
740-
746+
debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
741747
match ty::impl_trait_ref(ccx.tcx, local_def(it.id)) {
742748
Some(impl_trait_ref) => {
743749
check_impl_items_against_trait(ccx,
@@ -747,39 +753,9 @@ pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
747753
}
748754
None => { }
749755
}
750-
751-
for impl_item in impl_items {
752-
match impl_item.node {
753-
ast::MethodImplItem(ref sig, ref body) => {
754-
check_method_body(ccx, &impl_pty.generics, sig, body,
755-
impl_item.id, impl_item.span);
756-
}
757-
ast::TypeImplItem(_) |
758-
ast::MacImplItem(_) => {
759-
// Nothing to do here.
760-
}
761-
}
762-
}
763-
764756
}
765-
ast::ItemTrait(_, ref generics, _, ref trait_items) => {
757+
ast::ItemTrait(_, ref generics, _, _) => {
766758
check_trait_on_unimplemented(ccx, generics, it);
767-
let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id));
768-
for trait_item in trait_items {
769-
match trait_item.node {
770-
ast::MethodTraitItem(_, None) => {
771-
// Nothing to do, since required methods don't have
772-
// bodies to check.
773-
}
774-
ast::MethodTraitItem(ref sig, Some(ref body)) => {
775-
check_method_body(ccx, &trait_def.generics, sig, body,
776-
trait_item.id, trait_item.span);
777-
}
778-
ast::TypeTraitItem(..) => {
779-
// Nothing to do.
780-
}
781-
}
782-
}
783759
}
784760
ast::ItemStruct(..) => {
785761
check_struct(ccx, it.id, it.span);
@@ -814,6 +790,57 @@ pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
814790
}
815791
}
816792

793+
pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
794+
debug!("check_item_body(it.id={}, it.ident={})",
795+
it.id,
796+
ty::item_path_str(ccx.tcx, local_def(it.id)));
797+
let _indenter = indenter();
798+
match it.node {
799+
ast::ItemFn(ref decl, _, _, _, ref body) => {
800+
let fn_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
801+
let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id);
802+
check_bare_fn(ccx, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env);
803+
}
804+
ast::ItemImpl(_, _, _, _, _, ref impl_items) => {
805+
debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
806+
807+
let impl_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
808+
809+
for impl_item in impl_items {
810+
match impl_item.node {
811+
ast::MethodImplItem(ref sig, ref body) => {
812+
check_method_body(ccx, &impl_pty.generics, sig, body,
813+
impl_item.id, impl_item.span);
814+
}
815+
ast::TypeImplItem(_) |
816+
ast::MacImplItem(_) => {
817+
// Nothing to do here.
818+
}
819+
}
820+
}
821+
}
822+
ast::ItemTrait(_, _, _, ref trait_items) => {
823+
let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id));
824+
for trait_item in trait_items {
825+
match trait_item.node {
826+
ast::MethodTraitItem(_, None) => {
827+
// Nothing to do, since required methods don't have
828+
// bodies to check.
829+
}
830+
ast::MethodTraitItem(ref sig, Some(ref body)) => {
831+
check_method_body(ccx, &trait_def.generics, sig, body,
832+
trait_item.id, trait_item.span);
833+
}
834+
ast::TypeTraitItem(..) => {
835+
// Nothing to do.
836+
}
837+
}
838+
}
839+
}
840+
_ => {/* nothing to do */ }
841+
}
842+
}
843+
817844
fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
818845
generics: &ast::Generics,
819846
item: &ast::Item) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that we get an error when you use `<Self as Get>::Value` in
12+
// the trait definition but `Self` does not, in fact, implement `Get`.
13+
//
14+
// See also associated-types-no-suitable-supertrait.rs, which checks
15+
// that we see the same error when making this mistake on an impl
16+
// rather than the default method impl.
17+
//
18+
// See also run-pass/associated-types-projection-to-unrelated-trait.rs,
19+
// which checks that the trait interface itself is not considered an
20+
// error as long as all impls satisfy the constraint.
21+
22+
trait Get : ::std::marker::MarkerTrait {
23+
type Value;
24+
}
25+
26+
trait Other {
27+
fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
28+
//~^ ERROR the trait `Get` is not implemented for the type `Self`
29+
}
30+
31+
fn main() { }

src/test/compile-fail/associated-types-no-suitable-supertrait.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,30 @@
1010

1111
// Check that we get an error when you use `<Self as Get>::Value` in
1212
// the trait definition but `Self` does not, in fact, implement `Get`.
13+
//
14+
// See also associated-types-no-suitable-supertrait-2.rs, which checks
15+
// that we see the same error if we get around to checking the default
16+
// method body.
17+
//
18+
// See also run-pass/associated-types-projection-to-unrelated-trait.rs,
19+
// which checks that the trait interface itself is not considered an
20+
// error as long as all impls satisfy the constraint.
1321

1422
trait Get : ::std::marker::MarkerTrait {
1523
type Value;
1624
}
1725

1826
trait Other {
1927
fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
20-
//~^ ERROR the trait `Get` is not implemented for the type `Self`
28+
// (note that we no longer catch the error here, since the
29+
// error below aborts compilation.
30+
// See also associated-types-no-suitable-supertrait-2.rs
31+
// which checks that this error would be caught eventually.)
2132
}
2233

2334
impl<T:Get> Other for T {
2435
fn uhoh<U:Get>(&self, foo: U, bar: <(T, U) as Get>::Value) {}
2536
//~^ ERROR the trait `Get` is not implemented for the type `(T, U)`
26-
//~| ERROR the trait `Get` is not implemented for the type `(T, U)`
2737
}
2838

2939
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Tests that enum-to-float casts are disallowed.
12+
13+
enum E {
14+
L0 = -1,
15+
H0 = 1
16+
}
17+
18+
enum F {
19+
L1 = 1,
20+
H1 = 0xFFFFFFFFFFFFFFFF
21+
}
22+
23+
pub fn main() {
24+
let a = E::L0 as f32; //~ ERROR illegal cast
25+
let c = F::H1 as f32; //~ ERROR illegal cast
26+
assert_eq!(a, -1.0f32);
27+
assert_eq!(c, -1.0f32);
28+
}

src/test/compile-fail/enum-to-float-cast.rs

-4
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,8 @@ static C0: f32 = E::L0 as f32; //~ ERROR illegal cast
2424
static C1: f32 = F::H1 as f32; //~ ERROR illegal cast
2525

2626
pub fn main() {
27-
let a = E::L0 as f32; //~ ERROR illegal cast
2827
let b = C0;
29-
let c = F::H1 as f32; //~ ERROR illegal cast
3028
let d = C1;
31-
assert_eq!(a, -1.0f32);
3229
assert_eq!(b, -1.0f32);
33-
assert_eq!(c, -1.0f32);
3430
assert_eq!(d, -1.0f32);
3531
}

src/test/compile-fail/issue-16048.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl<'a> Test<'a> for Foo<'a> {
2929
impl<'a> NoLifetime for Foo<'a> {
3030
fn get<'p, T : Test<'a>>(&self) -> T {
3131
//~^ ERROR lifetime parameters or bounds on method `get` do not match the trait declaration
32-
return *self as T; //~ ERROR non-scalar cast: `Foo<'a>` as `T`
32+
return *self as T;
3333
}
3434
}
3535

src/test/compile-fail/issue-19244-1.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,5 @@ const TUP: (usize,) = (42,);
1212

1313
fn main() {
1414
let a: [isize; TUP.1];
15-
//~^ ERROR array length constant evaluation error: tuple index out of bounds
16-
//~| ERROR attempted out-of-bounds tuple index
17-
//~| ERROR attempted out-of-bounds tuple index
15+
//~^ ERROR attempted out-of-bounds tuple index
1816
}

src/test/compile-fail/issue-19244-2.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,5 @@ const STRUCT: MyStruct = MyStruct { field: 42 };
1313

1414
fn main() {
1515
let a: [isize; STRUCT.nonexistent_field];
16-
//~^ ERROR array length constant evaluation error: nonexistent struct field
17-
//~| ERROR attempted access of field `nonexistent_field`
18-
//~| ERROR attempted access of field `nonexistent_field`
16+
//~^ ERROR attempted access of field `nonexistent_field`
1917
}

src/test/compile-fail/issue-2063.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ impl to_str_2 for t {
2828
}
2929

3030
fn new_t(x: t) {
31-
x.my_to_string(); //~ ERROR does not implement
31+
x.my_to_string();
32+
// (there used to be an error emitted right here as well. It was
33+
// spurious, at best; if `t` did exist as a type, it clearly would
34+
// have an impl of the `to_str_2` trait.)
3235
}
3336

3437
fn main() {

src/test/compile-fail/issue-23729.rs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Regression test for #23729
12+
13+
fn main() {
14+
let fib = {
15+
struct Recurrence {
16+
mem: [u64; 2],
17+
pos: usize,
18+
}
19+
20+
impl Iterator for Recurrence {
21+
//~^ ERROR not all trait items implemented, missing: `Item` [E0046]
22+
#[inline]
23+
fn next(&mut self) -> Option<u64> {
24+
if self.pos < 2 {
25+
let next_val = self.mem[self.pos];
26+
self.pos += 1;
27+
Some(next_val)
28+
} else {
29+
let next_val = (self.mem[0] + self.mem[1]);
30+
self.mem[0] = self.mem[1];
31+
self.mem[1] = next_val;
32+
Some(next_val)
33+
}
34+
}
35+
}
36+
37+
Recurrence { mem: [0, 1], pos: 0 }
38+
};
39+
40+
for e in fib.take(10) {
41+
println!("{}", e)
42+
}
43+
}

0 commit comments

Comments
 (0)