1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

Inference of tuple 'params T`

This commit is contained in:
Brian Fiete 2025-02-18 07:56:02 -08:00
parent f6d18c111f
commit 66d3581911
14 changed files with 721 additions and 75 deletions

View file

@ -334,6 +334,7 @@ namespace System
static extern void Comptime_EmitMethodEntry(int64 methodHandle, StringView text); static extern void Comptime_EmitMethodEntry(int64 methodHandle, StringView text);
static extern void Comptime_EmitMethodExit(int64 methodHandle, StringView text); static extern void Comptime_EmitMethodExit(int64 methodHandle, StringView text);
static extern void Comptime_EmitMixin(StringView text); static extern void Comptime_EmitMixin(StringView text);
static extern String Comptime_GetStringById(int32 id);
[Comptime(OnlyFromComptime=true)] [Comptime(OnlyFromComptime=true)]
public static MethodBuilder CreateMethod(Type owner, StringView methodName, Type returnType, MethodFlags methodFlags) public static MethodBuilder CreateMethod(Type owner, StringView methodName, Type returnType, MethodFlags methodFlags)

View file

@ -1392,6 +1392,10 @@ namespace System
while (true) while (true)
{ {
int charsLeft = len - pos;
Reserve(mLength + charsLeft);
char8* ptr = Ptr;
int p = pos; int p = pos;
int i = pos; int i = pos;
while (pos < len) while (pos < len)
@ -1418,7 +1422,8 @@ namespace System
} }
} }
Append(ch); //Append(ch);
ptr[mLength++] = ch;
} }
if (pos == len) break; if (pos == len) break;
@ -1519,6 +1524,16 @@ namespace System
} }
if (ch != '}') return FormatError(); if (ch != '}') return FormatError();
pos++; pos++;
if ((provider == null) && (fmt.IsEmpty) && (width == 0))
{
if (arg == null)
s.Append("null");
else
arg.ToString(this);
}
else
{
if (s == null) if (s == null)
s = scope:: String(128); s = scope:: String(128);
@ -1539,6 +1554,7 @@ namespace System
Append(s); Append(s);
if (leftJustify && pad > 0) Append(' ', pad); if (leftJustify && pad > 0) Append(' ', pad);
} }
}
return .Ok; return .Ok;
} }
@ -2999,6 +3015,16 @@ namespace System
} }
} }
public static String GetById(int id)
{
if (Compiler.IsComptime)
{
return Compiler.[Friend]Comptime_GetStringById((.)id);
}
else
return sIdStringLiterals[id];
}
public struct RawEnumerator : IRefEnumerator<char8*>, IEnumerator<char8> public struct RawEnumerator : IRefEnumerator<char8*>, IEnumerator<char8>
{ {
char8* mPtr; char8* mPtr;

View file

@ -650,6 +650,17 @@ namespace System
} }
} }
public virtual TypeInstance WrappedType
{
get
{
if (Compiler.IsComptime)
return Comptime_GetWrappedType((.)mTypeId) as TypeInstance;
return null;
}
}
public virtual TypeInstance.InterfaceEnumerator Interfaces public virtual TypeInstance.InterfaceEnumerator Interfaces
{ {
get get
@ -754,6 +765,7 @@ namespace System
static extern int32 Comptime_Type_GetBaseType(int32 typeId); static extern int32 Comptime_Type_GetBaseType(int32 typeId);
static extern bool Comptime_Type_HasDeclaredMember(int32 typeId, int32 kind, StringView name); static extern bool Comptime_Type_HasDeclaredMember(int32 typeId, int32 kind, StringView name);
static extern Type Comptime_GetTypeById(int32 typeId); static extern Type Comptime_GetTypeById(int32 typeId);
static extern Type Comptime_GetWrappedType(int32 typeId);
static extern Type Comptime_GetTypeByName(StringView name); static extern Type Comptime_GetTypeByName(StringView name);
static extern String Comptime_Type_ToString(int32 typeId); static extern String Comptime_Type_ToString(int32 typeId);
static extern String Comptime_TypeName_ToString(int32 typeId); static extern String Comptime_TypeName_ToString(int32 typeId);
@ -864,6 +876,15 @@ namespace System
return type == this || (type.IsTypedPrimitive && type.UnderlyingType == this); return type == this || (type.IsTypedPrimitive && type.UnderlyingType == this);
} }
public virtual bool ImplementsInterface(Type checkInterface)
{
var wrappedType = WrappedType;
if (wrappedType != null)
return wrappedType.ImplementsInterface(checkInterface);
return false;
}
public virtual Result<FieldInfo> GetField(String fieldName) public virtual Result<FieldInfo> GetField(String fieldName)
{ {
return .Err; return .Err;
@ -1247,6 +1268,19 @@ namespace System.Reflection
} }
} }
public override bool ImplementsInterface(Type checkInterface)
{
for (int ifaceIdx < mInterfaceCount)
{
if (mInterfaceDataPtr[ifaceIdx].mInterfaceType == checkInterface.TypeId)
return true;
}
var baseType = BaseType;
if (baseType != null)
return baseType.ImplementsInterface(checkInterface);
return false;
}
public override void GetFullName(String strBuffer) public override void GetFullName(String strBuffer)
{ {
if (mTypeFlags.HasFlag(TypeFlags.Tuple)) if (mTypeFlags.HasFlag(TypeFlags.Tuple))
@ -1528,7 +1562,8 @@ namespace System.Reflection
public override void GetFullName(String strBuffer) public override void GetFullName(String strBuffer)
{ {
strBuffer.Append("const "); strBuffer.Append("const ");
switch (GetType(mValueType)) var type = GetType(mValueType);
switch (type)
{ {
case typeof(float): case typeof(float):
(*(float*)&mValue).ToString(strBuffer); (*(float*)&mValue).ToString(strBuffer);
@ -1543,6 +1578,10 @@ namespace System.Reflection
strBuffer.Append('\''); strBuffer.Append('\'');
case typeof(uint64), typeof(uint): case typeof(uint64), typeof(uint):
(*(uint64*)&mValue).ToString(strBuffer); (*(uint64*)&mValue).ToString(strBuffer);
case typeof(String):
int32 stringId = *(int32*)&mValue;
String str = String.GetById(stringId);
str.Quote(strBuffer);
default: default:
mValue.ToString(strBuffer); mValue.ToString(strBuffer);
} }

View file

@ -846,7 +846,7 @@ public:
return (mKind == BfTypedValueKind_NoValue) && (mType != NULL); return (mKind == BfTypedValueKind_NoValue) && (mType != NULL);
} }
bool IsParams() bool IsParams() const
{ {
return (mKind == BfTypedValueKind_ParamsSplat) || (mKind == BfTypedValueKind_Params); return (mKind == BfTypedValueKind_ParamsSplat) || (mKind == BfTypedValueKind_Params);
} }

View file

@ -2032,6 +2032,39 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
} }
} }
} }
else if (paramsType->IsParamsType())
{
paramsType = paramsType->GetUnderlyingType();
if (paramsType->IsGenericParam())
{
auto genericParamType = (BfGenericParamType*)paramsType;
if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
{
auto genericParamInst = methodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx];
if ((genericParamInst->mTypeConstraint != NULL) && (genericParamInst->mTypeConstraint->IsInstanceOf(mModule->mCompiler->mTupleTypeDef)))
{
bool isValid = true;
BfTypeVector genericArgs;
for (int argIdx = methodInstance->mParams.mSize - 1 - inferParamOffset; argIdx < (int)mArguments.size(); argIdx++)
{
BfTypedValue argTypedValue = ResolveArgTypedValue(mArguments[argIdx], NULL, genericArgumentsSubstitute);
if (!argTypedValue)
{
isValid = false;
break;
}
genericArgs.Add(mModule->FixIntUnknown(argTypedValue.mType));
}
if (isValid)
{
(*genericArgumentsSubstitute)[genericParamType->mGenericParamIdx] = mModule->CreateTupleType(genericArgs, SubstituteList());
}
}
}
}
}
} }
if (!deferredArgs.IsEmpty()) if (!deferredArgs.IsEmpty())
@ -4388,6 +4421,10 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef)
else else
localResult = BfTypedValue(varDecl->mAddr, varDecl->mResolvedType, varDecl->mIsReadOnly ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr); localResult = BfTypedValue(varDecl->mAddr, varDecl->mResolvedType, varDecl->mIsReadOnly ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr);
} }
else if ((varDecl->mResolvedType->IsModifiedTypeType()) && (((BfModifiedTypeType*)varDecl->mResolvedType)->mModifiedKind == BfToken_Params))
{
localResult = BfTypedValue(BfIRValue(), varDecl->mResolvedType);
}
else if (varDecl->mResolvedType->IsValuelessNonOpaqueType()) else if (varDecl->mResolvedType->IsValuelessNonOpaqueType())
{ {
if ((varDecl->mResolvedType->IsRef()) && (!allowRef)) if ((varDecl->mResolvedType->IsRef()) && (!allowRef))
@ -4400,9 +4437,99 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef)
localResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), varDecl->mResolvedType, true); localResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), varDecl->mResolvedType, true);
} }
else if (varDecl->mCompositeCount >= 0) else if (varDecl->mCompositeCount >= 0)
{
if ((mBfEvalExprFlags & BfEvalExprFlags_InParamsExpr) != 0)
{ {
localResult = BfTypedValue(BfIRValue(), mModule->GetPrimitiveType(BfTypeCode_None)); localResult = BfTypedValue(BfIRValue(), mModule->GetPrimitiveType(BfTypeCode_None));
} }
else if (!varDecl->mAddr)
{
bool isValid = true;
Array<BfTypedValue> argVals;
auto methodState = mModule->mCurMethodState->GetMethodStateForLocal(varDecl);
for (int compositeIdx = 0; compositeIdx < varDecl->mCompositeCount; compositeIdx++)
{
BfResolvedArg compositeResolvedArg;
auto compositeLocalVar = methodState->mLocals[varDecl->mLocalVarIdx + compositeIdx + 1];
auto argValue = LoadLocal(compositeLocalVar, true);
if (argValue)
{
if (!argValue.mType->IsStruct())
argValue = mModule->LoadValue(argValue, NULL, mIsVolatileReference);
argVals.Add(argValue);
}
else
isValid = false;
}
if (isValid)
{
BfTypeInstance* tupleType = NULL;
if (varDecl->mResolvedType->IsTuple())
tupleType = (BfTupleType*)varDecl->mResolvedType;
else if ((varDecl->mResolvedType->IsDelegateOrFunction()))
{
auto invokeFunction = mModule->GetDelegateInvokeMethod(varDecl->mResolvedType->ToTypeInstance());
if (invokeFunction != NULL)
{
BfTypeVector fieldTypes;
SubstituteList fieldNames;
for (int paramIdx = 0; paramIdx < invokeFunction->GetParamCount(); paramIdx++)
{
fieldNames.Add(invokeFunction->GetParamName(paramIdx));
fieldTypes.Add(invokeFunction->GetParamType(paramIdx));
}
tupleType = mModule->CreateTupleType(fieldTypes, fieldNames);
}
}
if (tupleType == NULL)
{
isValid = false;
}
else if (tupleType->IsValuelessType())
{
localResult = mModule->GetDefaultTypedValue(tupleType);
}
else
{
BF_ASSERT(tupleType->mFieldInstances.mSize == argVals.mSize);
auto instAlloca = mModule->CreateAlloca(tupleType);
for (int i = 0; i < argVals.mSize; i++)
{
auto& fieldInstance = tupleType->mFieldInstances[i];
if (fieldInstance.mDataIdx >= 0)
{
auto val = mModule->Cast(varDecl->mNameNode, argVals[i], fieldInstance.mResolvedType);
if (val)
{
val = mModule->LoadOrAggregateValue(val);
if (!val.mType->IsValuelessType())
{
auto elemPtr = mModule->mBfIRBuilder->CreateInBoundsGEP(instAlloca, 0, fieldInstance.mDataIdx);
mModule->mBfIRBuilder->CreateStore(val.mValue, elemPtr);
}
}
}
}
varDecl->mResolvedType = tupleType;
varDecl->mAddr = instAlloca;
varDecl->mIsReadOnly = true;
localResult = BfTypedValue(varDecl->mAddr, varDecl->mResolvedType, BfTypedValueKind_ReadOnlyAddr);
}
}
if (!isValid)
{
localResult = mModule->GetDefaultTypedValue(mModule->ResolveTypeDef(mModule->mCompiler->mTupleTypeDef));
}
}
}
else else
{ {
BF_ASSERT((mModule->mCurMethodState->mClosureState != NULL) || (mModule->mBfIRBuilder->mIgnoreWrites)); BF_ASSERT((mModule->mCurMethodState->mClosureState != NULL) || (mModule->mBfIRBuilder->mIgnoreWrites));
@ -4539,11 +4666,6 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI
if ((closureTypeInst != NULL) && (wantName == "this")) if ((closureTypeInst != NULL) && (wantName == "this"))
break; break;
if ((varDecl->mCompositeCount >= 0) && ((mBfEvalExprFlags & BfEvalExprFlags_AllowParamsExpr) == 0))
{
mModule->Fail("Invalid use of 'params' parameter", refNode);
}
if (varDecl->mResolvedType->IsVoid()) if (varDecl->mResolvedType->IsVoid())
{ {
if ((varDecl->mIsReadOnly) && (varDecl->mParamIdx == -2) && (varDecl->mParamFailed)) if ((varDecl->mIsReadOnly) && (varDecl->mParamIdx == -2) && (varDecl->mParamFailed))
@ -4556,6 +4678,12 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI
mModule->SetElementType(identifierNode, (varDecl->IsParam()) ? BfSourceElementType_Parameter : BfSourceElementType_Local); mModule->SetElementType(identifierNode, (varDecl->IsParam()) ? BfSourceElementType_Parameter : BfSourceElementType_Local);
BfTypedValue localResult = LoadLocal(varDecl); BfTypedValue localResult = LoadLocal(varDecl);
if ((localResult) && (localResult.mType->IsParamsType()) && ((mBfEvalExprFlags & BfEvalExprFlags_AllowParamsExpr) == 0))
{
localResult = mModule->LoadOrAggregateValue(localResult);
}
auto autoComplete = GetAutoComplete(); auto autoComplete = GetAutoComplete();
if (identifierNode != NULL) if (identifierNode != NULL)
{ {
@ -6355,9 +6483,8 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
auto methodState = mModule->mCurMethodState->GetMethodStateForLocal(localVar); auto methodState = mModule->mCurMethodState->GetMethodStateForLocal(localVar);
if (localVar->mCompositeCount >= 0) if (localVar->mCompositeCount >= 0)
{ {
if ((resolvedArg.mArgFlags & BfArgFlag_ParamsExpr) == 0) if ((resolvedArg.mArgFlags & BfArgFlag_ParamsExpr) != 0)
mModule->Warn(0, "'params' token expected", argExpr); {
for (int compositeIdx = 0; compositeIdx < localVar->mCompositeCount; compositeIdx++) for (int compositeIdx = 0; compositeIdx < localVar->mCompositeCount; compositeIdx++)
{ {
BfResolvedArg compositeResolvedArg; BfResolvedArg compositeResolvedArg;
@ -6372,10 +6499,20 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
resolvedArg.mExpression = argExpr; resolvedArg.mExpression = argExpr;
resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_FromParamComposite); resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_FromParamComposite);
resolvedArgs.mResolvedArgs.push_back(resolvedArg); resolvedArgs.mResolvedArgs.push_back(resolvedArg);
exprEvaluator.mIsVolatileReference = false;
} }
if ((localVar->mResolvedType->IsModifiedTypeType()) && (((BfModifiedTypeType*)localVar->mResolvedType)->mModifiedKind == BfToken_Params))
{
// Is a 'params'
}
else
continue; continue;
} }
else
exprEvaluator.mResult = mModule->LoadOrAggregateValue(exprEvaluator.mResult);
}
} }
exprEvaluator.CheckResultForReading(exprEvaluator.mResult); exprEvaluator.CheckResultForReading(exprEvaluator.mResult);
@ -7903,6 +8040,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
BfIRValue expandedParamAlloca; BfIRValue expandedParamAlloca;
BfTypedValue expandedParamsArray; BfTypedValue expandedParamsArray;
BfType* expandedParamsElementType = NULL; BfType* expandedParamsElementType = NULL;
bool hadDelegateParamIdx = false;
int extendedParamIdx = 0; int extendedParamIdx = 0;
AddCallDependencies(methodInstance); AddCallDependencies(methodInstance);
@ -8170,6 +8308,8 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
} }
} }
} }
else if (paramKind == BfParamKind_DelegateParam)
hadDelegateParamIdx = true;
} }
BfAstNode* arg = NULL; BfAstNode* arg = NULL;
@ -8181,7 +8321,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (argExprIdx < (int)argValues.size()) if (argExprIdx < (int)argValues.size())
{ {
arg = argValues[argExprIdx].mExpression; arg = argValues[argExprIdx].mExpression;
if (((argValues[argExprIdx].mArgFlags & BfArgFlag_StringInterpolateArg) != 0) && (!expandedParamsArray)) if (((argValues[argExprIdx].mArgFlags & BfArgFlag_StringInterpolateArg) != 0) && (!expandedParamsArray) && (!hadDelegateParamIdx))
{ {
BfAstNode* errorRef = arg; BfAstNode* errorRef = arg;
int checkIdx = argExprIdx - 1; int checkIdx = argExprIdx - 1;
@ -10418,7 +10558,10 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
if ((mModule->mCurMethodState != NULL) && (exprEvaluator.mResultLocalVar != NULL) && (exprEvaluator.mResultLocalVarRefNode != NULL)) if ((mModule->mCurMethodState != NULL) && (exprEvaluator.mResultLocalVar != NULL) && (exprEvaluator.mResultLocalVarRefNode != NULL))
{ {
auto localVar = exprEvaluator.mResultLocalVar; auto localVar = exprEvaluator.mResultLocalVar;
if ((localVar->mCompositeCount >= 0) && (localVar->mResolvedType == fieldVal.mType)) auto checkType = localVar->mResolvedType;
if (checkType->IsParamsType())
checkType = checkType->GetUnderlyingType();
if ((localVar->mCompositeCount >= 0) && (checkType == fieldVal.mType))
{ {
delegateFailed = false; delegateFailed = false;
if (mModule->mCurMethodInstance->mIsUnspecialized) if (mModule->mCurMethodInstance->mIsUnspecialized)
@ -19984,12 +20127,12 @@ void BfExprEvaluator::CheckResultForReading(BfTypedValue& typedValue)
int fieldIdx = mResultLocalVarField - 1; int fieldIdx = mResultLocalVarField - 1;
auto localVar = mResultLocalVar; auto localVar = mResultLocalVar;
if (localVar->mCompositeCount > 0) /*if (localVar->mCompositeCount > 0)
{ {
mModule->Fail(StrFormat("Cannot read from composite '%s', it can only be used in an argument list", localVar->mName.c_str()), mResultLocalVarRefNode); mModule->Fail(StrFormat("Cannot read from composite '%s', it can only be used in an argument list", localVar->mName.c_str()), mResultLocalVarRefNode);
typedValue = BfTypedValue(); typedValue = BfTypedValue();
return; return;
} }*/
if (localVar->mAssignedKind == BfLocalVarAssignKind_None) if (localVar->mAssignedKind == BfLocalVarAssignKind_None)
{ {
@ -22849,9 +22992,7 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp
/// ///
{ {
// If this is a cast, we don't want the value to be coerced before the unary operator is applied. SetAndRestoreValue<BfEvalExprFlags> prevFlags(mBfEvalExprFlags);
// WAIT: Why not?
//SetAndRestoreValue<BfType*> prevExpectingType(mExpectingType, NULL);
BfType* prevExpedcting = mExpectingType; BfType* prevExpedcting = mExpectingType;
switch (unaryOp) switch (unaryOp)
@ -22876,6 +23017,9 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp
mExpectingType = NULL; mExpectingType = NULL;
// Otherwise keep expecting type // Otherwise keep expecting type
break; break;
case BfUnaryOp_Params:
mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_InParamsExpr);
break;
default: default:
mExpectingType = NULL; mExpectingType = NULL;
} }
@ -23570,6 +23714,8 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr,
if (allowParams) if (allowParams)
{ {
mResult = mModule->LoadValue(mResult); mResult = mModule->LoadValue(mResult);
if ((mResult.mType != NULL) && (mResult.mType->IsParamsType()))
mResult.mType = mResult.mType->GetUnderlyingType();
if (mResult.IsSplat()) if (mResult.IsSplat())
mResult.mKind = BfTypedValueKind_ParamsSplat; mResult.mKind = BfTypedValueKind_ParamsSplat;
else else

View file

@ -578,6 +578,8 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType
name += "U5alloc"; name += "U5alloc";
else if (retTypeType->mModifiedKind == BfToken_Nullable) else if (retTypeType->mModifiedKind == BfToken_Nullable)
name += "U8nullable"; name += "U8nullable";
else if (retTypeType->mModifiedKind == BfToken_Params)
name += "U6params";
else else
BF_FATAL("Unhandled"); BF_FATAL("Unhandled");
Mangle(mangleContext, name, retTypeType->mElementType); Mangle(mangleContext, name, retTypeType->mElementType);
@ -1729,6 +1731,8 @@ void BfMSMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType*
name += "alloc$"; name += "alloc$";
else if (retType->mModifiedKind == BfToken_Nullable) else if (retType->mModifiedKind == BfToken_Nullable)
name += "nullable$"; name += "nullable$";
else if (retType->mModifiedKind == BfToken_Params)
name += "params$";
else else
BF_FATAL("Unhandled"); BF_FATAL("Unhandled");
Mangle(mangleContext, name, retType->mElementType); Mangle(mangleContext, name, retType->mElementType);

View file

@ -13520,6 +13520,16 @@ BfTypedValue BfModule::LoadOrAggregateValue(BfTypedValue typedValue)
return AggregateSplat(typedValue); return AggregateSplat(typedValue);
if (typedValue.IsAddr()) if (typedValue.IsAddr())
return LoadValue(typedValue); return LoadValue(typedValue);
if ((typedValue.mType != NULL) && (typedValue.mType->IsParamsType()) && (!typedValue.IsParams()))
{
return GetDefaultTypedValue(ResolveTypeDef(mCompiler->mTupleTypeDef));
}
else if ((typedValue.IsParams()) && (typedValue.mType->IsGenericParam()))
{
return BfTypedValue(mBfIRBuilder->GetFakeVal(), typedValue.mType);
}
return typedValue; return typedValue;
} }
@ -17144,6 +17154,8 @@ void BfModule::CreateDelegateInvokeMethod()
for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++) for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++)
{ {
if (mCurMethodState->mLocals[i]->mCompositeCount >= 0)
continue;
BfTypedValue localVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[i], true); BfTypedValue localVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[i], true);
exprEvaluator.PushArg(localVal, staticFuncArgs); exprEvaluator.PushArg(localVal, staticFuncArgs);
exprEvaluator.PushArg(localVal, memberFuncArgs); exprEvaluator.PushArg(localVal, memberFuncArgs);
@ -20029,6 +20041,16 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
paramVar->mIsImplicitParam = true; paramVar->mIsImplicitParam = true;
} }
if (methodInstance->GetParamKind(paramIdx) == BfParamKind_Params)
{
if ((paramVar->mResolvedType->IsModifiedTypeType()) && (((BfModifiedTypeType*)paramVar->mResolvedType)->mModifiedKind == BfToken_Params))
{
paramVar->mValue = BfIRValue();
paramVar->mCompositeCount = 0;
paramVar->mIsSplat = false;
}
}
if (!mCurTypeInstance->IsDelegateOrFunction()) if (!mCurTypeInstance->IsDelegateOrFunction())
CheckVariableDef(paramVar); CheckVariableDef(paramVar);
paramVar->Init(); paramVar->Init();
@ -20073,6 +20095,18 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) || if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) ||
((typeInstConstraint != NULL) && ((typeInstConstraint != NULL) &&
((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef))))) ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef)))))
{
bool doAdd = true;
// TODO: Remove
if (!mCurMethodState->mLocals.IsEmpty())
{
auto checkVar = mCurMethodState->mLocals.back();
if (checkVar->mName == paramDef->mName)
doAdd = false;
}
if (doAdd)
{ {
BfLocalVariable* localVar = new BfLocalVariable(); BfLocalVariable* localVar = new BfLocalVariable();
localVar->mName = paramDef->mName; localVar->mName = paramDef->mName;
@ -20082,6 +20116,7 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
} }
} }
} }
}
else if (paramsType->IsDelegate()) else if (paramsType->IsDelegate())
{ {
BfMethodInstance* invokeMethodInstance = GetRawMethodInstanceAtIdx(paramsType->ToTypeInstance(), 0, "Invoke"); BfMethodInstance* invokeMethodInstance = GetRawMethodInstanceAtIdx(paramsType->ToTypeInstance(), 0, "Invoke");
@ -24999,13 +25034,15 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
} }
else if (resolvedParamType->IsGenericParam()) else if (resolvedParamType->IsGenericParam())
{ {
bool isGenericDelegateParams = false;
bool addDelegateParamsType = false;
auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType, false, BfFailHandleKind_Ignore); auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType, false, BfFailHandleKind_Ignore);
if (genericParamInstance == NULL) if (genericParamInstance == NULL)
{ {
// Delegate case with a 'params T'? // Delegate case with a 'params T'?
mCurMethodInstance->mHadGenericDelegateParams = true; isGenericDelegateParams = true;
isValid = true; addDelegateParamsType = true;
addParams = false;
} }
else if (genericParamInstance->mTypeConstraint != NULL) else if (genericParamInstance->mTypeConstraint != NULL)
{ {
@ -25021,10 +25058,29 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) || else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) ||
((genericParamInstance != NULL) && (typeInstConstraint != NULL) && ((genericParamInstance != NULL) && (typeInstConstraint != NULL) &&
((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef))))) ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef)))))
{
isGenericDelegateParams = true;
//addDelegateParamsType = typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef);
addDelegateParamsType = true;
}
}
if (isGenericDelegateParams)
{ {
mCurMethodInstance->mHadGenericDelegateParams = true; mCurMethodInstance->mHadGenericDelegateParams = true;
isValid = true; isValid = true;
addParams = false; addParams = false;
if (addDelegateParamsType)
{
BfMethodParam methodParam;
methodParam.mResolvedType = CreateModifiedTypeType(resolvedParamType, BfToken_Params);
methodParam.mParamDefIdx = paramDefIdx;
mCurMethodInstance->mParams.Add(methodParam);
//TODO: Why do we have this 'if'
if (!methodInstance->IsSpecializedGenericMethod())
AddDependency(methodParam.mResolvedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
} }
} }
} }
@ -25032,9 +25088,9 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
{ {
auto paramTypeInst = resolvedParamType->ToTypeInstance(); auto paramTypeInst = resolvedParamType->ToTypeInstance();
if ((paramTypeInst != NULL) && if ((paramTypeInst != NULL) &&
((paramTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (paramTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef)))) ((paramTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (paramTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (paramTypeInst->IsInstanceOf(mCompiler->mTupleTypeDef))))
{ {
// If we have a 'params T' and 'T' gets specialized with actually 'Delegate' or 'Function' then just ignore it // If we have a 'params T' and 'T' gets specialized with actually 'Tuple', 'Delegate' or 'Function' then just ignore it
isValid = true; isValid = true;
addParams = false; addParams = false;
} }
@ -25042,7 +25098,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
if (!isValid) if (!isValid)
{ {
Fail("Parameters with 'params' specifiers can only be used for array, span, delegate, or function types", paramDef->mParamDeclaration->mModToken); Fail("Parameters with 'params' specifiers can only be used for array, span, tuple, delegate, or function types", paramDef->mParamDeclaration->mModToken);
// Failure case, make it an Object[] // Failure case, make it an Object[]
resolvedParamType = CreateArrayType(mContext->mBfObjectType, 1); resolvedParamType = CreateArrayType(mContext->mBfObjectType, 1);
} }
@ -25100,7 +25156,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
auto tupleType = (BfTupleType*)methodParam.mResolvedType; auto tupleType = (BfTupleType*)methodParam.mResolvedType;
auto& fieldInstance = tupleType->mFieldInstances[methodParam.mDelegateParamIdx]; auto& fieldInstance = tupleType->mFieldInstances[methodParam.mDelegateParamIdx];
auto paramName = fieldInstance.GetFieldDef()->mName; auto paramName = fieldInstance.GetFieldDef()->mName;
if (!usedNames.Add(paramName)) if ((!usedNames.Add(paramName)) || (::isdigit((uint8)paramName[0])))
methodParam.mDelegateParamNameCombine = true; methodParam.mDelegateParamNameCombine = true;
} }
else else

View file

@ -93,6 +93,7 @@ enum BfEvalExprFlags : int64
BfEvalExprFlags_AppendFieldInitializer = 0x80000000, BfEvalExprFlags_AppendFieldInitializer = 0x80000000,
BfEvalExprFlags_NameOf = 0x100000000LL, BfEvalExprFlags_NameOf = 0x100000000LL,
BfEvalExprFlags_NameOfSuccess = 0x200000000LL, BfEvalExprFlags_NameOfSuccess = 0x200000000LL,
BfEvalExprFlags_InParamsExpr = 0x400000000LL,
BfEvalExprFlags_InheritFlags = BfEvalExprFlags_NoAutoComplete | BfEvalExprFlags_Comptime | BfEvalExprFlags_DeclType BfEvalExprFlags_InheritFlags = BfEvalExprFlags_NoAutoComplete | BfEvalExprFlags_Comptime | BfEvalExprFlags_DeclType
}; };

