# HG changeset patch # User Paul Boddie # Date 1266187757 -3600 # Node ID 9f1f0b79c182ff1dc67b57218d5d718db22e319b # Parent 4125a2a53e5e69b83bd1cc7b0c5f99bb916583b2 Introduced a distinction between speculative attribute usage, which should affect the possible types associated with each name, and coverage-related usage. Added support for the recording of speculative usage as "alternatives" where the expectations of a given name might differ from the consensus due to branches whose usage are never merged back into the general control-flow. diff -r 4125a2a53e5e -r 9f1f0b79c182 micropython/data.py --- a/micropython/data.py Sun Feb 14 16:28:12 2010 +0100 +++ b/micropython/data.py Sun Feb 14 23:49:17 2010 +0100 @@ -140,15 +140,23 @@ # the consensus. self.abandon_attributes = 0 # used when a block will never contribute - self.abandoned_shelves = [] + + # Speculative attribute usage where names may not contribute to a + # consensus beyond a branch, but where the usage will still influence + # acceptable types for a name. + + self.speculative_attributes = [{}] # stack of speculative usage + self.speculative_shelves = [] # stack of unmerged speculative definitions # Attribute users, defining names which use attributes. self.attribute_users = [{}] # stack of assignments + self.all_attribute_users = [{}] # stack of assignments (to record any usage) self.user_shelves = [] # Define attribute usage to identify active program sections. + self.all_usage_shelves = [] # stack of unmerged coverage-related definitions self.all_attributes_used = [] # Attribute/name definition and access. @@ -269,6 +277,9 @@ return results def make_global(self, name): + + "Declare 'name' as a global in the current namespace." + if not self.namespace.has_key(name): self.globals.add(name) return 1 @@ -322,6 +333,9 @@ return usage def use_attribute(self, name, attrname): + + "Declare the usage on 'name' of the given 'attrname'." + return self._use_attribute(name, attrname) # These shadow various methods in the InspectedModule class, and provide @@ -370,7 +384,17 @@ defs = self.attributes_used[-1] users = self.attribute_users[-1] + all_users = self.all_attribute_users[-1] + speculative = self.speculative_attributes[-1] + # This node overrides previous definitions. + + if not speculative.has_key(name): + speculative[name] = defs.get(name, set()) + else: + self.all_usage_shelves[-1].append({name : defs[name]}) + + all_users[name] = set([node]) users[name] = set([node]) defs[name] = set() @@ -378,11 +402,10 @@ if not hasattr(node, "_attrnames"): node._attrnames = {} + node._alternative_attrnames = {} - # Copy the current usage for the user since this may eventually become - # independent from the current usage. - - node._attrnames[name] = set(defs[name]) + node._attrnames[name] = set() + node._alternative_attrnames[name] = set() # Remember all attribute combinations. @@ -396,7 +419,8 @@ """ self.attribute_shelves.append([]) - self.abandoned_shelves.append([]) + self.speculative_shelves.append([]) + self.all_usage_shelves.append([]) self.user_shelves.append([]) def _new_branch(self): @@ -416,6 +440,16 @@ self.attribute_users.append({}) + # Retain a record of all active users. + + d = {} + d.update(self.all_attribute_users[-1]) + self.all_attribute_users.append(d) + + # Speculative attributes are recorded for subsequent feedback to users. + + self.speculative_attributes.append({}) + def _abandon_branch(self): self.abandon_attributes = 1 @@ -428,12 +462,26 @@ observations after a merge. """ + usage = self.attributes_used.pop() + speculative = self.speculative_attributes.pop() + + # Record contributing usage. + if not self.abandon_attributes: - collector = self.attribute_shelves[-1] - else: - collector = self.abandoned_shelves[-1] + self.attribute_shelves[-1].append(usage) + + # Record all usage (for coverage). + + self.all_usage_shelves[-1].append(usage) - collector.append(self.attributes_used.pop()) + # Record speculative usage. + + if self.abandon_attributes: + for name, attrnames in usage.items(): + if not speculative.has_key(name): + speculative[name] = attrnames + + self.speculative_shelves[-1].append(speculative) # Forget about any nodes which defined names employing attributes in # this branch if the branch is abandoned. @@ -443,6 +491,7 @@ if not self.abandon_attributes: self.user_shelves[-1].append(users) + self.all_attribute_users.pop() self.abandon_attributes = 0 def _merge_branches(self): @@ -458,19 +507,22 @@ """ # The active dictionary holds the usage for names defined before the - # branches. The abandoned dictionary collects abandoned usage. + # branches. The users dictionary holds the active users defining each + # name. active = self.attributes_used[-1] + users = self.attribute_users[-1] + all_users = self.all_attribute_users[-1] + speculative = self.speculative_attributes[-1] # Take each alternative branch, currently shelved, and find the # intersection of their contributions for each name. - shelved_defs = self.attribute_shelves.pop() - abandoned_defs = self.abandoned_shelves.pop() - # Where branches contribute attribute usage observations, process these # as described above. Otherwise, preserve the previous observations. + shelved_defs = self.attribute_shelves.pop() + if shelved_defs: defs = dict(shelved_defs[0]) @@ -487,9 +539,28 @@ else: active[name] = attrnames - # Combine the attribute users. + # Feed speculative definitions back to the users previously known. + + speculative_defs = self.speculative_shelves.pop() + + if speculative_defs: + for defs in speculative_defs: + for name, attrnames in defs.items(): + + # Where users are active for a name, produce alternatives. - users = self.attribute_users[-1] + if all_users.has_key(name): + for node in all_users[name]: + + # Add the usage to the alternatives. + + if not node._alternative_attrnames.has_key(name): + node._alternative_attrnames[name] = set([tuple(attrnames)]) + else: + node._alternative_attrnames[name].add(tuple(attrnames)) + + # Combine the attribute users. This ensures that a list of users + # affected by attribute usage is maintained for the current branch. for shelved_users in self.user_shelves.pop(): for name, nodes in shelved_users.items(): @@ -502,7 +573,9 @@ # although they do not contribute to further usage in a namespace, any # attribute combinations do indicate function usage. - for defs in shelved_defs + abandoned_defs: + all_usage_defs = self.all_usage_shelves.pop() + + for defs in all_usage_defs: for name, attrnames in defs.items(): # Check for isolated pockets of attribute usage as well as more diff -r 4125a2a53e5e -r 9f1f0b79c182 micropython/trans.py --- a/micropython/trans.py Sun Feb 14 16:28:12 2010 +0100 +++ b/micropython/trans.py Sun Feb 14 23:49:17 2010 +0100 @@ -315,9 +315,20 @@ for name, names_used in node._attrnames.items(): + # Consider alternatives. + + if node._alternative_attrnames.has_key(name): + all_names_used = set() + all_names_used.update(node._alternative_attrnames) + all_names_used.add(tuple(names_used)) + else: + all_names_used = [names_used] + # Get the names of all object types supporting these names. - targets = self.objtable.all_possible_objects_plus_status(names_used) + targets = set() + for names_used in all_names_used: + targets.update(self.objtable.all_possible_objects_plus_status(names_used)) # Where only one object type is suggested, produce a guard. # NOTE: This only supports classes as types, not modules. diff -r 4125a2a53e5e -r 9f1f0b79c182 tests/abandoned_attribute_usage_multiple_candidates_nested.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/abandoned_attribute_usage_multiple_candidates_nested.py Sun Feb 14 23:49:17 2010 +0100 @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +This test attempts to cause the recording of the usage of 'C' in the function +'g', alongside the expectation that 'D' might be used instead. A guard +stipulating constraints for all of 'g' cannot therefore be generated. +""" + +class C: + def f(self): + return 1 + +class D: + def g(self): + return 2 + + def h(self): + return 3 + +def g(c): + if 1: + c.g() + if 1: + return c.h() + return c.f() + +c = C() +d = D() +result1_3 = g(d) + +# vim: tabstop=4 expandtab shiftwidth=4