Code-X Framework

The Code-X workflow is used to generate source code in different programming languages, based on the primary Code-X compliant C# code. Code generation features full syntactic and semantic translation: the resulting code is 100% functional and does not require any human post-processing.

To run the Code-X workflow, please use the Code-X Processor, which is provided as the SDK component codex.

Code Model

Only Code-X compliant source code can be processed with the Code-X workflow. To be considered compliant, source code must meet these conditions:

  1. It must be written in the C# programming language, using the language version 7.3.

  2. It must be parseable via CxCodeUnit, which imposes additional syntactic restrictions.

  3. It must be valid according to the code inspections that are defined for the Code-X workflow and impose additional semantic restrictions.

Condition (1) is satisfied if the code compiles successfully.

Conditions (2) and (3) are satisfied if the check command of Code-X Processor finishes successfully.

The Tinman.Core library contains a model for Code-X compliant code, which can be used to verify the above conditions programmatically, for example.

The CodeX.Framework library provides the functionality of the Code-X workflow and can be used to embed the Code-X Processor logic in applications.

The following code model description does not include statements and expressions.

Basic Structure

Code Model 1
Figure 1. Code-X Model / Library

A library is a collection of code units. References to external code elements of a code unit may be resolved within the scope of the enclosing library.

Each code unit represents a single C# source code file and may contain a single Delegate, Enumeration or Type declaration.

Contracts

Code MOdel 2
Figure 2. Code-X Model / Contract

Represents a contract for a member parameter, method return or property value.

Declarations

This section describes the type and member declarations of the code model.

Delegate

Code Model 3
Figure 3. Code-X Model / Delegate

This is a C# delegate declaration with optional Documentation.

Enumeration

Code Model 4
Figure 4. Code-X Model / Enumeration

This is a C# enum declaration with optional Documentation.

Member

Code Model 5
Figure 5. Code-X Model / Member

This is a C# member declaration with optional Documentation, which may be a constant, a constructor, a field, a method or a property.

Overloaded operators are represented as special methods, indexers are represented as special properties.

Type

Code Model 6
Figure 6. Code-X Model / Type

This is a C# class, interface or struct declaration with optional Documentation.

Type declarations contain zero or more Member declarations, which must be wrapped in regions.

Documentation

Code Model 7
Figure 7. Code-X Model / Documentation

Represents a source code comment that is formatted as XmlDoc content.

Types

Code Model 8
Figure 8. Code-X Model / Types

This represents the runtime type system of Code-X.

Code Inspections

The CxInspection class defines a constant for each inspection that exists for verification of Code-X compliance, where the inspection flags depict its name:

  • CX00000 …​ CX0FFFF (critical)

  • CX10000 …​ CX1FFFF (errors)

  • CX20000 …​ CX2FFFF (warnings)

  • CX30000 …​ CX3FFFF (notices)

  • CX40000 …​ CXFFFFF (reserved for future use)

The code examples on this page use the bullets 1…​4 to show where the problematic code is located:

... (1)
... (2)
... (3)
... (4)
1 A critical error
2 A non-critical error
3 A warning
4 A notice
Work in progress

The inspection messages on this page are being consolidated into CxInspection implementations. Documentation will move from this page to the respective constant, for example CX00001. When encountering an inspection message that is only documented on this page, please keep the following in mind:

  • The indicated severity (critical, error, warning, notice) might not be accurate.

  • The semantic meaning of the inspection might overlap with others.

  • The rules imposed by the inspection might be too strict.

Cpu-X Workflow

The Cpu-X workflow consumes Code-X compliant C# code and generates source code in other programming languages, which is intended to be executed by CPUs.

The following sections explain those inspection messages of the workflow which are not generated by a CxInspection object.

The code snippets illustrate the inspection messages and may not be correct regarding syntax and semantic.

Critical

Critical errors prevent the code generator from being run or the generated code from being compiled without errors and cannot be ignored.

All binary expressions must evaluate to simple types

Since there is no operator overloading in Code-X, all binary expressions must evaluate to bool, char, int8, int16, int32, int64, float32, float64 or an enumeration type.

"A" + "B" (1)
Assignment operators must not be used on pointers

Pointer variables of type IntPtr are translated to void*, which does not allow pointer arithmetic.

IntPtr p = ...;
...
p += 1234; (1)
Assignment operators must not be used on properties

C# properties may get translated to getter and/or setter methods, which prohibits the use of assignment operators.

SomeClass o = ...;
...
o.SomeProperty += 1234; (1)
Bad member name: X

The member name X is not allowed, because is violates one or more of the following rules:

  1. It must not start with CODEX_.

  2. It must not start with an underscore (_).

  3. It must not end with an underscore (_).

  4. It must not contain a double-underscore (__).

  5. It must not be a reserved name.

  6. It must not be a prohibited name.

Bad variable name: X

The local variable, loop variable or parameter name X is not allowed, because it violates one or more of the rules for member names, except that it may end with an underscore (_).

