Skip to content

py/{compile,runtime}: Fix *args after kwarg. #11441

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

dlech
Copy link
Contributor

@dlech dlech commented May 7, 2023

This fixes a compiler/runtime bug where *args after a kwarg was not handled correctly.

Prior to this change, if *args was encountered in a function call after a keyword argument, the compiler would push a single object and increment the positional arg count. However, two objects for the keyword argument key and value had already been pushed. This caused inconsistencies that the runtime could not resolve since it expects all of the positional args first followed by key/value pairs for the keyword args.

To fix it, we need to conditionally change what happens when *args is encountered depending on if it is before or after the first keyword argument. If it is before, everything is handled as before. If after, instead of pushing a single object and incrementing the positional arg count, we push two objects and increment the keyword arg count. This makes it possible for the runtime to handle it with minimal changes.

In the runtime, we have to add some extra checks to handle the new case of the possibility that one of the n_kw args is a *arg. We already have a case where **arg is handled as a keyword argument where the key is MP_OBJ_NULL. We now do the same for *arg as well. The existing star_args flags is used to determine if the value corresponding to a key of MP_OBJ_NULL is *arg or **arg.

A couple of test that failed before this fix are added.

Fixes: #11439

@github-actions
Copy link

github-actions bot commented May 7, 2023

Code size report:

   bare-arm:  +132 +0.234% 
minimal x86:  +192 +0.103% 
   unix x64:  +192 +0.024% standard
      stm32:  +128 +0.033% PYBV10
        rp2:  +136 +0.042% PICO

@dlech
Copy link
Contributor Author

dlech commented May 7, 2023

Technically, this is a breaking change for the MPY ABI but since it is a bug, I'm not sure what to suggest about bumping the MPY version for this or not. The current compiler (released in v1.19 and v1.20) generates invalid bytecode which will will trigger undefined behavior in the interpreter both before and after the fix for the interpreter is applied. Correct bytecode generated after this fix is applied will trigger undefined behavior if run in in the old broken interpreter versions.

Since the old broken interpreter has undefined behavior in any case, it makes this seem not strictly a breaking change.

This fixes a compiler/runtime bug where *args after a kwarg was not
handled correctly.

Prior to this change, if `*args` was encountered in a function call
after a keyword argument, the compiler would push a single object
and increment the positional arg count. However, two objects for the
keyword argument key and value had already been pushed. This caused
inconsistencies that the runtime could not resolve since it expects
all of the positional args first followed by key/value pairs for the
keyword args.

To fix it, we need to conditionally change what happens when `*args`
is encountered depending on if it is before or after the first keyword
argument. If it is before, everything is handled as before. If after,
instead of pushing a single object and incrementing the positional arg
count, we push two objects and increment the keyword arg count. This
makes it possible for the runtime to handle it with minimal changes.

In the runtime, we have to add some extra checks to handle the new
case of the possibility that one of the `n_kw` args is a `*arg`. We
already have a case where `**arg` is handled as a keyword argument
where the key is `MP_OBJ_NULL`. We now do the same for `*arg` as well.
The existing `star_args` flags is used to determine if the value
corresponding to a key of `MP_OBJ_NULL` is `*arg` or `**arg`.

A couple of test that failed before this fix are added.

Fixes: micropython#11439
Signed-off-by: David Lechner <david@pybricks.com>
@dlech dlech force-pushed the fix-star-arg-after-kwarg branch from 1faf1cc to 49cec95 Compare May 7, 2023 22:57
@codecov-commenter
Copy link

codecov-commenter commented May 7, 2023

Codecov Report

Merging #11441 (49cec95) into master (1093dea) will increase coverage by 0.00%.
The diff coverage is 100.00%.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

@@           Coverage Diff           @@
##           master   #11441   +/-   ##
=======================================
  Coverage   98.39%   98.39%           
=======================================
  Files         156      156           
  Lines       20603    20619   +16     
=======================================
+ Hits        20272    20288   +16     
  Misses        331      331           
Impacted Files Coverage Δ
py/compile.c 99.87% <100.00%> (+<0.01%) ⬆️
py/runtime.c 98.90% <100.00%> (+0.01%) ⬆️

@dlech
Copy link
Contributor Author

dlech commented May 7, 2023

Not sure why the macos test failed. It doesn't seem like it could possibly be affected by these changes and I can't reproduce locally on my Mac.

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label May 8, 2023
@dpgeorge
Copy link
Member

dpgeorge commented May 8, 2023

I re-ran the macos CI (are you able to do that yourself, as the author of this PR?) and it now passes.

@dlech
Copy link
Contributor Author

dlech commented May 8, 2023

(are you able to do that yourself, as the author of this PR?

No, that option is not available for me.

@dlech
Copy link
Contributor Author

dlech commented May 9, 2023

image

In addition to Developer Certificate of Origin (Signed-off-by:) are we now also requiring commits to be cryptographically signed?

@jimmo
Copy link
Member

jimmo commented May 10, 2023

In addition to Developer Certificate of Origin (Signed-off-by:) are we now also requiring commits to be cryptographically signed?

That was not the intention.

@dpgeorge
Copy link
Member

IMO the complexity added here to the runtime is too much. And honestly I think this syntax is bad and should not be used, one should always specify positional arguments before keyword arguments.

@jimmo pointed out the following inconsistency with this syntax (running on CPython):

>>> def f(a, b): pass
... 
>>> # the following is an error
>>> f(b=1, 2)
  File "<stdin>", line 1
    f(b=1, 2)
            ^
SyntaxError: positional argument follows keyword argument
>>>
>>> # but the following is allowed
>>> f(b=1, *(2,))
>>> 

My suggestion would be that we just disallow this syntax, disallow *args after keyword args in the compiler (raise a SyntaxError in this case). That's at least better than the current situation of silently doing the wrong thing.

Users can still write CPython compatible code: just put *args before all keyword args.

@stinos
Copy link
Contributor

stinos commented Jan 23, 2024

My suggestion would be that we just disallow this syntax, disallow *args after keyword args in the compiler (raise a SyntaxError in this case)

+1 I doubt there's a lot of code out there which uses this syntax

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
py-core Relates to py/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Weird (mis)behaviour when passing kwarg along with *args
5 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy