# The ancestor type for a given superclass
meth ancestor(c: MMLocalClass): MMType
do
- assert _ancestors != null
- if _ancestors.has_key(c) then
- return _ancestors[c].stype
- end
- return null
+ return _ancestors[c].stype
end
end
# Return a type approximation if the reveiver is not self
# Useful for virtual types
meth not_for_self: MMType do return self
+
+ # The nullable version of self (if needed)
+ attr _as_nullable_cache: MMType = null
+
+ # IS the type can accept null?
+ meth is_nullable: Bool do return false
+
+ # Return the nullable version of the type
+ # Noop if already nullable
+ meth as_nullable: MMType do
+ var cache = _as_nullable_cache
+ if cache != null then return cache
+ var res = new MMNullableType(self)
+ _as_nullable_cache = res
+ return res
+ end
+
+ # Return the not null version of the type
+ # Noop if already not null
+ meth as_notnull: MMType do return self
+end
+
+class MMNullableType
+special MMType
+ attr _base_type: MMType
+ redef meth is_nullable: Bool do return true
+ redef meth as_notnull do return _base_type
+ redef meth as_nullable do return self
+ init(t: MMType) do _base_type = t
+
+ redef meth module do return _base_type.module
+
+ redef meth local_class do return _base_type.local_class
+
+ redef meth <(t)
+ do
+ return t isa MMNullableType and _base_type < t.as_notnull
+ end
+
+ redef meth to_s
+ do
+ return "nullable {_base_type}"
+ end
+
+ redef meth is_supertype(t)
+ do
+ return _base_type.is_supertype(t)
+ end
+
+ redef meth for_module(mod)
+ do
+ return _base_type.for_module(mod).as_nullable
+ end
+
+ redef meth adapt_to(recv)
+ do
+ return _base_type.adapt_to(recv).as_nullable
+ end
+
+ redef meth upcast_for(c)
+ do
+ return _base_type.upcast_for(c)
+ end
+
+ redef meth not_for_self
+ do
+ return _base_type.not_for_self.as_nullable
+ end
end
class MMTypeClass
class MMTypeNone
special MMType
redef readable attr _module: MMModule
- redef meth <(t) do return true
+ redef meth is_nullable: Bool do return true
+ redef meth <(t) do return t isa MMTypeNone or t isa MMNullableType
redef meth to_s do return "null"
redef meth is_supertype(t) do return false
redef meth local_class do abort
redef meth upcast_for(c) do abort
+ redef meth as_nullable do return self
+ redef meth as_notnull do abort
private init(m: MMModule) do _module = m
end
if subtype < stype then
return true
end
- #error(n, "Type error: expected {stype}'{stype.module}, got {subtype}'{subtype.module}")
- #abort
+ # Do not enforce nullable subtype rules yet
+ if subtype isa MMTypeNone or subtype.as_notnull < stype.as_notnull then
+ warning(n, "Nullable type warning: expected {stype}, got {subtype}")
+ return true
+ end
error(n, "Type error: expected {stype}, got {subtype}")
return false
end
-
+
# Check that an expression has a static type and that
# Display an error and return false if n is a statement
# Require that the static type of n is known
var name = n_id.to_symbol
var mod = v.module
var cla = v.local_class
+ var t: MMType
if cla.formal_dict.has_key(name) then
if n_types.length > 0 then
v.error(self, "Type error: formal type {name} cannot have formal parameters.")
return null
end
- var formal = cla.formal_dict[name]
- _stype_cache = formal
- return formal
+ t = cla.formal_dict[name]
+ if n_kwnullable != null then t = t.as_nullable
+ _stype_cache = t
+ return t
end
if cla.global_properties != null and cla.has_global_property_by_name(name) then
v.error(self, "Type error: formal type {name} cannot have formal parameters.")
return null
end
- var t = cla.get_type.local_class.select_virtual_type(name).stype_for(cla.get_type)
+ t = cla.get_type.local_class.select_virtual_type(name).stype_for(cla.get_type)
if t == null then
v.error(self, "Type error: circular definition in formal type {name}.")
return null
end
+ if n_kwnullable != null then t = t.as_nullable
_stype_cache = t
return t
end
for p in n_types do
tab.add(p.get_unchecked_stype(v))
end
- var t = local_class.get_instantiate_type(tab)
- _stype_cache = t
- return t
+ t = local_class.get_instantiate_type(tab)
else
- var t = local_class.get_type
- _stype_cache = t
- return t
+ t = local_class.get_type
end
+ if n_kwnullable != null then t = t.as_nullable
+ _stype_cache = t
+ return t
end
redef meth get_stype(v)