== Type Checking

In this section, we explain the rules used to assign types to
expressions. FPP is a statically typed language. That means the
following:

* Type checking of expressions occurs during
<<Analysis-and-Translation_Analysis,analysis>>.

* If the type checking phase detects a violation of any of these rules,
then translation halts with an error message and does not produce any
code.

Each type represents a collection of
<<Values,values>>.
The type checking rules exist to ensure that whenever an expression of
type stem:[T] is <<Evaluation_Evaluating-Expressions,evaluated>>,
the result is a value of type stem:[T].

=== Integer Literals

To type an
<<Expressions_Integer-Literals,integer
literal expression>>, the semantic analyzer does the following:

. Evaluate the expression to an unsigned integer value stem:[v].

. Assign the type <<Types_Internal-Types_Integer,_Integer_>> to the expression.

**Examples:**

[source,fpp]
----
constant a = 0 # Type is Integer
constant b = 1 # Type is Integer
constant c = 256 # Type is Integer
constant d = 65536 # Type is Integer
constant e = 0x100000000 # Type is Integer
----

=== Floating-Point Literals

The type of a
<<Expressions_Floating-Point-Literals,floating-point
literal expression>> is `F64`.

=== Boolean Literals

The type of a
<<Expressions_Boolean-Literals,boolean
literal expression>> is `bool`.

=== String Literals

The type of a
<<Expressions_String-Literals,string
literal expression>> is `string`.

=== Identifier Expressions

To type an <<Expressions_Identifier-Expressions,identifier
expression>> stem:[e], the semantic analyzer
<<Scoping-of-Names_Resolution-of-Identifiers,resolves the identifier to a
definition>> and uses the type given in the definition.

**Example:**

[source,fpp]
----
constant a = 42 # a is a constant with type Integer
constant b = a # The expression a refers to the constant a and has type Integer
----

=== Dot Expressions

To type a
<<Expressions_Dot-Expressions,dot
expression>> stem:[e] `.x`, the semantic analyzer does the following:

. If stem:[e]`.x` is a
<<Scoping-of-Names_Qualified-Identifiers,qualified identifier>> that represents
the use of a definition according to the
<<Scoping-of-Names_Resolution-of-Qualified-Identifiers,rules
for resolving qualified identifiers>>, and the use is a valid
<<Expressions_Dot-Expressions,dot
expression>>, then use the type given in the definition.

. Otherwise do the following:

.. Compute the type stem:[T] of stem:[e].

.. If stem:[T] is a <<Types_Struct-Types,struct type>>
or <<Types_Internal-Types_Anonymous-Struct-Types,anonymous struct type>>
with a member `x`, then use the type associated with `x`.

.. Otherwise throw an error.

*Example:*

[source,fpp]
----
module M {
  constant a = 0 # The constant M.a has type Integer
  constant b = {
    c = "string1"
    d = "string2"
  }
}
constant b = M.a # Expression M.a represents a use of the definition M.a.
                 # Its type is Integer.

constant c = M.b.c # Expression M.b.c uses M.b and selects member `c`.
                   # Its type is string.
----

=== Array Expressions

To type an
<<Expressions_Array-Expressions,array expression>>
`[` stem:[e_1] `,` stem:[...] `,` stem:[e_n] `]`,
the semantic analyzer does the following:

. For each stem:[i in [1,n]], compute the type stem:[T_i] of stem:[e_i].

. Compute the <<Type-Checking_Computing-a-Common-Type_Lists-of-Types,common type>>
stem:[T] of the list of types stem:[T_i].

. Assign the type _[_ stem:[n] _]_ stem:[T] to the expression.

*Examples:*

[source,fpp]
----
constant a = [ 1, 2, 3 ] # Type is [3] Integer
constant b = [ 1, 2, 3.0 ] # Type is [3] F64
----

=== Array Subscript Expressions

To type an
<<Expressions_Array-Subscript-Expressions,array subscript expression>>
stem:[e_1] `[` stem:[e_2] `]`, the semantic analyzer does the following:

. Compute the type stem:[T_1] of stem:[e_1].

. Check that stem:[T_1] is an <<Types_Array-Types,array type>> or
<<Types_Internal-Types_Anonymous-Array-Types,anonymous array type>>
with element type stem:[T] and size stem:[n].

. Compute the type stem:[T_2] of stem:[e_2].

. Check that stem:[T_2] <<Type-Checking_Type-Conversion,may be converted to>>
_Integer_.

. Use stem:[T] as the type of the expression.

=== Struct Expressions

To type a
<<Expressions_Struct-Expressions,struct expression>>
`{` stem:[m_1] `=` stem:[e_1] `,` stem:[...] `,` stem:[m_n] `=` stem:[e_n] `}`,
the semantic analyzer does the following:

. Check that the member names stem:[m_i] are distinct.

. For each stem:[i in [1,n]], compute the type stem:[T_i] of stem:[e_i].

. Assign the type _{_ stem:[m_1] _=_ stem:[T_1] _,_ stem:[...] _,_ stem:[m_n] _=_ stem:[T_n] _}_
to the expression.

*Examples:*

[source,fpp]
----
constant a = { x = 1, y = 2.0 } # Type is { x: Integer, y: F64 }
constant b = { x = 1, x = 2 } # Error
----


=== Unary Negation Expressions

To type a
<<Expressions_Arithmetic-Expressions,unary
negation expression>> `-` stem:[e], the semantic analyzer does the following:

. Compute the type stem:[T] of stem:[e].

. If stem:[T] is a <<Types_Internal-Types_Numeric-Types,numeric type>>, then use stem:[T].

. Otherwise if stem:[T] <<Type-Checking_Type-Conversion,may be converted to>>
_Integer_, then use _Integer_.

. Otherwise throw an error.

**Examples:**

[source,fpp]
----
constant a = -1.0 # Type is F64
constant b = -1 # Type is Integer
constant c = -c # Type is Integer
constant d = -0xFFFFFFFF # Type is Integer
enum E { X = 1 }
constant e = -E.X # Type is Integer
constant f = -true # Error
----

=== Binary Arithmetic Expressions

To type a
<<Expressions_Arithmetic-Expressions,binary
arithmetic expression>> stem:[e_1] _op_ stem:[e_2], the semantic analyzer does
the following:

. Compute the common type stem:[T] of stem:[e_1] and stem:[e_2].

. If stem:[T] is a <<Types_Internal-Types_Numeric-Types,numeric type>>, then use stem:[T].

. Otherwise if stem:[T] <<Type-Checking_Type-Conversion,may be converted to>>
_Integer_, then use _Integer_.

. Otherwise throw an error.

**Examples:**

[source,fpp]
----
constant a = 1 + 2 # Type is Integer
constant b = 3 + 4 # Type is Integer
constant c = -0xFFFFFFFF # Type is Integer
enum E { X = 1, Y = 2 }
constant d = X + Y # Type is Integer
constant e = true + "abcd" # Error
----

=== Parenthesis Expressions

To type a
<<Expressions_Parenthesis-Expressions,parenthesis
expression>> `(` stem:[e] `)`, the semantic analyzer does the following:

. Compute the type stem:[T] of stem:[e].

. Use stem:[T] as the type of the expression.

**Examples:**

[source,fpp]
----
constant a = (1.0 + 2) # Type is F64
constant b = (3 + 4) # Type is Integer
constant c = (true) # Type is bool
constant d = ("abcd") # Type is string
constant e = ([ 1, 2, 3]) # Type is [3] Integer
----

=== Identical Types

We say that types stem:[T_1] and stem:[T_2] are *identical* if one
of the following holds:

. stem:[T_1] and stem:[T_2] are
<<Types_Internal-Types_Numeric-Types,numeric types>>
with the same name, e.g., `U32` or _Integer_.
In each case of a numeric type, the name of the type uniquely identifies
the type.

. stem:[T_1] and stem:[T_2]
are both
<<Types_The-Boolean-Type,the Boolean type>>.

. stem:[T_1] and stem:[T_2]
are both  the same
<<Types_String-Types,string type>>.

. Each of stem:[T_1] and stem:[T_2]
is an
<<Types_Abstract-Types,abstract type>>,
<<Types_Alias-Types,alias type>>,
<<Types_Array-Types,array type>>,
<<Types_Enum-Types,enum type>>, or
<<Types_Struct-Types,struct type>>,
and both types refer to the same definition.