Base type of non-struct type parameter must not be a [StaticTemplate] interface

A non-struct type parameter has a base interface that is annotated with StaticTemplateAttribute.

[StaticTemplate]
interface IMyInterface
{
  ...
}
...
class MyClass1<T> where T : IMyInterface (1)
class MyClass2<T> where T : class,
                            IMyInterface (1)
Base type of struct type parameter must be a [StaticTemplate] interface

A struct type parameter has a base interface that is neither IEquatable nor IComparable and is not annotated with StaticTemplateAttribute.

interface IMyInterface
{
  ...
}
...
class MyClass<T> where T : struct,
                           IMyInterface (1)
Boxing of non-object values is not supported

An expression implicitly causes a non-object value to be boxed as an object, which is not allowed. Use Boxed for explicit boxing of non-object values.

object o;
o = 1234; (1)
o = "1234"; (1)
o = new int[1234]; (1)
Code regions must be named 'Native'

A native code region is named incorrectly and will not be recognized by the code generator.

#region Natively {00000000-0000-0000-0000-000000000000} (1)
...
#endregion
Default section must be last

The default branch of a switch statement is not the last one, which is not allowed: code generation must be able to convert any switch statement to a sequence of if statements, without having to perform non-trivial code refactoring.

switch(...)
{
  default: (1)
    break;

  case ...:
    break;
}
Delegate type directly or indirectly refers back to itself

A delegate refers back to itself via its parameter ist and/or its return type, which is not allowed: such constructs cannot be converted to all Code-X targets.

delegate void MyDelegate(MyDelegate arg); (1)
delegate MyDelegate MyDelegate(); (1)
Duplicate GUID: X

There are two or more native code regions that have the same GUID.

#region Native {01234567-0000-0000-0000-000000000000} (1)
...
#endregion

#region Native {01234567-0000-0000-0000-000000000000} (1)
...
#endregion
Duplicate type IDs: X <-> Y

Two or more type declarations have the same type ID. The ID of a type is defined as the hash (see HashUtil.Str) of the fully-qualified type name (see FullName of CxDeclaration).

Duplicate type name: X

Two or more types within the same project have the same non-qualified name, which is not allowed.

namespace A
{
  class C { ... } (1)
}

namespace B
{
  class C { ... } (1)
}
Exception object is sliced

Code-X treats exceptions as values. Assigning an exception value to a base type in C++ will slice the object, which must be avoided.

class BoomException : Exception { ... }

Exception Boom()
{
  return new BoomException(); (1)
}
Expressions with more than one side effect must not be nested

The evaluation order of expressions is fully defined by the C# specification. However, this is not necessarily the case for all Code-X targets. To compensate for this, multiple nested expressions with side-effects are not allowed (see PureAttribute).

int[] a = ...;
int b = 0;

a[b++] = a[--b]; (1)
SomeMethod(b++, --b); (1)
Extension methods must be defined in the namespace of their target type

The declaration of an extension method is not located in the same namespace as its target type.

namespace A
{
  static class MyExtensions
  {
    static void MyMethod(this MyClass obj) { ... } (1)
  }
}

namespace B
{
  class MyClass { ... }
}
Initializer of constant must not refer to another constant

A constants with a string or non-primitive value type refers to another constant in its initializer expression. The creates a dependency on the order of initialization, which cannot be guaranteed for all Code-X targets.

const string A = "...";
const string B = A + "!"; (1)

static readonly IntPtr A = IntPtr.Zero;
static readonly IntPtr B = A + 1; (1)
Invalid array type binary expression

In Code-X, arrays cannot be treated as objects, as it is possible in C#.

int[] a = ...;
object o = ...;

if (a == o) (1)
  ...;
Invalid assertion throw statement

A method with the [Assertion] attribute does not have the required form:

  • The body must contain zero or more if statements.

  • All if statements must not have a false branch.

  • Each true branch must contain exactly one throw statement.

  • Each throw statement must have an invocation expression that resolves to a factory method.

  • All factory methods must be public and static, they must return a value that is assignable to Exception and they must have the [Assertion] attribute.

[Assertion]
class MyException : Exception
{
  public static ExceptionA Boom() { ... }
}
...
[Assertion]
void AssertTrue(bool value)
{
  if (!value)
    throw MyException.Boom();
}
Invalid initializer expression

A static and readonly field does not have an initializer expression or it is none of the following: an array creation expression, an expression of object, struct or delegate type.

Invalid return type for assertion method

An assertion method does not return void.

[Assertion]
bool AssertTrue() { ... }
Invalid usage of creation expression

An object or array creation expression is used as an initializer for a field that is not static and readonly.

class MyClass
{
  int[] A = new ...; (1)
  object B = new ...; (1)
  static int[] C = new ...; (1)
  static object D = new ...; (1)
  readonly int[] E = new ...; (1)
  readonly object F = new ...; (1)
}
Invalid usage of exception object

