Skip to content

Commit d644871

Browse files
authored
Prevent closure creation when all free variables are globals (#1145)
1 parent b06b1f0 commit d644871

File tree

5 files changed

+25
-5
lines changed

5 files changed

+25
-5
lines changed

Src/IronPython/Compiler/Ast/ClassDefinition.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,4 +359,4 @@ internal override void RewriteBody(MSAst.ExpressionVisitor visitor) {
359359
Body = new RewrittenBodyStatement(Body, visitor.Visit(Body));
360360
}
361361
}
362-
}
362+
}

Src/IronPython/Compiler/Ast/Comprehension.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,14 @@ internal override bool ExposesLocalVariable(PythonVariable variable) {
250250
return MSAst.Expression.Call(null, typeof(PythonOps).GetMethod(nameof(PythonOps.GetClosureTupleFromContext)), Parent.LocalContext);
251251
}
252252

253+
internal override void AddFreeVariable(PythonVariable variable, bool accessedInScope) {
254+
if (!accessedInScope) {
255+
ContainsNestedFreeVariables = true;
256+
}
257+
base.AddFreeVariable(variable, accessedInScope);
258+
}
259+
253260
internal override bool TryBindOuter(ScopeStatement from, PythonReference reference, out PythonVariable variable) {
254-
ContainsNestedFreeVariables = true;
255261
if (TryGetVariable(reference.Name, out variable)) {
256262
Debug.Assert(variable.Kind != VariableKind.Nonlocal, "there should be no nonlocals in a comprehension");
257263
variable.AccessedInNestedScope = true;
@@ -264,6 +270,7 @@ internal override bool TryBindOuter(ScopeStatement from, PythonReference referen
264270
}
265271

266272
AddCellVariable(variable);
273+
ContainsNestedFreeVariables = true;
267274
} else {
268275
from.AddReferencedGlobal(reference.Name);
269276
}

Src/IronPython/Compiler/Ast/FunctionDefinition.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,15 @@ internal override FunctionAttributes Flags {
185185
}
186186
}
187187

188+
internal override void AddFreeVariable(PythonVariable variable, bool accessedInScope) {
189+
if (!accessedInScope) {
190+
ContainsNestedFreeVariables = true;
191+
}
192+
base.AddFreeVariable(variable, accessedInScope);
193+
}
194+
188195
internal override bool TryBindOuter(ScopeStatement from, PythonReference reference, out PythonVariable variable) {
189196
// Functions expose their locals to direct access
190-
ContainsNestedFreeVariables = true;
191197
if (TryGetVariable(reference.Name, out variable) && variable.Kind != VariableKind.Nonlocal) {
192198
variable.AccessedInNestedScope = true;
193199

@@ -199,6 +205,7 @@ internal override bool TryBindOuter(ScopeStatement from, PythonReference referen
199205
}
200206

201207
AddCellVariable(variable);
208+
ContainsNestedFreeVariables = true;
202209
} else {
203210
from.AddReferencedGlobal(reference.Name);
204211
}

Src/IronPython/Compiler/Ast/ScopeStatement.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ internal bool IsClosure {
8080
}
8181

8282
/// <summary>
83-
/// True if an inner scope is accessing a variable defined in this scope.
83+
/// True if an inner scope is accessing a non-global variable defined in this or an outer scope.
8484
/// </summary>
8585
internal bool ContainsNestedFreeVariables { get; set; }
8686

@@ -184,7 +184,7 @@ internal virtual IList<string> GetVarNames() {
184184
}
185185

186186

187-
internal void AddFreeVariable(PythonVariable variable, bool accessedInScope) {
187+
internal virtual void AddFreeVariable(PythonVariable variable, bool accessedInScope) {
188188
Debug.Assert(variable?.Kind is VariableKind.Local or VariableKind.Parameter);
189189

190190
if (_freeVars == null) {

Tests/test_function.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,4 +1461,10 @@ def fn_no_closure():
14611461
fn_no_closure.__globals__, "name", fn_no_closure.__defaults__,
14621462
fn_with_closure.__closure__)
14631463

1464+
def test_ipy3_gh1143(self):
1465+
def f(): pass
1466+
self.assertIsNone(f.__closure__)
1467+
def g(): str
1468+
self.assertIsNone(g.__closure__)
1469+
14641470
run_test(__name__)

0 commit comments

Comments
 (0)