=== Type Conversion

We say that a type stem:[T_1] *may be converted to* another type stem:[T_2] if
every <<Values,value>> represented by type stem:[T_1] can be
<<Evaluation_Type-Conversion,converted>> into a value of type stem:[T_2].

Here are the rules for type conversion:

. stem:[T_1] may be converted to stem:[T_2] if stem:[T_1] and stem:[T_2]
are <<Type-Checking_Identical-Types,identical types>>.

. If either stem:[T_1] or stem:[T_2] or both is an
<<Types_Alias-Types,alias type>>, then stem:[T_1]
may be converted to stem:[T_2] if the <<Types_Underlying-Types,underlying
type>> of stem:[T_1] may be converted to
the <<Types_Underlying-Types,underlying type>> of stem:[T_2].

. Any <<Types_String-Types,string type>> may be converted
to any other string type.

. Any <<Types_Internal-Types_Numeric-Types,numeric
type>> may be converted to any other numeric type.

. An <<Types_Enum-Types,enum type>> may be converted to a
<<Types_Internal-Types_Numeric-Types,numeric type>>.

. If stem:[T_1] or stem:[T_2] or both are
<<Types_Array-Types,array types>>, then
stem:[T_1] may be converted to stem:[T_2] if the conversion
is allowed after replacing the array type or types with
structurally equivalent
<<Types_Internal-Types_Anonymous-Array-Types,anonymous array types>>.