In Code-X, exceptions may only carry error information from the throw statement to the catch block, which limits their allowed usages throw and return statements.

Local variables must not be declared in a switch section

A case in a switch statement declares a local variable, which is not allowed by Code-X. Instead, a block statement may be used as case body.

switch (...)
{
  case ...:
    int a; (1)
    break;

  case ...:
  {
    int b;
    break;
  }
}
Member name and type name must not be identical

A member has the same name as its enclosing type declaration. Although valid in C#, this is not supported in all Code-X targets and thus not allowed. Property members are an exception, they may have the same name.

Modulo operator is not allowed on floating-point types

The modulo operator % behaves differently across Code-X targets and is thus not allowed.

float a = ...;
float b = ...;
float c;

c = a % b; (1)
Name of member clashes with mangled name of: X

For some Code-X targets, properties must be output as separate getter and setter methods. An existing member has a conflicting name.

int Value { get; set; }
void SetValue(); (1)
void GetValue(); (1)
Nested assignments with a property as l-value are not allowed

An expression that sets a property value is not an expression statement, which is not allowed. Such expressions need to be translated into calls to a setter method, which returns void.

int Value { get; set; }
...
int a = Value = 100; (1)
new[…​] is only allowed for static readonly fields

A field uses an array creation expression (with length) as initializer but is not both static and readonly.

readonly int[] A = new int[100]; (1)
static int[] A = new int[100]; (1)
static readonly int[] A = new int[100];
new[] {…​} initializer not allowed here

An array creation expression (with value list) is used in a context that is not a field initializer.

int[] a;
a = new int[] { 1, 2, 3 }; (1)
new[] {…​} must not be nested

An array creation expression (with value list) is used in a nested expression, which is not allowed.

MyMethod(new int[] { 1, 2, 3 }); (1)
Non-beta node is referring to beta node X

Type and member declarations may be annotated with the BetaAttribute attribute, in order to tag them as experimental. Per convention, regular code must not break when all experimental code is removed. In consequence, a non-beta node must not reference a beta node.

[Beta]
class MyClass { ... }
...
class MyClass2 : MyClass { ... } (1)
...
[Beta]
void MyMethod() { ... }
...
void MyMethod2()
{
  MyMethod(); (1)
}
Non-readonly static fields must not have an initializer

Semantics of global initialization of static fields differ between Code-X targets. To get well-defined behaviour, static non-readonly fields may only be initialized to their default value and further initialization must be performed by code, after global initialization has completed.

static readonly Vec3I OneTwoThree = new Vec3I(1, 2, 3);
...
static Vec3I SomeValue;
...
static Vec3I SomeValue = new Vec3I(1, 2, 3); (1)
Overloaded methods with delegate parameters are not supported

Within a group of equally named methods that have the same number of parameters, there are two or more signatures that have a delegate parameter, which is not allowed by Code-X:

delegate void MyDelegate1();
delegate void MyDelegate2();
...
void MyMethod(int argument);
void MyMethod(MyDelegate1 func); (1)
void MyMethod(MyDelegate2 func); (1)
RAII invocation must target public or protected API

The finally block of a try statement uses internal or private members, which is not allowed by Code-X:

public void Cleanup1() { ... }
internal void Cleanup2() { ... }
...
try { ... }
finally
{
  Cleanup1();
  Cleanup2(); (1)
}
RAII invocation target must be a variable

The finally block of a try statement may only target local variables, field members or type declarations:

public class MyClass
{
  public static Cleanup() { ... }
}
...
Begin();
try { ... }
finally
{
  End(); // Begin/End pair is ok.
}
...
End();
try { ... }
finally
{
  Begin(); // End/Begin pair is ok.
}
...
try { ... }
finally
{
  Dispose(); // Dispose is ok.
}
...
try { ... }
finally
{
  MyClass.Cleanup(); // Static calls are ok.
}
...
// <T> is a local variable, a field member or a type declaration.
// <Call> is Begin, End or Dispose.
// <E?> is a name expression.
try { ... }
finally
{
  <T>.<Call>(); // Ok
  <T>.<E1>.<Call>(); // Ok
  <T>.<E1>.<E2>.<Call>(); // Ok
  <T>.<E1>...<En>.<Call>(); // Ok
}

Using Dispose() as indicated above is equivalent to the using statement, which is not yet supported by Code-X.

[Raw] must be applied consistently along overridden method chain

The [Raw] attribute is used by some methods in an override chain, but not by all of them:

public virtual void MethodA([Raw] int[] values) { ... }
...
public override void MethodA(int[] values) { ... } (1)

The [Raw] attribute indicates that only the array elements will be used, which allows to pass plain pointers for some Code-X targets, instead of array wrapper objects.

Readonly field has not been initialized

A readonly instance field has not been initialized in the constructor:

class MyClass
{
  readonly Vec3I a;
  readonly Vec3I b; (1)

