|
118 | 118 | MypyFile, |
119 | 119 | OverloadedFuncDef, |
120 | 120 | SymbolTable, |
| 121 | + TypeInfo, |
121 | 122 | ) |
122 | 123 | from mypy.options import OPTIONS_AFFECTING_CACHE_NO_PLATFORM |
123 | 124 | from mypy.partially_defined import PossiblyUndefinedVariableVisitor |
@@ -3483,17 +3484,20 @@ def finish_passes(self) -> None: |
3483 | 3484 | if options.export_types: |
3484 | 3485 | manager.all_types.update(self.type_map()) |
3485 | 3486 |
|
| 3487 | + # Possible sources of indirect dependencies: |
| 3488 | + # * Symbols not directly imported in this module but accessed via an attribute |
| 3489 | + # or via a re-export (vast majority of these recorded in semantic analysis). |
| 3490 | + # * For each expression type we need to record definitions of type components |
| 3491 | + # since "meaning" of the type may be updated when definitions are updated. |
| 3492 | + # * For mypyc-compiled modules only: modules defining MRO ancestors of |
| 3493 | + # classes defined here, since the generated C embeds each ancestor's |
| 3494 | + # method/attribute layout. |
| 3495 | + indirect_refs = self.tree.module_refs | self.type_checker().module_refs |
| 3496 | + if self.options.mypyc: |
| 3497 | + indirect_refs |= self.compiled_class_ancestor_refs() |
3486 | 3498 | # We should always patch indirect dependencies, even in full (non-incremental) builds, |
3487 | 3499 | # because the cache still may be written, and it must be correct. |
3488 | | - self.patch_indirect_dependencies( |
3489 | | - # Two possible sources of indirect dependencies: |
3490 | | - # * Symbols not directly imported in this module but accessed via an attribute |
3491 | | - # or via a re-export (vast majority of these recorded in semantic analysis). |
3492 | | - # * For each expression type we need to record definitions of type components |
3493 | | - # since "meaning" of the type may be updated when definitions are updated. |
3494 | | - self.tree.module_refs | self.type_checker().module_refs, |
3495 | | - set(self.type_map().values()), |
3496 | | - ) |
| 3500 | + self.patch_indirect_dependencies(indirect_refs, set(self.type_map().values())) |
3497 | 3501 |
|
3498 | 3502 | if self.options.dump_inference_stats: |
3499 | 3503 | dump_type_stats( |
@@ -3535,6 +3539,27 @@ def patch_indirect_dependencies(self, module_refs: set[str], types: set[Type]) - |
3535 | 3539 | self.add_dependency(dep) |
3536 | 3540 | self.priorities[dep] = PRI_INDIRECT |
3537 | 3541 |
|
| 3542 | + def compiled_class_ancestor_refs(self) -> set[str]: |
| 3543 | + """Modules defining MRO ancestors of classes defined in this module. |
| 3544 | +
|
| 3545 | + Only used for mypyc-compiled modules: the generated C for a class |
| 3546 | + (vtable arrays, getter/setter tables, object struct) references |
| 3547 | + every inherited method/attribute of every ancestor, including |
| 3548 | + ancestors defined in modules this module does not import directly. |
| 3549 | + Recording them as indirect dependencies makes an ancestor's |
| 3550 | + interface change re-trigger type checking (and hence C regeneration) |
| 3551 | + of this module. Only top-level classes are scanned: mypyc rejects |
| 3552 | + nested class definitions. |
| 3553 | + """ |
| 3554 | + assert self.tree is not None |
| 3555 | + mods: set[str] = set() |
| 3556 | + for sym in self.tree.names.values(): |
| 3557 | + node = sym.node |
| 3558 | + if isinstance(node, TypeInfo) and node.module_name == self.id: |
| 3559 | + for ancestor in node.mro[1:]: |
| 3560 | + mods.add(ancestor.module_name) |
| 3561 | + return mods |
| 3562 | + |
3538 | 3563 | def compute_fine_grained_deps(self) -> dict[str, set[str]]: |
3539 | 3564 | assert self.tree is not None |
3540 | 3565 | if self.id in ("builtins", "typing", "types", "sys", "_typeshed"): |
|
0 commit comments