Youg-Otricked/QuantumC

The compiler for the QuarticC (C⁴) programming language.

C++

8

432 commits

updated Sep 23, 2026

See the code

See what people are saying

SourceMessageScoreDate

The C^4 programming language

2

Sep 22, 2026

README

QuantumC (C⁴/C^4)

The 4th Evolution of C

QuantumC (C⁴) is a general-purpose systems programming language. It is not a quantum computing language. It does not simulate quantum logic, compile to quantum circuits, or run on quantum hardware. QuantumC (C^4) is unrelated to the C99-to-OpenQASM compiler, QuantumC

int main() {
    `qout("Hello, World!");
    return 0;
}

Docs are here. Book coming soon.

More Powerful Than Explosives™/j

C⁴ combines explicitness and simplicity to produce readable code, improving both DX and UX. Code should still make sense next week, even to the person who wrote it.


What is QuantumC?

QuantumC (C⁴/C^4) is a compiled, statically typed C-Style Systems programming language, focusing on clean code and low global scope pollution.


Video

Go to the docs

Quick Start

Install the binaries from GitHub, use the Package & Version Manager, or:

Installation

Dependencies

You must have clang installed. (Obviously, it's a clang project)


After cloning the repo to your machine, run.

cd QuantumC
chmod +x install.sh
./install.sh
# Now, you can type
qc [flags] (filepath)
# to run a file
# or
qc [flags]
# to enter the REPL

Sections


Versioning Scheme

QuantumC uses the following versioning scheme: cMa.Mo.MiP , where c is critical, for massive additions, such as the compiler being added, Ma being major versions, tracking large collections of features, Mo being moderate versions, tracking collections of similar features, Mi being minor versions, which track individual feature milestones within the current moderate version's theme, and P being the patch version. For the version x1.2.34 c = x Ma = 1 Mo = 2 Mi = 3 P = 4

P is omitted if it is 0. Critical versions represent the largest generational milestones in QuantumC's development.

v = Interpreter x = Compiler (Current) f = Feature-complete compiler s = Self-hosted compiler

Critical versions are intentionally rare and denote architectural milestones, not language features.

Development toward future critical versions may begin before the current critical version is complete. Multiple critical generations may therefore be in development simultaneously.

Minor (Mi) is always a single decimal digit (0-9). Once a minor version reaches 9, the next release increments the moderate version instead.

Unlike semantic versioning, QuantumC versions describe the scale and category of language evolution rather than API compatibility.

Development Status

Current Version: x1.0.47 = "I actually fixed try catch" Next Version: x1.0.5 = "Probably tagged enums"

Current Version Highlights

Critical
└─ Added Compiler

Major
└─ N/A

Moderate
└─ Modifiers

Minor
└─ Filesystem

Patch
└─ Fixed default args on namespaced functions

Recent Deprecations / Breaking Changes

These are deprecations in the past 3 moderate versions (x0.26.* -> x1.0.*)

Self parameter is now a pointer. This now requires explicit dereference (must use -> instead of .) Concepts as constraints must start with proves

Feature Roadmap

CategoryFeatureStatus
Core LogicVariables & Types (int, float, double, string, char, bool)Done
Constants (const) & auto InferenceDone
Long and Short typesDone
OperatorsStandard Math & Expressions (includes #^ power operator)Done
Control Flow (if/else, switch, while, for, foreach)Done
FunctionsFunctions with Default ParametersDone
Lambdas & Higher-Order FunctionsDone
Native Multi-Return ValuesDone
Data StructuresArrays (with .length member)Done
Spread Syntax for Arrays (@)Done
EnumsDone
AdvancedStructs, Classes, & NamespacesDone
Union Types (TypeScript-style)Done
Advanced OOP & Operator OverloadingDone
Manual Memory ManagementDone
SystemF-Strings (Python-style interpolation)Done
Random Number GenerationDone
Stdlib Part 1 & 2Done
FutureStdlib Part 3Planned
Inline ASMDone
GenericsDone
ClassesDone
StructsDone
UnionsDone
Functions & MethodsDone
Variadic Generic ArgumentsPlanned
restrict, out, inout, volatileDone
ExternDone
Bitwise LogicDone
Really fancy operator overloadsDone
Try/Catch and throwDone
Error message quality and helpfulness upgradeDone

See the full list of remaining features in the roadmap.

Contributing

If you want to contribute to C^4, fork the repository, make your changes, and open a pull request.

Commit Syntax

Commits should follow this format:

category(thing): message

Available categories:

improve   Improvements to existing functionality
feat      New functionality or language features
clean     Code cleanup, refactoring, or technical debt
errors    Changes to diagnostics, warnings, or error handling / adding more errors, warnings, notes, help, suggestions, etc
fix       Bug fixes

For example:

feat(parser, compiler): add modifier support
errors(warnings, diagnostics, help, suggestions): add help suggestions to warnings
clean(compiler): simplify type checking
fix(codegen): handle missing returns in main
improve(lexer): reduce tokenization overhead

If your commit is about multiple catagorys, comma-seperate.

errors(warnings),feat(cli-flags): added warnings & -W flags

But at least make your commits sensical. Unlike mine

FINALLY IM DONE WHY DID THAT TAKE 5 DAYS AAGHAGAHGAH IT WORKS

Please don't. If you forgot something in a previous commit, i like to use

oops(README): forgot to re-add readme.md

License

MIT License - See LICENSE for details


i2 Primitive Type

qbool qb = both;
qif (qb && qtrue /* evaluates to both */) {
    `qout("True path");   // Executes
} qelse {
    `qout("False path");
}


Manual Memory Management

int main() {
    int* ptr = `malloc(sizeof "int");
    *ptr = 42;
    `qout("%p", ptr); 
    `free(ptr);
}

Include system

namespace Exported {
/*
Exported namespaces are merged during include resolution.
See the include-system docs for full details.
*/
    #include <Math, std> // std is an alias for ~/.qc/lib/stdlib.qc. This line imports the Math namespace from the standard library.
}

int main() {
    return Math::Max(1234, 432); // Using the math namespace.
}

Namespaces can also declare dependencies on other namespaces in the same file using #depends, ensuring includers automatically see required types even if they only explicitly included one namespace from that file:

#depends(x: y)
namespace x { /* uses y::Something internally */ }
namespace y { /* ... */ }

Want to learn more? Check out the docs for it.


Why QuantumC?

FeatureC++ZigRustQuantumC
Total RuntimeMediumMediumMediumMedium
Compile Time (relative)SlowMediumMediumMedium
RuntimeFastMediumMediumFast
Memory safetyManualGPABorrow checkerManual
Multi-returnStructsTuplesTuplesNative
GenericsTemplates + ConceptsType as ArgumentTrait BasedConstraint-Based

