1.1 --- a/docs/annotations.txt Mon Mar 10 23:14:26 2014 +0100
1.2 +++ b/docs/annotations.txt Thu Mar 13 23:10:25 2014 +0100
1.3 @@ -54,8 +54,9 @@
1.4 Attribute Users
1.5 ---------------
1.6
1.7 -_attrtypes defines types deduced either from combined attribute usage
1.8 - details (for users)
1.9 +_attrtypes defines types deduced from combined attribute usage
1.10 + details (for users), indicating a set of (attribute,
1.11 + value) tuples for each name
1.12 _values defines a name-to-value mapping for objects that may be
1.13 used to access attributes
1.14 _guard_types mapping from names to ("single", "multiple", "impossible")
2.1 --- a/micropython/common.py Mon Mar 10 23:14:26 2014 +0100
2.2 +++ b/micropython/common.py Thu Mar 13 23:10:25 2014 +0100
2.3 @@ -3,7 +3,7 @@
2.4 """
2.5 Common classes.
2.6
2.7 -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Paul Boddie <paul@boddie.org.uk>
2.8 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Paul Boddie <paul@boddie.org.uk>
2.9
2.10 This program is free software; you can redistribute it and/or modify it under
2.11 the terms of the GNU General Public License as published by the Free Software
2.12 @@ -20,7 +20,7 @@
2.13 """
2.14
2.15 from micropython.stdcompiler import compiler
2.16 -from micropython.basicdata import Constant, TypedInstance
2.17 +from micropython.basicdata import Constant, TypedInstance, Instance
2.18 from micropython.data import BaseAttr, Class
2.19 from micropython.errors import *
2.20 from os.path import split
2.21 @@ -108,6 +108,30 @@
2.22
2.23 return None
2.24
2.25 + def get_type_for_attribute(self, attr, value):
2.26 +
2.27 + """
2.28 + For an attribute 'attr' and 'value', return the "parent" type of the
2.29 + attribute if possible. Otherwise, return None.
2.30 + """
2.31 +
2.32 + if attr:
2.33 + return attr.get_type()
2.34 + elif value and not isinstance(value, Instance) and value.parent:
2.35 + return value.parent
2.36 + else:
2.37 + return None
2.38 +
2.39 + def get_values_for_attribute(self, attr, value):
2.40 +
2.41 + """
2.42 + Return values defined for the given attribute 'attr' unless a specific
2.43 + 'value' is provided, in which case a list containing that value is
2.44 + returned. Where no values are defined, return None in a list.
2.45 + """
2.46 +
2.47 + return value and [value] or attr and attr.get_values() or [None]
2.48 +
2.49 def get_module_name(node, module):
2.50
2.51 """
3.1 --- a/micropython/data.py Mon Mar 10 23:14:26 2014 +0100
3.2 +++ b/micropython/data.py Thu Mar 13 23:10:25 2014 +0100
3.3 @@ -427,6 +427,15 @@
3.4
3.5 self.static_assignments = 0
3.6
3.7 + def get_type(self):
3.8 +
3.9 + "Return the type of the referenced object or None if not known."
3.10 +
3.11 + if isinstance(self.parent, Instance):
3.12 + return self.parent_type
3.13 + else:
3.14 + return self.parent
3.15 +
3.16 def get_contexts(self):
3.17 return [c for (c, v) in self.get_context_values()]
3.18
4.1 --- a/micropython/deduce.py Mon Mar 10 23:14:26 2014 +0100
4.2 +++ b/micropython/deduce.py Thu Mar 13 23:10:25 2014 +0100
4.3 @@ -181,14 +181,8 @@
4.4 general definition-based type observations.
4.5 """
4.6
4.7 - targets = set()
4.8 target_names = self.possible_accessor_types_from_usage(node, defining_users)
4.9 -
4.10 - if target_names:
4.11 - for target_name, is_static in target_names:
4.12 - targets.add(self.objtable.get_object(target_name))
4.13 -
4.14 - return targets
4.15 + return self.get_targets_from_type_names(target_names)
4.16
4.17 def possible_accessors_for_attribute(self, attrname):
4.18
4.19 @@ -201,6 +195,21 @@
4.20
4.21 return targets
4.22
4.23 + def get_targets_from_type_names(self, target_names):
4.24 +
4.25 + """
4.26 + Given a collection of 'target_names' of the form (name, is_static),
4.27 + return a set of types for the 'name' part of each tuple.
4.28 + """
4.29 +
4.30 + targets = set()
4.31 +
4.32 + if target_names:
4.33 + for target_name, is_static in target_names:
4.34 + targets.add(self.objtable.get_object(target_name))
4.35 +
4.36 + return targets
4.37 +
4.38 # Visitor methods.
4.39
4.40 def _visitUnit(self, node):
4.41 @@ -238,11 +247,16 @@
4.42 node._guard_types = {}
4.43 node._guards = {}
4.44
4.45 - for name in node.unit.positional_names:
4.46 - # NOTE: Test for tuple parameters (see micropython.report.Report._parameters).
4.47 - # NOTE: No _values usage here because it is mostly useless information, except perhaps for defaults.
4.48 - attrtypes = node._attrtypes.get(name)
4.49 - self._visitGuardForNameDeduced(node, name, attrtypes)
4.50 + self._visitParameters(node, node.unit.positional_names)
4.51 +
4.52 + def _visitParameters(self, node, parameters):
4.53 + for name in parameters:
4.54 + if isinstance(name, tuple):
4.55 + self._visitParameters(node, name)
4.56 + else:
4.57 + # NOTE: No _values usage here because it is mostly useless information, except perhaps for defaults.
4.58 + types = self.get_targets_from_type_names(node._attrtypes.get(name))
4.59 + self._visitGuardForNameDeduced(node, name, types)
4.60
4.61 def _visitAttr(self, node):
4.62
4.63 @@ -532,14 +546,14 @@
4.64
4.65 # Need to check any concrete value against deductions.
4.66
4.67 - attrtypes = node._attrtypes.get(name)
4.68 + types = self.get_targets_from_type_names(node._attrtypes.get(name))
4.69 value = node._values.get(name)
4.70
4.71 # Where a concrete type conflicts with deductions, the usage of
4.72 # attributes cannot be supported and is thus impossible.
4.73
4.74 if value:
4.75 - if attrtypes and value not in attrtypes:
4.76 + if types and value not in types:
4.77 node._guard_types[name] = "impossible"
4.78 else:
4.79 node._guard_types[name] = "single"
4.80 @@ -547,19 +561,19 @@
4.81
4.82 # Where no concrete type is known, usage must be checked.
4.83
4.84 - elif attrtypes:
4.85 - self._visitGuardForNameDeduced(node, name, attrtypes)
4.86 + elif types:
4.87 + self._visitGuardForNameDeduced(node, name, types)
4.88
4.89 # Where no deductions are made, no guards are needed.
4.90
4.91 - def _visitGuardForNameDeduced(self, node, name, attrtypes):
4.92 - if not attrtypes:
4.93 + def _visitGuardForNameDeduced(self, node, name, types):
4.94 + if not types:
4.95 return
4.96 - if len(attrtypes) == 1:
4.97 + if len(types) == 1:
4.98 node._guard_types[name] = "single"
4.99 else:
4.100 node._guard_types[name] = "multiple"
4.101 - node._guards[name] = attrtypes
4.102 + node._guards[name] = types
4.103
4.104 def visitCallFunc(self, node):
4.105
5.1 --- a/micropython/report.py Mon Mar 10 23:14:26 2014 +0100
5.2 +++ b/micropython/report.py Thu Mar 13 23:10:25 2014 +0100
5.3 @@ -3,7 +3,7 @@
5.4 """
5.5 View annotated sources.
5.6
5.7 -Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Paul Boddie <paul@boddie.org.uk>
5.8 +Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013, 2014 Paul Boddie <paul@boddie.org.uk>
5.9
5.10 This program is free software; you can redistribute it and/or modify it under
5.11 the terms of the GNU General Public License as published by the Free Software
5.12 @@ -93,6 +93,8 @@
5.13 z-index: 3;
5.14 }
5.15
5.16 + .impossible-guard,
5.17 + .impossible-guard .name,
5.18 .no-attributes {
5.19 background-color: #d00;
5.20 color: white;
5.21 @@ -375,6 +377,14 @@
5.22 all_attrnames = list(all_attrnames)
5.23 all_attrnames.sort()
5.24
5.25 + # Show guard details.
5.26 +
5.27 + guard_type = node._guard_types and node._guard_types.get(name) or None
5.28 + guards = node._guards and node._guards.get(name) or []
5.29 + typenames = [obj.full_name() for obj in guards]
5.30 +
5.31 + self._accessor_start(typenames, guard_type)
5.32 +
5.33 # Write the lists of attribute names.
5.34
5.35 self._name_start()
5.36 @@ -384,6 +394,9 @@
5.37 self._attrnames(attrnames)
5.38 self._popup_end()
5.39 self._name_end()
5.40 +
5.41 + self._accessor_end(typenames, guard_type)
5.42 +
5.43 return True
5.44
5.45 def _attrnames(self, attrnames):
5.46 @@ -392,15 +405,21 @@
5.47 def _typenames(self, typenames):
5.48 self._names_list(typenames, "types", "typenames")
5.49
5.50 - def _accessor_start(self, target_names):
5.51 - if target_names:
5.52 - self._span_start("accessor")
5.53 - self._popup_start("types-popup")
5.54 - self._typenames(target_names)
5.55 - self._popup_end()
5.56 + def _accessor_start(self, target_names, guard_type=None):
5.57 + if target_names or guard_type:
5.58 + if guard_type == "impossible":
5.59 + self._span_start("impossible-guard")
5.60 + else:
5.61 + self._span_start("accessor")
5.62 + self._types_list(target_names)
5.63
5.64 - def _accessor_end(self, target_names):
5.65 - if target_names:
5.66 + def _types_list(self, typenames):
5.67 + self._popup_start("types-popup")
5.68 + self._typenames(typenames)
5.69 + self._popup_end()
5.70 +
5.71 + def _accessor_end(self, target_names, guard_type=None):
5.72 + if target_names or guard_type:
5.73 self._span_end()
5.74
5.75 def _values_to_attribute_names(self, attr):
5.76 @@ -430,14 +449,11 @@
5.77
5.78 def _attribute_value_to_name(self, attr, value, target=False):
5.79 fullname = None
5.80 +
5.81 if target:
5.82 - if attr:
5.83 - if isinstance(attr.parent, Instance):
5.84 - fullname = attr.parent_type.full_name()
5.85 - else:
5.86 - fullname = attr.parent.full_name()
5.87 - elif value and not isinstance(value, Instance) and value.parent:
5.88 - fullname = value.parent.full_name()
5.89 + type = self.get_type_for_attribute(attr, value)
5.90 + if type:
5.91 + return type.full_name()
5.92 else:
5.93 if value and not isinstance(value, Instance):
5.94 fullname = value.full_name()
5.95 @@ -462,7 +478,7 @@
5.96
5.97 if attributes:
5.98 for attr, value in attributes:
5.99 - values = value and [value] or attr and attr.get_values() or [None]
5.100 + values = self.get_values_for_attribute(attr, value)
5.101
5.102 for value in values:
5.103 fullname = self._attribute_value_to_name(attr, value, target)
5.104 @@ -471,9 +487,11 @@
5.105 elif not target:
5.106 output.add((fullname, value))
5.107
5.108 - output = list(output)
5.109 - output.sort()
5.110 - return output
5.111 + output = list(output)
5.112 + output.sort()
5.113 + return output
5.114 + else:
5.115 + return []
5.116
5.117 def _attribute_start(self, attrname, attributes):
5.118 if attributes: