Skip to content

Commit 02b41ea

Browse files
author
whitequark
committedJul 2, 2015
Add MonomorphismChecker.
1 parent 73a8f3c commit 02b41ea

File tree

9 files changed

+123
-7
lines changed

9 files changed

+123
-7
lines changed
 

Diff for: ‎artiq/compiler/asttyped.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class FunctionDefT(ast.FunctionDef, scoped):
3333
class ModuleT(ast.Module, scoped):
3434
pass
3535

36-
class ExceptHandlerT(ast.ExceptHandler, commontyped):
36+
class ExceptHandlerT(ast.ExceptHandler):
3737
_fields = ("filter", "name", "body") # rename ast.ExceptHandler.type
3838
_types = ("name_type",)
3939

Diff for: ‎artiq/compiler/module.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,33 @@
77
from . import prelude, types, transforms
88

99
class Module:
10-
def __init__(self, source_buffer, engine=diagnostic.Engine(all_errors_are_fatal=True)):
10+
def __init__(self, source_buffer, engine=None):
11+
if engine is None:
12+
engine = diagnostic.Engine(all_errors_are_fatal=True)
13+
1114
asttyped_rewriter = transforms.ASTTypedRewriter(engine=engine)
1215
inferencer = transforms.Inferencer(engine=engine)
1316
int_monomorphizer = transforms.IntMonomorphizer(engine=engine)
17+
monomorphism_checker = transforms.MonomorphismChecker(engine=engine)
1418

1519
parsetree, comments = parse_buffer(source_buffer, engine=engine)
1620
typedtree = asttyped_rewriter.visit(parsetree)
1721
inferencer.visit(typedtree)
1822
int_monomorphizer.visit(typedtree)
1923
inferencer.visit(typedtree)
24+
monomorphism_checker.visit(typedtree)
2025

2126
self.name = os.path.basename(source_buffer.name)
2227
self.globals = asttyped_rewriter.globals
2328

2429
@classmethod
25-
def from_string(klass, source_string, name="input.py", first_line=1):
26-
return klass(source.Buffer(source_string + "\n", name, first_line))
30+
def from_string(klass, source_string, name="input.py", first_line=1, engine=None):
31+
return klass(source.Buffer(source_string + "\n", name, first_line), engine=engine)
Has a conversation. Original line has a conversation.
2732

2833
@classmethod
29-
def from_filename(klass, filename):
34+
def from_filename(klass, filename, engine=None):
3035
with open(filename) as f:
31-
return klass(source.Buffer(f.read(), filename, 1))
36+
return klass(source.Buffer(f.read(), filename, 1), engine=engine)
3237

3338
def __repr__(self):
3439
printer = types.TypePrinter()

Diff for: ‎artiq/compiler/testbench/inferencer.py

-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,5 @@ def process_diagnostic(diag):
7070
printer.rewriter.remove(comment.loc)
7171
print(printer.rewrite().source)
7272

73-
7473
if __name__ == "__main__":
7574
main()

Diff for: ‎artiq/compiler/testbench/module.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import sys, fileinput
2+
from pythonparser import diagnostic
3+
from .. import Module
4+
5+
def main():
6+
if len(sys.argv) > 1 and sys.argv[1] == '+diag':
Has a conversation. Original line has a conversation.
7+
del sys.argv[1]
8+
def process_diagnostic(diag):
9+
print("\n".join(diag.render(only_line=True)))
10+
if diag.level == 'fatal':
11+
exit()
12+
else:
13+
def process_diagnostic(diag):
14+
print("\n".join(diag.render()))
15+
if diag.level in ('fatal', 'error'):
16+
exit(1)
17+
18+
engine = diagnostic.Engine()
19+
engine.process = process_diagnostic
20+
21+
mod = Module.from_string("".join(fileinput.input()).expandtabs(), engine=engine)
22+
print(repr(mod))
23+
24+
if __name__ == "__main__":
25+
main()

Diff for: ‎artiq/compiler/transforms/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .asttyped_rewriter import ASTTypedRewriter
22
from .inferencer import Inferencer
33
from .int_monomorphizer import IntMonomorphizer
4+
from .monomorphism_checker import MonomorphismChecker