Based on the last reliable benchmark results, QuantumC showed performance in the same general range as C++, while offering a similar set of quality-of-life improvements found in languages such as Zig.According to most recent benchmarks, in tested cases C^4 runs either at a similar or faster speed than C++, with equal or faster compiles.


Conventions

QuantumC has unusual naming conventions:

TypeConventionWhy?
Variablessnake_caseIt's familiar to Python devs who changed their ways, C++, C, Zig, Go, and Rust devs.
FunctionscamelCaseIt allows for instant knowledge between if an identifier is a var, or function (lambdas use var casing, not function casing)
User TypesPascalCaseIt is common across basically every programming language.
ConstantsSCREAMING_SNAKE_CASESame as above.
Private Member Variables__snake_caseVariable case prepended with __. Most underscores.
Protected Member Variables_snake_caseLess underscores.
Protected Methods__camelCaseUnique casing, more underscores.
Private Methodscamel_Snake_CaseFunction casing, more underscores.
NamespacesPascalCaseSame as user types.
Namespaces Not Meant For InclusionPascal_Snake_CaseUnique casing style, more underscores, you have to be trying to include this.
Global Scope Functionscamel_Snake_CaseUnique casing style, more underscores, similarity to private methods is intentional, because global scope cannot be included.
Methods Used By Compiler_camelCaseDifferent from everything else. (these methods are iterators and stuff. Methods you define and compiler uses)
Compiler Reserved_qc_, __qc_ and qc_Unique, hard to use accidently
Compiler Intrinsics` + snake_caseUnique, impossible to use accidently

Max line size is around 120 relative to your starting indentation, tabs or spaces, lf newlines, comments are //, doc comments are ///, and top-level doc comments are //!. File paths are unquoted, everything other than main should go in a namespace when applicable, and namespaces should fit the following rules:

  1. Namespaces should do one thing well, similar to the UNIX philosophy,

  2. Namespaces should have either:

     1. one type or group of tightly related types such as bigints and their core helpers,
     2. above + namespaces containing extra helpers
     3. helper functions / utility functions (think a `Math` namespace with log, cos...)
     4. OR anything if directly mapping  C/C++/Zig/Rust code to C^4
    
  3. Types in namespaces should have short names: The namespace should have the longer name e.g.

namespace Array {
    class Arr<T, int S = 0> {
        ...
    }
}

Pointer asterisks bind to the type rather than the variable. The final * belongs to the declarator, unless it's a function return type. Then it's all on the type.

int** *x;
int* ptr_add(int *p) ...

Files are kebab-case. This is optional. QuantumC naming conventions are designed to make code readable without requiring the reader to inspect library code. Names should provide immediate context. My rule: RTFM once, not RTMSCE5S (Read The Manual and Source Code Every 5 Seconds), and these conventions make things hard to forget or mis-type, unlike C/C++ where every library uses entirely different conventions.

Example:

namespace Network {
    class Client {
        string server_name;

        void connectToServer() {
            ...
        }
    }
}
namespace Not_Embezzeling { // Intentionally formatted as a non-inclusion namespace.
    // Sure, you may not want to type all of that. That means your users absolutely don't.
    ..
}

Ideals

QuantumC follows four core rules:

  • Forced Cleanliness: QuantumC is designed to reward readable code. Language features should make the obvious solution the clean solution. Clean is not defined as 'Convenient for language', it means what it should be. Clean is not a "pythonic" equivalent; it is self-explanatory.
  • Your Memory, Your Problem: QuantumC does not prevent dangerous code. It expects the programmer to understand the consequences. If you want, you can write a segfault handler with a segfault in it. QuantumC is strongly typed, but union types are designed to be ergonomic rather than restrictive. Unlike Rust or TypeScript, QuantumC does not force exhaustive narrowing before every union operation. I will give you a loaded shotgun. If you blow your leg off, don't blame the gunsmith.
  • No Hiding: QuantumC is an explicit language, so your code does what it looks like it does. Nothing is hidden inside the parser while pretending to be stdlib, nothing is hidden away in some back catacomb. If it is an intrinsic, it says it is.
  • No Excessive Syntax: No capture lists on lambdas, no templates, no infinite <>, no Rust "bird droppings", no ! and @ everywhere.

Compiler Architecture

QuantumC uses a classic multi-pass compilation pipeline:

  1. Lexical Analysis / Preprocessing: Custom lexer converts text to tokens in one loop.
  2. AST Parsing: Recursive descent parser generating a strongly typed Abstract Syntax Tree.
  3. Type Checking & Semantic Analysis: Resolves user-defined types, namespaces, and TypeScript-style union types. The unique thing is that this pass is merged with the compilation/codegen phase
  4. Intermediate Representation (IR): Generates LLVM IR. Target-aware pointer arithmetic is achieved dynamically via target-specific DataLayout queries.
  5. Codegen: Emits native platform object files (.o) or WebAssembly binaries via LLVM's target machines.

Multi Return

int, string GetStatus() {
    return 200, "Success";
}
int main() {
    int code, string alias = GetStatus();
    `qout("%s", f"Code: {code}, AKA: {alias}\n");
    return 0;
}

Type collections and aliases

Define variables that can hold multiple types using a simple | syntax. The parser automatically distinguishes these from standard aliases:

int main() {
    // A Union Type (TypeScript-style)
    type IdT = int | string;
    IdT id = 101;
    id = "A101"; // Perfectly valid

    // A Standard Alias
    type UserID = int;
    UserID myId = 5;
}

C interop and inline ASM

Extern "C" is the only supported extern style, and thus no string is needed to say where you are externing to.

extern:
int add(int a, int b) {
    return a + b;
}
:extern

Extern is only for externalizing api. To use foreign code, you must put it in a foreign block.

foreign:
int do_some_c_stuff(int x);
:foreign

The QuantumC inline ASM syntax is like a simplified version of the GCC syntax:

inline(R"(
mov rax, 1
mov rdi, 1 
mov rsi, $0r ; argument 1 (Hello, World!) 
mov rdx, $1r ; argument 2 (14)
syscall
)"/* your inline asm string */, "Hello, World", 14, "~{rax,rdi,rsi,rdx}" /* clobbers */);  

You can also use AT&T ASM syntax by making sure the first 5 characters of your ASM string are

; ATT

Storage & Argument Specifiers

QuantumC has 4 special (non-const) storage modifiers.

  1. volatile Volatile means the compiler won't optimize it. It can be used before variable declarations, or on function definitions. On methods, it must go after the access modifier and final, and before the return type.
volatile void infinite_time() {
    while(true) {
    }
    return;
}
  1. restrict

restrict tells the compiler that this pointer is the exclusive access path to the referenced memory. Other unrelated pointers must not access the same memory in a way that violates the restrict contract. This allows more aggressive optimization.

Example:

