[mypyc] Fix some nondeterminism caused by iterating over sets/dicts (#7594)
Tested by compiling mypy and comparing outputs on Python 3.5.
diff --git a/mypyc/emitclass.py b/mypyc/emitclass.py
index 542e3ec..3d68bfe 100644
--- a/mypyc/emitclass.py
+++ b/mypyc/emitclass.py
@@ -69,7 +69,8 @@
def generate_slots(cl: ClassIR, table: SlotTable, emitter: Emitter) -> Dict[str, str]:
fields = OrderedDict() # type: Dict[str, str]
- for name, (slot, generator) in table.items():
+ # Sort for determinism on Python 3.5
+ for name, (slot, generator) in sorted(table.items()):
method = cl.get_method(name)
if method:
fields[slot] = generator(cl, method, emitter)
diff --git a/mypyc/emitwrapper.py b/mypyc/emitwrapper.py
index 3b99f49..40fc6c9 100644
--- a/mypyc/emitwrapper.py
+++ b/mypyc/emitwrapper.py
@@ -127,7 +127,8 @@
def generate_richcompare_wrapper(cl: ClassIR, emitter: Emitter) -> Optional[str]:
"""Generates a wrapper for richcompare dunder methods."""
- matches = [name for name in RICHCOMPARE_OPS if cl.has_method(name)]
+ # Sort for determinism on Python 3.5
+ matches = sorted([name for name in RICHCOMPARE_OPS if cl.has_method(name)])
if not matches:
return None
diff --git a/mypyc/genops.py b/mypyc/genops.py
index 9ef93f2..f3221e3 100644
--- a/mypyc/genops.py
+++ b/mypyc/genops.py
@@ -361,7 +361,7 @@
self.type_to_ir = {} # type: Dict[TypeInfo, ClassIR]
self.func_to_decl = {} # type: Dict[SymbolNode, FuncDecl]
# Maps integer, float, and unicode literals to a static name
- self.literals = {} # type: LiteralsMap
+ self.literals = OrderedDict() # type: LiteralsMap
def type_to_rtype(self, typ: Optional[Type]) -> RType:
if typ is None:
@@ -1873,7 +1873,8 @@
env_for_func = self.fn_info.callable_class
if self.fn_info.fitem in self.free_variables:
- for var in self.free_variables[self.fn_info.fitem]:
+ # Sort the variables to keep things deterministic
+ for var in sorted(self.free_variables[self.fn_info.fitem], key=lambda x: x.name()):
if isinstance(var, Var):
rtype = self.type_to_rtype(var.type)
self.add_var_to_env_class(var, rtype, env_for_func, reassign=False)
diff --git a/mypyc/prebuildvisitor.py b/mypyc/prebuildvisitor.py
index f159969..1f21e08 100644
--- a/mypyc/prebuildvisitor.py
+++ b/mypyc/prebuildvisitor.py
@@ -27,9 +27,9 @@
self.prop_setters = set() # type: Set[FuncDef]
# A map from any function that contains nested functions to
# a set of all the functions that are nested within it.
- self.encapsulating_funcs = dict() # type: Dict[FuncItem, Set[FuncItem]]
+ self.encapsulating_funcs = {} # type: Dict[FuncItem, List[FuncItem]]
# A map from a nested func to it's parent/encapsulating func.
- self.nested_funcs = dict() # type: Dict[FuncItem, FuncItem]
+ self.nested_funcs = {} # type: Dict[FuncItem, FuncItem]
self.funcs_to_decorators = {} # type: Dict[FuncDef, List[Expression]]
def add_free_variable(self, symbol: SymbolNode) -> None:
@@ -57,7 +57,7 @@
# being a nested function.
if self.funcs:
# Add the new func to the set of nested funcs within the func at top of the func stack.
- self.encapsulating_funcs.setdefault(self.funcs[-1], set()).add(func)
+ self.encapsulating_funcs.setdefault(self.funcs[-1], []).append(func)
# Add the func at top of the func stack as the parent of new func.
self.nested_funcs[func] = self.funcs[-1]