  MyClass(Vec3I a)
  {
    this.a = a;
  }
}
Readonly field is initialized more than once

A readonly instance field is initialized more than once in the constructor:

class MyClass
{
  readonly int a; (1)
  readonly int b; (1)
  readonly int c;
  readonly int d;

  MyClass(int a, int b, int c, int d)
  {
    int temp;

    this.a = a;
    this.a += b;

    if (b < 0)
      this.b = -b;
    else
      this.b = b;

    this.c = c < 0 ? -c : c;

    if (d < 0)
      temp = -d;
    else
      temp = d;

    this.d = temp;
  }
}

Some Code-X targets require that readonly fields may be assigned to only once and produce compilation errors when trying to do so. Code-X requires the programmer to perform trivial code adjustments to make sure that this does not happen.

Reference type values must not be assigned to parameter variables

For some Code-X targets, method parameters with reference types are passed as plain pointers, instead of using smart pointer wrapper objects. Assigning another reference to such a plain pointer would break the reference-counting logic:

void Method(object o)
{
  o = new object(); (1)
}
Runtime test for generic type is not allowed

Safe-cast and instance-of expressions on generic types are only allowed when referring to the enclosing generic type declaration:

class MyClass<T1, T2>
{
  void Method(object o)
  {
    MyClass<?, ?> a;
    MyClass<T1, T2> b;

    a = (MyClass<?, ?>) o; // Ok

    if (o is MyClass<T1, T2>) ... // Ok

    b = o as MyClass<T1, T2> // Ok

    if (o is MyClass<T2, T1>) ... (1)

    if (o is MyClass<int, int>) ... (1)
  }
}
Shift left by 32 or more bits requires left hand expression of type long

A 32-bit integer value is shifted leftwards by 32 bits or more, which effectively produces zero:

int a = 1 << 32; (1)
int b = 1L << 32;
Static readonly field has not been initialized

A static readonly field does not have an initializer expression:

public static readonly Vec3I A; (1)
public static readonly Vec3I B = new Vec3I(1, 2, 3);
Static readonly field must be converted to constant

A static readonly field has a compile-time constant value and must be declared as a constant:

public static readonly int Zero = 0; (1)
public const int One = 1;
[StaticTemplate] interfaces may only be implemented by struct types

A class implements an interface that is annotated with StaticTemplateAttribute, which is not allowed.

[StaticTemplate]
interface ITrait { ... }

class MyClass : ITrait (1)
{ ... }
Struct types may only implement [StaticTemplate] interfaces

A struct type implements a prohibited interface. Allowed interfaces are StaticTemplateAttribute, IEquatable and IComparable.

[StaticTemplate]
interface ITrait { ... }

struct MyStruct : IInterface (1)
{ ... }

struct MyStruct : ITrait
{ ... }

struct MyStruct : IEquatable<MyStruct>
{ ... }

struct MyStruct : IComparable<MyStruct>
{ ... }
Struct value parameters must not be modified

For some Code-X targets, method parameters with struct types are passed as constant references, instead of value copies. Assigning values to such a constant reference would cause compilation errors:

void Method(Vec3I v)
{
  v = new Vec3I(1, 2, 3); (1)
}
This kind of access to an exception object is prohibited

A variable of an Exception type may only be accessed in specific ways:

  • It may be passed to methods as an argument.

  • It may be used to access a member of the Exception object.

Try statement is incompatible with CodeX::Finally

For some Code-X targets, Code-X translates try-finally statements to alternative code constructs, for example RAII for C++. To be able to define precise semantics for this translation, only the following finally block flavours are allowed:

...
// See IDisposable
finally { Dispose(); }
finally { <expr>.Dispose(); }
...
// See IBeginEnd
finally { Begin(); }
finally { End(); }
finally { <expr>.Begin(); }
finally { <expr>.End(); }
...
// Static method call
finally { <Type>.<Name>(); }
Type names must not end with X

A type has a prohibited name suffix:

  • …​Native

  • …​Test

// Reserved for non-Code-X native code.
class MyClassNative (1)
{ ... }

// Reserved for unit-test code.
class MyClassTest (1)
{ ... }
Types would have interdependent C++ header includes

The dependencies within the given set of types would have cycles when being output to C, for example:

struct MyStructA (1)
{
  public MyStructB ToB() { ... }
}
...
struct MyStructB (1)
{
  public MyStructA ToA() { ... }
}
Virtual method call in constructor

A constructor of a non-sealed class calls an effectively virtual method, which is not allowed in all Code-X targets:

class MyBase
{
  protected virtual DoSomething1() { ... }
}
...
class MyClass : MyBase
{
  protected override sealed DoSomething1() { ... }

  protected virtual DoSomething2() { ... }

  protected DoSomething3() { ... }

  public MyClass()
  {
    DoSomething1();
    DoSomething2(); (1)
    DoSomething3();
  }
}
X is a [Raw] method parameter and cannot be used in this context

An array-typed method parameter may only be used to access the array elements:

