about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2013-01-07 19:34:13 -0800
committerTim Chevalier <chevalier@alum.wellesley.edu>2013-01-08 13:56:26 -0800
commit80435ad429429ae0e5e9c5c131e4719306bd50fa (patch)
tree0e2a320e9b17b7a38d658b913640d93eb43948a9
parent2d1ce01a8a8ef491d0490b5627c9b21076c03c10 (diff)
Make `self` visible when typechecking nested functions
Necessary to allow supertrait methods to be called in default methods.

r=nikomatsakis
-rw-r--r--src/librustc/middle/typeck/check/method.rs5
-rw-r--r--src/librustc/middle/typeck/check/mod.rs13
-rw-r--r--src/test/compile-fail/issue-3563.rs (renamed from src/test/run-pass/issue-3563.rs)4
-rw-r--r--src/test/run-pass/issue-3563-2.rs22
-rw-r--r--src/test/run-pass/issue-3563-3.rs212
5 files changed, 248 insertions, 8 deletions
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 1a788fd9574..f903270e15c 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);
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 4eaf6311148..1d8b0a74199 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<ast::def_id>,
+
+    // Refers to whichever `self` is in scope, even this fn_ctxt is
+    // for a nested closure that captures `self`
+    self_info: Option<self_info>,
     ret_ty: ty::t,
     // Used by loop bodies that return from the outer function
     indirect_ret_ty: Option<ty::t>,
@@ -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
index 2c390bf1372..9f7a0745c3b 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 00000000000..aa4598fc90b
--- /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 <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.
+
+#[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 00000000000..4517ce770ae
--- /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 <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.
+
+// 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();
+}
+