Skip to content

Commit ab9c0d7

Browse files
alexisthedevtheosotr
authored andcommitted
Add an extra subtyping check for when additional subtyping relations exist
1 parent 09ccae6 commit ab9c0d7

File tree

4 files changed

+47
-10
lines changed

4 files changed

+47
-10
lines changed

src/generators/generator.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1950,7 +1950,8 @@ def get_types(self,
19501950
exclude_covariants=False,
19511951
exclude_contravariants=False,
19521952
exclude_type_vars=False,
1953-
exclude_function_types=False) -> List[tp.Type]:
1953+
exclude_function_types=False,
1954+
exclude_dynamic_types=False) -> List[tp.Type]:
19541955
"""Get all available types.
19551956
19561957
Including user-defined types, built-ins, and function types.
@@ -1964,6 +1965,7 @@ def get_types(self,
19641965
exclude_contravariants: exclude contravariant type parameters.
19651966
exclude_type_vars: exclude type variables.
19661967
exclude_function_types: exclude function types.
1968+
exclude_dynamic_types: exclude dynamic types.
19671969
19681970
Returns:
19691971
A list of available types.
@@ -1993,7 +1995,9 @@ def get_types(self,
19931995
if t.name != self.bt_factory.get_array_type().name
19941996
]
19951997

1996-
dynamic = self.bt_factory.get_dynamic_types(self)
1998+
dynamic = (self.bt_factory.get_dynamic_types(self)
1999+
if not exclude_dynamic_types
2000+
else [])
19972001
if exclude_function_types:
19982002
return usr_types + builtins + dynamic
19992003
return usr_types + builtins + dynamic + self.function_types
@@ -2003,7 +2007,8 @@ def select_type(self,
20032007
exclude_arrays=False,
20042008
exclude_covariants=False,
20052009
exclude_contravariants=False,
2006-
exclude_function_types=False) -> tp.Type:
2010+
exclude_function_types=False,
2011+
exclude_dynamic_types=False) -> tp.Type:
20072012
"""Select a type from the all available types.
20082013
20092014
It will always instantiating type constructors to parameterized types.
@@ -2014,8 +2019,8 @@ def select_type(self,
20142019
exclude_arrays: exclude array types.
20152020
exclude_covariants: exclude covariant type parameters.
20162021
exclude_contravariants: exclude contravariant type parameters.
2017-
exclude_type_vars: exclude type variables.
20182022
exclude_function_types: exclude function types.
2023+
eclude_dynamic_types: exclude dynamic types.
20192024
20202025
Returns:
20212026
Returns a type.
@@ -2024,7 +2029,8 @@ def select_type(self,
20242029
exclude_arrays=exclude_arrays,
20252030
exclude_covariants=exclude_covariants,
20262031
exclude_contravariants=exclude_contravariants,
2027-
exclude_function_types=exclude_function_types)
2032+
exclude_function_types=exclude_function_types,
2033+
exclude_dynamic_types=exclude_dynamic_types)
20282034
stype = ut.random.choice(types)
20292035
if stype.is_type_constructor():
20302036
exclude_type_vars = stype.name == self.bt_factory.get_array_type().name
@@ -2033,7 +2039,8 @@ def select_type(self,
20332039
exclude_covariants=True,
20342040
exclude_contravariants=True,
20352041
exclude_type_vars=exclude_type_vars,
2036-
exclude_function_types=exclude_function_types),
2042+
exclude_function_types=exclude_function_types,
2043+
exclude_dynamic_types=exclude_dynamic_types),
20372044
enable_pecs=self.enable_pecs,
20382045
disable_variance_functions=self.disable_variance_functions,
20392046
variance_choices={}

src/ir/decorators.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def two_way_subtyping(is_subtype):
2+
def inner(self, other):
3+
return is_subtype(self, other) or other.dynamic_subtyping(self)
4+
return inner

src/ir/types.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import List, Dict, Set
66

77
from src.ir.node import Node
8+
from src.ir.decorators import two_way_subtyping
89

910

1011
class Variance(object):
@@ -66,6 +67,19 @@ def has_type_variables(self):
6667
def is_subtype(self, other: Type):
6768
raise NotImplementedError("You have to implement 'is_subtype()'")
6869