void doSomePointer(int *restrict ptr, int *other) {
    *ptr = 10;
    *other = 20; // Undefined behavior if other points to the same memory as ptr
}
  1. out Out tells the compiler that this parameter is write-only, and this memory address will not be copied. It also only exists for optimization purposes.
void writeOnly(out int p) {
    p = 123; // OK
    int x = p; // ILLEGAL. Breaks contract.
    int *x = &p; // ILLEGAL also.
}
  1. inout Inout tells the compiler that this parameter will be read and written from, but its address will not be copied.
void rw(inout int p) {
    p = 123; // OK
    int x = p; // OK
    int *x = &p; // NOT OK
}

Variadic arguments

QuantumC variadic arguments look like this:

int add_all(...args) {
    int res = 0;
    while (!`is_empty(args)) {
        res += `next(args, "int");
    }
    return res;
}

args is a variadic argument (hence the ...), which can be passed any number of parameters. is_empty is a runtime compiler intrinsic that takes a variadic arg-list and returns whether it is empty. next is a runtime compiler intrinsic that takes a variadic arg-list and the expected type and returns the next element in the arg list cast to that type.

You may use C-Style variadic arguments too, but only in foreign blocks.

foreign:
void printf(char* fmt, ...);
:foreign

Bitwise logic

QuantumC has all the standard bitwise logic operators; however, it has a non-standard XOR and Right-Shift token. The Bitwise XOR operator in C^4 is $. It is $ because ^ and ^^ are already used tokens, and C^4 avoids repeating tokens to improve quick readability. The same logic applies for right-shift: |> is the right-shift token, because it allows the parser to immediately determine:

Node<Vector<X>> // Is this RSHIFT or ending generics?

This is easy to determine (because it would be illegal for rshift to be there); however, using this non-normal rshift operator allows it to be instant because:

Node<Vector<X>> // This is unrelated to RSHIFT

QuantumC also has a special operator for logical right shift: :>. This is because it allows avoiding constantly casting between signed and unsigned integer types, unlike C++'s "arithmetic if signed, logical if unsigned"

2 more unique bitwise things QuantumC does:

  1. Built-in rotations: <<< and |>> and LROT and RROT, allowing 1 instruction rotations instead of 6+.
    • and - have lower priority than shifts. 3 << 3 + 2 == 26. This is because the shift expression l << r is equivalent to l * 2 ^ r. If r was 2 + 2, that would be l * 2 ^ 2 + 2, which would be l * 4 + 2.

Inheritance

All methods are virtual. If you want to use polymorphism, inherited methods MUST be defined in the same order as the parent class, and new methods must be defined after.

Generics

QuantumC generics have simple syntax:

class C<T> {
    T x;
    C() {
    }
}
int main() {
    C<int> thing = C();
}

Generics are allowed on structs, classes, concepts, unions, aliases, functions, and methods. The unique thing about QuantumC's generics is its constraint system: The constraint system follows this syntax:

<T([constraint]:[[!]<[typename]'|'...>]>

Main constraints are as follows:

usertype: non-primitive type
primitive: primitive type
pointer: any pointer type
numeric: any numeric type

So

<T(numeric:)>

Can be any numeric type. The subconstraint system is like this:

<T(:!int|string)>

This means "T can be any type other than int or string". The ! means not anything in this set, and the type|type means these types. QuantumC also has non-type generic parameters.

<int S> // S is a non-type generic parameter (a compile time int)

In Rust,

<T(numeric:)>

Would be

<T: std::ops::Add<Output = T> + std::ops::Sub<Output = T> + std::ops::Mul<Output = T> + std::ops::Div<Output = T> + PartialOrd + Copy>

And in C++, it would be

template <typename T>
requires std::is_arithmetic_v<T>
T

or in old SFINAE C++

template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
T

And in Zig:

...(comptime T: type, ...) ... {
    switch (@typeInfo(T)) {
        .Int, .Float => ...,
        else => @compileError("T must be numeric"),
    }
}

Iterators

Iterators work as follows: The class you want to iterate must define the following method:

MyIterator _begin() // Returns an iterator to the beginning of the class

_end is optional, and returns an iterator to the end of the class. The iterator itself MUST define the following 2 methods:

bool _atEnd() // Returns true if there is nothing left to iterate
T _next() // Returns the current value then moves to the next

and may define the following recommended extra methods:

bool _atStart() // Returns true if the iterator is currently at index 0
T _prev() // Returns the element at current index -- and decrements current index
void _moveTo(whateveryouwant idx) // sets index to idx
MyIterator<U> _map<U>(fn(T elem) -> U) // preforms a operation on every element
MyClass<T> _collect() // returns the class that this iterator iterates
MyIterator<T> _filter(fn(T elem) -> bool) // returns a iterator of only the elements that are true for that lambda
void _inval_destroy() // invalidates the collection this iterator iterates
MyIterator<T> _take(int n) // returns a iterator to the first n elements
MyIterator<T> _drop(int n) // returns a iterator skipping the first n elements

Example:

class ArrayIterator<T> {
    T* data;
    int size;
    int current_index;
    ArrayIterator(T* data, int size, bool is_end) {
        this.data = data;
        this.size = size;
        this.current_index = `ternary(is_end, size, 0);
    }
    bool _atEnd() {
        return this.size <= this.current_index;
    }
    T _next() {
        if (!this._atEnd()) {
            return this.data[this.current_index++];
        }
        return this.data[this.current_index];
    } 
    bool _atStart() {
        return this.current_index <= 0;
    }
    T _prev() {
        if (!this._atStart()) {
            return this.data[--this.current_index];
        }
        return this.data[this.current_index];
    }
    void _moveTo(int index) {
        if (index >= this.size) {
            index = this.size - 1;
        } else if (index < 0) {
            index = 0;
        }
        this.current_index = index;
    }
}
class Array<T, int S = 0> {
    T* data;
    int size;
    Array() {
        this.data = nullptr;
        this.size = 0;
    }
    void operator[]=(T* data, int length) {
        if (length > S) {
            this.size = length;
        } else {
            this.size = S;
        }
        this.data = `malloc(sizeof "T" * this.size);
        for (int i = 0; i < this.size; i++) {
            this.data[i] = data[i];
        }
    }
    T operator[](int index) {
        return this.data[index];
    }
    ArrayIterator<T> _begin() {
        return ArrayIterator<T>(this.data, this.size, false);
    }
    ArrayIterator<T> _end() {
        return ArrayIterator<T>(this.data, this.size, true);
    }
}

The standard library collections have iterators in the structure of this:

namespace Array
    namespace Iterator {
        class It {
            ...
        }
    }
    class Arr {
        Iterator::It _begin() {
            ...
        }
        ...
    }
}

Try/Catch and Error Returns

