1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +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_EmitMethodExit(int64 methodHandle, StringView text);
static extern void Comptime_EmitMixin(StringView text);
static extern String Comptime_GetStringById(int32 id);
[Comptime(OnlyFromComptime=true)]
public static MethodBuilder CreateMethod(Type owner, StringView methodName, Type returnType, MethodFlags methodFlags)

View file

@ -1392,6 +1392,10 @@ namespace System
while (true)
{
int charsLeft = len - pos;
Reserve(mLength + charsLeft);
char8* ptr = Ptr;
int p = pos;
int i = pos;
while (pos < len)
@ -1418,7 +1422,8 @@ namespace System
}
}
Append(ch);
//Append(ch);
ptr[mLength++] = ch;
}
if (pos == len) break;
@ -1519,25 +1524,36 @@ namespace System
}
if (ch != '}') return FormatError();
pos++;
if (s == null)
s = scope:: String(128);
s.Clear();
IFormattable formattableArg = arg as IFormattable;
if (formattableArg != null)
formattableArg.ToString(s, fmt, provider);
else if (arg != null)
arg.ToString(s);
if ((provider == null) && (fmt.IsEmpty) && (width == 0))
{
if (arg == null)
s.Append("null");
else
arg.ToString(this);
}
else
s.Append("null");
if (fmt != (Object)"")
fmt.Clear();
if (s == null) s = String.Empty;
int pad = width - s.Length;
if (!leftJustify && pad > 0) Append(' ', pad);
Append(s);
if (leftJustify && pad > 0) Append(' ', pad);
{
if (s == null)
s = scope:: String(128);
s.Clear();
IFormattable formattableArg = arg as IFormattable;
if (formattableArg != null)
formattableArg.ToString(s, fmt, provider);
else if (arg != null)
arg.ToString(s);
else
s.Append("null");
if (fmt != (Object)"")
fmt.Clear();
if (s == null) s = String.Empty;
int pad = width - s.Length;
if (!leftJustify && pad > 0) Append(' ', pad);
Append(s);
if (leftJustify && pad > 0) Append(' ', pad);
}
}
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>
{
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
{
get
@ -754,6 +765,7 @@ namespace System
static extern int32 Comptime_Type_GetBaseType(int32 typeId);
static extern bool Comptime_Type_HasDeclaredMember(int32 typeId, int32 kind, StringView name);
static extern Type Comptime_GetTypeById(int32 typeId);
static extern Type Comptime_GetWrappedType(int32 typeId);
static extern Type Comptime_GetTypeByName(StringView name);
static extern String Comptime_Type_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);
}
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)
{
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)
{
if (mTypeFlags.HasFlag(TypeFlags.Tuple))
@ -1528,7 +1562,8 @@ namespace System.Reflection
public override void GetFullName(String strBuffer)
{
strBuffer.Append("const ");
switch (GetType(mValueType))
var type = GetType(mValueType);
switch (type)
{
case typeof(float):
(*(float*)&mValue).ToString(strBuffer);
@ -1543,6 +1578,10 @@ namespace System.Reflection
strBuffer.Append('\'');
case typeof(uint64), typeof(uint):
(*(uint64*)&mValue).ToString(strBuffer);
case typeof(String):
int32 stringId = *(int32*)&mValue;
String str = String.GetById(stringId);
str.Quote(strBuffer);
default:
mValue.ToString(strBuffer);
}

View file

@ -846,7 +846,7 @@ public:
return (mKind == BfTypedValueKind_NoValue) && (mType != NULL);
}
bool IsParams()
bool IsParams() const
{
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())
@ -4388,6 +4421,10 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef)
else
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())
{
if ((varDecl->mResolvedType->IsRef()) && (!allowRef))
@ -4401,7 +4438,97 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef)
}
else if (varDecl->mCompositeCount >= 0)
{
localResult = BfTypedValue(BfIRValue(), mModule->GetPrimitiveType(BfTypeCode_None));
if ((mBfEvalExprFlags & BfEvalExprFlags_InParamsExpr) != 0)
{
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
{
@ -4539,11 +4666,6 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI
if ((closureTypeInst != NULL) && (wantName == "this"))
break;
if ((varDecl->mCompositeCount >= 0) && ((mBfEvalExprFlags & BfEvalExprFlags_AllowParamsExpr) == 0))
{
mModule->Fail("Invalid use of 'params' parameter", refNode);
}
if (varDecl->mResolvedType->IsVoid())
{
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);
BfTypedValue localResult = LoadLocal(varDecl);
if ((localResult) && (localResult.mType->IsParamsType()) && ((mBfEvalExprFlags & BfEvalExprFlags_AllowParamsExpr) == 0))
{
localResult = mModule->LoadOrAggregateValue(localResult);
}
auto autoComplete = GetAutoComplete();
if (identifierNode != NULL)
{
@ -6355,26 +6483,35 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
auto methodState = mModule->mCurMethodState->GetMethodStateForLocal(localVar);
if (localVar->mCompositeCount >= 0)
{
if ((resolvedArg.mArgFlags & BfArgFlag_ParamsExpr) == 0)
mModule->Warn(0, "'params' token expected", argExpr);
for (int compositeIdx = 0; compositeIdx < localVar->mCompositeCount; compositeIdx++)
if ((resolvedArg.mArgFlags & BfArgFlag_ParamsExpr) != 0)
{
BfResolvedArg compositeResolvedArg;
auto compositeLocalVar = methodState->mLocals[localVar->mLocalVarIdx + compositeIdx + 1];
auto argValue = exprEvaluator.LoadLocal(compositeLocalVar, true);
if (argValue)
for (int compositeIdx = 0; compositeIdx < localVar->mCompositeCount; compositeIdx++)
{
if (!argValue.mType->IsStruct())
argValue = mModule->LoadValue(argValue, NULL, exprEvaluator.mIsVolatileReference);
}
resolvedArg.mTypedValue = argValue;
resolvedArg.mExpression = argExpr;
resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_FromParamComposite);
resolvedArgs.mResolvedArgs.push_back(resolvedArg);
}
BfResolvedArg compositeResolvedArg;
auto compositeLocalVar = methodState->mLocals[localVar->mLocalVarIdx + compositeIdx + 1];
auto argValue = exprEvaluator.LoadLocal(compositeLocalVar, true);
if (argValue)
{
if (!argValue.mType->IsStruct())
argValue = mModule->LoadValue(argValue, NULL, exprEvaluator.mIsVolatileReference);
}
resolvedArg.mTypedValue = argValue;
resolvedArg.mExpression = argExpr;
resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_FromParamComposite);
resolvedArgs.mResolvedArgs.push_back(resolvedArg);
continue;
exprEvaluator.mIsVolatileReference = false;
}
if ((localVar->mResolvedType->IsModifiedTypeType()) && (((BfModifiedTypeType*)localVar->mResolvedType)->mModifiedKind == BfToken_Params))
{
// Is a 'params'
}
else
continue;
}
else
exprEvaluator.mResult = mModule->LoadOrAggregateValue(exprEvaluator.mResult);
}
}
@ -7903,6 +8040,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
BfIRValue expandedParamAlloca;
BfTypedValue expandedParamsArray;
BfType* expandedParamsElementType = NULL;
bool hadDelegateParamIdx = false;
int extendedParamIdx = 0;
AddCallDependencies(methodInstance);
@ -8170,6 +8308,8 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
}
}
}
else if (paramKind == BfParamKind_DelegateParam)
hadDelegateParamIdx = true;
}
BfAstNode* arg = NULL;
@ -8181,7 +8321,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (argExprIdx < (int)argValues.size())
{
arg = argValues[argExprIdx].mExpression;
if (((argValues[argExprIdx].mArgFlags & BfArgFlag_StringInterpolateArg) != 0) && (!expandedParamsArray))
if (((argValues[argExprIdx].mArgFlags & BfArgFlag_StringInterpolateArg) != 0) && (!expandedParamsArray) && (!hadDelegateParamIdx))
{
BfAstNode* errorRef = arg;
int checkIdx = argExprIdx - 1;
@ -10418,7 +10558,10 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
if ((mModule->mCurMethodState != NULL) && (exprEvaluator.mResultLocalVar != NULL) && (exprEvaluator.mResultLocalVarRefNode != NULL))
{
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;
if (mModule->mCurMethodInstance->mIsUnspecialized)
@ -19984,12 +20127,12 @@ void BfExprEvaluator::CheckResultForReading(BfTypedValue& typedValue)
int fieldIdx = mResultLocalVarField - 1;
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);
typedValue = BfTypedValue();
return;
}
}*/
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.
// WAIT: Why not?
//SetAndRestoreValue<BfType*> prevExpectingType(mExpectingType, NULL);
SetAndRestoreValue<BfEvalExprFlags> prevFlags(mBfEvalExprFlags);
BfType* prevExpedcting = mExpectingType;
switch (unaryOp)
@ -22875,10 +23016,13 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp
if ((mExpectingType != NULL) && (mExpectingType->IsInteger()) && (mExpectingType->mSize == 8))
mExpectingType = NULL;
// Otherwise keep expecting type
break;
break;
case BfUnaryOp_Params:
mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_InParamsExpr);
break;
default:
mExpectingType = NULL;
}
}
VisitChild(unaryOpExpr);
mExpectingType = prevExpedcting;
}
@ -23570,6 +23714,8 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr,
if (allowParams)
{
mResult = mModule->LoadValue(mResult);
if ((mResult.mType != NULL) && (mResult.mType->IsParamsType()))
mResult.mType = mResult.mType->GetUnderlyingType();
if (mResult.IsSplat())
mResult.mKind = BfTypedValueKind_ParamsSplat;
else

View file

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

View file

@ -13520,6 +13520,16 @@ BfTypedValue BfModule::LoadOrAggregateValue(BfTypedValue typedValue)
return AggregateSplat(typedValue);
if (typedValue.IsAddr())
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;
}
@ -17144,6 +17154,8 @@ void BfModule::CreateDelegateInvokeMethod()
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);
exprEvaluator.PushArg(localVal, staticFuncArgs);
exprEvaluator.PushArg(localVal, memberFuncArgs);
@ -20029,6 +20041,16 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
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())
CheckVariableDef(paramVar);
paramVar->Init();
@ -20074,11 +20096,24 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
((typeInstConstraint != NULL) &&
((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef)))))
{
BfLocalVariable* localVar = new BfLocalVariable();
localVar->mName = paramDef->mName;
localVar->mResolvedType = paramsType;
localVar->mCompositeCount = 0;
DoAddLocalVariable(localVar);
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();
localVar->mName = paramDef->mName;
localVar->mResolvedType = paramsType;
localVar->mCompositeCount = 0;
DoAddLocalVariable(localVar);
}
}
}
}
@ -24998,14 +25033,16 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
addParams = false;
}
else if (resolvedParamType->IsGenericParam())
{
{
bool isGenericDelegateParams = false;
bool addDelegateParamsType = false;
auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType, false, BfFailHandleKind_Ignore);
if (genericParamInstance == NULL)
{
// Delegate case with a 'params T'?
mCurMethodInstance->mHadGenericDelegateParams = true;
isValid = true;
addParams = false;
isGenericDelegateParams = true;
addDelegateParamsType = true;
}
else if (genericParamInstance->mTypeConstraint != NULL)
{
@ -25022,9 +25059,28 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
((genericParamInstance != NULL) && (typeInstConstraint != NULL) &&
((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef)))))
{
mCurMethodInstance->mHadGenericDelegateParams = true;
isValid = true;
addParams = false;
isGenericDelegateParams = true;
//addDelegateParamsType = typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef);
addDelegateParamsType = true;
}
}
if (isGenericDelegateParams)
{
mCurMethodInstance->mHadGenericDelegateParams = true;
isValid = true;
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();
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;
addParams = false;
}
@ -25042,7 +25098,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
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[]
resolvedParamType = CreateArrayType(mContext->mBfObjectType, 1);
}
@ -25100,7 +25156,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
auto tupleType = (BfTupleType*)methodParam.mResolvedType;
auto& fieldInstance = tupleType->mFieldInstances[methodParam.mDelegateParamIdx];
auto paramName = fieldInstance.GetFieldDef()->mName;
if (!usedNames.Add(paramName))
if ((!usedNames.Add(paramName)) || (::isdigit((uint8)paramName[0])))
methodParam.mDelegateParamNameCombine = true;
}
else