Diff for: ‎artiq/compiler/transforms/monomorphism_checker.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
:class:`MonomorphismChecker` verifies that all type variables have been
3+
elided, which is necessary for code generation.
4+
"""
5+
6+
from pythonparser import algorithm, diagnostic
7+
from .. import asttyped, types, builtins
8+
9+
class MonomorphismChecker(algorithm.Visitor):
10+
def __init__(self, engine):
11+
self.engine = engine
12+
13+
def visit_FunctionDefT(self, node):
14+
super().generic_visit(node)
15+
16+
return_type = node.signature_type.find().ret
17+
if types.is_polymorphic(return_type):
18+
note = diagnostic.Diagnostic("note",
19+
"the function has return type {type}",
20+
{"type": types.TypePrinter().name(return_type)},
21+
node.name_loc)
22+
diag = diagnostic.Diagnostic("error",
23+
"the return type of this function cannot be fully inferred", {},
24+
node.name_loc, notes=[note])
25+
self.engine.process(diag)
26+
27+
def generic_visit(self, node):
28+
super().generic_visit(node)
29+
30+
if isinstance(node, asttyped.commontyped):
31+
if types.is_polymorphic(node.type):
32+
note = diagnostic.Diagnostic("note",
33+
"the expression has type {type}",
34+
{"type": types.TypePrinter().name(node.type)},
35+
node.loc)
36+
diag = diagnostic.Diagnostic("error",
37+
"the type of this expression cannot be fully inferred", {},
38+
node.loc, notes=[note])
39+
self.engine.process(diag)

Diff for: ‎artiq/compiler/types.py

+33
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ def unify(self, other):
5656
else:
5757
self.find().unify(other)
5858

59+
def fold(self, accum, fn):
60+
if self.parent is self:
61+
return fn(accum, self)
62+
else:
63+
return self.find().fold(accum, fn)
64+
5965
def __repr__(self):
6066
if self.parent is self:
6167
return "<py2llvm.types.TVar %d>" % id(self)
@@ -92,6 +98,11 @@ def unify(self, other):
9298
else:
9399
raise UnificationError(self, other)
94100

101+
def fold(self, accum, fn):
102+
for param in self.params:
103+
accum = self.params[param].fold(accum, fn)
104+
return fn(accum, self)
105+
95106
def __repr__(self):
96107
return "py2llvm.types.TMono(%s, %s)" % (repr(self.name), repr(self.params))
97108

@@ -131,6 +142,11 @@ def unify(self, other):
131142
else:
132143
raise UnificationError(self, other)
133144

145+
def fold(self, accum, fn):
146+
for elt in self.elts:
147+
accum = elt.fold(accum, fn)
148+
return fn(accum, self)
149+
134150
def __repr__(self):
135151
return "py2llvm.types.TTuple(%s)" % repr(self.elts)
136152

@@ -177,6 +193,14 @@ def unify(self, other):
177193
else:
178194
raise UnificationError(self, other)
179195

196+
def fold(self, accum, fn):
197+
for arg in self.args:
198+
accum = arg.fold(accum, fn)
199+
for optarg in self.optargs:
200+
accum = self.optargs[optarg].fold(accum, fn)
201+
accum = self.ret.fold(accum, fn)
202+
return fn(accum, self)
203+
180204
def __repr__(self):
181205
return "py2llvm.types.TFunction(%s, %s, %s)" % \
182206
(repr(self.args), repr(self.optargs), repr(self.ret))
@@ -208,6 +232,9 @@ def unify(self, other):
208232
if self != other:
209233
raise UnificationError(self, other)
210234

235+
def fold(self, accum, fn):
236+
return fn(accum, self)
237+
211238
def __repr__(self):
212239
return "py2llvm.types.TBuiltin(%s)" % repr(self.name)
213240

@@ -258,6 +285,9 @@ def unify(self, other):
258285
elif self != other:
259286
raise UnificationError(self, other)
260287

288+
def fold(self, accum, fn):
289+
return fn(accum, self)
290+
261291
def __repr__(self):
262292
return "py2llvm.types.TValue(%s)" % repr(self.value)
Has a conversation. Original line has a conversation.
263293

@@ -281,6 +311,9 @@ def is_mono(typ, name=None, **params):
281311
return isinstance(typ, TMono) and \
282312
(name is None or (typ.name == name and params_match))
283313

314+
def is_polymorphic(typ):
315+
return typ.fold(False, lambda accum, typ: accum or is_var(typ))
316+
284317
def is_tuple(typ, elts=None):
285318
typ = typ.find()
286319
if elts:

Diff for: ‎lit-test/compiler/monomorphism/error_notmono.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# RUN: %python -m artiq.compiler.testbench.module +diag %s >%t
2+
# RUN: OutputCheck %s --file-to-check=%t
3+
4+
# CHECK-L: ${LINE:+1}: error: the type of this expression cannot be fully inferred
5+
x = int(1)
6+
7+
# CHECK-L: ${LINE:+1}: error: the return type of this function cannot be fully inferred
8+
def fn():
9+
return int(1)

Diff for: ‎lit-test/compiler/monomorphism/integers.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# RUN: %python -m artiq.compiler.testbench.module %s >%t
2+
# RUN: OutputCheck %s --file-to-check=%t
3+
4+
x = 1
5+
# CHECK-L: x: int(width=32)

0 commit comments

Comments
 (0)
Please sign in to comment.