Repository navigation
pycc-next v0.2.0
Rust speed, numpy, more Python. Every benchmark is within 3x of hand-written Rust,
typed numpy arrays compile to native loads and stores, strict mode compiles 124/200
real-world programs unchanged (was 93), and --non-strict runs 95% of their top-level
statements natively (was 84%). Prebuilt archives for Linux x86_64 and macOS 26+ on
Apple silicon.
Added
-
Typed numpy arrays:
numpy.ndarray/NDArray[float64|int64|bool]of 1 and 2
dimensions are native in strict and--extcode: element reads and writes,.shape,
len, iteration,numpy.zeros/ones/empty/array, with no CPython call in the loop
body. Benchmarksstencil2d(436x faster than CPython, 2.5x of Rust) and
prefix_sum_np(80x, 1.8x). -
macOS arm64: a
pycc-vX.Y.Z-aarch64-macos.tar.gzarchive (macOS 26.0+, Apple
silicon), built and smoke-tested next to the Linux one; CI runs the full test suite
and the corpus gate on macos-26. -
Strict mode infers the signature of an unannotated public function or method that
only the module itself calls; conflicting call sites,--extmodules and exported
functions still need annotations (T0001). -
--non-strictcompiles more everyday code natively: CPython objects as indexes,
slice bounds andrange()arguments (__index__), mixed-type containers promoted to
CPython containers,str % args,strmethods andsys.stdout.writewith object
arguments, keyedsort/sorted/min/maxandheapq/bisecton objects,
x is True/x is False, names bound on only some paths (unbound reads raise
UnboundLocalError/NameErroras in CPython),open(0)as standard input,
sys.stdin.bufferas bytes. -
10 ** -10is a float, as in CPython (was a compile error). -
The generator protocol, natively in strict mode:
send(),throw(),
close()(GeneratorExit, "generator ignored GeneratorExit"),yieldas
an expression,return valueasStopIteration.value,yield from
delegating all three and returning the subgenerator's value, PEP 479, and
generators in module globals closed bydeland at exit. -
Full call signatures:
*args, keyword-only parameters,**kwargs,
positional-only parameters and defaults of every kind, for functions,
methods, constructors and nested functions, called with keywords,*seq
and**mapping, with CPython'sTypeErrormessages. -
Function values (module-level functions and closures) stored in lists,
dicts and instance attributes and called from there (fs[0](x),
table[k](x),self.f(x));print,str()and f-strings render a
function or a generator as CPython does (<function f at 0x...>,
<generator object g at 0x...>). -
--ext: a public function or method with defaults, positional-only or
keyword-only parameters,*argsor**kwargsis published to Python
callers with its declared signature (#1194); CPython binds the call and
raises its ownTypeErrors.
Fixed
- Debug builds (no
--release) stored aNone-typed variable --x = None,
orgot = yield vin a generator whose send type isNone-- as two bytes
into its one-byte slot, overrunning the stack frame: depending on the
layout a generator lost a yielded value or the program crashed. Codegen
now refuses, as an internal error in both profiles, any store wider than
the stack slot or global it writes. - A nested function's plain assignment to a
nonlocalvariable (k = v, not
onlyk += v) is seen by the enclosing function; it used to rebind a copy. sys.stdin.bufferread asstrprinted and compared unlike CPython's bytes.- Security review: an object used as an index could leak a heap int; a
StopIteration.valueof another type is refused instead of reinterpreted;
gen.send(None)is type-checked; numpy array headers are re-read after any call
that may run Python.
Performance
- Every benchmark is within 3x of its Rust twin (fib 1.8x, mandelbrot 1.1x, nbody 2.4x,
sieve 2.1x, spectral_norm 1.8x; v0.1: up to 4.2x). Whole-program proofs drop checks that
cannot fire: an attribute every constructor assigns before the instance escapes skips the
"assigned" test; a function that cannot raise is called without the exception check; a
defthat precedes all module code skips the call-before-definition check. A tree of
intoperators tests its leaves' tags once;x += yreleases the old value only on the
bigint path; range loops with one level of nested loops count with a machine integer;
x ** 0.5provespow == sqrtwith exact integer arithmetic. bench/run.py: best of 7 by default; Rust twins build for the default target CPU and
black-box every input.PYCC_DUMP_LLVM=<path>writes the LLVM IR before and after optimization.
Known gaps
- See
docs/DEVIATIONS.mdfor every known difference from CPython 3.13.