C^4 supports both try/catch exception handling and multireturn/unions for error handling. You are encouraged to use both, or combine them where appropriate. Just document if you throw or not. Example:

int main() {
    try {
        throw 123;
    } catch (int e) {
        ...
    }
}

Or for union-based:

struct myerror {
    ...
}
type MyResult = int | myerror;
MyResult myThing() {
    return myerror{...};
}

Nested try/catch blocks are not allowed becuase they just obfuscate what your code is doing without adding any new functionality.

Defer

C^4 has defer, like Go and Zig. Unlike Go's defer, C^4 defer does not participate in stack unwinding. Deferred code is not automatically executed when an exception propagates out of a scope. This behavior is intentional: making defer participate in stack unwinding would introduce additional runtime overhead. defer should not be considered a destructor mechanism. It is a scope-exit convenience feature, not RAII.

Concepts

C^4's concepts are like a combination of Typescript interfaces, Rust traits, and C++ concepts.

concept Printable {
    1_of {
        void print();
        void print(Self self);
    }
    default {
class:
        void print() {
            `qout("Printing. . .");
        }
else:
        void print(Self self) {
            `qout ("Printing. . .");
        }
    }
}
class PDF {
    string data;
    PDF(string data) {
        this.data = data;
    }
    void print() {
        `qout("%s", this.data);
    }
}
class ASCII {
    string data;
    ASCII(string data) {
        this.data = data;
    }
}
struct Paper {
    string data;
}
type File = PDF | ASCII;
/// Print would print PDFs data
PDF proves Printable;
/// Print would print "Printing. . ."
ASCII proves Printable;
/// Print would print the paper's data
Paper proves Printable with_proof { // with_proof block to add additional definitions, like impl in Rust
    void print(Self self) {
        `qout("%s", self.data);
    }
}
/// Would print "Printing. . ."
File proves Printable;

The Self arguments are explicit for non-class methods added through concepts. This is to emphasize structs are not classes, and should stick to being POD. Concepts only allow methods on structs because it's better for DRY than having 50 functions with similar names.

Simple File Example

namespace Exported {
    #include<Vector, std>
}
int main() {
    Vector::Vec<int> my_vec = [1, 2, 3];
    my_vec.push(123);
    `qout("%i", my_vec[2]);
    return 0;
}

Self Hosted Runtime

There is currently an unknown bug with the self-hosted runtime.

Benchmarks

All benchmarks were executed on Linux (x86_64) using /usr/bin/time across 500 runs per optimization tier (4,000 total benchmark runs). Workload: identical compute-heavy arithmetic and bitwise algorithm global state. Src can be found in the bench<...> files in the project root.


1. Executive Summary

CategoryWinnerAdvantage
Compilation SpeedC^4 (qc)~1.85x faster (up to 47.3% lower latency)
Compiler Memory (Peak RSS)C^4 (qc)31.6% less RAM (~60 MB leaner)
Compiler OS Page FaultsC^4 (qc)68.4% fewer page faults (~10,000 fewer faults/compile)
Unoptimized Runtime (-O0)C^4 (qc)2.0x faster (50.0% reduction in execution time)
Optimized Runtime (-O1-O3)TieExact Parity (Both clock identical 30.00 ms)
Binary Runtime MemoryC^4 (qc)44.7% less memory (2.11 MB vs 3.81 MB)

2. Compilation Performance (500 Runs Averaged)

Speed & CPU Overhead

Opt LevelCompilerElapsed TimeUser CPU TimeSys CPU TimeCPU UtilRelative Speed
-O0Clang C++301.54 ms271.60 ms23.82 ms99.0%1.00x
C^4 (qc)159.02 ms95.40 ms8.50 ms68.7%1.90x faster (-47.3%)
-O1Clang C++313.14 ms282.86 ms23.64 ms99.0%1.00x
C^4 (qc)168.74 ms48.28 ms11.22 ms39.0%1.86x faster (-46.1%)
-O2Clang C++314.12 ms283.80 ms24.12 ms99.0%1.00x
C^4 (qc)169.04 ms49.26 ms11.40 ms39.5%1.86x faster (-46.2%)
-O3Clang C++315.72 ms284.98 ms23.88 ms99.0%1.00x
C^4 (qc)171.54 ms55.04 ms11.78 ms42.6%1.84x faster (-45.7%)

Note: At -O3, C^4 uses 5.2x less User CPU time (55.04 ms vs 284.98 ms) by eliminating C++ preprocessor and template instantiation overhead.

Memory & System Overhead

Opt LevelCompilerPeak RSS (MB)Minor Page FaultsMajor FaultsI/O (Out)
-O0Clang C++177.84 MB (182,110 KB)14,777.7040.0
C^4 (qc)129.70 MB (132,818 KB)5,080.10416.0
Delta-27.1% RAM-65.6% faults
-O1Clang C++177.97 MB (182,239 KB)14,802.2040.0
C^4 (qc)128.79 MB (131,886 KB)4,625.0072.0
Delta-27.6% RAM-68.8% faults
-O2Clang C++177.97 MB (182,244 KB)14,835.1040.0
C^4 (qc)128.75 MB (131,841 KB)4,627.8072.0
Delta-27.6% RAM-68.8% faults
-O3Clang C++188.09 MB (192,601 KB)14,635.2040.0
C^4 (qc)128.73 MB (131,819 KB)4,620.6072.0
Delta-31.6% RAM (-59.4 MB)-68.4% faults

3. Binary Runtime & Efficiency (500 Runs Averaged)

Execution Time

Opt LevelClang (C++) BinaryC^4 BinarySpeedup / Delta
-O060.00 ms30.00 ms2.00x faster (+100% speedup)
-O130.00 ms30.00 msParity (1.00x)
-O230.00 ms30.00 msParity (1.00x)
-O330.00 ms30.00 msParity (1.00x)

Runtime Memory & Footprint

Opt LevelBinaryPeak RSS (KB)Peak RSS (MB)Minor Faults
-O0Clang C++3,891.34 KB3.80 MB150.9
C^4 (qc)2,334.83 KB2.28 MB97.9
Delta-39.9% memory-35.1% faults
-O1Clang C++3,892.55 KB3.80 MB150.7
C^4 (qc)2,156.53 KB2.11 MB90.6
Delta-44.6% memory-39.9% faults
-O2Clang C++3,898.58 KB3.81 MB150.8
C^4 (qc)

Known Limitations

&& and || do not short-circuit on unions. Volatile does not exist on structs or class fields, and does not work on property accesses. In general, volatile is not 100%.

Standard Library Namespaces:

Vector
List
Array
AdvQBool
Math
Utils
OSInterop

Made by Luca Fazio

compiler
cpp20
lang
not-toy
programming-language
usable-programming-language

Contributors

Youg-Otricked

432 commits

Youg-Otricked/QuantumC

The compiler for the QuarticC (C⁴) programming language.

C++

8

432 commits

updated Sep 23, 2026

See the code

See what people are saying

SourceMessageScoreDate

The C^4 programming language

2

Sep 22, 2026

README

QuantumC (C⁴/C^4)

The 4th Evolution of C

QuantumC (C⁴) is a general-purpose systems programming language. It is not a quantum computing language. It does not simulate quantum logic, compile to quantum circuits, or run on quantum hardware. QuantumC (C^4) is unrelated to the C99-to-OpenQASM compiler, QuantumC

int main() {
    `qout("Hello, World!");
    return 0;
}

