munificent / craftinginterpreters

Repository for the book "Crafting Interpreters"
http://www.craftinginterpreters.com/
Other
8.84k stars 1.04k forks source link

Your Java Resolver Allow Returning from a Compiler #498

Closed ChayimFriedman2 closed 4 years ago

ChayimFriedman2 commented 4 years ago

When displaying the idea of constructors in lox (Chapter 12, "Classes", section "Constructors and Initializers"), you said you don't want to allow returning with value from compiler. So you added the following check to the Resolver:

public Void visitReturnStmt(Stmt.Return stmt) {
    if (stmt.value != null) {
        if (currentFunction == FunctionType.INITIALIZER) {
            Lox.error(...);
        }
        // ...
    }
}

But this code has a problem. Imagine the following situation:

class C {
    init() {
        // ...
        return nil;
    }
}

nil is translated to Java's null, so theif won't be executed.

But, although you may claim this is OK, it has several problems:

The solution I implemented is: add a field, I named it hasValue, to the class Stmt.Return (change AstGenerator.java). Then, in the parser, set it to true only when the statement has a value. All you've left is to check in the resolver as follows:

public Void visitReturnStmt(Stmt.Return stmt) {
    if (stmt.value != null) {
        // ...
    }

    if (stmt.hasValue && currentFunction == FunctionType.INITIALIZER) {
        Lox.error(...);
    }
}
munificent commented 4 years ago

I think you are confusing the runtime representation of values and the compile-time ASTs.

The resolver works with ASTs, not runtime values. A null value for stmt.value does not mean the runtime value nil, it means the return statement value expression is null instead of having an AST. That means we know there is no return value at all, and an error is shown. All of this happens before any code is run. If you run your example program in jlox, it correctly prints:

[line 3] Error at 'return': Cannot return a value from an initializer.