From f428405c9b35a4eccf934a83e59198c7073994f9 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Mon, 7 Jan 2013 19:33:32 -0800 Subject: [PATCH 1/3] Suppress error messages about function types whose result is ty_err Ideally we would suppress error messages involving any types that contain ty_err, but that's awkward to do right now. --- src/librustc/middle/typeck/infer/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 279318079b9a2..ccd0066de28c1 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -737,10 +737,19 @@ impl infer_ctxt { fn type_error_message(sp: span, mk_msg: fn(~str) -> ~str, actual_ty: ty::t, err: Option<&ty::type_err>) { let actual_ty = self.resolve_type_vars_if_possible(actual_ty); + let mut actual_sty = ty::get(copy actual_ty); // Don't report an error if actual type is ty_err. - match ty::get(actual_ty).sty { + match actual_sty.sty { ty::ty_err => return, + // Should really not report an error if the type + // has ty_err anywhere as a component, but that's + // annoying since we haven't written a visitor for + // ty::t yet + ty::ty_fn(ref fty) => match ty::get(fty.sig.output).sty { + ty::ty_err => return, + _ => () + }, _ => () } let error_str = err.map_default(~"", |t_err| From be06a3eea51d47d31d6361746d50e126ac5743fa Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Mon, 7 Jan 2013 19:34:13 -0800 Subject: [PATCH 2/3] Treat `self` as if it's dynamically scoped when typechecking nested functions Necessary to allow supertrait methods to be called in default functions. As per #3563 --- src/librustc/middle/typeck/check/mod.rs | 13 +- .../{run-pass => compile-fail}/issue-3563.rs | 4 +- src/test/run-pass/issue-3563-2.rs | 22 ++ src/test/run-pass/issue-3563-3.rs | 212 ++++++++++++++++++ 4 files changed, 245 insertions(+), 6 deletions(-) rename src/test/{run-pass => compile-fail}/issue-3563.rs (82%) create mode 100644 src/test/run-pass/issue-3563-2.rs create mode 100644 src/test/run-pass/issue-3563-3.rs diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index f4bae52bd9130..051b037589009 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -178,7 +178,10 @@ pub struct fn_ctxt { // var_bindings, locals and next_var_id are shared // with any nested functions that capture the environment // (and with any functions whose environment is being captured). - self_impl_def_id: Option, + + // Refers to whichever `self` is in scope, even this fn_ctxt is + // for a nested closure that captures `self` + self_info: Option, ret_ty: ty::t, // Used by loop bodies that return from the outer function indirect_ret_ty: Option, @@ -227,7 +230,7 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t, // It's kind of a kludge to manufacture a fake function context // and statement context, but we might as well do write the code only once @fn_ctxt { - self_impl_def_id: None, + self_info: None, ret_ty: rty, indirect_ret_ty: None, purity: ast::pure_fn, @@ -320,7 +323,7 @@ fn check_fn(ccx: @crate_ctxt, } else { None }; @fn_ctxt { - self_impl_def_id: self_info.map(|self_info| self_info.def_id), + self_info: self_info, ret_ty: ret_ty, indirect_ret_ty: indirect_ret_ty, purity: purity, @@ -1553,7 +1556,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, fcx.write_ty(expr.id, fty); - check_fn(fcx.ccx, None, &fn_ty, decl, body, + // We inherit the same self info as the enclosing scope, + // since the function we're checking might capture `self` + check_fn(fcx.ccx, fcx.self_info, &fn_ty, decl, body, fn_kind, Some(fcx)); } diff --git a/src/test/run-pass/issue-3563.rs b/src/test/compile-fail/issue-3563.rs similarity index 82% rename from src/test/run-pass/issue-3563.rs rename to src/test/compile-fail/issue-3563.rs index 2c390bf1372b7..9f7a0745c3b1b 100644 --- a/src/test/run-pass/issue-3563.rs +++ b/src/test/compile-fail/issue-3563.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-test trait A { fn a(&self) { - || self.b() + || self.b() //~ ERROR type `&self/self` does not implement any method in scope named `b` } } +fn main() {} diff --git a/src/test/run-pass/issue-3563-2.rs b/src/test/run-pass/issue-3563-2.rs new file mode 100644 index 0000000000000..aa4598fc90b30 --- /dev/null +++ b/src/test/run-pass/issue-3563-2.rs @@ -0,0 +1,22 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[allow(default_methods)] +trait Canvas { + fn add_point(point: &int); + fn add_points(shapes: &[int]) { + for shapes.each |pt| { + self.add_point(pt) + } + } + +} + +fn main() {} diff --git a/src/test/run-pass/issue-3563-3.rs b/src/test/run-pass/issue-3563-3.rs new file mode 100644 index 0000000000000..4517ce770ae40 --- /dev/null +++ b/src/test/run-pass/issue-3563-3.rs @@ -0,0 +1,212 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ASCII art shape renderer. +// Demonstrates traits, impls, operator overloading, non-copyable struct, unit testing. +// To run execute: rustc --test shapes.rs && ./shapes + +// Rust's core library is tightly bound to the language itself so it is automatically linked in. +// However the std library is designed to be optional (for code that must run on constrained +// environments like embedded devices or special environments like kernel code) so it must +// be explicitly linked in. +extern mod std; + +// Extern mod controls linkage. Use controls the visibility of names to modules that are +// already linked in. Using WriterUtil allows us to use the write_line method. +use io::WriterUtil; + +// Represents a position on a canvas. +struct Point +{ + x: int, + y: int, +} + +// Represents an offset on a canvas. (This has the same structure as a Point. +// but different semantics). +struct Size +{ + width: int, + height: int, +} + +struct Rect +{ + top_left: Point, + size: Size, +} + +// TODO: operators + +// Contains the information needed to do shape rendering via ASCII art. +struct AsciiArt +{ + width: uint, + height: uint, + priv fill: char, + priv lines: ~[~[mut char]], + + // This struct can be quite large so we'll disable copying: developers need + // to either pass these structs around via borrowed pointers or move them. + drop {} +} + +// It's common to define a constructor sort of function to create struct instances. +// If there is a canonical constructor it is typically named the same as the type. +// Other constructor sort of functions are typically named from_foo, from_bar, etc. +fn AsciiArt(width: uint, height: uint, fill: char) -> AsciiArt +{ + // Use an anonymous function to build a vector of vectors containing + // blank characters for each position in our canvas. + let lines = do vec::build_sized(height) + |push| + { + for height.times + { + let mut line = ~[]; + vec::grow_set(&mut line, width-1, &'.', '.'); + push(vec::to_mut(line)); + } + }; + + // Rust code often returns values by omitting the trailing semi-colon + // instead of using an explicit return statement. + AsciiArt {width: width, height: height, fill: fill, lines: lines} +} + +// Methods particular to the AsciiArt struct. +impl AsciiArt +{ + fn add_pt(x: int, y: int) + { + if x >= 0 && x < self.width as int + { + if y >= 0 && y < self.height as int + { + // Note that numeric types don't implicitly convert to each other. + let v = y as uint; + let h = x as uint; + + // Vector subscripting will normally copy the element, but &v[i] + // will return a reference which is what we need because the + // element is: + // 1) potentially large + // 2) needs to be modified + let row = &self.lines[v]; + row[h] = self.fill; + } + } + } +} + +// Allows AsciiArt to be converted to a string using the libcore ToStr trait. +// Note that the %s fmt! specifier will not call this automatically. +impl AsciiArt : ToStr +{ + pure fn to_str() -> ~str + { + // Convert each line into a string. + let lines = do self.lines.map |line| {str::from_chars(*line)}; + + // Concatenate the lines together using a new-line. + str::connect(lines, "\n") + } +} + +// This is similar to an interface in other languages: it defines a protocol which +// developers can implement for arbitrary concrete types. +#[allow(default_methods)] +trait Canvas +{ + fn add_point(shape: Point); + fn add_rect(shape: Rect); + + // Unlike interfaces traits support default implementations. + // Got an ICE as soon as I added this method. + fn add_points(shapes: &[Point]) + { + for shapes.each |pt| {self.add_point(*pt)}; + } +} + +// Here we provide an implementation of the Canvas methods for AsciiArt. +// Other implementations could also be provided (e.g. for PDF or Apple's Quartz) +// and code can use them polymorphically via the Canvas trait. +impl AsciiArt : Canvas +{ + fn add_point(shape: Point) + { + self.add_pt(shape.x, shape.y); + } + + fn add_rect(shape: Rect) + { + // Add the top and bottom lines. + for int::range(shape.top_left.x, shape.top_left.x + shape.size.width) + |x| + { + self.add_pt(x, shape.top_left.y); + self.add_pt(x, shape.top_left.y + shape.size.height - 1); + } + + // Add the left and right lines. + for int::range(shape.top_left.y, shape.top_left.y + shape.size.height) + |y| + { + self.add_pt(shape.top_left.x, y); + self.add_pt(shape.top_left.x + shape.size.width - 1, y); + } + } +} + +// Rust's unit testing framework is currently a bit under-developed so we'll use +// this little helper. +pub fn check_strs(actual: &str, expected: &str) -> bool +{ + if actual != expected + { + io::stderr().write_line(fmt!("Found:\n%s\nbut expected\n%s", actual, expected)); + return false; + } + return true; +} + + +fn test_ascii_art_ctor() +{ + let art = AsciiArt(3, 3, '*'); + assert check_strs(art.to_str(), "...\n...\n..."); +} + + +fn test_add_pt() +{ + let art = AsciiArt(3, 3, '*'); + art.add_pt(0, 0); + art.add_pt(0, -10); + art.add_pt(1, 2); + assert check_strs(art.to_str(), "*..\n...\n.*."); +} + + +fn test_shapes() +{ + let art = AsciiArt(4, 4, '*'); + art.add_rect(Rect {top_left: Point {x: 0, y: 0}, size: Size {width: 4, height: 4}}); + art.add_point(Point {x: 2, y: 2}); + assert check_strs(art.to_str(), "****\n*..*\n*.**\n****"); +} + +fn main() { + test_ascii_art_ctor(); + test_add_pt(); + test_shapes(); +} + From 485c839ffaa223116101d7ee72edf2d67f8853a3 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Mon, 7 Jan 2013 19:32:59 -0800 Subject: [PATCH 3/3] Improve a typeck ICE --- src/librustc/middle/typeck/check/method.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 1a788fd957444..f903270e15cba 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -282,8 +282,9 @@ impl LookupContext { ty_self => { // Call is of the form "self.foo()" and appears in one // of a trait's default method implementations. - let self_did = self.fcx.self_impl_def_id.expect( - ~"unexpected `none` for self_impl_def_id"); + let self_did = self.fcx.self_info.expect( + ~"self_impl_def_id is undefined (`self` may not \ + be in scope here").def_id; let substs = {self_r: None, self_ty: None, tps: ~[]}; self.push_inherent_candidates_from_self( self_ty, self_did, &substs);