import edu.rice.cs.mint.runtime.*;

public class ScopeExtrusion {
    public static separable int foo() { return 123; }
    
    public static class CodeContainer {
        public Code<Integer> x;
    }
    public static class NoCodeButNotDeclaredClass {
        public int i;
        public separable NoCodeButNotDeclaredClass(int j) { i = j; }
    }
    public static final class CodeFreeFinalClass {
        public int i;
        public separable CodeFreeFinalClass(int j) { i = j; }
    }
    public static class DeclaredCodeFreeClass implements CodeFree {
        public int i;
        public separable DeclaredCodeFreeClass(int j) { i = j; }
    }
    
    public static int gx = 0;
    public static Code<Integer> gcx = <| 0 |>;
    public static NoCodeButNotDeclaredClass gnc = new NoCodeButNotDeclaredClass(0);
    public static CodeFreeFinalClass gcffi = new CodeFreeFinalClass(0);
    public static DeclaredCodeFreeClass gdcf = new DeclaredCodeFreeClass(0);
    
    public static separable Code<Integer> assignToGx() {
        gx = foo();
        return <| gx |>;
    }
    
    public static separable Code<Integer> assignToGcx() {
        gcx = <| foo() |>; // ERROR: gcx not code-free
        return gcx;
    }
    
    public static separable Code<Integer> assignToGnc() {
        gnc = new NoCodeButNotDeclaredClass(foo()); // ERROR: gnc not declared code-free
        return <| gnc.i |>;
    }

    public static separable Code<Integer> assignToGcffi() {
        // okay: gcffi is code-free and final
        gcffi = new CodeFreeFinalClass(foo());
        return <| gcffi.i |>;
    }

    public static separable Code<Integer> assignToGdcf() {
        // okay: gdcf is code-free and implements CodeFree
        gdcf = new DeclaredCodeFreeClass(foo());
        return <| gdcf.i |>;
    }
    
    public static void main(String[] args) {
        Code<Integer> x = <| 0 |>;
        <| {
            Integer y = foo();
            Integer z = `(x = <| y |>); // ERROR: x defined outside escape
            Integer z2 = `(gcx = <| y |>); // ERROR: x defined outside escape
            // okay: x2 defined inside escape
            Integer z3 = `(let Code<Integer> x2 = <| 0 |>; x2 = <| y |>);
            // okay: gx code-free
            Integer z4 = `(assignToGx());
            Integer z5 = `(let final Code<Integer> temp = (gcx = <| 123 |>); temp); // ERROR: gcx not code-free
            Integer z6 = `(let final NoCodeButNotDeclaredClass temp = (gnc = new NoCodeButNotDeclaredClass(123)); <| 123 |>); // ERROR: gnc code-free
            // okay: gcffi code-free
            Integer z7 = `(let final CodeFreeFinalClass temp = (gcffi = new CodeFreeFinalClass(123)); <| temp.i |>);
            // okay: gdcf code-free
            Integer z8 = `(let final DeclaredCodeFreeClass temp = (gdcf = new DeclaredCodeFreeClass(123)); <| temp.i |>);
        } |>.run();
        
        CodeContainer cc = new CodeContainer();
        <| {
            Integer y = foo();
            Integer z = `(cc.x = <| y |>); // ERROR: cc defined outside escape
            // error: is this an error? cc2 is defined inside escape
            Integer z2 = `(let CodeContainer cc2 = new CodeContainer(); cc2.x = <| y |>); // BUG?
        } |>.run();

    }
}
