Skip to content

Commit d657b74

Browse files
committed
Merge remote-tracking branch 'upstream/main' into bytecode-instr-super-macro
2 parents 9c3fd3e + 9cf7bcd commit d657b74

5 files changed

Lines changed: 438 additions & 141 deletions

File tree

‎.github/workflows/ci.yaml‎

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,42 +32,6 @@ env:
3232
CARGO_TERM_COLOR: always
3333

3434
jobs:
35-
determine_changes:
36-
name: Determine changes
37-
runs-on: ubuntu-latest
38-
outputs:
39-
# Flag that is raised when any rust code is changed.
40-
rust_code: ${{ steps.check_rust_code.outputs.changed }}
41-
steps:
42-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
43-
with:
44-
fetch-depth: 0
45-
persist-credentials: false
46-
47-
- name: Determine merge base
48-
id: merge_base
49-
run: |
50-
sha=$(git merge-base HEAD "origin/${BASE_REF}")
51-
echo "sha=${sha}" >> "$GITHUB_OUTPUT"
52-
env:
53-
BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }}
54-
55-
- name: Check if there was any code related change
56-
id: check_rust_code
57-
run: |
58-
if git diff --quiet "${MERGE_BASE}...HEAD" -- \
59-
':!Lib/**' \
60-
':!scripts/**' \
61-
':!extra_tests/**' \
62-
':.github/workflows/ci.yaml' \
63-
; then
64-
echo "changed=false" >> "$GITHUB_OUTPUT"
65-
else
66-
echo "changed=true" >> "$GITHUB_OUTPUT"
67-
fi
68-
env:
69-
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
70-
7135
rust_tests:
7236
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }}
7337
env:
@@ -145,13 +109,9 @@ jobs:
145109
if: runner.os == 'Linux'
146110

147111
cargo_check:
112+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }}
148113
name: cargo check
149114
runs-on: ${{ matrix.os }}
150-
needs:
151-
- determine_changes
152-
if: |
153-
!contains(github.event.pull_request.labels.*.name, 'skip:ci') &&
154-
needs.determine_changes.outputs.rust_code == 'true'
155115
strategy:
156116
matrix:
157117
include:
@@ -429,7 +389,6 @@ jobs:
429389
with:
430390
cache: false
431391
show-verbose-logs: false
432-
continue-on-error: true
433392

434393
- name: save prek cache
435394
if: ${{ github.ref == 'refs/heads/main' }} # only save on main
@@ -439,6 +398,7 @@ jobs:
439398
path: ~/.cache/prek
440399

441400
- name: reviewdog
401+
if: ${{ !cancelled() }}
442402
uses: reviewdog/action-suggester@aa38384ceb608d00f84b4690cacc83a5aba307ff # 1.24.0
443403
with:
444404
level: warning

‎.github/workflows/release.yml‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ env:
1616
X86_64_PC_WINDOWS_MSVC_OPENSSL_LIB_DIR: C:\Program Files\OpenSSL\lib\VC\x64\MD
1717
X86_64_PC_WINDOWS_MSVC_OPENSSL_INCLUDE_DIR: C:\Program Files\OpenSSL\include
1818

19+
permissions: {}
20+
1921
jobs:
2022
build:
2123
runs-on: ${{ matrix.os }}
2224
# Disable this scheduled job when running on a fork.
2325
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
26+
permissions:
27+
contents: read
2428
strategy:
2529
matrix:
2630
include:
@@ -84,6 +88,8 @@ jobs:
8488
runs-on: ubuntu-latest
8589
# Disable this scheduled job when running on a fork.
8690
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
91+
permissions:
92+
contents: read
8793
steps:
8894
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
8995
with:
@@ -147,6 +153,8 @@ jobs:
147153
# Disable this scheduled job when running on a fork.
148154
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
149155
needs: [build, build-wasm]
156+
permissions:
157+
contents: write # for creating a release
150158
steps:
151159
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
152160
with:
@@ -187,7 +195,7 @@ jobs:
187195
$PRERELEASE_ARG \
188196
bin/rustpython-release-*
189197
env:
190-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
198+
GH_TOKEN: ${{ github.token }}
191199
tag: ${{ github.ref_name }}
192200
run: ${{ github.run_number }}
193201
PRE_RELEASE_INPUT: ${{ github.event.inputs.pre-release }}

‎crates/codegen/src/compile.rs‎

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,11 +1512,14 @@ impl Compiler {
15121512
}
15131513