70+
def dynamic_subtyping(self, other: Type):
71+
"""
72+
Overwritten when a certain type needs
73+
two-way subtyping checks.
74+
75+
Eg. when checking if a string type is a subtype
76+
of union type 'Foo | string' we call this method
77+
as `union-type.dynamic_subtyping(string_type)`
78+
to check from the union's side.
79+
80+
"""
81+
return False
82+
6983
def is_assignable(self, other: Type):
7084
"""
7185
Checks of a value of the current type is assignable to 'other' type.
@@ -153,6 +167,7 @@ def __hash__(self):
153167
"""Hash based on the Type"""
154168
return hash(str(self.__class__))
155169

170+
@two_way_subtyping
156171
def is_subtype(self, other: Type) -> bool:
157172
return other == self or other in self.get_supertypes()
158173

@@ -219,6 +234,7 @@ def _check_supertypes(self):
219234
str(t_class[0].t_constructor) + " " + \
220235
"do not have the same types"
221236

237+
@two_way_subtyping
222238
def is_subtype(self, other: Type) -> bool:
223239
supertypes = self.get_supertypes()
224240
# Since the subtyping relation is transitive, we must also check
@@ -273,6 +289,7 @@ def get_bound_rec(self, factory):
273289
# are out of scope in the context where we use this bound.
274290
return t.to_type_variable_free(factory)
275291

292+
@two_way_subtyping
276293
def is_subtype(self, other):
277294
if not self.bound:
278295
return False
@@ -302,6 +319,7 @@ def __init__(self, bound=None, variance=Invariant):
302319
self.bound = bound
303320
self.variance = variance
304321

322+
@two_way_subtyping
305323
def is_subtype(self, other):
306324
if isinstance(other, WildCardType):
307325
if other.bound is not None:
@@ -471,6 +489,7 @@ def __hash__(self):
471489
def is_type_constructor(self):
472490
return True
473491

492+
@two_way_subtyping
474493
def is_subtype(self, other: Type):
475494
supertypes = self.get_supertypes()
476495
matched_supertype = None
@@ -695,6 +714,7 @@ def get_name(self):
695714
return "{}<{}>".format(self.name, ", ".join([t.get_name()
696715
for t in self.type_args]))
697716

717+
@two_way_subtyping
698718
def is_subtype(self, other: Type) -> bool:
699719
if super().is_subtype(other):
700720
return True

src/ir/typescript_types.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import src.ir.types as tp
55
import src.ir.ast as ast
66
import src.utils as ut
7+
from src.ir.decorators import two_way_subtyping
78

89
class TypeScriptBuiltinFactory(bt.BuiltinFactory):
910
def get_language(self):
@@ -263,6 +264,7 @@ def __init__(self, alias, name="AliasType", primitive=False):
263264
def get_type(self):
264265
return self.alias
265266

267+
@two_way_subtyping
266268
def is_subtype(self, other):
267269
if isinstance(other, AliasType):
268270
return self.alias.is_subtype(other.alias)
@@ -291,6 +293,7 @@ def __init__(self, literal, name="NumberLiteralType", primitive=False):
291293
def get_literal(self):
292294
return self.literal
293295

296+
@two_way_subtyping
294297
def is_subtype(self, other):
295298
""" A number literal type is assignable to any
296299
supertype of type 'number'.
@@ -338,6 +341,7 @@ def __init__(self, literal, name="StringLiteralType", primitive=False):
338341
def get_literal(self):
339342
return '"' + self.literal + '"'
340343

344+
@two_way_subtyping
341345
def is_subtype(self, other):
342346
""" A string literal type is assignable to any
343347
supertype of type 'string'.
@@ -426,11 +430,15 @@ def __init__(self, types, name="UnionType", primitive=False):
426430
def get_types(self):
427431
return self.types
428432

433+
@two_way_subtyping
429434
def is_subtype(self, other):
430435
if isinstance(other, UnionType):
431436
return set(self.types).issubset(other.types)
432437
return other.name == 'Object'
433438

439+
def dynamic_subtyping(self, other):
440+
return other in set(self.types)
441+
434442
def get_name(self):
435443
return self.name
436444

@@ -448,7 +456,6 @@ def __init__(self, max_ut, max_in_union):
448456
self.max_ut = max_ut
449457
self.unions = []
450458
self.candidates = [
451-
ObjectType(),
452459
NumberType(),
453460
BooleanType(),
454461
StringType(),
@@ -462,11 +469,10 @@ def get_number_of_types(self):
462469
return ut.random.integer(2, self.max_in_union)
463470

464471
def gen_union_type(self, gen):
465-
""" Generates a union type that consists of
466-
N types (where N is num_of_types).
472+
""" Generates a union type that consists of N types
473+
where N is a number in [2, self.max_in_union].
467474
468475
Args:
469-
num_of_types - Number of types to be unionized
470476
gen - Instance of Hephaestus' generator
471477
472478
"""

0 commit comments

Comments
 (0)