Implicitly-declared locals should have function scope

Bug #513638 reported by Matt Giuca
12
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Mars
Fix Released
Critical
Matt Giuca

Bug Description

Mars allows implicitly-declared locals in a limited way (through case statement pattern bindings). It will soon have full support for implicitly-declared locals (see bug #483082, bug #513633).

Currently, such things are either a) scoped for the rest of the function, from their declaration point (for assignments) or b) scoped for just the scope of their "case" statement (for case patterns). They need to be scoped for the entirety of the function, including before the point of assignment. This means they are treated as if there was an explicit declaration. There are a few subtle differences. Firstly, consider:

def foo() :: Int:
    var y :: Int
    y = x # "Use of uninitialised variable: x"
    # Now bind x = 4
    switch 4:
        case x:
            pass
    return y

The third line will currently give the error "Undefined variable: x". But x *is* defined, it's just defined later, so it should have the error message "Use of uninitialised variable: x" instead.

Less subtly, say x was already a global variable in the above example. Now the current behaviour would be to successfully compile and run -- y would be bound to the value of the global x, because the local x hasn't been declared yet. The fact that x is global for part of the function and local for another part is confusing, so we want that to be an error. If the variable is implicitly declared anywhere in the function, then it should be treated as local for the whole function. Therefore, the above should *still* generate the error "Use of uninitialised variable: x", ignoring the presence of the global variable entirely. Note that this is exactly how Python behaves as well.

This can also (critically) be used to break type safety. The following program will currently type-check successfully, and then throw an internal error at runtime, on the call to add.

def foo(x :: Int) :: Int:
    switch [1,2,3]:
        case x: # Should be a type error
            pass
    return add(x, 1)

On the other hand, this program will not compile, when it should:

def bar(x :: Int) :: Int:
    switch x:
        case y:
            y = add(y, 1)
    return y # Undefined variable: y

The variable y should be bound to Int in the case statement, and remain bound throughout.

Fixes required:
- Need to know in advance of type checking which variable names are local. It is an error to read from a local which hasn't been declared.
- If a variable is bound in a pattern which is already declared, it should be unified with the existing binding, with a potential type error.
- Static variable bindings (type bindings) should be scoped to the function, not the case statement.

Tags: language

Related branches

Revision history for this message
Matt Giuca (mgiuca) wrote :

Once this is fixed, uncomment shadowed_implicitvar_return2 in cases/compiler/inaccessible.mar. This tests other functionality, but couldn't be enabled until this bug is fixed.

Matt Giuca (mgiuca)
description: updated
Revision history for this message
Matt Giuca (mgiuca) wrote :

This means we need to do a first pass over the function to find all variables which are the target of an assignment or appear in a pattern -- store these as locals. Checking whether a variable is local/global currently is done by testing whether it is in the local table, which means it has been declared.

This will allow variables to be assigned which have not been declared, so we should no longer require variable declaration at all (bug #483082).

Assigning a variable with unknown type gives that variable a type. Any other use of a variable with unknown type is clearly a use-without-def error.

Revision history for this message
Matt Giuca (mgiuca) wrote :

Also, fixing this should fix bug #513578 -- critical failure if there is a naming conflict between a pattern binding and local variable, with a different type.

Revision history for this message
Matt Giuca (mgiuca) wrote :

Marked as a duplicate of bug #513578 (merged descriptions). Now critical.

description: updated
Changed in mars:
importance: Low → Critical
Revision history for this message
Matt Giuca (mgiuca) wrote :

Fixed in newtypes branch, r1019.

Changed in mars:
status: Triaged → Fix Committed
Revision history for this message
Matt Giuca (mgiuca) wrote :

Merged to trunk, r1030.

Matt Giuca (mgiuca)
Changed in mars:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Duplicates of this bug

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.