View file

@ -93,6 +93,7 @@ enum BfEvalExprFlags : int64
BfEvalExprFlags_AppendFieldInitializer = 0x80000000,
BfEvalExprFlags_NameOf = 0x100000000LL,
BfEvalExprFlags_NameOfSuccess = 0x200000000LL,
BfEvalExprFlags_InParamsExpr = 0x400000000LL,
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;
if ((typedVal.IsParams()) && (toType->IsParamsType()))
{
if (typedVal.mType == toType->GetUnderlyingType())
return BfTypedValue(mBfIRBuilder->GetFakeVal(), toType);
}
auto castedValue = CastToValue(srcNode, typedVal, toType, castFlags, &castResultFlags);
if (!castedValue)
return BfTypedValue();

View file

@ -1174,12 +1174,7 @@ void BfMethodInstance::GetParamName(int paramIdx, StringImpl& name, int& namePre
if (methodParam->mDelegateParamNameCombine)
name = paramDef->mName + "__" + fieldInstance.GetFieldDef()->mName;
else
name = fieldInstance.GetFieldDef()->mName;
if (name == "p__a__a")
{
NOP;
}
name = fieldInstance.GetFieldDef()->mName;
return;
}

View file

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

View file

@ -6311,6 +6311,25 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
_FixVariables();
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)
{
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);
}
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)
{
int32 sleepMS = *(int32*)((uint8*)stackPtr);
@ -10019,6 +10051,10 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction)
{
ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeById;
}
else if (methodDef->mName == "Comptime_GetWrappedType")
{
ceFunction->mFunctionKind = CeFunctionKind_GetWrappedType;
}
else if (methodDef->mName == "Comptime_GetTypeByName")
{
ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeByName;
@ -10126,6 +10162,10 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction)
{
ceFunction->mFunctionKind = CeFunctionKind_EmitMixin;
}
else if (methodDef->mName == "Comptime_GetStringById")
{
ceFunction->mFunctionKind = CeFunctionKind_GetStringById;
}
}
else if (owner->IsInstanceOf(mCeModule->mCompiler->mDiagnosticsDebugTypeDef))
{

View file

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

View file

@ -1,9 +1,293 @@
#pragma warning disable 168
using System;
using System.Reflection;
namespace Tests;
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
{
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]
public static void TestBasics()
{
@ -25,5 +342,15 @@ class Params
return (.)a + (.)__a + (.)b;
}, 10, 2.3f);
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.");
}
}