void Method([Raw] int[] a)
{
  int v;

  v = a[0];
  v += a[a.Length - 1]; (1)
}
X has incomplete / unresolved value

The code element indicated by X contains an unresolved reference or is otherwise incomplete.

Errors

Non-critical errors depict problems in the program structure model that will lead to software bugs and/or runtime errors and should not be ignored.

All assertion and precondition statements must be wrapped in #if DEBUG..#endif

A statement that performs an assertion or a precondition check is not wrapped in a conditional DEBUG preprocessor-block:

class MyType : Disposable
{
  void MyMethod()
  {
    #if DEBUG
    this.AssertValid("MyType.MyMethod");
    #endif

    this.AssertValid("MyType.MyMethod"); (1)
  }
}
All fields of a struct must be readonly

For some Code-X targets, struct values are passed by-reference, to avoid copying. Allowing modification of field values would break the pass-by-value semantic. Because of this, Code-X requires that all fields of a struct are readonly:

struct
{
  int A; (1)
  readonly int B;
}
Assertion throw statements must be wrapped in assertion methods

The statement throws an assertion exception but is not wrapped in an assertion method:

[Assertion]
void AssertTrue(string source, bool value)
{
  if (!value)
    throw FailedAssertionException.BadState(source, "not true");
}
...
void MyMethod(bool value)
{
#if DEBUG
  AssertTrue(value);

  if (!value)
    throw FailedAssertionException.BadState(source, "not true"); (1)
#endif
}
Assignment to null must be preceded by disposal

A field with ownership is set to null, without a prior call to Dispose.

class MyClass : IDisposable
{
  [Owner]
  MyClass next;

  void ClearOk()
  {
    next.Dispose();
    next = null;
  }

  void ClearBad()
  {
    next = null; (1)
  }
}
Body of disposable constructor must be wrapped in catch-all/dispose block iff throwing
class MyClass : Disposable
{
  int a;

  MyClass(int a)
  {
    // Not throwing => must not have block
    this. a = a;
  }

  MyClass(int a) (1)
  {
    try
    {
      // Not throwing => must not have block
      this. a = a;
    }
    catch(Exception)
    {
      Dispose();
      throw();
    }
  }

  MyClass(string a) (1)
  {
    // Throwing => must have block
    FileSystem.Delete(a);
  }

  MyClass(int a)
  {
    try
    {
      // Throwing => must have block
      FileSystem.Delete(a);
    }
    catch(Exception)
    {
      Dispose();
      throw();
    }
  }
}
By-reference parameter X of enclosing method is passed as argument both by-value and by-reference

The value of a ref / out method parameter is used for multiple arguments of an invocation expression, having both by-value and by-reference semantic. Code-X does not allow that, in order to be able to pass values via immutable references.

void MethodA(ref Vec3I value)
{
  MethodB(value, value); (1)
}

void MethodB(Vec3I a, ref Vec3I b)
{
  b = a.Add(b);
}
Class is missing disposal method

A disposable class has one ore more fields of a reference type. Such fields must be cleared upon disposal, but the class does not define a disposal method.

class MyClass1 : Disposable (1)
{
  object o;
}
...
class MyClass2 : Disposable (1)
{
  object o;

  void Dispose()
  {
    o = null;
  }
}
Code regions must be preceded by a comment

Each native code region must be preceded by a single-line comment in the form // NATIVE: …​, which may optionally be followed by a // THROWS: TException single-line comment, which indicates that the native code block may throw exceptions of type TException.

#region Native {...} (1)
...
#endregion

// NATIVE: Short description of native code block
// THROWS: MyException

#region Native {...}
...
#endregion
Constructor of a non-public type must be private or internal

For some Code-X targets, types cannot be declared as non-public. To make sure that constructors of non-public types are not used inadvertently, Code-X requires that the constructors themselves are declared as non-public.

public class MyClass1
{
  public void MyClass1() { ... }
}
...
internal class MyClass1
{
  public void MyClass1() { ... } (1)
}
Disposal methods of base class must be called from overriding methods

A disposable class overrides the Dispose method of its base class but does not call base.Dispose(), which breaks disposal.

Disposal must be followed by assignment to null

A field with ownership is disposed, without a subsequent clear to null.

class MyClass : IDisposable
{
  [Owner]
  MyClass next;

  void ClearOk()
  {
    next.Dispose();
    next = null;
  }

  void ClearBad()
  {
    next.Dispose(); (1)
  }
}
Dispose call must be root expression

The statement that calls the dispose method (see DisposeAttribute) is not an expression statement.

IDisposable[] d = ...;

for (int i = 0; i < d.Length; d[i].Dispose(), ++i); (1)

for (int i = 0; i < d.Length; ++i)
  d[i].Dispose();
Field X is not cleared upon disposal

The dispose method of a disposable class does not clear its instance field named X.

class MyClass : Disposable
{
  int a;
  object b;
  string c; (1)

