fixed variable freshness bug

This commit is contained in:
pca006132 2020-12-23 16:05:45 +08:00 committed by pca006132
parent c0a44756e3
commit 4577d0cc12
3 changed files with 21 additions and 7 deletions

View File

@ -52,7 +52,7 @@ doing unification.
Consider the following example: Consider the following example:
```py ```python
X = TypeVar('X') X = TypeVar('X')
def head(a: list[X]) -> X: def head(a: list[X]) -> X:
@ -68,7 +68,7 @@ algorithm tries to fit `(list[int32])` into `(list[X])`, giving a substitution
Substitution can also substitute variables into another variable. Consider the Substitution can also substitute variables into another variable. Consider the
following example: following example:
``` ```python
X = TypeVar('X') X = TypeVar('X')
Y = TypeVar('Y', int32, int64) Y = TypeVar('Y', int32, int64)
@ -89,7 +89,7 @@ So the function is well typed.
Note that variables are fresh in every invocation. Consider the following Note that variables are fresh in every invocation. Consider the following
example: example:
``` ```python
I = TypeVar('I', int32, list[int32]) I = TypeVar('I', int32, list[int32])
def add(a: int32, b: I) -> int32: def add(a: int32, b: I) -> int32:
@ -102,7 +102,7 @@ def add(a: int32, b: I) -> int32:
add(1, [1, 2, 3]) add(1, [1, 2, 3])
``` ```
This one should type check (bug now). `I -> list[int32]` only affects 1 call, This one should type check. `I -> list[int32]` only affects 1 call,
and the recursion inside could substitute `I -> int32`. and the recursion inside could substitute `I -> int32`.
## Variable Scoping ## Variable Scoping

View File

@ -14,3 +14,14 @@ class Vec:
return Vec([self.v[i] + other.v[i] for i in range(len(self.v))]) return Vec([self.v[i] + other.v[i] for i in range(len(self.v))])
T = TypeVar('T', int32, list[int32])
def add(a: int32, b: T) -> int32:
if type(b) == int32:
return a + b
else:
for x in b:
a = add(a, x)
return a

View File

@ -1,5 +1,6 @@
import ast import ast
import sys import sys
import copy
from helper import CustomError from helper import CustomError
from type_def import SelfType, ClassType from type_def import SelfType, ClassType
from parse_stmt import parse_stmts from parse_stmt import parse_stmts
@ -24,9 +25,11 @@ try:
for c, name, fn in fns: for c, name, fn in fns:
if c is None: if c is None:
params, result, _ = ctx.functions[name] params, result, var = ctx.functions[name]
else: else:
params, result, _ = ctx.types[c].methods[name] params, result, var = ctx.types[c].methods[name]
# create substitution for type variables
subst = {k: copy.deepcopy(ctx.variables[k]) for k in var}
# check if fully annotated all params # check if fully annotated all params
sym_table = {} sym_table = {}
for n, ty in zip(fn.args.args, params): for n, ty in zip(fn.args.args, params):
@ -36,7 +39,7 @@ try:
fn) fn)
if isinstance(ty, SelfType): if isinstance(ty, SelfType):
ty = ctx.types[c] ty = ctx.types[c]
sym_table[n.arg] = ty sym_table[n.arg] = ty.subst(subst)
_, _, returned = parse_stmts(ctx, sym_table, sym_table, result, fn.body) _, _, returned = parse_stmts(ctx, sym_table, sym_table, result, fn.body)
if result is not None and not returned: if result is not None and not returned:
raise CustomError('Function may have no return value', fn) raise CustomError('Function may have no return value', fn)