. An anonymous array type stem:[T_1 =] _[_ stem:[n] _]_ stem:[T'_1]
may be converted to the anonymous array type
stem:[T_2 =] _[_ stem:[m] _]_ stem:[T'_2]
if stem:[n = m] and stem:[T'_1] may be converted to stem:[T'_2].

. A <<Types_Internal-Types_Numeric-Types,numeric type>>,
  <<Types_The-Boolean-Type,Boolean type>>, <<Types_Enum-Types,enum type>>, or
<<Types_String-Types,string type>> stem:[T_1] may be converted to an anonymous
array type stem:[T_2] if stem:[T_1] may be converted to the member type of
stem:[T_2].

. If stem:[T_1] or stem:[T_2] or both are
<<Types_Struct-Types,struct types>>, then
stem:[T_1] may be converted to stem:[T_2] if the conversion
is allowed after replacing the struct type or types with
structurally equivalent
<<Types_Internal-Types_Anonymous-Struct-Types,anonymous struct types>>.
For purposes of structural equivalence, the member sizes of
struct types are ignored.
For example, the struct type `S` defined by `struct S { x: [3] U32 }`
is structurally equivalent to the anonymous struct type
`{ x: U32 }`.

. An anonymous struct type
stem:[T =]_{_ stem:[m_1] _:_ stem:[T_1] _,_
stem:[...] _,_ stem:[m_1] _:_ stem:[T_n] _}_ may be converted to
the anonymous struct type stem:[T'] if for each stem:[i in [1,n\]],

.. stem:[m_i] _:_ stem:[T'_i] is a member of stem:[T']; and

.. stem:[T_i] may be converted to stem:[T'_i].

. A <<Types_Internal-Types_Numeric-Types,numeric type>> type, <<Types_The-Boolean-Type,Boolean
type>>,
<<Types_Enum-Types,enum type>>, or <<Types_String-Types,string type>> stem:[T]
may be converted to an
<<Types_Internal-Types_Anonymous-Struct-Types,anonymous struct type>> stem:[T'] if
for each member stem:[m_i] `:` stem:[T_i] of stem:[T'],
stem:[T] may be converted to stem:[T_i].

. Type convertibility is transitive: if stem:[T_1] may be converted to
stem:[T_2]
and stem:[T_2] may be converted to stem:[T_3], then stem:[T_1]
may be converted to stem:[T_3].

=== Computing a Common Type

==== Pairs of Types

Here are the rules for resolving two types stem:[T_1] and stem:[T_2]
(e.g., the
types of two subexpressions) to a common type stem:[T] (e.g., the type of
the whole expression):

. If stem:[T_1] and stem:[T_2] are
<<Type-Checking_Identical-Types,identical types>>, then let
stem:[T] be stem:[T_1].

. Otherwise if stem:[T_1] or stem:[T_2] or both are <<Types_Alias-Types,alias
types>>, then do the following:

.. Let stem:[L_1] be the <<Types_Alias-Lists,alias list>> of stem:[T_1].

.. Let stem:[L_2] be the <<Types_Alias-Lists,alias list>> of stem:[T_2].

.. If there is no type in stem:[L_1] that is
<<Type-Checking_Identical-Types,identical>>
to a type in stem:[L_2], then replace each each alias type with its
<<Types_Underlying-Types,underlying type>> and reapply these rules.

.. Otherwise let stem:[T] be the first type in stem:[L_2] that is identical
to a type in stem:[L_1].

+
Note that the algorithm is symmetric in stem:[L_1] and stem:[L_2], i.e.,
reversing stem:[L_1] and stem stem:[L_2] in the last two items would
produce the same result.

. Otherwise if stem:[T_1] and stem:[T_2] are both
<<Types_Internal-Types_Numeric-Types,numeric types>>, then do the following:

.. If stem:[T_1] and stem:[T_2] are both floating-point types, then use `F64`.

.. Otherwise use <<Types_Internal-Types_Integer,_Integer_>>.

. Otherwise if stem:[T_1] and stem:[T_2] are both
<<Types_String-Types,string types>>, then use `string`.

. Otherwise if stem:[T_1] or stem:[T_2] or both are enum types, then replace
the enum type or types with the representation type specified in the enum definitions and
and reapply these rules.

. Otherwise if stem:[T_1] or stem:[T_2] or both are array types,
then replace the array type or types with structurally equivalent
anonymous array types and reapply these rules.

. Otherwise if stem:[T_1] and stem:[T_2] are anonymous array types with the same size stem:[n]
and with member types stem:[T'_1] and stem:[T'_2], then apply these rules to resolve
stem:[T'_1] and stem:[T'_2] to stem:[T'] and let stem:[T] be _[_ stem:[n] _]_ stem:[T'].

. Otherwise if one of stem:[T_1] and stem:[T_2] is a type stem:[T'] that is
convertible to an anonymous array type
and the other
one is an anonymous array type _[_ stem:[n] _]_ stem:[T''], then apply these
rules to resolve
stem:[T'] and stem:[T''] to a common type stem:[T''']. Let stem:[T] be
the
array type _[_ stem:[n] _]_ stem:[T'''].

. Otherwise if stem:[T_1] or stem:[T_2] or both are struct types,
then replace the struct type or types with structurally equivalent
anonymous struct types and reapply these rules.

. Otherwise if stem:[T_1] and stem:[T_2] are both anonymous struct types, then use
the anonymous struct type stem:[T] with the following members:

.. For each member stem:[m_1] _:_ stem:[T'_1] of stem:[T_1],
if stem:[T_2] has a member stem:[m_1] _:_ stem:[T'_2], then apply these rules
to convert stem:[T'_1] and stem:[T'_2] to a common type stem:[T'] and
use stem:[m_1] _:_ stem:[T']. Otherwise use stem:[m_1] _:_ stem:[T'_1].

.. For each member stem:[m_2] _:_ stem:[T'_2] of stem:[T_2] such
that stem:[T_1] has no member named stem:[m_2],
use stem:[m_2] _:_ stem:[T'_2].

. Otherwise if one of stem:[T_1] and stem:[T_2] is a type stem:[T'] that is
convertible to an anonymous struct type and the other
one is an anonymous struct type stem:[S], then apply these rules to resolve
stem:[T'] and each of the struct member types to a common type.
Let stem:[T] be the struct type whose member names are the member names of
stem:[S] and
whose member types are the corresponding common types.

. Otherwise the attempted resolution is invalid. Throw an error.

==== Lists of Types

To compute a common type for a list of types
stem:[T_1, ... , T_n], do the following:

.  Check that stem:[n > 0]. If not, then throw an error.

. Let stem:[T'_1] be stem:[T_1].

.  For each stem:[i in [2,n]], compute the
<<Type-Options_Computing-a-Common-Type-Option,
common type option>> stem:[T'_i] of stem:[T'_(i-1)] and stem:[T_i].

.  Use stem:[T'_n] as the common type option of the list.