Docs are here. Book coming soon.

More Powerful Than Explosives™/j

C⁴ combines explicitness and simplicity to produce readable code, improving both DX and UX. Code should still make sense next week, even to the person who wrote it.


What is QuantumC?

QuantumC (C⁴/C^4) is a compiled, statically typed C-Style Systems programming language, focusing on clean code and low global scope pollution.


Video

Go to the docs

Quick Start

Install the binaries from GitHub, use the Package & Version Manager, or:

Installation

Dependencies

You must have clang installed. (Obviously, it's a clang project)


After cloning the repo to your machine, run.

cd QuantumC
chmod +x install.sh
./install.sh
# Now, you can type
qc [flags] (filepath)
# to run a file
# or
qc [flags]
# to enter the REPL

Sections


Versioning Scheme

QuantumC uses the following versioning scheme: cMa.Mo.MiP , where c is critical, for massive additions, such as the compiler being added, Ma being major versions, tracking large collections of features, Mo being moderate versions, tracking collections of similar features, Mi being minor versions, which track individual feature milestones within the current moderate version's theme, and P being the patch version. For the version x1.2.34 c = x Ma = 1 Mo = 2 Mi = 3 P = 4

P is omitted if it is 0. Critical versions represent the largest generational milestones in QuantumC's development.

v = Interpreter x = Compiler (Current) f = Feature-complete compiler s = Self-hosted compiler

Critical versions are intentionally rare and denote architectural milestones, not language features.

Development toward future critical versions may begin before the current critical version is complete. Multiple critical generations may therefore be in development simultaneously.

Minor (Mi) is always a single decimal digit (0-9). Once a minor version reaches 9, the next release increments the moderate version instead.

Unlike semantic versioning, QuantumC versions describe the scale and category of language evolution rather than API compatibility.

Development Status

Current Version: x1.0.47 = "I actually fixed try catch" Next Version: x1.0.5 = "Probably tagged enums"

Current Version Highlights

Critical
└─ Added Compiler

Major
└─ N/A

Moderate
└─ Modifiers

Minor
└─ Filesystem

Patch
└─ Fixed default args on namespaced functions

Recent Deprecations / Breaking Changes

These are deprecations in the past 3 moderate versions (x0.26.* -> x1.0.*)

Self parameter is now a pointer. This now requires explicit dereference (must use -> instead of .) Concepts as constraints must start with proves

Feature Roadmap

CategoryFeatureStatus
Core LogicVariables & Types (int, float, double, string, char, bool)Done
Constants (const) & auto InferenceDone
Long and Short typesDone
OperatorsStandard Math & Expressions (includes #^ power operator)Done
Control Flow (if/else, switch, while, for, foreach)Done
FunctionsFunctions with Default ParametersDone
Lambdas & Higher-Order FunctionsDone
Native Multi-Return ValuesDone
Data StructuresArrays (with .length member)Done
Spread Syntax for Arrays (@)Done
EnumsDone
AdvancedStructs, Classes, & NamespacesDone
Union Types (TypeScript-style)Done
Advanced OOP & Operator OverloadingDone
Manual Memory ManagementDone
SystemF-Strings (Python-style interpolation)Done
Random Number GenerationDone
Stdlib Part 1 & 2Done
FutureStdlib Part 3Planned
Inline ASMDone
GenericsDone
ClassesDone
StructsDone
UnionsDone
Functions & MethodsDone
Variadic Generic ArgumentsPlanned
restrict, out, inout, volatileDone
ExternDone
Bitwise LogicDone
Really fancy operator overloadsDone
Try/Catch and throwDone
Error message quality and helpfulness upgradeDone

See the full list of remaining features in the roadmap.

Contributing

If you want to contribute to C^4, fork the repository, make your changes, and open a pull request.

Commit Syntax

Commits should follow this format:

category(thing): message

Available categories:

improve   Improvements to existing functionality
feat      New functionality or language features
clean     Code cleanup, refactoring, or technical debt
errors    Changes to diagnostics, warnings, or error handling / adding more errors, warnings, notes, help, suggestions, etc
fix       Bug fixes

For example:

feat(parser, compiler): add modifier support
errors(warnings, diagnostics, help, suggestions): add help suggestions to warnings
clean(compiler): simplify type checking
fix(codegen): handle missing returns in main
improve(lexer): reduce tokenization overhead

If your commit is about multiple catagorys, comma-seperate.

errors(warnings),feat(cli-flags): added warnings & -W flags

But at least make your commits sensical. Unlike mine

FINALLY IM DONE WHY DID THAT TAKE 5 DAYS AAGHAGAHGAH IT WORKS

Please don't. If you forgot something in a previous commit, i like to use

oops(README): forgot to re-add readme.md

License

MIT License - See LICENSE for details


i2 Primitive Type

qbool qb = both;
qif (qb && qtrue /* evaluates to both */) {
    `qout("True path");   // Executes
} qelse {
    `qout("False path");
}


Manual Memory Management

int main() {
    int* ptr = `malloc(sizeof "int");
    *ptr = 42;
    `qout("%p", ptr); 
    `free(ptr);
}

Include system

namespace Exported {
/*
Exported namespaces are merged during include resolution.
See the include-system docs for full details.
*/
    #include <Math, std> // std is an alias for ~/.qc/lib/stdlib.qc. This line imports the Math namespace from the standard library.
}

int main() {
    return Math::Max(1234, 432); // Using the math namespace.
}

Namespaces can also declare dependencies on other namespaces in the same file using #depends, ensuring includers automatically see required types even if they only explicitly included one namespace from that file:

#depends(x: y)
namespace x { /* uses y::Something internally */ }
namespace y { /* ... */ }

Want to learn more? Check out the docs for it.


Why QuantumC?

FeatureC++ZigRustQuantumC
Total RuntimeMediumMediumMediumMedium
Compile Time (relative)SlowMediumMediumMedium
RuntimeFastMediumMediumFast
Memory safetyManualGPABorrow checkerManual
Multi-returnStructsTuplesTuplesNative
GenericsTemplates + ConceptsType as ArgumentTrait BasedConstraint-Based

Based on the last reliable benchmark results, QuantumC showed performance in the same general range as C++, while offering a similar set of quality-of-life improvements found in languages such as Zig.According to most recent benchmarks, in tested cases C^4 runs either at a similar or faster speed than C++, with equal or faster compiles.


Conventions

QuantumC has unusual naming conventions:

TypeConventionWhy?
Variablessnake_caseIt's familiar to Python devs who changed their ways, C++, C, Zig, Go, and Rust devs.
FunctionscamelCaseIt allows for instant knowledge between if an identifier is a var, or function (lambdas use var casing, not function casing)
User TypesPascalCaseIt is common across basically every programming language.
ConstantsSCREAMING_SNAKE_CASESame as above.
Private Member Variables__snake_caseVariable case prepended with __. Most underscores.
Protected Member Variables_snake_caseLess underscores.
Protected Methods__camelCaseUnique casing, more underscores.
Private Methodscamel_Snake_CaseFunction casing, more underscores.
NamespacesPascalCaseSame as user types.
Namespaces Not Meant For InclusionPascal_Snake_CaseUnique casing style, more underscores, you have to be trying to include this.
Global Scope Functionscamel_Snake_CaseUnique casing style, more underscores, similarity to private methods is intentional, because global scope cannot be included.
Methods Used By Compiler_camelCaseDifferent from everything else. (these methods are iterators and stuff. Methods you define and compiler uses)
Compiler Reserved_qc_, __qc_ and qc_Unique, hard to use accidently
Compiler Intrinsics` + snake_caseUnique, impossible to use accidently

Max line size is around 120 relative to your starting indentation, tabs or spaces, lf newlines, comments are //, doc comments are ///, and top-level doc comments are //!. File paths are unquoted, everything other than main should go in a namespace when applicable, and namespaces should fit the following rules:

  1. Namespaces should do one thing well, similar to the UNIX philosophy,

  2. Namespaces should have either:

     1. one type or group of tightly related types such as bigints and their core helpers,
     2. above + namespaces containing extra helpers
     3. helper functions / utility functions (think a `Math` namespace with log, cos...)
     4. OR anything if directly mapping  C/C++/Zig/Rust code to C^4
    
  3. Types in namespaces should have short names: The namespace should have the longer name e.g.

namespace Array {
    class Arr<T, int S = 0> {
        ...
    }
}

Pointer asterisks bind to the type rather than the variable. The final * belongs to the declarator, unless it's a function return type. Then it's all on the type.

int** *x;
int* ptr_add(int *p) ...

Files are kebab-case. This is optional. QuantumC naming conventions are designed to make code readable without requiring the reader to inspect library code. Names should provide immediate context. My rule: RTFM once, not RTMSCE5S (Read The Manual and Source Code Every 5 Seconds), and these conventions make things hard to forget or mis-type, unlike C/C++ where every library uses entirely different conventions.

Example:

namespace Network {
    class Client {
        string server_name;

        void connectToServer() {
            ...
        }
    }
}
namespace Not_Embezzeling { // Intentionally formatted as a non-inclusion namespace.
    // Sure, you may not want to type all of that. That means your users absolutely don't.
    ..
}

Ideals

QuantumC follows four core rules:

  • Forced Cleanliness: QuantumC is designed to reward readable code. Language features should make the obvious solution the clean solution. Clean is not defined as 'Convenient for language', it means what it should be. Clean is not a "pythonic" equivalent; it is self-explanatory.
  • Your Memory, Your Problem: QuantumC does not prevent dangerous code. It expects the programmer to understand the consequences. If you want, you can write a segfault handler with a segfault in it. QuantumC is strongly typed, but union types are designed to be ergonomic rather than restrictive. Unlike Rust or TypeScript, QuantumC does not force exhaustive narrowing before every union operation. I will give you a loaded shotgun. If you blow your leg off, don't blame the gunsmith.
  • No Hiding: QuantumC is an explicit language, so your code does what it looks like it does. Nothing is hidden inside the parser while pretending to be stdlib, nothing is hidden away in some back catacomb. If it is an intrinsic, it says it is.
  • No Excessive Syntax: No capture lists on lambdas, no templates, no infinite <>, no Rust "bird droppings", no ! and @ everywhere.

Compiler Architecture

QuantumC uses a classic multi-pass compilation pipeline:

  1. Lexical Analysis / Preprocessing: Custom lexer converts text to tokens in one loop.
  2. AST Parsing: Recursive descent parser generating a strongly typed Abstract Syntax Tree.
  3. Type Checking & Semantic Analysis: Resolves user-defined types, namespaces, and TypeScript-style union types. The unique thing is that this pass is merged with the compilation/codegen phase
  4. Intermediate Representation (IR): Generates LLVM IR. Target-aware pointer arithmetic is achieved dynamically via target-specific DataLayout queries.
  5. Codegen: Emits native platform object files (.o) or WebAssembly binaries via LLVM's target machines.

Multi Return

int, string GetStatus() {
    return 200, "Success";
}
int main() {
    int code, string alias = GetStatus();
    `qout("%s", f"Code: {code}, AKA: {alias}\n");
    return 0;
}

Type collections and aliases

Define variables that can hold multiple types using a simple | syntax. The parser automatically distinguishes these from standard aliases:

int main() {
    // A Union Type (TypeScript-style)
    type IdT = int | string;
    IdT id = 101;
    id = "A101"; // Perfectly valid

    // A Standard Alias
    type UserID = int;
    UserID myId = 5;
}

C interop and inline ASM

Extern "C" is the only supported extern style, and thus no string is needed to say where you are externing to.

extern:
int add(int a, int b) {
    return a + b;
}
:extern

Extern is only for externalizing api. To use foreign code, you must put it in a foreign block.

foreign:
int do_some_c_stuff(int x);
:foreign

The QuantumC inline ASM syntax is like a simplified version of the GCC syntax:

inline(R"(
mov rax, 1
mov rdi, 1 
mov rsi, $0r ; argument 1 (Hello, World!) 
mov rdx, $1r ; argument 2 (14)
syscall
)"/* your inline asm string */, "Hello, World", 14, "~{rax,rdi,rsi,rdx}" /* clobbers */);  

You can also use AT&T ASM syntax by making sure the first 5 characters of your ASM string are

; ATT

Storage & Argument Specifiers

QuantumC has 4 special (non-const) storage modifiers.

  1. volatile Volatile means the compiler won't optimize it. It can be used before variable declarations, or on function definitions. On methods, it must go after the access modifier and final, and before the return type.
volatile void infinite_time() {
    while(true) {
    }
    return;
}
  1. restrict

restrict tells the compiler that this pointer is the exclusive access path to the referenced memory. Other unrelated pointers must not access the same memory in a way that violates the restrict contract. This allows more aggressive optimization.

Example:

void doSomePointer(int *restrict ptr, int *other) {
    *ptr = 10;
    *other = 20; // Undefined behavior if other points to the same memory as ptr
}
  1. out Out tells the compiler that this parameter is write-only, and this memory address will not be copied. It also only exists for optimization purposes.
void writeOnly(out int p) {
    p = 123; // OK
    int x = p; // ILLEGAL. Breaks contract.
    int *x = &p; // ILLEGAL also.
}
  1. inout Inout tells the compiler that this parameter will be read and written from, but its address will not be copied.
void rw(inout int p) {
    p = 123; // OK
    int x = p; // OK
    int *x = &p; // NOT OK
}

Variadic arguments

QuantumC variadic arguments look like this:

int add_all(...args) {
    int res = 0;
    while (!`is_empty(args)) {
        res += `next(args, "int");
    }
    return res;
}

args is a variadic argument (hence the ...), which can be passed any number of parameters. is_empty is a runtime compiler intrinsic that takes a variadic arg-list and returns whether it is empty. next is a runtime compiler intrinsic that takes a variadic arg-list and the expected type and returns the next element in the arg list cast to that type.

You may use C-Style variadic arguments too, but only in foreign blocks.

foreign:
void printf(char* fmt, ...);
:foreign

Bitwise logic

QuantumC has all the standard bitwise logic operators; however, it has a non-standard XOR and Right-Shift token. The Bitwise XOR operator in C^4 is $. It is $ because ^ and ^^ are already used tokens, and C^4 avoids repeating tokens to improve quick readability. The same logic applies for right-shift: |> is the right-shift token, because it allows the parser to immediately determine:

Node<Vector<X>> // Is this RSHIFT or ending generics?

This is easy to determine (because it would be illegal for rshift to be there); however, using this non-normal rshift operator allows it to be instant because:

Node<Vector<X>> // This is unrelated to RSHIFT

QuantumC also has a special operator for logical right shift: :>. This is because it allows avoiding constantly casting between signed and unsigned integer types, unlike C++'s "arithmetic if signed, logical if unsigned"

2 more unique bitwise things QuantumC does:

  1. Built-in rotations: <<< and |>> and LROT and RROT, allowing 1 instruction rotations instead of 6+.
    • and - have lower priority than shifts. 3 << 3 + 2 == 26. This is because the shift expression l << r is equivalent to l * 2 ^ r. If r was 2 + 2, that would be l * 2 ^ 2 + 2, which would be l * 4 + 2.

Inheritance

All methods are virtual. If you want to use polymorphism, inherited methods MUST be defined in the same order as the parent class, and new methods must be defined after.

Generics

QuantumC generics have simple syntax:

class C<T> {
    T x;
    C() {
    }
}
int main() {
    C<int> thing = C();
}

Generics are allowed on structs, classes, concepts, unions, aliases, functions, and methods. The unique thing about QuantumC's generics is its constraint system: The constraint system follows this syntax:

<T([constraint]:[[!]<[typename]'|'...>]>

Main constraints are as follows:

usertype: non-primitive type
primitive: primitive type
pointer: any pointer type
numeric: any numeric type

So

<T(numeric:)>

Can be any numeric type. The subconstraint system is like this:

<T(:!int|string)>

This means "T can be any type other than int or string". The ! means not anything in this set, and the type|type means these types. QuantumC also has non-type generic parameters.

<int S> // S is a non-type generic parameter (a compile time int)

In Rust,

<T(numeric:)>

Would be

<T: std::ops::Add<Output = T> + std::ops::Sub<Output = T> + std::ops::Mul<Output = T> + std::ops::Div<Output = T> + PartialOrd + Copy>

And in C++, it would be

template <typename T>
requires std::is_arithmetic_v<T>
T

or in old SFINAE C++

template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
T

And in Zig:

...(comptime T: type, ...) ... {
    switch (@typeInfo(T)) {
        .Int, .Float => ...,
        else => @compileError("T must be numeric"),
    }
}

Iterators

Iterators work as follows: The class you want to iterate must define the following method:

MyIterator _begin() // Returns an iterator to the beginning of the class

_end is optional, and returns an iterator to the end of the class. The iterator itself MUST define the following 2 methods:

bool _atEnd() // Returns true if there is nothing left to iterate
T _next() // Returns the current value then moves to the next

and may define the following recommended extra methods:

bool _atStart() // Returns true if the iterator is currently at index 0
T _prev() // Returns the element at current index -- and decrements current index
void _moveTo(whateveryouwant idx) // sets index to idx
MyIterator<U> _map<U>(fn(T elem) -> U) // preforms a operation on every element
MyClass<T> _collect() // returns the class that this iterator iterates
MyIterator<T> _filter(fn(T elem) -> bool) // returns a iterator of only the elements that are true for that lambda
void _inval_destroy() // invalidates the collection this iterator iterates
MyIterator<T> _take(int n) // returns a iterator to the first n elements
MyIterator<T> _drop(int n) // returns a iterator skipping the first n elements

Example:

class ArrayIterator<T> {
    T* data;
    int size;
    int current_index;
    ArrayIterator(T* data, int size, bool is_end) {
        this.data = data;
        this.size = size;
        this.current_index = `ternary(is_end, size, 0);
    }
    bool _atEnd() {
        return this.size <= this.current_index;
    }
    T _next() {
        if (!this._atEnd()) {
            return this.data[this.current_index++];
        }
        return this.data[this.current_index];
    } 
    bool _atStart() {
        return this.current_index <= 0;
    }
    T _prev() {
        if (!this._atStart()) {
            return this.data[--this.current_index];
        }
        return this.data[this.current_index];
    }
    void _moveTo(int index) {
        if (index >= this.size) {
            index = this.size - 1;
        } else if (index < 0) {
            index = 0;
        }
        this.current_index = index;
    }
}
class Array<T, int S = 0> {
    T* data;
    int size;
    Array() {
        this.data = nullptr;
        this.size = 0;
    }
    void operator[]=(T* data, int length) {
        if (length > S) {
            this.size = length;
        } else {
            this.size = S;
        }
        this.data = `malloc(sizeof "T" * this.size);
        for (int i = 0; i < this.size; i++) {
            this.data[i] = data[i];
        }
    }
    T operator[](int index) {
        return this.data[index];
    }
    ArrayIterator<T> _begin() {
        return ArrayIterator<T>(this.data, this.size, false);
    }
    ArrayIterator<T> _end() {
        return ArrayIterator<T>(this.data, this.size, true);
    }
}

The standard library collections have iterators in the structure of this:

namespace Array
    namespace Iterator {
        class It {
            ...
        }
    }
    class Arr {
        Iterator::It _begin() {
            ...
        }
        ...
    }
}

Try/Catch and Error Returns

C^4 supports both try/catch exception handling and multireturn/unions for error handling. You are encouraged to use both, or combine them where appropriate. Just document if you throw or not. Example:

int main() {
    try {
        throw 123;
    } catch (int e) {
        ...
    }
}

Or for union-based:

struct myerror {
    ...
}
type MyResult = int | myerror;
MyResult myThing() {
    return myerror{...};
}

Nested try/catch blocks are not allowed becuase they just obfuscate what your code is doing without adding any new functionality.

Defer

C^4 has defer, like Go and Zig. Unlike Go's defer, C^4 defer does not participate in stack unwinding. Deferred code is not automatically executed when an exception propagates out of a scope. This behavior is intentional: making defer participate in stack unwinding would introduce additional runtime overhead. defer should not be considered a destructor mechanism. It is a scope-exit convenience feature, not RAII.

Concepts

C^4's concepts are like a combination of Typescript interfaces, Rust traits, and C++ concepts.

concept Printable {
    1_of {
        void print();
        void print(Self self);
    }
    default {
class:
        void print() {
            `qout("Printing. . .");
        }
else:
        void print(Self self) {
            `qout ("Printing. . .");
        }
    }
}
class PDF {
    string data;
    PDF(string data) {
        this.data = data;
    }
    void print() {
        `qout("%s", this.data);
    }
}
class ASCII {
    string data;
    ASCII(string data) {
        this.data = data;
    }
}
struct Paper {
    string data;
}
type File = PDF | ASCII;
/// Print would print PDFs data
PDF proves Printable;
/// Print would print "Printing. . ."
ASCII proves Printable;
/// Print would print the paper's data
Paper proves Printable with_proof { // with_proof block to add additional definitions, like impl in Rust
    void print(Self self) {
        `qout("%s", self.data);
    }
}
/// Would print "Printing. . ."
File proves Printable;

The Self arguments are explicit for non-class methods added through concepts. This is to emphasize structs are not classes, and should stick to being POD. Concepts only allow methods on structs because it's better for DRY than having 50 functions with similar names.

Simple File Example

namespace Exported {
    #include<Vector, std>
}
int main() {
    Vector::Vec<int> my_vec = [1, 2, 3];
    my_vec.push(123);
    `qout("%i", my_vec[2]);
    return 0;
}

Self Hosted Runtime

There is currently an unknown bug with the self-hosted runtime.

Benchmarks

All benchmarks were executed on Linux (x86_64) using /usr/bin/time across 500 runs per optimization tier (4,000 total benchmark runs). Workload: identical compute-heavy arithmetic and bitwise algorithm global state. Src can be found in the bench<...> files in the project root.


1. Executive Summary

CategoryWinnerAdvantage
Compilation SpeedC^4 (qc)~1.85x faster (up to 47.3% lower latency)
Compiler Memory (Peak RSS)C^4 (qc)31.6% less RAM (~60 MB leaner)
Compiler OS Page FaultsC^4 (qc)68.4% fewer page faults (~10,000 fewer faults/compile)
Unoptimized Runtime (-O0)C^4 (qc)2.0x faster (50.0% reduction in execution time)
Optimized Runtime (-O1-O3)TieExact Parity (Both clock identical 30.00 ms)
Binary Runtime MemoryC^4 (qc)44.7% less memory (2.11 MB vs 3.81 MB)

2. Compilation Performance (500 Runs Averaged)

Speed & CPU Overhead

Opt LevelCompilerElapsed TimeUser CPU TimeSys CPU TimeCPU UtilRelative Speed
-O0Clang C++301.54 ms271.60 ms23.82 ms99.0%1.00x
C^4 (qc)159.02 ms95.40 ms8.50 ms68.7%1.90x faster (-47.3%)
-O1Clang C++313.14 ms282.86 ms23.64 ms99.0%1.00x
C^4 (qc)168.74 ms48.28 ms11.22 ms39.0%1.86x faster (-46.1%)
-O2Clang C++314.12 ms283.80 ms24.12 ms99.0%1.00x
C^4 (qc)169.04 ms49.26 ms11.40 ms39.5%1.86x faster (-46.2%)
-O3Clang C++315.72 ms284.98 ms23.88 ms99.0%1.00x
C^4 (qc)171.54 ms55.04 ms11.78 ms42.6%1.84x faster (-45.7%)

Note: At -O3, C^4 uses 5.2x less User CPU time (55.04 ms vs 284.98 ms) by eliminating C++ preprocessor and template instantiation overhead.

Memory & System Overhead

Opt LevelCompilerPeak RSS (MB)Minor Page FaultsMajor FaultsI/O (Out)
-O0Clang C++177.84 MB (182,110 KB)14,777.7040.0
C^4 (qc)129.70 MB (132,818 KB)5,080.10416.0
Delta-27.1% RAM-65.6% faults
-O1Clang C++177.97 MB (182,239 KB)14,802.2040.0
C^4 (qc)128.79 MB (131,886 KB)4,625.0072.0
Delta-27.6% RAM-68.8% faults
-O2Clang C++177.97 MB (182,244 KB)14,835.1040.0
C^4 (qc)128.75 MB (131,841 KB)4,627.8072.0
Delta-27.6% RAM-68.8% faults
-O3Clang C++188.09 MB (192,601 KB)14,635.2040.0
C^4 (qc)128.73 MB (131,819 KB)4,620.6072.0
Delta-31.6% RAM (-59.4 MB)-68.4% faults

3. Binary Runtime & Efficiency (500 Runs Averaged)

Execution Time

Opt LevelClang (C++) BinaryC^4 BinarySpeedup / Delta
-O060.00 ms30.00 ms2.00x faster (+100% speedup)
-O130.00 ms30.00 msParity (1.00x)
-O230.00 ms30.00 msParity (1.00x)
-O330.00 ms30.00 msParity (1.00x)

Runtime Memory & Footprint

Opt LevelBinaryPeak RSS (KB)Peak RSS (MB)Minor Faults
-O0Clang C++3,891.34 KB3.80 MB150.9
C^4 (qc)2,334.83 KB2.28 MB97.9
Delta-39.9% memory-35.1% faults
-O1Clang C++3,892.55 KB3.80 MB150.7
C^4 (qc)2,156.53 KB2.11 MB90.6
Delta-44.6% memory-39.9% faults
-O2Clang C++3,898.58 KB3.81 MB150.8
C^4 (qc)

Known Limitations

&& and || do not short-circuit on unions. Volatile does not exist on structs or class fields, and does not work on property accesses. In general, volatile is not 100%.

Standard Library Namespaces:

Vector
List
Array
AdvQBool
Math
Utils
OSInterop

Made by Luca Fazio

compiler
cpp20
lang
not-toy
programming-language
usable-programming-language

Contributors

Youg-Otricked

432 commits

Languages

C++

93.6%

C

6.1%