/** @flow */

// -- types ---

// type aliases are hoisted and always available

type T1 = T2;   // ok
type T2 = number;

// --- lets ---

// to be correct, we would
// - not allow forward refs to lets from value positions,
// while let is in TDZ.
// - allow forward refs to lets from type positions.
//
// currently we're too lenient about TDZ in closures -
// from value positions, we currently enforce TDZ only in-scope.
// this is unsound - a let or const may remain uninitialized when
// a lambda runs. But a simple conservative approach would prohibit
// forward references to let/consts from within lambdas entirely,
// which would be annoying. TODO

function f0() {
  var v = x * c;  // errors, let + const referenced before decl
  let x = 0;
  const c = 0;
}

function f1(b: boolean) {
  x = 10;         // error, attempt to write to let before decl
  let x = 0;
  if (b) {
    y = 10;       // error, attempt to write to let before decl
    let y = 0;
  }
}

function f2() {
  {
    var v = x * c; // errors, let + const referenced before decl
  }
  let x = 0;
  const c = 0;
}

// functions are let-scoped and hoisted
function f3() {
  var s: string = foo();          // ok, finds hoisted outer
  {
    var n: number = foo();        // ok, finds hoisted inner
    function foo() { return 0; }
  }
  var s2: string = foo();         // ok, hoisted outer not clobbered
  function foo() { return ""; }
}

// out-of-scope TDZ not enforced. sometimes right...
function f4() {
  function g() { return x + c; }  // ok, g doesn't run in TDZ
  let x = 0;
  const c = 0;
}

// ...sometimes wrong
function f5() {
  function g() { return x; }
  g();          // should error, but doesn't currently
  let x = 0;
  const c = 0;
}

// - from type positions, we currently error on forward refs to any
// value (i.e., class or function). this is a basic flaw in our
// phasing of AST traversal, and will be fixed.
//

var x1: C;       // ok
var x2: typeof C // error
var y = new C(); // error: let ref before decl from value position

class C {}

var z: C = new C(); // ok

// --- vars ---

// it's possible to annotate a var with a non-maybe type,
// but leave it uninitialized until later (as long as it's
// initialized before use)

var a: number;  // not an error per se - only if used before init

function f(n: number) { return n; }

f(a); // error: undefined ~/> number

a = 10;

f(a); // ok, a: number (not ?number)

// --- classes ---
class A extends A {} // error