View file

@ -15280,6 +15280,13 @@ BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, Bf
}*/ }*/
BfCastResultFlags castResultFlags = BfCastResultFlags_None; BfCastResultFlags castResultFlags = BfCastResultFlags_None;
if ((typedVal.IsParams()) && (toType->IsParamsType()))
{
if (typedVal.mType == toType->GetUnderlyingType())
return BfTypedValue(mBfIRBuilder->GetFakeVal(), toType);
}
auto castedValue = CastToValue(srcNode, typedVal, toType, castFlags, &castResultFlags); auto castedValue = CastToValue(srcNode, typedVal, toType, castFlags, &castResultFlags);
if (!castedValue) if (!castedValue)
return BfTypedValue(); return BfTypedValue();

View file

@ -1176,11 +1176,6 @@ void BfMethodInstance::GetParamName(int paramIdx, StringImpl& name, int& namePre
else else
name = fieldInstance.GetFieldDef()->mName; name = fieldInstance.GetFieldDef()->mName;
if (name == "p__a__a")
{
NOP;
}
return; return;
} }

View file

@ -626,6 +626,7 @@ public:
virtual bool IsOnDemand() { return false; } virtual bool IsOnDemand() { return false; }
virtual bool IsTemporary() { return false; } virtual bool IsTemporary() { return false; }
virtual bool IsModifiedTypeType() { return false; } virtual bool IsModifiedTypeType() { return false; }
virtual bool IsParamsType() { return false; }
virtual bool IsConcreteInterfaceType() { return false; } virtual bool IsConcreteInterfaceType() { return false; }
virtual bool IsTypeAlias() { return false; } virtual bool IsTypeAlias() { return false; }
virtual bool HasPackingHoles() { return false; } virtual bool HasPackingHoles() { return false; }
@ -1142,6 +1143,7 @@ public:
BfType* mElementType; BfType* mElementType;
virtual bool IsModifiedTypeType() override { return true; } virtual bool IsModifiedTypeType() override { return true; }
virtual bool IsParamsType() override { return mModifiedKind == BfToken_Params; }
virtual bool CanBeValuelessType() override { return true; } virtual bool CanBeValuelessType() override { return true; }
virtual bool IsValuelessType() override { return true; } virtual bool IsValuelessType() override { return true; }

