1.1 --- a/docs/annotations.txt Mon Mar 10 23:12:41 2014 +0100
1.2 +++ b/docs/annotations.txt Mon Mar 10 23:14:26 2014 +0100
1.3 @@ -58,6 +58,13 @@
1.4 details (for users)
1.5 _values defines a name-to-value mapping for objects that may be
1.6 used to access attributes
1.7 +_guard_types mapping from names to ("single", "multiple", "impossible")
1.8 + defines the nature of any type check upon assignment of an
1.9 + object to a name/user; an impossible guard occurs when
1.10 + concrete type information conflicts with deduced type
1.11 + information
1.12 +_guards a mapping to a set of actual guard types to which the
1.13 + assigned object must comply
1.14
1.15 Attribute Contributors
1.16 ----------------------
2.1 --- a/micropython/deduce.py Mon Mar 10 23:12:41 2014 +0100
2.2 +++ b/micropython/deduce.py Mon Mar 10 23:14:26 2014 +0100
2.3 @@ -214,16 +214,35 @@
2.4 self.dispatch(node.node)
2.5 self.units.pop()
2.6
2.7 - def _visitOptionalUnit(self, node):
2.8 + visitModule = _visitUnit
2.9
2.10 - "Optionally visit a unit, depending on whether it is used."
2.11 + def visitClass(self, node):
2.12 +
2.13 + "Optionally visit a class, depending on whether it is used."
2.14
2.15 if not used_by_unit(node):
2.16 return
2.17 self._visitUnit(node)
2.18
2.19 - visitModule = _visitUnit
2.20 - visitClass = visitFunction = _visitOptionalUnit
2.21 + def visitFunction(self, node):
2.22 +
2.23 + "Optionally visit a function, depending on whether it is used."
2.24 +
2.25 + if not used_by_unit(node):
2.26 + return
2.27 +
2.28 + self.units.append(node.unit)
2.29 + self.dispatch(node.code)
2.30 + self.units.pop()
2.31 +
2.32 + node._guard_types = {}
2.33 + node._guards = {}
2.34 +
2.35 + for name in node.unit.positional_names:
2.36 + # NOTE: Test for tuple parameters (see micropython.report.Report._parameters).
2.37 + # NOTE: No _values usage here because it is mostly useless information, except perhaps for defaults.
2.38 + attrtypes = node._attrtypes.get(name)
2.39 + self._visitGuardForNameDeduced(node, name, attrtypes)
2.40
2.41 def _visitAttr(self, node):
2.42
2.43 @@ -481,8 +500,10 @@
2.44 elif isinstance(self.expr, TypedInstance):
2.45 expr = self.expr
2.46 else:
2.47 + self._visitGuard(node)
2.48 return
2.49 else:
2.50 + self._visitGuard(node)
2.51 return
2.52
2.53 attr = node._values and node._values.get(node.name) or None
2.54 @@ -498,6 +519,48 @@
2.55 if value and isinstance(value, Instance) and not isinstance(value, TypedInstance):
2.56 node._values[node.name] = expr
2.57
2.58 + self._visitGuard(node)
2.59 +
2.60 + def _visitGuard(self, node):
2.61 + node._guard_types = {}
2.62 + node._guards = {}
2.63 + self._visitGuardForName(node, node.name)
2.64 +
2.65 + def _visitGuardForName(self, node, name):
2.66 +
2.67 + "Make an annotation stating the acceptable types for a name."
2.68 +
2.69 + # Need to check any concrete value against deductions.
2.70 +
2.71 + attrtypes = node._attrtypes.get(name)
2.72 + value = node._values.get(name)
2.73 +
2.74 + # Where a concrete type conflicts with deductions, the usage of
2.75 + # attributes cannot be supported and is thus impossible.
2.76 +
2.77 + if value:
2.78 + if attrtypes and value not in attrtypes:
2.79 + node._guard_types[name] = "impossible"
2.80 + else:
2.81 + node._guard_types[name] = "single"
2.82 + node._guards[name] = set([value])
2.83 +
2.84 + # Where no concrete type is known, usage must be checked.
2.85 +
2.86 + elif attrtypes:
2.87 + self._visitGuardForNameDeduced(node, name, attrtypes)
2.88 +
2.89 + # Where no deductions are made, no guards are needed.
2.90 +
2.91 + def _visitGuardForNameDeduced(self, node, name, attrtypes):
2.92 + if not attrtypes:
2.93 + return
2.94 + if len(attrtypes) == 1:
2.95 + node._guard_types[name] = "single"
2.96 + else:
2.97 + node._guard_types[name] = "multiple"
2.98 + node._guards[name] = attrtypes
2.99 +
2.100 def visitCallFunc(self, node):
2.101
2.102 "Identify any concrete types involved with instantiation."
2.103 @@ -509,7 +572,7 @@
2.104
2.105 attr = node.node._attr
2.106
2.107 - if attr:
2.108 + if attr and not isinstance(attr, Instance):
2.109 value = attr.get_value()
2.110 if value and isinstance(value, Class):
2.111 return TypedInstance(value)
3.1 --- a/tests/failure/attributes_instance_assignment_external_only_indirect.py Mon Mar 10 23:12:41 2014 +0100
3.2 +++ b/tests/failure/attributes_instance_assignment_external_only_indirect.py Mon Mar 10 23:14:26 2014 +0100
3.3 @@ -4,11 +4,18 @@
3.4 def __init__(self, a):
3.5 self.a = a
3.6
3.7 +class D:
3.8 + def __init__(self, a):
3.9 + self.a = a
3.10 + self.b = 0
3.11 +
3.12 def f(obj, b):
3.13 - obj.a
3.14 + # Should fail when isinstance(obj, C).
3.15 obj.b = b
3.16
3.17 c = C(1)
3.18 +# Should fail due to subsequent usage.
3.19 +d = D(3)
3.20 f(c, 2)
3.21 result_1 = c.a
3.22 result_2 = c.b