Skip to content
This repository has been archived by the owner on Sep 14, 2018. It is now read-only.

Commit

Permalink
fix compilation error at closures inside of dict/set comprehensions (#…
Browse files Browse the repository at this point in the history
…1199)

* fixed compilation error at closures inside of dict/set comprehensions

* fix identation
  • Loading branch information
kunom authored and slide committed Jul 19, 2016
1 parent 2988c5a commit 1124511
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 120 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Languages/IronPython/Tests/test.xaml
Languages/IronPython/Tests/testfile.tmp
Languages/IronPython/Tests/testilcode.dll
Languages/IronPython/Tests/the_dir/
Languages/IronPython/Tests/tmp*
Languages/IronPython/Tests/tempfile.txt
Languages/IronPython/Tests/vbproptest0.*.dll

# created by side by side tests
Expand Down
16 changes: 11 additions & 5 deletions Languages/IronPython/IronPython/Compiler/Ast/Comprehension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

using Microsoft.Scripting;

using IronPython.Runtime;
using IronPython.Runtime.Operations;

#if FEATURE_CORE_DLR
using MSAst = System.Linq.Expressions;
Expand All @@ -31,7 +34,6 @@

namespace IronPython.Compiler.Ast {
using Ast = MSAst.Expression;
using System.Runtime.CompilerServices;

public abstract class ComprehensionIterator : Node {
internal abstract MSAst.Expression Transform(MSAst.Expression body);
Expand Down Expand Up @@ -285,7 +287,7 @@ internal ComprehensionScope Scope {
/// </summary>
class ComprehensionScope : ScopeStatement {
private readonly Expression _comprehension;
internal static readonly MSAst.ParameterExpression _compContext = Ast.Parameter(typeof(CodeContext), "$compContext");
private static readonly MSAst.ParameterExpression _compContext = Ast.Parameter(typeof(CodeContext), "$compContext");

public ComprehensionScope(Expression comprehension) {
_comprehension = comprehension;
Expand All @@ -300,6 +302,11 @@ internal override bool ExposesLocalVariable(PythonVariable variable) {
return _comprehension.Parent.ExposesLocalVariable(variable);
}

internal override MSAst.Expression/*!*/ GetParentClosureTuple() {
Debug.Assert(NeedsLocalContext);
return MSAst.Expression.Call(null, typeof(PythonOps).GetMethod("GetClosureTupleFromContext"), _comprehension.Parent.LocalContext);
}

internal override PythonVariable BindReference(PythonNameBinder binder, PythonReference reference) {
PythonVariable variable;
if (TryGetVariable(reference.Name, out variable)) {
Expand Down Expand Up @@ -358,11 +365,10 @@ internal Ast AddVariables(Ast expression) {
if (localContext != null) {
var createLocal = CreateLocalContext(_comprehension.Parent.LocalContext);
body.Add(Ast.Assign(_compContext, createLocal));
body.Add(expression);
} else {
body.Add(expression);
}

body.Add(expression);

return Expression.Block(
locals,
body
Expand Down
59 changes: 0 additions & 59 deletions Languages/IronPython/Tests/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -1207,65 +1207,6 @@ def __missing__(self, key):
def test_cp29914():
AreEqual(dict(o=42), {'o':42})

def test_dict_comp():
pass

def test_dict_comp():
AreEqual({locals()['x'] : locals()['x'] for x in (2,3,4)}, {2:2, 3:3, 4:4})

x = 100
{x:x for x in (2,3,4)}
AreEqual(x, 100)

class C:
{x:x for x in (2,3,4)}

AreEqual(hasattr(C, 'x'), False)

class C:
abc = {locals()['x']:locals()['x'] for x in (2,3,4)}

AreEqual(C.abc, {2:2,3:3,4:4})

d = {}
exec compile("abc = {locals()['x']:locals()['x'] for x in (2,3,4)}", 'exec', 'exec') in d, d
AreEqual(d['abc'], {2:2,3:3,4:4})

d = {'y':42}
exec compile("abc = {y:y for x in (2,3,4)}", 'exec', 'exec') in d, d
AreEqual(d['abc'], {42:42})

d = {'y':42, 't':(2,3,42)}
exec compile("abc = {y:y for x in t if x == y}", 'exec', 'exec') in d, d
AreEqual(d['abc'], {42:42})

t = (2,3,4)
v = 2
abc = {v:v for x in t}
AreEqual(abc, {2:2})

abc = {x:x for x in t if x == v}
AreEqual(abc, {2:2})

def f():
abc = {x:x for x in t if x == v}
AreEqual(abc, {2:2})

f()

def f():
abc = {v:v for x in t}
AreEqual(abc, {2:2})


class C:
abc = {v:v for x in t}
AreEqual(abc, {2:2})

class C:
abc = {x:x for x in t if x == v}
AreEqual(abc, {2:2})

def test_cp32527():
'''test for duplicate key in dict under specific hash value conditions'''
d = {'1': 1, '2': 1, '3': 1, 'a7': 1, 'a8': 1}
Expand Down
115 changes: 115 additions & 0 deletions Languages/IronPython/Tests/test_dictcomp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#####################################################################################
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# This source code is subject to terms and conditions of the Apache License, Version 2.0. A
# copy of the license can be found in the License.html file at the root of this distribution. If
# you cannot locate the Apache License, Version 2.0, please send an email to
# [email protected]. By using this source code in any fashion, you are agreeing to be bound
# by the terms of the Apache License, Version 2.0.
#
# You must not remove this notice, or any other, from this software.
#
#
#####################################################################################

##
## Testing dictionary comprehension
##

from iptest.assert_util import *

## moved from test_dict.py:
def test_dict_comp():
AreEqual({locals()['x'] : locals()['x'] for x in (2,3,4)}, {2:2, 3:3, 4:4})

x = 100
{x:x for x in (2,3,4)}
AreEqual(x, 100)

class C:
{x:x for x in (2,3,4)}

AreEqual(hasattr(C, 'x'), False)

class C:
abc = {locals()['x']:locals()['x'] for x in (2,3,4)}

AreEqual(C.abc, {2:2,3:3,4:4})

d = {}
exec compile("abc = {locals()['x']:locals()['x'] for x in (2,3,4)}", 'exec', 'exec') in d, d
AreEqual(d['abc'], {2:2,3:3,4:4})

d = {'y':42}
exec compile("abc = {y:y for x in (2,3,4)}", 'exec', 'exec') in d, d
AreEqual(d['abc'], {42:42})

d = {'y':42, 't':(2,3,42)}
exec compile("abc = {y:y for x in t if x == y}", 'exec', 'exec') in d, d
AreEqual(d['abc'], {42:42})

t = (2,3,4)
v = 2
abc = {v:v for x in t}
AreEqual(abc, {2:2})

abc = {x:x for x in t if x == v}
AreEqual(abc, {2:2})

def f():
abc = {x:x for x in t if x == v}
AreEqual(abc, {2:2})

f()

def f():
abc = {v:v for x in t}
AreEqual(abc, {2:2})


class C:
abc = {v:v for x in t}
AreEqual(abc, {2:2})

class C:
abc = {x:x for x in t if x == v}
AreEqual(abc, {2:2})

def test_scope_mixing():
k = 1
v = 3

# in source
r = {k:k for k in xrange(v)} # TODO: "xrange(v + k)" fails in IPY, but not in CPython
AreEqual(r, {0:0, 1:1, 2:2})

# in condition
r = {k:k for k in xrange(4) if k < v}
AreEqual(r, {0:0, 1:1, 2:2})

# in item generation
r = {k:(k+v) for k in xrange(2)}
AreEqual(r, {0:3, 1:4})

def test_scope_mixing_closures():
# see also: GitHub issue #1196

def eval(f, i):
return f(i)

v = 2

# in source
r = {k:k for k in eval(lambda i: xrange(i+v), v)}
AreEqual(r, {0:0, 1:1, 2:2, 3:3})

# in condition
r = {k:k for k in xrange(4) if eval(lambda i: i>=v, k)}
AreEqual(r, {2:2, 3:3})

# in item generation
r = {k:eval(lambda i: i+v, k+v) for k in xrange(2)}
AreEqual(r, {0:4, 1:5})

run_test(__name__)
56 changes: 0 additions & 56 deletions Languages/IronPython/Tests/test_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,61 +309,5 @@ def test_frozenness():
s.add(4)
AreEqual(4 in f, False)

def test_set_comp():
AreEqual({locals()['x'] for x in (2,3,4)}, set([2, 3, 4]))

x = 100
{x for x in (2,3,4)}
AreEqual(x, 100)

class C:
{x for x in (2,3,4)}

AreEqual(hasattr(C, 'x'), False)

class C:
abc = {locals()['x'] for x in (2,3,4)}

AreEqual(C.abc, set([2,3,4]))

d = {}
exec compile("abc = {locals()['x'] for x in (2,3,4)}", 'exec', 'exec') in d, d
AreEqual(d['abc'], set([2,3,4]))

d = {'y':42}
exec compile("abc = {y for x in (2,3,4)}", 'exec', 'exec') in d, d
AreEqual(d['abc'], set([42]))

d = {'y':42, 't':(2,3,42)}
exec compile("abc = {y for x in t if x == y}", 'exec', 'exec') in d, d
AreEqual(d['abc'], set([42]))

t = (2,3,4)
v = 2
abc = {v for x in t}
AreEqual(abc, set([2]))

abc = {x for x in t if x == v}
AreEqual(abc, set([2]))

def f():
abc = {x for x in t if x == v}
AreEqual(abc, set([2]))

f()

def f():
abc = {v for x in t}
AreEqual(abc, set([2]))


class C:
abc = {v for x in t}
AreEqual(abc, set([2]))

class C:
abc = {x for x in t if x == v}
AreEqual(abc, set([2]))

#--MAIN------------------------------------------------------------------------
run_test(__name__)
Loading

0 comments on commit 1124511

Please sign in to comment.