diff --git a/HISTORY.md b/HISTORY.md index d91ba53..6fb438d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -22,6 +22,8 @@ Castile 0.5 * Requesting the AST be dumped, will also dump the AST with type assignments, if an error occurs during type checking. * Established an abstract base class for compiler backends. +* Fixed a bug where tagged values were being tagged again + during a cast from a union type to another union type. Castile 0.4 ----------- diff --git a/TODO.md b/TODO.md index ad03f89..6878dea 100644 --- a/TODO.md +++ b/TODO.md @@ -6,8 +6,10 @@ TODO call equal_tagged_value() when you find a union type when comparing structs deeply. (C backend, probably others) -There appears to be a bug with casting a union type to -itself? The tag in the tagged value is the union? +Actually the idea of deep equality of structs is suspect. +Especially if the visibility of those structs is limited. +Instead users should write their own `equal` functions +(possibly abstract, possibly only testing equivalence.) Name mangling for compilers (prepend with `_` most likely.) diff --git a/eg/assoc.castile b/eg/assoc.castile index cc994b9..ceea2a6 100644 --- a/eg/assoc.castile +++ b/eg/assoc.castile @@ -22,7 +22,7 @@ fun lookup(a: assoc, k: string) { return null as string|void } typecase n is assoc { - return lookup(n, k) + return lookup(n, k) as string|void } } diff --git a/src/castile/backends/c.py b/src/castile/backends/c.py index 8856345..3c328ad 100644 --- a/src/castile/backends/c.py +++ b/src/castile/backends/c.py @@ -348,9 +348,13 @@ def find_field(name): self.compile(ast.children[0]) self.write('->%s' % ast.value) elif ast.tag == 'TypeCast': - self.write('tag("%s",(void *)' % str(ast.children[0].type)) - self.compile(ast.children[0]) - self.write(')') + # If the LHS is not already a union type, promote it to a tagged value. + if isinstance(ast.children[0].type, Union): + self.compile(ast.children[0]) + else: + self.write('tag("%s",(void *)' % str(ast.children[0].type)) + self.compile(ast.children[0]) + self.write(')') elif ast.tag == 'TypeCase': self.write_indent('if (is_tag("%s",' % str(ast.children[1].type)) self.compile(ast.children[0]) diff --git a/src/castile/backends/javascript.py b/src/castile/backends/javascript.py index 1f86f6f..20482db 100644 --- a/src/castile/backends/javascript.py +++ b/src/castile/backends/javascript.py @@ -191,9 +191,13 @@ def compile(self, ast): self.compile(ast.children[0]) self.write('.%s' % ast.value) elif ast.tag == 'TypeCast': - self.write("['%s'," % str(ast.children[0].type)) - self.compile(ast.children[0]) - self.write(']') + # If the LHS is not already a union type, promote it to a tagged value. + if isinstance(ast.children[0].type, Union): + self.compile(ast.children[0]) + else: + self.write("['%s'," % str(ast.children[0].type)) + self.compile(ast.children[0]) + self.write(']') elif ast.tag == 'TypeCase': self.write('if (') self.compile(ast.children[0]) diff --git a/src/castile/backends/ruby.py b/src/castile/backends/ruby.py index 101a1d9..e0d8911 100644 --- a/src/castile/backends/ruby.py +++ b/src/castile/backends/ruby.py @@ -1,4 +1,5 @@ from castile.backends.base import BaseCompiler +from castile.types import Union OPS = { @@ -186,9 +187,13 @@ def compile(self, ast): self.compile(ast.children[0]) self.write('["%s"]' % ast.value) elif ast.tag == 'TypeCast': - self.write("['%s'," % str(ast.children[0].type)) - self.compile(ast.children[0]) - self.write(']') + # If the LHS is not already a union type, promote it to a tagged value. + if isinstance(ast.children[0].type, Union): + self.compile(ast.children[0]) + else: + self.write("['%s'," % str(ast.children[0].type)) + self.compile(ast.children[0]) + self.write(']') elif ast.tag == 'TypeCase': self.write_indent('if (') self.compile(ast.children[0]) diff --git a/src/castile/backends/stackmac.py b/src/castile/backends/stackmac.py index 0e1315a..07ffb25 100644 --- a/src/castile/backends/stackmac.py +++ b/src/castile/backends/stackmac.py @@ -238,12 +238,14 @@ def compile(self, ast): elif ast.tag == 'TypeCast': self.compile(ast.children[0]) t = str(ast.children[0].type) - self.out.write('; tag with "%s"\n' % t) - if self.size_of(ast.children[0].type) == 0: - # special case. there is nothing on the stack - self.out.write('push 0\n') - tag = self.get_tag(t) - self.out.write('tag %d\n' % tag) + # If the LHS is not already a union type, promote it to a tagged value. + if not isinstance(ast.children[0].type, Union): + self.out.write('; tag with "%s"\n' % t) + if self.size_of(ast.children[0].type) == 0: + # special case. there is nothing on the stack + self.out.write('push 0\n') + tag = self.get_tag(t) + self.out.write('tag %d\n' % tag) elif ast.tag == 'TypeCase': end_typecase = self.get_label('end_typecase') self.compile(ast.children[0]) diff --git a/src/castile/eval.py b/src/castile/eval.py index c7b9d02..c92d7bb 100644 --- a/src/castile/eval.py +++ b/src/castile/eval.py @@ -142,7 +142,9 @@ def eval(self, ast=None): return v elif ast.tag == 'TypeCast': v = self.eval(ast.children[0]) - return TaggedValue(typeof(v), v) + if not isinstance(v, TaggedValue): + v = TaggedValue(typeof(v), v) + return v elif ast.tag == 'TypeCase': r = self.eval(ast.children[0]) assert isinstance(r, TaggedValue)