# HG changeset patch # User Paul Boddie # Date 1394489666 -3600 # Node ID ff40924fa22a8530fd6863fde22396c0e463962b # Parent 32c9d23850b09ae55bdb809473134cd13d045354 Added some support for guard annotations, comparing concrete and deduced type information. diff -r 32c9d23850b0 -r ff40924fa22a docs/annotations.txt --- a/docs/annotations.txt Mon Mar 10 23:12:41 2014 +0100 +++ b/docs/annotations.txt Mon Mar 10 23:14:26 2014 +0100 @@ -58,6 +58,13 @@ details (for users) _values defines a name-to-value mapping for objects that may be used to access attributes +_guard_types mapping from names to ("single", "multiple", "impossible") + defines the nature of any type check upon assignment of an + object to a name/user; an impossible guard occurs when + concrete type information conflicts with deduced type + information +_guards a mapping to a set of actual guard types to which the + assigned object must comply Attribute Contributors ---------------------- diff -r 32c9d23850b0 -r ff40924fa22a micropython/deduce.py --- a/micropython/deduce.py Mon Mar 10 23:12:41 2014 +0100 +++ b/micropython/deduce.py Mon Mar 10 23:14:26 2014 +0100 @@ -214,16 +214,35 @@ self.dispatch(node.node) self.units.pop() - def _visitOptionalUnit(self, node): + visitModule = _visitUnit - "Optionally visit a unit, depending on whether it is used." + def visitClass(self, node): + + "Optionally visit a class, depending on whether it is used." if not used_by_unit(node): return self._visitUnit(node) - visitModule = _visitUnit - visitClass = visitFunction = _visitOptionalUnit + def visitFunction(self, node): + + "Optionally visit a function, depending on whether it is used." + + if not used_by_unit(node): + return + + self.units.append(node.unit) + self.dispatch(node.code) + self.units.pop() + + node._guard_types = {} + node._guards = {} + + for name in node.unit.positional_names: + # NOTE: Test for tuple parameters (see micropython.report.Report._parameters). + # NOTE: No _values usage here because it is mostly useless information, except perhaps for defaults. + attrtypes = node._attrtypes.get(name) + self._visitGuardForNameDeduced(node, name, attrtypes) def _visitAttr(self, node): @@ -481,8 +500,10 @@ elif isinstance(self.expr, TypedInstance): expr = self.expr else: + self._visitGuard(node) return else: + self._visitGuard(node) return attr = node._values and node._values.get(node.name) or None @@ -498,6 +519,48 @@ if value and isinstance(value, Instance) and not isinstance(value, TypedInstance): node._values[node.name] = expr + self._visitGuard(node) + + def _visitGuard(self, node): + node._guard_types = {} + node._guards = {} + self._visitGuardForName(node, node.name) + + def _visitGuardForName(self, node, name): + + "Make an annotation stating the acceptable types for a name." + + # Need to check any concrete value against deductions. + + attrtypes = node._attrtypes.get(name) + value = node._values.get(name) + + # Where a concrete type conflicts with deductions, the usage of + # attributes cannot be supported and is thus impossible. + + if value: + if attrtypes and value not in attrtypes: + node._guard_types[name] = "impossible" + else: + node._guard_types[name] = "single" + node._guards[name] = set([value]) + + # Where no concrete type is known, usage must be checked. + + elif attrtypes: + self._visitGuardForNameDeduced(node, name, attrtypes) + + # Where no deductions are made, no guards are needed. + + def _visitGuardForNameDeduced(self, node, name, attrtypes): + if not attrtypes: + return + if len(attrtypes) == 1: + node._guard_types[name] = "single" + else: + node._guard_types[name] = "multiple" + node._guards[name] = attrtypes + def visitCallFunc(self, node): "Identify any concrete types involved with instantiation." @@ -509,7 +572,7 @@ attr = node.node._attr - if attr: + if attr and not isinstance(attr, Instance): value = attr.get_value() if value and isinstance(value, Class): return TypedInstance(value) diff -r 32c9d23850b0 -r ff40924fa22a tests/failure/attributes_instance_assignment_external_only_indirect.py --- a/tests/failure/attributes_instance_assignment_external_only_indirect.py Mon Mar 10 23:12:41 2014 +0100 +++ b/tests/failure/attributes_instance_assignment_external_only_indirect.py Mon Mar 10 23:14:26 2014 +0100 @@ -4,11 +4,18 @@ def __init__(self, a): self.a = a +class D: + def __init__(self, a): + self.a = a + self.b = 0 + def f(obj, b): - obj.a + # Should fail when isinstance(obj, C). obj.b = b c = C(1) +# Should fail due to subsequent usage. +d = D(3) f(c, 2) result_1 = c.a result_2 = c.b