  void Dispose()
  {
    b = null;
  }
}
Invalid ownership transfer: X

An expression tries to perform an invalid ownership transfer. See OwnerAttribute for details.

Invalid preconditions: X

The method or property preconditions are invalid. For details on how to write valid preconditions, please refer to InvalidArgumentException and Precondition.

Invalid struct Serializer field declaration

The Serializer field of a struct does not have the required form:

struct A
{
  public static readonly ITypeSerializer<A> Serializer = ...;
}

struct B
{
  public static readonly ITypeSerializerEx<B> Serializer = ...;
}
Last enum item (all flags) must have the ordinal value X or if missing Y

The last item of an enumeration that is annotated with FlagsAttribute is required to contain all defined flags. The values X or Y may be used to correct the code.

[Flags]
enum MyEnum (1)
{
  None = 0,
  One = 1,
  Two = 2,
  All = 1 // X = 3
}

[Flags]
enum MyEnum (2)
{
  None = 0,
  One = 1,
  Two = 2,
  Four = 4 // X = 3, Y = 7
}
Members marked as [Internal] must be public

A member is annotated with [Internal] but is not declared as public.

[Internal]
protected void MyMethod() { ... } (1)
Missing default enum item with ordinal value 0

Code-X requires enum declaration to have an item with the ordinal value 0, in order to have a well-defined default value in all targets.

enum MyEnum (1)
{
  One = 1,
  Two = 2
}
Must be wrapped in DEBUG code block

A throw statement throws an assertion exception but is not wrapped in a DEBUG preprocessor-block.

#if DEBUG
throw FailedAssertionException.BadState(...);
#endif

throw FailedAssertionException.BadState(...); (1)
Non-public member X is referenced from public node Y

The member X is non-public but is referenced from the public code element Y in a way that would make it visible in the public scope.

Only precondition checks and assertions are allowed in debug statements

A DEBUG preprocessor block contains a statement that is neither an assertion nor a precondition check.

#if DEBUG
// Assertion is ok.
Assert(...);

// Precondition check is ok.
if (...)
  throw InvalidArgumentException.Custom(...);

Console.Write("not ok"); (1)
#endif
Only struct values may be passed as ref/out

A method parameter uses ref or out semantic but does not have a struct type.

void MyMethod(ref int a, out string b) { ... } (1)
Orphaned expression giving ownership

An expression with ownership is assigned to a target without ownership (see OwnerAttribute), so the ownership transfer is not possible.

Disposable a;
Disposable b; // [!]
Disposable c;

a = new Disposable(); (1)
c = b = new Disposable();
[Owner*] attributes must be declared on root member

Along the chain of overriding methods, the [Owner*] attributes must only be present on the root member.

[Owner] attribute must not be declared on static readonly fields

After initialization, the value of a static readonly field will not change. Regarding ownership, this would mean that the owned object will never be disposed.

[Owner] must not be used on instance field of non-disposable type

A non-disposable type declares an instance field with ownership. This is only allowed in disposable types, to make sure that all ownership rules are obeyed.

Qualified named type is invalid: X != Y

The last name part of a name with multiple parts resolved to a different type. For example: MyStuff.TerrainDecal resolves to an application-specific class, where as TerrainDecal refers to TerrainDecal.

Result of new() must be stored in a variable

The result of an object creation expression is not stored in a variable.

MyClass a;
MyClass b;

a = new MyClass(...);
b = ... ? new MyClass(...) : new MyClass(...); (1)
Result of new[] must be stored in a variable

The result of an array creation expression is not stored in a variable.

int[] a;
int[] b;

a = new int[10];
b = ... ? new int[10] : new [20]; (1)
Shutdown methods must be public and static

A method is part of the global shutdown sequence (see ShutdownAttribute) but is not declared as public and static.

class MyClass
{
  [Shutdown]
  public static void ProperShutdown() { ... }

  [Shutdown]
  static void PrivateShutdown() { ... } (1)

  [Shutdown]
  public void InstanceShutdown() { ... } (1)
}
Static field type must have [ShutdownSurvive] annotation

A static readonly field with on object or delegate type is declared without being annotated with ShutdownSurviveAttribute.

Struct field must be initialized explicitly in constructor: X

An instance field with a struct value is not initialized in the type constructor. This does not apply to constructors annotated with [Internal].

class MyClass
{
  Vec3I a;
  Vec3I b; (1)

  [Internal]
  MyClass() { }

  MyClass(Vec3I a) (1)
  {
    this.a = a;
  }
}
Target for dispose call must have ownership

The target of an invocation expression that calls a dispose method does not have ownership.

class MyClass : Disposable
{
  [Owner]
  MyDisposable a;

  MyDisposable b;

  void Dispose()
  {
    a.Dispose();
    b.Dispose(); (1)
  }
}
Unresolved throws specifier in comment

The // THROWS: comment of a native code block cannot be resolved into an Exception type.