15141514
FBlockType::ForLoop => {
1515-
// Pop the iterator
1515+
// When returning from a for-loop, CPython swaps the preserved
1516+
// value with the iterator and uses POP_TOP for the iterator slot.
15161517
if preserve_tos {
15171518
emit!(self, Instruction::Swap { i: 2 });
1519+
emit!(self, Instruction::PopTop);
1520+
} else {
1521+
emit!(self, Instruction::PopIter);
15181522
}
1519-
emit!(self, Instruction::PopIter);
15201523
}
15211524

15221525
FBlockType::TryExcept => {
@@ -11148,6 +11151,99 @@ def f(base, cls, state):
1114811151
assert_eq!(return_count, 2);
1114911152
}
1115011153

11154+
#[test]
11155+
fn test_loop_store_subscr_threads_direct_backedge() {
11156+
let code = compile_exec(
11157+
"\
11158+
def f(kwonlyargs, kwonlydefaults, arg2value):
11159+
missing = 0
11160+
for kwarg in kwonlyargs:
11161+
if kwarg not in arg2value:
11162+
if kwonlydefaults and kwarg in kwonlydefaults:
11163+
arg2value[kwarg] = kwonlydefaults[kwarg]
11164+
else:
11165+
missing += 1
11166+
return missing
11167+
",
11168+
);
11169+
let f = find_code(&code, "f").expect("missing function code");
11170+
let ops: Vec<_> = f
11171+
.instructions
11172+
.iter()
11173+
.map(|unit| unit.op)
11174+
.filter(|op| !matches!(op, Instruction::Cache))
11175+
.collect();
11176+
11177+
let store_subscr = ops
11178+
.iter()
11179+
.position(|op| matches!(op, Instruction::StoreSubscr))
11180+
.expect("missing STORE_SUBSCR");
11181+
let next_op = ops
11182+
.get(store_subscr + 1)
11183+
.expect("missing jump after STORE_SUBSCR");
11184+
let window_start = store_subscr.saturating_sub(3);
11185+
let window_end = (store_subscr + 5).min(ops.len());
11186+
let window = &ops[window_start..window_end];
11187+
11188+
assert!(
11189+
matches!(next_op, Instruction::JumpBackward { .. }),
11190+
"expected direct loop backedge after STORE_SUBSCR, got {next_op:?}; ops={window:?}"
11191+
);
11192+
}
11193+
11194+
#[test]
11195+
fn test_loop_return_reorders_backedge_before_exit_cleanup() {
11196+
let code = compile_exec(
11197+
"\
11198+
def f(obj):
11199+
for base in obj.__mro__:
11200+
if base is not object:
11201+
doc = base.__doc__
11202+
if doc is not None:
11203+
return doc
11204+
",
11205+
);
11206+
let f = find_code(&code, "f").expect("missing function code");
11207+
let ops: Vec<_> = f
11208+
.instructions
11209+
.iter()
11210+
.map(|unit| unit.op)
11211+
.filter(|op| !matches!(op, Instruction::Cache))
11212+
.collect();
11213+
11214+
let cond_idx = ops
11215+
.iter()
11216+
.position(|op| matches!(op, Instruction::PopJumpIfNotNone { .. }))
11217+
.expect("missing POP_JUMP_IF_NOT_NONE");
11218+
assert!(
11219+
matches!(ops.get(cond_idx + 1), Some(Instruction::NotTaken)),
11220+
"expected NOT_TAKEN after conditional jump, got {:?}; ops={ops:?}",
11221+
ops.get(cond_idx + 1)
11222+
);
11223+
assert!(
11224+
matches!(
11225+
ops.get(cond_idx + 2),
11226+
Some(Instruction::JumpBackward { .. })
11227+
),
11228+
"expected loop backedge immediately after NOT_TAKEN, got {:?}; ops={ops:?}",
11229+
ops.get(cond_idx + 2)
11230+
);
11231+
11232+
let end_for_idx = ops
11233+
.iter()
11234+
.position(|op| matches!(op, Instruction::EndFor))
11235+
.expect("missing END_FOR");
11236+
let return_before_end = ops[..end_for_idx]
11237+
.iter()
11238+
.rposition(|op| matches!(op, Instruction::ReturnValue))
11239+
.expect("missing loop-body RETURN_VALUE");
11240+
assert!(
11241+
matches!(ops.get(return_before_end - 1), Some(Instruction::PopTop)),
11242+
"expected POP_TOP before loop-body RETURN_VALUE, got {:?}; ops={ops:?}",
11243+
ops.get(return_before_end.saturating_sub(1))
11244+
);
11245+
}
11246+
1115111247
#[test]
1115211248
fn test_assert_without_message_raises_class_directly() {
1115311249
let code = compile_exec(

0 commit comments

Comments
 (0)