-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix AST generated from a dict literal containing dict unpacking #4449
Conversation
compiler/parser/src/snapshots/rustpython_parser__parser__tests__dict_containing_spread.snap
Outdated
Show resolved
Hide resolved
compiler/parser/python.lalrpop
Outdated
// let (keys, values) = match pairs.iter().position(|(k,_)| k.is_none()) { | ||
// Some(unpack_idx) => { | ||
// let mut pairs = pairs; | ||
// let (keys, mut values): (_, Vec<_>) = pairs.drain(..unpack_idx).map(|(k, v)| (*k.unwrap(), v)).unzip(); | ||
// | ||
// fn build_map(items: &mut Vec<(ast::Expr, ast::Expr)>) -> ast::Expr { | ||
// let location = items[0].0.location; | ||
// let end_location = items[0].0.end_location; | ||
// let (keys, values) = items.drain(..).unzip(); | ||
// ast::Expr { | ||
// location, | ||
// end_location, | ||
// custom: (), | ||
// node: ast::ExprKind::Dict { keys, values } | ||
// } | ||
// } | ||
// | ||
// let mut items = Vec::new(); | ||
// for (key, value) in pairs.into_iter() { | ||
// if let Some(key) = key { | ||
// items.push((*key, value)); | ||
// continue; | ||
// } | ||
// if !items.is_empty() { | ||
// values.push(build_map(&mut items)); | ||
// } | ||
// values.push(value); | ||
// } | ||
// if !items.is_empty() { | ||
// values.push(build_map(&mut items)); | ||
// } | ||
// (keys, values) | ||
// }, | ||
// None => pairs.into_iter().map(|(k, v)| (k, v)).unzip() | ||
// }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What this block does is:
{
"a": "b",
**c,
"d": "e", # <- treat this as Dict so we can process this item with Instruction::DictUpdate
**f,
}
We can remove this.
compiler/codegen/src/compile.rs
Outdated
keys: &[Option<ast::Expr>], | ||
values: &[ast::Expr], | ||
) -> CompileResult<()> { | ||
emit!(self, Instruction::BuildMap { size: 0 }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation looks like to make performance regression
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. Reverted.
This reverts commit b31b08a.
@youknowone @coolreader18 Would you mind taking a look? |
@@ -195,7 +195,7 @@ pub enum ExprKind<U = ()> { | |||
orelse: Box<Expr<U>>, | |||
}, | |||
Dict { | |||
keys: Vec<Expr<U>>, | |||
keys: Vec<Option<Expr<U>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@youknowone Why did you change it to the previous form?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't remember well so looking in it again.
I thought CPython's Python.asdl actually changed it to not optional here.
https://github.com/RustPython/RustPython/pull/4042/files#diff-242a44b9d53b6c6212a752e0c97d6cf2f87794f579700aa8015bb99cef63925cR63
But I still can see ast.parse generate None for empty keys in Python 3.10 as you described. I am confused.
Do you know what the '?' means in Python.asdl
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think ?
means optional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that was the reason I thought CPython changed it from optional key to non-optional one in 3.10. How they still have None in parse.dump?
compiler/ast/src/ast_gen.rs
Outdated
@@ -195,7 +195,7 @@ pub enum ExprKind<U = ()> { | |||
orelse: Box<Expr<U>>, | |||
}, | |||
Dict { | |||
keys: Vec<Expr<U>>, | |||
keys: Vec<Option<Box<Expr<U>>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keys: Vec<Option<Box<Expr<U>>>>, | |
keys: Vec<Option<Expr<U>>>, |
@youknowone Is Box
necessary?
compiler/ast/Python.asdl
Outdated
@@ -60,7 +60,7 @@ module Python | |||
| UnaryOp(unaryop op, expr operand) | |||
| Lambda(arguments args, expr body) | |||
| IfExp(expr test, expr body, expr orelse) | |||
| Dict(expr* keys, expr* values) | |||
| Dict(expr?* keys, expr* values) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CPython doesn't have ?
in keys anymore.
https://github.com/python/cpython/blob/main/Parser/Python.asdl#L64
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@youknowone True, but dict.keys
does contain None
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/python/cpython/blob/main/Parser/Python.asdl#L64 used to look like | Dict(expr?* keys, expr* values)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this line hasn't changed for 11 years: https://github.com/python/cpython/blame/3325f054e33b318aa56b74472f76a56b8afc0510/Parser/Python.asdl#L64
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
double_starred_kvpair[KeyValuePair*]:
| '**' a=bitwise_or { _PyPegen_key_value_pair(p, NULL, a) }
^^^^
| kvpair
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@youknowone Can we move forward with expr?* keys
or should we fix compiler/ast/asdl_rs.py
as I did in
ea9db0e?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think expr?*
is the right thing here. Not only is it probably not valid ASDL syntax (see here, the modifiers are in brackets as opposed to braces, though this might not really be an issue), but it is also kinda ambiguous, is it an Optional list or a list of optionals?
Can't we get the same behavior by modifying asdl_rs
and keeping Python.asdl
the same as CPythons?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not only is it probably not valid ASDL syntax (see here, the modifiers are in brackets as opposed to braces, though this might not really be an issue), but it is also kinda ambiguous, is it an Optional list or a list of optional?
Agreed!
Can't we get the same behavior by modifying asdl_rs and keeping Python.asdl the same as CPythons?
We can. I'm happy to revert the last commit.
@youknowone What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we could do, I prefer to keep Python.asdl as same as CPython. It was your original approach, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, updated the code.
Sure, i am sorry for that.I will add a comment that part is our local patch not to break it again in future2023. 1. 21. 12:16, Harutaka Kawamura ***@***.***> 작성:
@harupy commented on this pull request.
In compiler/ast/Python.asdl:
@@ -60,7 +60,7 @@ module Python
| UnaryOp(unaryop op, expr operand)
| Lambda(arguments args, expr body)
| IfExp(expr test, expr body, expr orelse)
- | Dict(expr* keys, expr* values)
+ | Dict(expr?* keys, expr* values)
@youknowone Can we move forward with expr?* keys?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Makes sense! Feel free to push a commit to this PR if necessary :) |
b0df453
to
88e3c83
Compare
@youknowone @DimitrisJim Updated the code! Would you mind taking another look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! And I am really sorry for confusing review for this PR 😅
Not a problem at all! |
This PR upgrades RustPython to fix the type of `Dict.keys` to `Vec<Option<Expr>>` (see RustPython/RustPython#4449 for why this change was needed) and unblock #1884.
Related to astral-sh/ruff#1879.
{"foo": 1, **{"bar": 1}}
is parsed into the AST below in CPython and RustPython. In CPython,Dict.keys
contains two keys (Constant
andNone
), but in RustPython,Dict.keys
only contains one key. This PR fixes it.CPython:
Expand
RustPython
Expand