// NATIVE: ...
// THROWS: UnresolvableException (1)

#region Native {...}
...
#endregion

Warnings

Warnings indicate possible problems or defects in the program structure model and should not be ignored.

[Assertion] attribute used on non-assertion method

The [Assertion] attribute is present on a method that is not an assertion method.

Assertion method must have [Assertion] attribute

A method performs an assertion but is not annotated as an assertion method:

[Assertion]
static void AssertTrue(string source, bool value)
{
  if (!value)
    throw FailedAssertionException.BadState(source, "not true");
}

static void AssertFalse(string source, bool value) (1)
{
  if (value)
    throw FailedAssertionException.BadState(source, "not false");
}

For a RELEASE build, all assertion method may be omitted, without breaking the code.

Boolean-getter property must start with a question word

A property with a getter but not setter must have a name that starts with a question word:

  • Can…​

  • Does…​

  • Has…​

  • Is…​

  • Must…​

  • Needs…​

  • Shall…​

  • Should…​

When a boolean-getter property is translated to a method for a Code-X target, the Get…​ prefix will be omitted, for example: a boolean-getter property bool IsFine { get; } would be translated to a method bool IsFine();, instead of bool GetIsFine();.

Caught exception is never thrown

The catch block of a try statement tries to catch an exception type that is never thrown.

try
{
  int a = 10;
}
catch(IOException e) (1)
{
  ...
}
Code region comment must start with: 'NATIVE:'

Each native code block must be preceded by a single-line comment that starts with // NATIVE:.

// NATIVELY: ... (1)

#region Native {...}
...
#endregion
Delegate parameters should not have constraints

The parameters of a delegate should not have precondition constraints (see InvalidArgumentException), because these will not be checked by Code-X.

Documented exception is not thrown by code: X

An exception is documented as being thrown by a member, but the member body never actually throws it.

/// <summary>
///   Summary
/// </summary>
/// <exception cref="BadException">If something bad happens.</exception> (1)
int Add(int a, int b)
{
  return a + b;
}
Duplicate [ShutdownSurvive] annotations

The [ShutdownSurvive] annotation is present both on the field and the type.

[ShutdownSurvive]
class MyType { ... }

class MyOtherType
{
  [ShutdownSurvive]
  static MyType Surviver = new MyType(); (1)
}
Enum items should form a contiguous ordinal value range

The items of an enumeration that is annotated with neither FlagsAttribute nor SemanticEnumAttribute should have contiguous ordinal values, to avoid undefined values when iterating over the value range.

enum MyEnum (1)
{
  Zero = 0,
  Two = 2
}

[Flags]
enum MyEnum
{
  Zero = 0,
  Two = 2
}

[PersistentEnum]
enum MyEnum
{
  Zero = 0,
  Two = 2
}
Implementing and overriding members must not be documented

In a chain of overriding members, only the root member may be documented, because only the root member provides the full specification for its implementation. Implementing and overriding members must adhere to this specification.

Implicit exceptions must not be documented

The member documentation contains a documentation entry for an implicit exception:

Incorrect error source tag

Error source tags (see TinmanError.ErrorSource) are used as a minimalistic stacktrace facility that acts as fallback when the native stacktrace information is not available. Error source tags break easily during refactoring, so this warning makes sure that they stay up-to-date.

Invalid exception factory method

A factory method that creates an exception for throwing must accept an error source tag (see TinmanError.ErrorSource) as first parameter.

static MyException Boom(string source)
{
  return new MyException(source);
}
Member has no documentation

A member does not have a documentation comment.

Member name should be lowercase

The default Code-X coding guidelines indicate that the member name should be lowercase.

Member name should be uppercase

The default Code-X coding guidelines indicate that the member name should be uppercase.

Method is missing the <returns>…​</returns> tag!

The documentation of a method does not document the return value.

Method returning void must not have documentation for return value

The documentation comment of a method contains an element for the return value, but the method returns void.

Name for parameter #X is wrong: should be Y

Overriding method must use the same parameter names as the root member.

Non-boolean-getter property must not start with a question word

This is the opposite of 'Boolean-getter property must start with a question word'.

Parameter should be [Raw]

An array is passed to a method that only uses the array elements and should therefore be passed as [Raw], which will allow raw pointers to be passed instead of an array wrapper object for some Code-X targets.

Parameters are not documented properly

The documentation of the member parameters is incomplete or incorrect.

Property is missing the <value>…​</value> tag

The documentation of a property does not document the property value.

[Raw] may only be used on array-typed method parameters

The [Raw] attribute is used in an invalid context.

Serializable struct declaration is missing its Serializer

A struct type is not marked with [NotSerializable] (and thus serializable) but does not declare a Serializer field.

Static field is missing [ShutdownClear] resp. [ShutdownSurvive] annotation

For each static field, the behaviour during global shutdown must be specified by using one of the [Shutdown*] attributes.

Struct Serializer field must be public and static

The Serializer of a struct type must be a public and static field.

Structs must not override methods

Code-X does not allow automatic boxing of value types into objects, so a struct type must not override any method of object, except GetHashCode and ToString.

Thrown exception is not documented: X

The body of a member throws an exception which is not included in the documentation of the root member.

Type has no documentation

A type declaration has no documentation comment.

X does not allow exception Y thrown by: Z

A method is assigned to a delegate variable which may throw an exception that is not allowed by the delegate declaration.

Notices

Notices give hints on how to improve the program structure model and may be ignored.

Incomplete native X code for Y

A native code block is incomplete for one or more Code-X targets. A block is considered incomplete in any of the following cases:

  • The body contains only whitespaces.

  • The body contains the token TODO:.

  • The body contains incomplete #if preprocessor directives.

Preprocessor #if directives may be nested and are considered complete if they have and #else branch.

Incorrect spelling: X

A word in a code comment is spelled incorrectly. To resolve, add the word to one of the word lists of the dictionary when running the Code-X Processor.

Gpu-X Workflow

The Gpu-X workflow consumes Code-X compliant C# code and generates source code in other programming languages, which is intended to be executed by GPUs.

This workflow is used to generate source code in a GPU programming language, such as GLSL or HLSL.

Input for GPU code generation is a Code-X compliant C# library, which must be based solely on the Tinman.Gpu component. Please refer to the GpuCode class for details on how to write GPU code.

The Tinman.Shaders component represents the GPU shader code of the Tinman 3D SDK.

The input C# source code is parsed (see CxCodeUnit), the program structure is analyzed and GPU code is generated. The input code does not need to be compiled (however this is advisable to avoid errors up-front) and should not be executed, since the provided API implementation is based mainly on dummy code. For example, gxTexture2D always returns the default value instead of performing an actual texture lookup.

Table 1. Supported GPU languages
Language Graphics API Tinman API Project Files? Compiler?

GLSL

OpenGL 4.1+

OpenGLContext

-

-

OpenGLES 3.0+

OpenGLESContext

-

HLSL

Direct3D 9 / SM 3.0

DirectX9Context

Visual Studio / MSBuild (.vcxproj, .filters)

Effect-Compiler Tool / fxc

Direct3D 11 / SM 5.0

DirectX11Context

Direct3D 12 / SM 6.5

DirectX12Context

DirectX Shader Compiler / dxc

The following sections explain the various messages that may be output by the workflow.

Critical

Critical errors prevent the code generator from outputting GPU code.

For details on how to write correct code, please refer to the documentation of the Tinman.Gpu component.

Bad declaration name: X

The fully-qualified name of the type declaration in the code unit does not start with the common name prefix that has been configured for the GPU code module.

Unprocessable code unit: X

The code unit does not match any of the processable flavours, for example a constant buffer, shader resources or shader code.

Not supported (X): …​

The code construct X is not supported.

Appendix

Reserved Names

The set of reserved names is built into Code-X and cannot be modified.

C# Keywords

abstract, as, base, bool, break, byte, case, catch, char, checked, class, const, continue, decimal, default, delegate, do, double, else, enum, event, explicit, extern, false, finally, fixed, float, for, foreach, goto, if, implicit, in, int, interface, internal, is, lock, long, namespace, new, null, object, operator, out, override, params, private, protected, public, readonly, ref, return, sbyte, sealed, short, sizeof, stackalloc, static, string, struct, switch, this, throw, true, try, typeof, uint, ulong, unchecked, unsafe, ushort, using, virtual, void, volatile, while

C++ Keywords

alignas, alignof, and, and_eq, asm, auto, bitand, bitor, bool, break, case, catch, char, char8_t, char16_t, char32_t, class, compl, concept, const, consteval, constexpr, constinit, const_cast, continue, co_await, co_return, co_yield, decltype, default, delete, do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, noexcept, not, not_eq, nullptr, operator, or, or_eq, private, protected, public, register, reinterpret_cast, requires, return, short, signed, sizeof, static, static_assert, static_cast, struct, switch, template, this, thread_local, throw, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volatile, wchar_t, while, xor, xor_eq

Java Keywords

abstract, continue, for, new, switch, assert, default, goto, package, synchronized, boolean, do, if, private, this, break, double, implements, protected, throw, byte, else, import, public, throws, case, enum, instanceof, return, transient, catch, extends, int, short, try, char, final, interface, static, void, class, finally, long, strictfp, volatile, const, float, native, super, while

Other Keywords

id

Code-X Identifiers

CSTRING, NATIVE_BLOCK_BEGIN, NATIVE_BLOCK_END, NATIVE_INCLUDE_BEGIN, NATIVE_INCLUDE_END, NATIVE_REGION_BEGIN, NATIVE_REGION_END, ZCHAR, ZSTRING, float32, float64, int16, int32, int64, int8, ptr, uint64, zchar

Prohibited Names

Prohibited names may be specified as input to the Code-X workflow and may thus be defined freely.