Fix ObjectType mangled names: use type modifiers
Fix TypeChecker::findCommonSuperType() wildcards

This method tries to generate wildcard type arguments in order to find
the lowest common super-type possible for a NamedType.
When it finds two NamedTypes for the same TypeDefinition which have
different type arguments, it will combine the type arguments into a
single wildcard type argument instead of falling back to the object
type.
The generated wildcard will be of the form '? extends X' where X is the
common super-type of all of the type arguments for this type parameter,
or it will default to just '?' if there is no common super-type.
Since it uses findCommonSuperType() to find X, it can choose X=double if
the type arguments are for example uint and double.
The problem with this is that at runtime, a conversion from Foo<uint> to
Foo<? extends double> will fail, because the runtime type checker checks
that the wildcard matches before allowing the conversion, and it has
slightly different rules about conversion, which say that primitive
types cannot be converted between each other.
(The reason for this is that for conversions between primitive types to
be possible, the source and destination types must both be known at
compile time.)
This mismatch in rules means that there was previously no way to detect
this type of scenario at compile time. Therefore, a new method was added
to Type called canRuntimeAssign(), which is like canAssign() but with
the runtime's rules.
In findCommonSuperType(), we now explicitly try to detect this scenario
when generating wildcard super-types, and if a canRuntimeAssign() check
fails, we just use '?' as the super-type.
Make Disambiguator's comment make sense
Improve test debug output for "Out:" line failures
Add builtin equals(); change ==, !=; add ===, !==

The equals() method is a builtin which exists on every object and value.
There are several implementations, which depend on the object's type:
boolean: true iff passed a boolean which is equivalent to this
integer: true iff passed an equivalent integer (of any integer type)
         Each integer's equals() checks for all other types, so e.g.
         short.equals(ulong) will be true if they are the same value
         under ===.
floating: true iff passed an equivalent float or double
          As with integers, both types' equals() methods check for both
          types, so float.equals(double) can return true.
object: true iff passed an object with an identical address (i.e. ===)
array: true iff passed an array with the same RTTI, the same length, and
       all of the elements compare equal with ==
function: true iff passed a function with the same RTTI, which compares
          equal with ==
tuple: true iff passed a tuple with the same RTTI, with elements that
       compare equal with ==

The == and != operators call the equals() method after performing any
necessary null checks. It is up to the implementation which way around
to call it, so:
x == y
may come out as:
x.equals(y)
or:
y.equals(x)
(though, it is still guaranteed that x will be evaluated before y)
This allows the compiler to optimise null checks like:
x == null
into pure value comparisons. So rather than calling:
x.equals(null)
it can do:
null.equals(x)
which is equivalent to:
null === x

The === and !== operators just compare the pure values provided, so for
objects, arrays, and functions the pointers will be compared, while for
tuples and compound types, the members will be compared recursively with
===.
For primitives, == and === both do the same thing, which is to compare
the actual values - not just the bit patterns, so:
(cast<long> -1) ==  (cast<ulong> 255)
(cast<long> -1) === (cast<ulong> 255)
are both false.

This commit also adds extensive tests for the new equality operators and
methods, including one for an unintuitive edge case with generic arrays:

Since generic arrays do not store RTTI for the type parameters they
contain, and just replace it with references to the object type, it is
impossible to distinguish two arrays of the same generic type once they
have been converted to object. So, if Foo<uint> makes an empty []T where
T=uint, and stores it as an object, and then Foo<double> comes along and
compares an empty []T where T=double with it, then the arrays will
compare as equal despite their intended types not matching.