View file

@ -6311,6 +6311,25 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
_FixVariables(); _FixVariables();
CeSetAddrVal(stackPtr + 0, reflectType, ptrSize); CeSetAddrVal(stackPtr + 0, reflectType, ptrSize);
} }
else if (checkFunction->mFunctionKind == CeFunctionKind_GetWrappedType)
{
int32 typeId = *(int32*)((uint8*)stackPtr + ceModule->mSystem->mPtrSize);
BfType* type = GetBfType(typeId);
bool success = false;
if (type == NULL)
{
_Fail("Invalid type");
return false;
}
addr_ce reflectType = NULL;
type = ceModule->GetWrappedStructType(type);
if (type != NULL)
reflectType = GetReflectType(type->mTypeId);
_FixVariables();
CeSetAddrVal(stackPtr + 0, reflectType, ptrSize);
}
else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectTypeByName) else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectTypeByName)
{ {
addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + ptrSize); addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + ptrSize);
@ -6896,6 +6915,19 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
mCurModule->CEMixin(mCurCallSource->mRefNode, emitStr); mCurModule->CEMixin(mCurCallSource->mRefNode, emitStr);
} }
else if (checkFunction->mFunctionKind == CeFunctionKind_GetStringById)
{
int32 stringId = *(int32*)((uint8*)stackPtr + ptrSize);
String string = "";
BfStringPoolEntry* valuePtr = NULL;
mCurModule->mContext->mStringObjectIdMap.TryGetValue(stringId, &valuePtr);
if (valuePtr != NULL)
string = valuePtr->mString;
CeSetAddrVal(stackPtr + 0, GetString(string), ptrSize);
_FixVariables();
}
else if (checkFunction->mFunctionKind == CeFunctionKind_Sleep) else if (checkFunction->mFunctionKind == CeFunctionKind_Sleep)
{ {
int32 sleepMS = *(int32*)((uint8*)stackPtr); int32 sleepMS = *(int32*)((uint8*)stackPtr);
@ -10019,6 +10051,10 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction)
{ {
ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeById; ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeById;
} }
else if (methodDef->mName == "Comptime_GetWrappedType")
{
ceFunction->mFunctionKind = CeFunctionKind_GetWrappedType;
}
else if (methodDef->mName == "Comptime_GetTypeByName") else if (methodDef->mName == "Comptime_GetTypeByName")
{ {
ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeByName; ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeByName;
@ -10126,6 +10162,10 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction)
{ {
ceFunction->mFunctionKind = CeFunctionKind_EmitMixin; ceFunction->mFunctionKind = CeFunctionKind_EmitMixin;
} }
else if (methodDef->mName == "Comptime_GetStringById")
{
ceFunction->mFunctionKind = CeFunctionKind_GetStringById;
}
} }
else if (owner->IsInstanceOf(mCeModule->mCompiler->mDiagnosticsDebugTypeDef)) else if (owner->IsInstanceOf(mCeModule->mCompiler->mDiagnosticsDebugTypeDef))
{ {

View file

@ -436,6 +436,7 @@ enum CeFunctionKind
CeFunctionKind_HasDeclaredMember, CeFunctionKind_HasDeclaredMember,
CeFunctionKind_GetReflectType, CeFunctionKind_GetReflectType,
CeFunctionKind_GetReflectTypeById, CeFunctionKind_GetReflectTypeById,
CeFunctionKind_GetWrappedType,
CeFunctionKind_GetReflectTypeByName, CeFunctionKind_GetReflectTypeByName,
CeFunctionKind_GetReflectSpecializedType, CeFunctionKind_GetReflectSpecializedType,
CeFunctionKind_Type_ToString, CeFunctionKind_Type_ToString,
@ -463,6 +464,7 @@ enum CeFunctionKind
CeFunctionKind_EmitMethodEntry, CeFunctionKind_EmitMethodEntry,
CeFunctionKind_EmitMethodExit, CeFunctionKind_EmitMethodExit,
CeFunctionKind_EmitMixin, CeFunctionKind_EmitMixin,
CeFunctionKind_GetStringById,
CeFunctionKind_BfpDirectory_Create, CeFunctionKind_BfpDirectory_Create,
CeFunctionKind_BfpDirectory_Rename, CeFunctionKind_BfpDirectory_Rename,

View file

@ -1,9 +1,293 @@
#pragma warning disable 168
using System; using System;
using System.Reflection;
namespace Tests; namespace Tests;
class Params class Params
{ {
[AttributeUsage(.Method)]
struct StringFormatAttribute : Attribute, IOnMethodInit
{
[Comptime]
public void OnMethodInit(MethodInfo methodInfo, Self* prev)
{
String code = scope .();
String format = null;
if (var constType = methodInfo.GetGenericArgType(0) as ConstExprType)
{
if (constType.ValueType == typeof(String))
{
format = String.GetById(constType.ValueData);
}
}
if (String.IsNullOrEmpty(format))
return;
int pos = 0;
int len = format.Length;
char8 ch = '\x00';
String s = null;
String fmt = "";
int autoArgIdx = 0;
bool hasTempStr = false;
void FormatError()
{
Runtime.FatalError("Format Error");
}
String bufOut = scope .();
void FlushOut()
{
if (bufOut.IsEmpty)
return;
code.Append("outStr.Append(");
bufOut.Quote(code);
code.Append(");\n");
bufOut.Clear();
}
while (true)
{
int p = pos;
int i = pos;
while (pos < len)
{
ch = format[pos];
pos++;
if (ch == '}')
{
if (pos < len && format[pos] == '}') // Treat as escape character for }}
pos++;
else
FormatError();
}
if (ch == '{')
{
if (pos < len && format[pos] == '{') // Treat as escape character for {{
pos++;
else
{
pos--;
break;
}
}
bufOut.Append(ch);
}
if (pos == len) break;
pos++;
int index = 0;
if (pos == len || (ch = format[pos]) < '0' || ch > '9')
{
if ((pos < len) &&
((ch == '}') || (ch == ':') || (ch == ',')))
index = autoArgIdx++;
else
FormatError();
}
else
{
repeat
{
index = index * 10 + ch - '0';
pos++;
if (pos == len) FormatError();
ch = format[pos];
}
while (ch >= '0' && ch <= '9' && index < 1000000);
}
var paramType = methodInfo.GetParamType(index + 3);
var paramName = methodInfo.GetParamName(index + 3);
while (pos < len && (ch = format[pos]) == ' ') pos++;
bool leftJustify = false;
int width = 0;
if (ch == ',')
{
pos++;
while (pos < len && format[pos] == ' ') pos++;
if (pos == len) FormatError();
ch = format[pos];
if (ch == '-')
{
leftJustify = true;
pos++;
if (pos == len) FormatError();
ch = format[pos];
}
if (ch < '0' || ch > '9') FormatError();
repeat
{
width = width * 10 + ch - '0';
pos++;
if (pos == len) FormatError();
ch = format[pos];
}
while (ch >= '0' && ch <= '9' && width < 1000000);
}
while (pos < len && (ch = format[pos]) == ' ') pos++;
//Object arg = args[index];
if (ch == ':')
{
if (fmt == "")
fmt = scope:: String(64);
else
fmt.Clear();
bool isFormatEx = false;
pos++;
p = pos;
i = pos;
while (true)
{
if (pos == len) FormatError();
ch = format[pos];
pos++;
if (ch == '{')
{
isFormatEx = true;
if (pos < len && format[pos] == '{') // Treat as escape character for {{
pos++;
else
FormatError();
}
else if (ch == '}')
{
// We only treat '}}' as an escape character if the format had an opening '{'. Otherwise we just close on the first '}'
if ((isFormatEx) && (pos < len && format[pos] == '}')) // Treat as escape character for }}
pos++;
else
{
pos--;
break;
}
}
if (fmt == null)
{
fmt = scope:: String(0x100);
}
fmt.Append(ch);
}
}
if (ch != '}') Runtime.FatalError("Format error");
pos++;
FlushOut();
bool checkNull = paramType.IsObject || paramType.IsInterface;
bool hasWidth = width != 0;
bool hasFmt = fmt != "";
if (checkNull)
{
code.AppendF($"if ({paramName} == null)\n");
code.Append(" outStr.Append(\"null\");\nelse\n{\n");
}
if ((!leftJustify) && (width > 0))
{
// We need a temporary string
if (!hasTempStr)
{
code.Insert(0, "var s = scope String(128);\n");
hasTempStr = false;
}
else
{
code.Append("s.Clear();\n");
}
if (paramType.ImplementsInterface(typeof(IFormattable)))
{
if (!hasFmt)
{
code.AppendF("if (provider != null)\n ");
}
code.AppendF($"((IFormattable){paramName}).ToString(s, ");
fmt.Quote(code);
code.AppendF($", provider);\n");
if (!hasFmt)
{
code.AppendF("else\n");
code.AppendF($" {paramName}.ToString(s);\n");
}
}
else
{
code.AppendF($"{paramName}.ToString(s);\n");
}
code.AppendF($"outStr.Append(' ', {width} - s.[Friend]mLength);\n");
code.Append("outStr.Append(s);\n");
}
else
{
if (hasWidth)
{
code.AppendF($"int start_{pos} = outStr.[Friend]mLength;\n");
}
if (paramType.ImplementsInterface(typeof(IFormattable)))
{
if (!hasFmt)
{
code.AppendF("if (provider != null)\n ");
}
code.AppendF($"((IFormattable){paramName}).ToString(outStr, ");
fmt.Quote(code);
code.AppendF($", provider);\n");
if (!hasFmt)
{
code.AppendF("else\n");
code.AppendF($" {paramName}.ToString(outStr);\n");
}
}
else
{
code.AppendF($"{paramName}.ToString(outStr);\n");
}
if (hasWidth)
{
code.AppendF($"outStr.Append(' ', {width} - (outStr.[Friend]mLength - start_{pos}));\n");
}
}
if (checkNull)
{
code.Append("}\n");
}
}
FlushOut();
Compiler.EmitMethodEntry(methodInfo, code);
}
}
[StringFormat]
static void StrFormat<TStr, TArgs>(String outStr, IFormatProvider provider, TStr formatStr, params TArgs args) where TStr : const String where TArgs : Tuple
{
}
class ClassA<T> where T : Tuple class ClassA<T> where T : Tuple
{ {
public static int Test(delegate int(char8 a, params T) dlg, params T par) public static int Test(delegate int(char8 a, params T) dlg, params T par)
@ -17,6 +301,39 @@ class Params
} }
static void Test1<T>(params T args) where T : Tuple
{
Test3<T>(args);
}
static void Test2<T>(params T args) where T : Delegate
{
var vals = args;
Test3(args);
}
static void Test3<T>(T args) where T : Tuple
{
}
static void Test4<T>(params (int, float) args)
{
var a = args;
int arg0 = a.0;
float arg1 = a.1;
}
struct StructA
{
public int mA;
public override void ToString(String strBuffer)
{
strBuffer.AppendF($"StructA({mA})");
}
}
[Test] [Test]
public static void TestBasics() public static void TestBasics()
{ {
@ -25,5 +342,15 @@ class Params
return (.)a + (.)__a + (.)b; return (.)a + (.)__a + (.)b;
}, 10, 2.3f); }, 10, 2.3f);
Test.Assert(val == 65+10+2); Test.Assert(val == 65+10+2);
Test1(1, 2.3f);
delegate void(int a, float b) dlg = default;
Test2<delegate void(int a, float b)>(1, 2.3f);
String tStr = null;
StructA sa = .() { mA = 123 };
String str = StrFormat(.. scope .(), null, $"This is a test string {123,-6} with some numbers in it {234,6} {0x1234:X} {sa} {tStr}.");
Test.Assert(str == "This is a test string 123 with some numbers in it 234 1234 StructA(123) null.");
} }
} }