Z# (Zee-sharp)

A new .NET language

Meta Programming

Allow to write Z# code that executes at compile time in order to shift the workload as much as possible to the compiler.

.NET we can let the Z# compiler extract all the compile time code and generate an assembly behind the scenes during compilation. Then we can integrate calls to this assembly during further compilation.


Intrinsic Attributes

The compiler will allow accessing intrinsic attributes of the compiled code. These attributes are constants whose value was determined by the compiler at compile time.

A special operator is used to access them: #

Attribute Description
#type Full type info.
#typeid A unique numerical type identifier.
#name The identifier (name).
#min The minimum value possible.
#max The maximum value possible.
#bits The number of bits for the type.
#count The number of elements.
#size The size in bytes the type takes up in memory.
#default Default value for the type.
#mask Mask for retrieving a bit field value.
#offset Byte offset from the start of a structure to a field.
#expr Expression that was passed as a parameter (tracing)
#caller name of the caller of the function (tracing)

Not all types support all attributes. The compiler will give an error when the code accesses an attribute that is not supported by the type in question.

a = 42      // U8
a#size      // 1
a#bits      // 8
a#min       // 0
a#max       // 255
a#name      // 'a'

U8#size     // 1
U8#bits     // 8
U8#min      // 0
U8#max      // 255
U8#name     // 'U8'

Bit<3>#size // 1
Bit<3>#bits // 3
Bit<3>#min  // 0
Bit<3>#max  // 7
Bit<3>#name // 'Bit3'

Perhaps have a very short version of #$name (no space) for it will be used most often. (# = compile-time, $ = string)

a := 42
#$a              // 'a'
#$U8             // 'U8'
#$MyFunction     // 'MyFunction'

Compiler Directives

TBD: Difference between Compiler-Directives and -Functions? Directives have no return value? Directives have no () around their parameters?

Directive Description
module Assigning code to a module
use Importing code from a module
pub Making code public
push Pushing compiler configuration onto the (compile-time) stack
pop Popping compiler configuration from the (compile-time) stack
enable Enable a compiler feature (checks)
ignore Disable a compiler warning
restore re-enable previously disabled warnings
align align structure
pack pack structure
#ignore("CW3091") // ignore this warning
code_that_causes_CW3091
more_code_that_causes_CW3091
#restore("CW3091")  // back to normal

well_behaved_code

TBD: Hints to the compiler how to compile code…

Syntax?

#inline     // pragma (hint)
@inline()   // compiler function
[[inline]]  // extension/decorator
inlineFn: (p: U8): Bool -> p = 42

The expression body function syntax = could be used for inline functions…

Hint Description
inline duplicate function body at each call site (before optimization)
align x line struct up at a memory address that is a multiple of specified value

Use standard .NET code attributes for align? (I think those only work on .NET structs and not on classes)

TBD: allow call-site inlineing? b := #inline inlineFn(42)


TBD

Add a directive that provides the compilation context and the project settings (think Visual Studio macros).

Project Setting Description
#project.targetPath Full path to the generated assembly.
#project.configuration Debug or Release

Compiler context are the current file, line, column, symbol etc functions/directives?


Compiler Functions

A compilier-function call is prefixed with #.

A compiler function instructs the compiler to take some action. For instance turn off a compiler warning temporarily.

#ignore("CW3091")
code_that_causes_CW3091
more_code_that_causes_CW3091
#restore("CW3091")

well_behaved_code

The compiler supplies a set of functions that allows interaction with- and modification of the generated code. There is also contextual information available for formatting diagnostic messages.

Function Note
line() the current source code line number
col() the current source code column number
file() the current source code file name
module() the current module the source code is part of
name() the name of current function or type being compiled
location() Fully formatted file/name/line/col text.
msg := "Error in '{#file()}' at line {#line()}: {#name()} is invalid."

Compile-Time Code

Any Z# code can be executed at compile-time. By placing a #! in front of the function, the compiler knows it is not to be included in the binary.

TBD: #! is considered for compiler-error directive. Something else: #>?

m = MyStruct
    ...

// this code can only run at compile time and is not included in the binary
#! compTimeFn: <T>(m: T)
    t = m#type
    t.name                      // 'MyStruct'
    loop f in t.fields
        "field: {f.name} of type {f.type.name}"

#compTimeFn(m)   // ok, call at compile time

// normal runtime function included in the binary
runtimeFn: <T>(m: T)
    ...

# runtimeFn(m) // call at compile time. Error if function body cannot be run at compile-time.
runtimeFn(m)      // call at runtime.

// alternate: use a #run directive to run any code at compile time.
#run
    runTimeFn(m)    // can give compile error
    compTimeFn(m)   // run after previous

Type Information

The full Type information is available at compile-time and runtime as is provided by the dotnet metadata (reflection).


Type Traits

This needs to be redone.

A Type-characteristic that defines a specific behavior.

A lot of the IsXxxx properties on the .NET Type class are traits.

fn: <T>()
    // special syntax?
    T::IsIntegral
    T::IsImmutable
    // traits as common template functions
    IsIntegral<T>()
    IsImmutable<T>()

// custom trait
#! IsTrait: <T>(): Bool
    ...

All traits are compile time functions. (You may want this at runtime?)


Symbol Exists

TBD: Test for field exists

MyStruct
    field1: U8

s := MyStruct
    ...

// does 'field1' exist at compile time?
if s?#field1
    ...

// does 'field1' exist at runtime (dynamic)?
if s?field1
    ...

TBD: Transparently pass custom meta programs to roslyn source generators?

TBD: Allow compile time code to build code using (perhaps) a builder pattern. TypeBuilder, FunctionBuilder and CodeBuilder. Building the structure is not the problem, but the function body is. How to add the actual code (CodeBuilder)?

TBD: How could we expose the type information of the to-be-compiled code to meta/compile time code?

TBD: Give compile time code access to the type info of source code and possibly extend the source code. The original code cannot be altered but it can be enhanced or extended. Pre and post processing of functions, wrapping of structs, implementing interfaces etc. See also Extensions. Also influencing type inferrence desicions the compiler makes for generic or template types.

TBD: #! = compile error, #? compile warning, #$ compile info.

if (compile-time-condition)
    #! Hey, this is not suppose to happen.