using System; using System.Diagnostics; using System.Reflection; using System.Collections; namespace Tests { class Comptime { [AttributeUsage(.All)] struct AddFieldAttribute : Attribute { public Type mType; public String mName; public int mVal; public this(Type type, String name, int val) { mType = type; mName = name; mVal = val; } } [AttributeUsage(.All)] struct IFaceAAttribute : Attribute, IComptimeTypeApply { String mMemberName; int32 mInitVal; public int32 InitVal { set mut { mInitVal = value; } } public this(String memberName) { mMemberName = memberName; mInitVal = 0; } [Comptime] public void ApplyToType(Type type) { Compiler.EmitTypeBody(type, scope $""" public int32 m{mMemberName} = {mInitVal}; public int32 GetVal{mMemberName}() => mC; """); } } [AttributeUsage(.Method)] struct LogAttribute : Attribute, IComptimeMethodApply { public static String gLog = new .() ~ delete _; [Comptime] public void ApplyToMethod(MethodInfo method) { String emit = scope $"LogAttribute.gLog.AppendF($\"Called {method}"; for (var fieldIdx < method.ParamCount) emit.AppendF($" {{ {method.GetParamName(fieldIdx)} }}"); emit.Append("\\n\");"); Compiler.EmitMethodEntry(method, emit); if (var genericType = method.ReturnType as SpecializedGenericType) { if ((genericType.UnspecializedType == typeof(Result<>)) || (genericType.UnspecializedType == typeof(Result<,>))) { Compiler.EmitMethodExit(method, """ if (@return case .Err) LogAttribute.gLog.AppendF($"Error: {@return}"); """); } } } } [AddField(typeof(float), "D", 4)] [IFaceA("C", InitVal=345)] class ClassA { public int mA = 123; [OnCompile(.TypeInit), Comptime] public static void Generate() { Compiler.EmitTypeBody(typeof(Self), """ public int32 mB = 234; public int32 GetValB() => mB; """); if (var addFieldAttr = typeof(Self).GetCustomAttribute()) Compiler.EmitTypeBody(typeof(Self), scope $"public {addFieldAttr.mType} {addFieldAttr.mName} = {addFieldAttr.mVal};"); } } [IFaceA("C", InitVal=345)] struct StructA { public int mA = 123; public static StructA sSA; public const StructA cSA = .(); [OnCompile(.TypeInit), Comptime] public static void Generate() { Compiler.EmitTypeBody(typeof(Self), """ public int32 mB = 234; public int32 GetValB() => mB; """); } } enum MethodAErr { ErrorA, ErrorB } [Log] static Result MethodA(int a, int b) { return .Err(.ErrorB); } static Type GetBiggerType(Type t) { switch (t) { case typeof(int8): return typeof(int16); case typeof(int16): return typeof(int32); case typeof(int32): return typeof(int64); case typeof(float): return typeof(double); } return null; } static TTo GetBigger(TFrom val) where TTo : comptype(GetBiggerType(typeof(TFrom))), operator explicit TFrom { return (.)val; } public struct TypePrinter { const int cFieldCount = GetFieldCount(); const (int32, StringView)[cFieldCount] cMembers = Make(); static int GetFieldCount() { int fieldCount = 0; for (let field in typeof(T).GetFields()) if (!field.IsStatic) fieldCount++; return fieldCount; } static decltype(cMembers) Make() { if (cFieldCount == 0) return default(decltype(cMembers)); #unwarn decltype(cMembers) fields = ?; int i = 0; for (let field in typeof(T).GetFields()) { if (!field.IsStatic) fields[i++] = (field.MemberOffset, field.Name); } return fields; } public override void ToString(String strBuffer) { for (var t in cMembers) { if (@t != 0) strBuffer.Append("\n"); strBuffer.AppendF($"{t.0} {t.1}"); } } } struct TestType { public float mX; public float mY; public float mZ; } class SerializationContext { public String mStr = new String() ~ delete _; public void Serialize(String name, T val) where T : struct { mStr.AppendF($"{name} {val}\n"); } } interface ISerializable { void Serialize(SerializationContext ctx); } [AttributeUsage(.Enum | .Struct | .Class, .NotInherited | .ReflectAttribute | .DisallowAllowMultiple)] struct SerializableAttribute : Attribute, IComptimeTypeApply { [Comptime] public void ApplyToType(Type type) { const String SERIALIZE_NAME = "void ISerializable.Serialize(SerializationContext ctx)\n"; String serializeBuffer = new .(); Runtime.Assert(!type.IsUnion); for (let field in type.GetFields()) { if (!field.IsInstanceField || field.DeclaringType != type) continue; serializeBuffer.AppendF($"\n\tctx.Serialize(\"{field.Name}\", {field.Name});"); } Compiler.EmitTypeBody(type, scope $"{SERIALIZE_NAME}{{{serializeBuffer}\n}}\n"); Compiler.EmitAddInterface(type, typeof(ISerializable)); } } [Serializable] struct Foo : this(float x, float y) { } public class ComponentHandler where T : struct { uint8* data; protected override void GCMarkMembers() { T* ptr = (T*)data; GC.Mark!((*ptr)); } } [CheckPayloadEnum] enum EnumA { case A(int64 aa); case B(float bb); } [CheckEnum] enum EnumB { case A = 123; case B = 1000; case C = 1200; } [AttributeUsage(.All)] public struct CheckPayloadEnumAttribute : Attribute, IComptimeTypeApply { public void ApplyToType(Type type) { int fieldIdx = 0; for (var field in type.GetFields()) { switch (fieldIdx) { case 0: Test.Assert(field.Name == "$payload"); Test.Assert(field.MemberOffset == 0); Test.Assert(field.FieldType == typeof(int64)); case 1: Test.Assert(field.Name == "$discriminator"); Test.Assert(field.MemberOffset == 8); Test.Assert(field.FieldType == typeof(int8)); } fieldIdx++; } Test.Assert(fieldIdx == 4); } } [AttributeUsage(.All)] public struct CheckEnumAttribute : Attribute, IComptimeTypeApply { public void ApplyToType(Type type) { int fieldIdx = 0; for (var field in type.GetFields()) { switch (fieldIdx) { case 0: Test.Assert(field.Name == "A"); Test.Assert(field.FieldType.UnderlyingType == typeof(int64)); } fieldIdx++; } Test.Assert(fieldIdx == 3); } } const String cTest0 = Compiler.ReadText("Test0.txt"); const String cTest1 = new String('A', 12); const uint8[?] cTest0Binary = Compiler.ReadBinary("Test0.txt"); class ClassB where T : const int { public typealias TA = comptype(GetVal(10, T)); public const int cTimesTen = 10 * T; [Comptime] static Type GetVal(int a, int b) { return typeof(float); } } struct Yes; struct No; struct IsDictionary { public typealias Result = comptype(_isDict(typeof(T))); [Comptime] private static Type _isDict(Type type) { if (let refType = type as SpecializedGenericType && refType.UnspecializedType == typeof(Dictionary<,>)) return typeof(Yes); return typeof(No); } } struct GetArg where C : const int { public typealias Result = comptype(_getArg(typeof(T), C)); [Comptime] private static Type _getArg(Type type, int argIdx) { if (let refType = type as SpecializedGenericType) return refType.GetGenericArg(argIdx); return typeof(void); } } public class DictWrapper where T : var { private T mValue = new .() ~ delete _; } extension DictWrapper where T : var where IsDictionary.Result : Yes { typealias TKey = GetArg.Result; typealias TValue = GetArg.Result; typealias KeyValuePair = (TKey key, TValue value); typealias KeyRefValuePair = (TKey key, TValue* valueRef); public ValueEnumerator GetValueEnumerator() { return ValueEnumerator(this); } public struct ValueEnumerator : IRefEnumerator, IEnumerator, IResettable { private SelfOuter mParent; private int_cosize mIndex; private TValue mCurrent; const int_cosize cDictEntry = 1; const int_cosize cKeyValuePair = 2; public this(SelfOuter parent) { mParent = parent; mIndex = 0; mCurrent = default; } public bool MoveNext() mut { // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue while ((uint)mIndex < (uint)mParent.[Friend]mValue.[Friend]mCount) { if (mParent.[Friend]mValue.[Friend]mEntries[mIndex].mHashCode >= 0) { mCurrent = mParent.[Friend]mValue.[Friend]mEntries[mIndex].mValue; mIndex++; return true; } mIndex++; } mIndex = mParent.[Friend]mValue.[Friend]mCount + 1; mCurrent = default; return false; } public TValue Current { #unwarn get { return mCurrent; } } public ref TValue CurrentRef { get mut { return ref mCurrent; } } public ref TKey Key { get { return ref mParent.[Friend]mValue.[Friend]mEntries[mIndex].mKey; } } public void Reset() mut { mIndex = 0; mCurrent = default; } public Result GetNext() mut { if (!MoveNext()) return .Err; return Current; } public Result GetNextRef() mut { if (!MoveNext()) return .Err; #unwarn return &CurrentRef; } } } public struct GetTupleField where C : const int { public typealias Type = comptype(GetTupleFieldType(typeof(TTuple), C)); [Comptime] private static Type GetTupleFieldType(Type type, int index) { if (type.IsGenericParam) { Runtime.Assert(type.IsGenericParam); String tName = type.GetFullName(.. scope .()); Runtime.Assert(tName == "TTuple"); return typeof(var); } Runtime.Assert(type.IsTuple); return type.GetField(index).Get().FieldType; } } [Comptime] static int GetMixinVal() { int a = 23; Compiler.Mixin("a += 100;"); Compiler.MixinRoot(scope String("b += 200;")); return a; } static Type GetMathRetType() { return typeof(T); } static String GetMathString(String expr) { if ((typeof(T) == typeof(float)) && (typeof(T2) == typeof(int))) return scope $"return a{expr}(T)b;"; else return "return default;"; } static comptype(GetMathRetType()) ComputeMath(T a, T2 b, TExpr expr) where TExpr : const String { Compiler.Mixin(GetMathString(expr)); } class GenClass where TDesc : const String { [OnCompile(.TypeInit), Comptime] static void Init() { Compiler.EmitTypeBody(typeof(Self), TDesc); } } class ClassB { public static int mA = 123; } class ClassC { [OnCompile(.TypeInit), Comptime] static void Init() { typeof(ClassB).GetField("mA").Value.GetValue(null, var value); Compiler.EmitTypeBody(typeof(Self), scope $"public static int sA = {1000 + value};"); } } struct Float3 : this(float x, float y, float z = 0) { } struct Pos3f : Float3 { [OnCompile(.TypeInit), Comptime] static void TypeInit() { Compiler.EmitTypeBody(typeof(Self), """ public this(float x, float y, float z) : base(x, y, z) {} """); } } struct DefaultCtorTest { public int mA; [OnCompile(.TypeInit), Comptime] static void InitType() { Compiler.EmitTypeBody(typeof(Self), "public this() { mA = 123; }"); } } struct StructD { [Comptime] public static Span GetTypes() { List list = scope .(); list.Add(typeof(StructA)); list.Add(typeof(EnumA)); return list; } } struct StructE { [Comptime] public static int GetSizes() { const let typeList = StructD.GetTypes(); return typeList[0].InstanceSize + typeList[1].InstanceSize; } } public static int GetLocalVal1() { static int sVal = 100; sVal++; return sVal; } [Comptime] public static int GetLocalVal2() { GetLocalVal1(); return GetLocalVal1(); } [Test] public static void TestBasics() { ClassA ca = scope .(); Test.Assert(ca.mA == 123); Test.Assert(ca.mB == 234); Test.Assert(ca.GetValB() == 234); Test.Assert(ca.mC == 345); Test.Assert(ca.GetValC() == 345); Test.Assert(ca.D == 4); StructA sa = .(); Test.Assert(sa.mA == 123); Test.Assert(sa.mB == 234); Test.Assert(sa.GetValB() == 234); Test.Assert(sa.mC == 345); Test.Assert(sa.GetValC() == 345); Test.Assert(ClassC.sA == 1123); Compiler.Mixin("int val = 99;"); Test.Assert(val == 99); MethodA(34, 45).IgnoreError(); Debug.Assert(LogAttribute.gLog == "Called Tests.Comptime.MethodA(int a, int b) 34 45\nError: Err(ErrorB)"); var v0 = GetBigger((int8)123); Test.Assert(v0.GetType() == typeof(int16)); String str = scope .(); TypePrinter tp = .(); tp.ToString(str); Debug.Assert(str == """ 0 mX 4 mY 8 mZ """); Foo bar = .(10, 2); ISerializable iSer = bar; SerializationContext serCtx = scope .(); iSer.Serialize(serCtx); Test.Assert(serCtx.mStr == "x 10\ny 2\n"); Test.Assert(cTest0 == "Test\n0" || cTest0 == "Test\r\n0"); Test.Assert(cTest1 == "AAAAAAAAAAAA"); Test.Assert((Object)cTest1 == (Object)"AAAAAAAAAAAA"); Test.Assert((cTest0Binary[0] == (.)'T') && ((cTest0Binary.Count == 6) || (cTest0Binary.Count == 7))); ClassB.TA f = default; Test.Assert(typeof(decltype(f)) == typeof(float)); Test.Assert(ClassB.cTimesTen == 30); DictWrapper> dictWrap = scope .(); dictWrap.[Friend]mValue.Add(1, 2.3f); dictWrap.[Friend]mValue.Add(2, 3.4f); int idx = 0; for (var value in dictWrap.GetValueEnumerator()) { if (idx == 0) Test.Assert(value == 2.3f); else Test.Assert(value == 3.4f); ++idx; } Test.Assert(idx == 2); var tuple = ((int16)1, 2.3f); GetTupleField.Type tupType0; GetTupleField.Type tupType1; Test.Assert(typeof(decltype(tupType0)) == typeof(int16)); Test.Assert(typeof(decltype(tupType1)) == typeof(float)); int b = 34; Test.Assert(GetMixinVal() == 123); Test.Assert(b == 234); float math = ComputeMath(2.3f, 2, "*"); Test.Assert(math == 4.6f); float math2 = ComputeMath(2.3f, 1, "+"); Test.Assert(math2 == 3.3f); GenClass< """ public int mA = 123; """> genClass = scope .(); Test.Assert(genClass.mA == 123); DefaultCtorTest dct = .(); Test.Assert(dct.mA == 123); const int typeSizes = StructE.GetSizes(); Test.Assert(typeSizes == sizeof(StructA) + sizeof(EnumA)); const int cVal = GetLocalVal2(); Test.Assert(cVal == 102); } } }