# HG changeset patch # User Paul Boddie # Date 1394748625 -3600 # Node ID 1180161dbc63c8fd02d82a2536ea3d2d625cbe14 # Parent ff40924fa22a8530fd6863fde22396c0e463962b Fixed the processing of _attrtypes when building guard type details. Added indications of impossible guard situations in reports. diff -r ff40924fa22a -r 1180161dbc63 docs/annotations.txt --- a/docs/annotations.txt Mon Mar 10 23:14:26 2014 +0100 +++ b/docs/annotations.txt Thu Mar 13 23:10:25 2014 +0100 @@ -54,8 +54,9 @@ Attribute Users --------------- -_attrtypes defines types deduced either from combined attribute usage - details (for users) +_attrtypes defines types deduced from combined attribute usage + details (for users), indicating a set of (attribute, + value) tuples for each name _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") diff -r ff40924fa22a -r 1180161dbc63 micropython/common.py --- a/micropython/common.py Mon Mar 10 23:14:26 2014 +0100 +++ b/micropython/common.py Thu Mar 13 23:10:25 2014 +0100 @@ -3,7 +3,7 @@ """ Common classes. -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Paul Boddie +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -20,7 +20,7 @@ """ from micropython.stdcompiler import compiler -from micropython.basicdata import Constant, TypedInstance +from micropython.basicdata import Constant, TypedInstance, Instance from micropython.data import BaseAttr, Class from micropython.errors import * from os.path import split @@ -108,6 +108,30 @@ return None + def get_type_for_attribute(self, attr, value): + + """ + For an attribute 'attr' and 'value', return the "parent" type of the + attribute if possible. Otherwise, return None. + """ + + if attr: + return attr.get_type() + elif value and not isinstance(value, Instance) and value.parent: + return value.parent + else: + return None + + def get_values_for_attribute(self, attr, value): + + """ + Return values defined for the given attribute 'attr' unless a specific + 'value' is provided, in which case a list containing that value is + returned. Where no values are defined, return None in a list. + """ + + return value and [value] or attr and attr.get_values() or [None] + def get_module_name(node, module): """ diff -r ff40924fa22a -r 1180161dbc63 micropython/data.py --- a/micropython/data.py Mon Mar 10 23:14:26 2014 +0100 +++ b/micropython/data.py Thu Mar 13 23:10:25 2014 +0100 @@ -427,6 +427,15 @@ self.static_assignments = 0 + def get_type(self): + + "Return the type of the referenced object or None if not known." + + if isinstance(self.parent, Instance): + return self.parent_type + else: + return self.parent + def get_contexts(self): return [c for (c, v) in self.get_context_values()] diff -r ff40924fa22a -r 1180161dbc63 micropython/deduce.py --- a/micropython/deduce.py Mon Mar 10 23:14:26 2014 +0100 +++ b/micropython/deduce.py Thu Mar 13 23:10:25 2014 +0100 @@ -181,14 +181,8 @@ general definition-based type observations. """ - targets = set() target_names = self.possible_accessor_types_from_usage(node, defining_users) - - if target_names: - for target_name, is_static in target_names: - targets.add(self.objtable.get_object(target_name)) - - return targets + return self.get_targets_from_type_names(target_names) def possible_accessors_for_attribute(self, attrname): @@ -201,6 +195,21 @@ return targets + def get_targets_from_type_names(self, target_names): + + """ + Given a collection of 'target_names' of the form (name, is_static), + return a set of types for the 'name' part of each tuple. + """ + + targets = set() + + if target_names: + for target_name, is_static in target_names: + targets.add(self.objtable.get_object(target_name)) + + return targets + # Visitor methods. def _visitUnit(self, node): @@ -238,11 +247,16 @@ 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) + self._visitParameters(node, node.unit.positional_names) + + def _visitParameters(self, node, parameters): + for name in parameters: + if isinstance(name, tuple): + self._visitParameters(node, name) + else: + # NOTE: No _values usage here because it is mostly useless information, except perhaps for defaults. + types = self.get_targets_from_type_names(node._attrtypes.get(name)) + self._visitGuardForNameDeduced(node, name, types) def _visitAttr(self, node): @@ -532,14 +546,14 @@ # Need to check any concrete value against deductions. - attrtypes = node._attrtypes.get(name) + types = self.get_targets_from_type_names(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: + if types and value not in types: node._guard_types[name] = "impossible" else: node._guard_types[name] = "single" @@ -547,19 +561,19 @@ # Where no concrete type is known, usage must be checked. - elif attrtypes: - self._visitGuardForNameDeduced(node, name, attrtypes) + elif types: + self._visitGuardForNameDeduced(node, name, types) # Where no deductions are made, no guards are needed. - def _visitGuardForNameDeduced(self, node, name, attrtypes): - if not attrtypes: + def _visitGuardForNameDeduced(self, node, name, types): + if not types: return - if len(attrtypes) == 1: + if len(types) == 1: node._guard_types[name] = "single" else: node._guard_types[name] = "multiple" - node._guards[name] = attrtypes + node._guards[name] = types def visitCallFunc(self, node): diff -r ff40924fa22a -r 1180161dbc63 micropython/report.py --- a/micropython/report.py Mon Mar 10 23:14:26 2014 +0100 +++ b/micropython/report.py Thu Mar 13 23:10:25 2014 +0100 @@ -3,7 +3,7 @@ """ View annotated sources. -Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Paul Boddie +Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013, 2014 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -93,6 +93,8 @@ z-index: 3; } + .impossible-guard, + .impossible-guard .name, .no-attributes { background-color: #d00; color: white; @@ -375,6 +377,14 @@ all_attrnames = list(all_attrnames) all_attrnames.sort() + # Show guard details. + + guard_type = node._guard_types and node._guard_types.get(name) or None + guards = node._guards and node._guards.get(name) or [] + typenames = [obj.full_name() for obj in guards] + + self._accessor_start(typenames, guard_type) + # Write the lists of attribute names. self._name_start() @@ -384,6 +394,9 @@ self._attrnames(attrnames) self._popup_end() self._name_end() + + self._accessor_end(typenames, guard_type) + return True def _attrnames(self, attrnames): @@ -392,15 +405,21 @@ def _typenames(self, typenames): self._names_list(typenames, "types", "typenames") - def _accessor_start(self, target_names): - if target_names: - self._span_start("accessor") - self._popup_start("types-popup") - self._typenames(target_names) - self._popup_end() + def _accessor_start(self, target_names, guard_type=None): + if target_names or guard_type: + if guard_type == "impossible": + self._span_start("impossible-guard") + else: + self._span_start("accessor") + self._types_list(target_names) - def _accessor_end(self, target_names): - if target_names: + def _types_list(self, typenames): + self._popup_start("types-popup") + self._typenames(typenames) + self._popup_end() + + def _accessor_end(self, target_names, guard_type=None): + if target_names or guard_type: self._span_end() def _values_to_attribute_names(self, attr): @@ -430,14 +449,11 @@ def _attribute_value_to_name(self, attr, value, target=False): fullname = None + if target: - if attr: - if isinstance(attr.parent, Instance): - fullname = attr.parent_type.full_name() - else: - fullname = attr.parent.full_name() - elif value and not isinstance(value, Instance) and value.parent: - fullname = value.parent.full_name() + type = self.get_type_for_attribute(attr, value) + if type: + return type.full_name() else: if value and not isinstance(value, Instance): fullname = value.full_name() @@ -462,7 +478,7 @@ if attributes: for attr, value in attributes: - values = value and [value] or attr and attr.get_values() or [None] + values = self.get_values_for_attribute(attr, value) for value in values: fullname = self._attribute_value_to_name(attr, value, target) @@ -471,9 +487,11 @@ elif not target: output.add((fullname, value)) - output = list(output) - output.sort() - return output + output = list(output) + output.sort() + return output + else: + return [] def _attribute_start(self, attrname, attributes): if attributes: