From 75cc02581ede2f447970a1f49877686febc807fb Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 13 Aug 2026 06:14:51 +0900 Subject: [PATCH] Fix future annotation block alignment --- crates/codegen/src/compile.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index b16540762fd..1ca7afb8e1e 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -5083,6 +5083,14 @@ impl<'warnings> Compiler<'warnings> { func_range: TextRange, ) -> CompileResult { if !self.next_function_annotation_symbol_table_uses_annotations() { + // CPython creates a hidden AnnotationBlock for every function + // signature under `from __future__ import annotations`, including + // an unannotated one. It still belongs to this function: consume + // it so the next function sees its own block rather than remaining + // pinned to this unused entry. + if self.push_annotation_symbol_table() { + self.pop_annotation_symbol_table(); + } return Ok(false); } @@ -31247,6 +31255,25 @@ def f(x: T): pass ); } + #[test] + fn future_unannotated_function_does_not_hide_next_annotation_block() { + let code = compile_exec( + "\ +from __future__ import annotations +def plain(x): pass +def annotated(x: int): pass +", + ); + let annotate = find_direct_child_code(&code, "__annotate__") + .expect("second function must retain its annotation closure"); + assert!( + annotate.constants.iter().any( + |constant| matches!(constant, ConstantData::Str { value } if value.as_str() == Ok("int")) + ), + "annotation closure must belong to the annotated function" + ); + } + #[test] fn deferred_annotation_format_name_does_not_capture_helper_parameter() { let code = compile_exec(