diff --git a/BeefLibs/corlib/src/Compiler.bf b/BeefLibs/corlib/src/Compiler.bf index b0785400..20e5affc 100644 --- a/BeefLibs/corlib/src/Compiler.bf +++ b/BeefLibs/corlib/src/Compiler.bf @@ -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) diff --git a/BeefLibs/corlib/src/String.bf b/BeefLibs/corlib/src/String.bf index e4e56d5d..d458cfe3 100644 --- a/BeefLibs/corlib/src/String.bf +++ b/BeefLibs/corlib/src/String.bf @@ -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, IEnumerator { char8* mPtr; diff --git a/BeefLibs/corlib/src/Type.bf b/BeefLibs/corlib/src/Type.bf index 0b5cd53f..8c278860 100644 --- a/BeefLibs/corlib/src/Type.bf +++ b/BeefLibs/corlib/src/Type.bf @@ -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 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); } diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 1411422a..621a9759 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -846,7 +846,7 @@ public: return (mKind == BfTypedValueKind_NoValue) && (mType != NULL); } - bool IsParams() + bool IsParams() const { return (mKind == BfTypedValueKind_ParamsSplat) || (mKind == BfTypedValueKind_Params); } diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 441a3cc7..b92aac05 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -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 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 prevExpectingType(mExpectingType, NULL); + SetAndRestoreValue 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 diff --git a/IDEHelper/Compiler/BfMangler.cpp b/IDEHelper/Compiler/BfMangler.cpp index 4d322600..91ba6536 100644 --- a/IDEHelper/Compiler/BfMangler.cpp +++ b/IDEHelper/Compiler/BfMangler.cpp @@ -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); diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 2e6a645c..e175f6e6 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -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 diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 8e0f3416..ddf1a3e8 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -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 }; diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index b985ddb5..0153f1a8 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -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(); diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 89f1680c..c5dee081 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -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; } diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index bc3a5dab..ccefc279 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -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; } diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index 8cf17d9f..a754a825 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -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)) { diff --git a/IDEHelper/Compiler/CeMachine.h b/IDEHelper/Compiler/CeMachine.h index 6034e0f5..78b039e0 100644 --- a/IDEHelper/Compiler/CeMachine.h +++ b/IDEHelper/Compiler/CeMachine.h @@ -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, diff --git a/IDEHelper/Tests/src/Params.bf b/IDEHelper/Tests/src/Params.bf index 8cb1cf68..0725dfc3 100644 --- a/IDEHelper/Tests/src/Params.bf +++ b/IDEHelper/Tests/src/Params.bf @@ -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(String outStr, IFormatProvider provider, TStr formatStr, params TArgs args) where TStr : const String where TArgs : Tuple + { + + } + class ClassA 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(params T args) where T : Tuple + { + Test3(args); + } + + static void Test2(params T args) where T : Delegate + { + var vals = args; + Test3(args); + } + + static void Test3(T args) where T : Tuple + { + + } + + static void Test4(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(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."); } } \ No newline at end of file