Scope and type rules for quirk20. [Revision 0] The judgment |- S must return means that the statement S is guaranteed to execute a return statement. 1 <= i <= n |- Si must return ---------------------------- |- { D1 ... Dm S1 ... Sn } must return |- S1 must return |- S2 must return --------------------------------- |- if (E) S1 else S2 must return ------------------------- |- return E; must return **************************************************************** The types needed for semantic analysis of quirk20 programs are generated by T --> VOID | NULL | BOOLEAN | CHAR | STRING | INT | DOUBLE | R | T[] | function T (T, ...) R --> <typeid> **************************************************************** An abstract data type of type environments is used below to express the typing rules for quirk20. This ADT is named Tenv, and is specified as follows. Signature: error: -> Tenv empty: -> Tenv extend: Tenv x Qsymbol x Qtype x boolean x boolean -> Tenv extendWithClass: Tenv x Qsymbol x Tenv x Tenv -> Tenv extendWithTenv: Tenv x Tenv -> Tenv lookupType: Tenv x Qsymbol -> Qtype lookupFinal: Tenv x Qsymbol -> boolean lookupGlobal: Tenv x Qsymbol -> boolean lookupStatic: Tenv x Qsymbol -> Tenv lookupDynamic: Tenv x Qsymbol -> Tenv length: Tenv -> int identifier: Tenv x int -> Qsymbol isError: Tenv -> boolean initial: -> Tenv Algebraic specification: extendWithTenv (tenv0, empty()) = tenv0 extendWithTenv (tenv0, extend (tenv1, I1, T, f, g)) = extend (extendWithTenv (tenv0, tenv1), I1, T, f, g) lookupType (empty(), I1) = VOID lookupType (extend (tenv, I1, T, f, g), I1) = T lookupType (extend (tenv, I2, T, f, g), I1) = lookupType (tenv, I1) if I1 != I2 lookupType (extendWithClass(tenv, R, tenv1, tenv2), I1) = VOID lookupFinal (empty(), I1) = true lookupFinal (extend (tenv, I1, T, f, g), I1) = f lookupFinal (extend (tenv, I2, T, f, g), I1) = lookupFinal (tenv, I1) if I1 != I2 lookupFinal (extendWithClass (tenv, R, tenv1, tenv2), I1) = true lookupGlobal (empty(), I1) = true lookupGlobal (extend (tenv, I1, T, f, g), I1) = g lookupGlobal (extend (tenv, I2, T, f, g), I1) = lookupGlobal (tenv, I1) if I1 != I2 lookupGlobal (extendWithClass (tenv, R, tenv1, tenv2), I1) = true lookupStatic (empty(), I1) = error() lookupStatic (extend (tenv, I1, T, f, g), I1) = error() lookupStatic (extend (tenv, I2, T, f, g), I1) = lookupStatic (tenv, I1) if I1 != I2 lookupStatic (extendWithClass (tenv, R1, tenv1, tenv2), R1) = tenv1 lookupStatic (extendWithClass (tenv, R2, tenv1, tenv2), R1) = lookupStatic (tenv, I1) if R1 != R2 lookupDynamic (empty(), I1) = error() lookupDynamic (extend (tenv, I1, T, f, g), I1) = error() lookupDynamic (extend (tenv, I2, T, f, g), I1) = lookupDynamic (tenv, I1) if I1 != I2 lookupDynamic (extendWithClass (tenv, R1, tenv1, tenv2), R1) = tenv2 lookupDynamic (extendWithClass (tenv, R2, tenv1, tenv2), R1) = lookupDynamic (tenv, I1) if R1 != R2 length (empty()) = 0 length (extend (tenv, I1, T, f, g)) = 1 + length (tenv) length (extendWithClass (tenv, R, tenv1, tenv2)) = 0 identifier (extend (tenv, I1, T, f, g), 0) = I1 identifier (extend (tenv, I1, T, f, g), n) = identifier (tenv, n - 1) if n > 0 isError (error()) = true isError (empty()) = false isError (extend(_, _, _, _, _)) = false isError (extendWithClass(_, _, _, _)) = false initial() is the standard initial environment that contains bindings for predefined classes and their constants and methods. This initial environment will be specified separately, because it is expected to evolve rapidly as the language develops. Notation: tenv[] means tenv. tenv[I1: T1, ...] means (extend (tenv, I1, T1, false, false))[...]. **************************************************************** The static local environment of a <classdec> C is static(C), defined below. The dynamic local environment of a <classdec> C is dynamic(C), defined below. The global environment of a quirk20 <program> P is global(P), which is defined below. The static local environment of a <classdec> C is static(C). The dynamic local environment of a <classdec> C is dynamic(C). Both static(C) and dynamic(C) are defined below. If the <program> P consists of a single <classdec> C of the form class R { ... }, then global(P) = extendWithClass (initial(), R, static(C), dynamic(C)) If the <program> P has more than one <classdec>, so that it has the form class R { ... } P2 where P2 is a program, and isError (lookupStatic (global(P2), R)) then global(P) = extendWithClass (global(P2), R, static(C), dynamic(C)) If C contains no <bodydec>, then static(C) = dynamic(C) = empty() Otherwise let D be the first <bodydec> in C, and let tenv1 and tenv2 be the static and dynamic local environments of the class obtained from C by deleting its first <bodydec> D. If D is of the form <access> constant T I1 = E1; and lookupType (tenv1, I1) = VOID, then dynamic(C) = tenv2 and static (C) = extend (tenv1, I1, T, true, true) If D is of the form <access> static T I1 = <literal> ; and lookupType (tenv1, I1) = VOID, then dynamic(C) = tenv2 and static (C) = extend (tenv1, I1, T, false, true) If D is of the form <access> T I1; and lookupType (tenv2, I1) = VOID, then static(C) = tenv1 and dynamic (C) = extend (tenv2, I1, T, false, false) If D is of the form proc T0 I1 (T1 I1, ..., Tn In) { ... } and lookupType (tenv1, I1) = VOID, then dynamic(C) = tenv2 and static (C) = extend (tenv1, I1, function T0 (T1, ..., Tn), true, true) **************************************************************** The typing judgment |- P means that the <program> contains no scope or type errors. The typing judgment tenv |- T means that, in the type environment tenv, the <type> T contains no scope or type errors. (In practice, this means that all of the class names that are mentioned within T are bound to static and dynamic type environments within tenv.) The typing judgment tenv |- C means that, in the type environment tenv, the <classdec> C contains no scope or type errors. The typing judgment tenv |- D means that, in the type environment tenv, the <bodydec> or <localdec> D contains no scope or type errors. The typing judgment tenv |- S : T means that, in the type environment tenv, the <stm> S contains no scope or type errors and all return statements that it may execute return a value of type T. The typing judgment tenv |- E : T means that, in the type environment tenv, the expression E has type T. The subtyping judgment tenv |- E <: T mean that there exists a type T0 such that tenv |- E : T0 respectively, and T0 is a subtype of T. T0 is a subtype of T, written T0 <: T, iff one of the following is true: 1. T0 = T 2. T0 = NULL and T is a <typeid> 3. T = VOID **************************************************************** The scope and type rules of quirk20 are defined by the following inference rules. (There are no rules for when statements, unless statements, for statements, or parenthesized expressions because the parser translates those constructs into abstract syntax trees that are handled by the rules below.) Programs: for each <classdec> C of P, global(P) |- C --------------------------- |- P Types: ------------ tenv |- void --------------- tenv |- boolean ------------ tenv |- char -------------- tenv |- String ----------- tenv |- int -------------- tenv |- double tenv |- T ----------- tenv |- T[] tenv |- T0 tenv |- T1 ... tenv |- Tn ------------------------------------------- tenv |- function (from (T1, ..., Tn) to T0) false = isError (lookupStatic (tenv, R)) ---------------------------------------- tenv |- R Classes: tenv1 = extendWithTenv (tenv, lookupStatic (tenv, R)) tenv1 |- D1 ... tenv1 |- Dn ------------------------------------------------------ tenv |- class R { D1 ... Dn } Class body declarations: tenv |- T1 tenv |- E1 <: T1 -------------------------------------- tenv |- <access> constant T1 I1 = E1 ; tenv |- T1 tenv |- E1 <: T1 ------------------------------------ tenv |- <access> static T1 I1 = E1 ; tenv |- T1 ------------------------ tenv |- <access> T1 I1 ; I1 through In are distinct tenv |- T0 tenv |- T1 ... tenv |- Tn tenv1 = tenv [I1 : T1, ..., In : Tn] tenv1 |- S : VOID --------------------------------------------------- tenv |- <access> proc void I0 (T1 I1, ..., Tn In) S I1 through In are distinct tenv |- T0 tenv |- T1 ... tenv |- Tn tenv1 = tenv [I1 : T1, ..., In : Tn] |- S must return tenv1 |- S : T0 ------------------------------------------------- tenv |- <access> proc T0 I0 (T1 I1, ..., Tn In) S Blocks: --------------- tenv |- { } : T tenv |- S1 : T ... tenv |- Sn : T ---------------------------- tenv |- { S1 S2 ... Sn } : T tenv |- T1 tenv |- E1 <: T1 tenv1 = tenv [ I1 : T1 ] tenv1 |- { ... S1 ... Sn } : T ----------------------------------------- tenv |- { T1 I1 = E1; ... S1 ... Sn } : T I1 through In are distinct tenv |- T0 tenv |- T1 ... tenv |- Tn tenv1 = extend (tenv, I0, function VOID (T1, ..., Tn), true, false) tenv2 = tenv1 [ I1 : T1, ..., In : Tn ] tenv2 |- S : VOID tenv1 |- { ... S1 ... Sn } : T ---------------------------------------------------------------- tenv |- { proc void I0 (T1 I1, ..., Tn In) S ... S1 ... Sn } : T I1 through In are distinct |- S must return tenv |- T0 tenv |- T1 ... tenv |- Tn tenv1 = extend (tenv, I0, function T0 (T1, ..., Tn), true, false) tenv2 = tenv1 [ I1 : T1, ..., In : Tn ] tenv2 |- S : T0 |- S must return tenv1 |- { ... S1 ... Sn } : T -------------------------------------------------------------- tenv |- { proc T0 I0 (T1 I1, ..., Tn In) S ... S1 ... Sn } : T Other statements: ------------- tenv |- ; : T tenv |- E : T1 -------------- tenv |- E; : T tenv |- E : BOOLEAN tenv |- S1 : T tenv |- S2 : T ----------------------------- tenv |- if (E) S1 else S2 : T tenv |- E : BOOLEAN tenv |- S1 : T ------------------------ tenv |- while (E) S1 : T tenv |- E <: T --------------------- tenv |- return E; : T Expressions: false = lookupFinal (tenv, I1) tenv |- I1 : T1 tenv |- E1 <: T1 ------------------------------ tenv |- I1 = E1 : VOID tenv |- R tenv0 = lookupStatic (tenv, R) false = lookupFinal (tenv0, I1) tenv0 |- I1 : T1 tenv |- E1 <: T1 ------------------------------- tenv |- R.I1 = E1 : VOID tenv |- E0 : R tenv0 = lookupDynamic (tenv, R) false = lookupFinal (tenv0, I1) tenv0 |- I1 : T1 tenv |- E1 <: T1 -------------------------------- tenv |- E0.I1 = E1 : VOID tenv |- E1 : T1[] tenv |- E2 : int tenv |- E3 <: T1 ----------------------------- tenv |- E1 [ E2 ] = E3 : VOID B is || or && tenv |- E1 : BOOLEAN tenv |- E2 : BOOLEAN ------------------------- tenv |- E1 B E2 : BOOLEAN B is one of the following binary operations: == != tenv |- E1 : BOOLEAN tenv |- E2 : BOOLEAN -------------------------------------------- tenv |- E1 B E2 : BOOLEAN B is one of the following binary operations: == != tenv |- E1 : CHAR tenv |- E2 : CHAR -------------------------------------------- tenv |- E1 B E2 : BOOLEAN B is one of the following binary operations: == != < <= > >= tenv |- E1 : STRING tenv |- E2 : STRING -------------------------------------------- tenv |- E1 B E2 : BOOLEAN B is one of the following binary operations: == != < <= > >= tenv |- E1 : INT tenv |- E2 : INT -------------------------------------------- tenv |- E1 B E2 : BOOLEAN B is one of the following binary operations: == != < <= > >= tenv |- E1 : DOUBLE tenv |- E2 : DOUBLE -------------------------------------------- tenv |- E1 B E2 : BOOLEAN B is one of the following binary operations: == != T is a <typeid> or the NULL type tenv |- E1 <: T tenv |- E2 <: T -------------------------------------------- tenv |- E1 B E2 : BOOLEAN B is one of the following binary operations: + tenv |- E1 : STRING tenv |- E2 : STRING -------------------------------------------- tenv |- E1 B E2 : STRING B is one of the following binary operations: + - * / tenv |- E1 : INT tenv |- E2 : INT -------------------------------------------- tenv |- E1 B E2 : INT B is one of the following binary operations: + - * / tenv |- E1 : DOUBLE tenv |- E2 : DOUBLE ------------------------ tenv |- E1 B E2 : DOUBLE tenv |- E1 : BOOLEAN ---------------------- tenv |- ! E1 : BOOLEAN tenv |- E1 : INT ------------------ tenv |- - E1 : INT tenv |- E1 : DOUBLE --------------------- tenv |- - E1 : DOUBLE ------------------- tenv |- null : NULL ---------------------- tenv |- true : BOOLEAN ----------------------- tenv |- false : BOOLEAN L is a charliteral ------------------ tenv |- L : CHAR L is a Stringliteral -------------------- tenv |- L : STRING L is an intliteral ------------------ tenv |- L : INT L is a floatliteral ------------------- tenv |- L : DOUBLE tenv |- R -------------------- tenv |- new R () : R tenv |- T tenv |- E : INT ------------------------- tenv |- new T [ E ] : T[] tenv |- R tenv0 = lookupStatic (tenv, R) tenv0 |- I1 : T1 ------------------------------ tenv |- R.I1 : T1 tenv |- E0 : function T0 (T1, ..., Tn) tenv |- E1 <: T1 ... tenv |- En <: Tn -------------------------------------- tenv |- E0 (E1, ..., En) : T0 T1 = lookupType (tenv, I1) VOID != T1 -------------------------- tenv |- I1 : T1 tenv |- E : R tenv0 = lookupDynamic (tenv, R) tenv0 |- I1 : T1 ------------------------------- tenv |- E.I1 : T1 tenv |- E1 : T[] tenv |- E2 : INT --------------------- tenv |- E1 [ E2 ] : T