diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index 15e35451..b1b3819f 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -1672,6 +1672,7 @@ const char* Beefy::BfGetOpName(BfUnaryOp unaryOp) case BfUnaryOp_Out: return "out"; case BfUnaryOp_Mut: return "mut"; case BfUnaryOp_Params: return "params"; + case BfUnaryOp_Cascade: return ".."; default: return "???"; } } @@ -1759,6 +1760,8 @@ BfUnaryOp Beefy::BfTokenToUnaryOp(BfToken token) return BfUnaryOp_Out; case BfToken_Params: return BfUnaryOp_Params; + case BfToken_DotDot: + return BfUnaryOp_Cascade; default: return BfUnaryOp_None; } diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 2debcda4..3b5f7695 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -1834,7 +1834,8 @@ enum BfUnaryOp BfUnaryOp_Ref, BfUnaryOp_Out, BfUnaryOp_Mut, - BfUnaryOp_Params, + BfUnaryOp_Params, + BfUnaryOp_Cascade }; class BfTokenNode : public BfAstNode diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 944eb4db..17b00021 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -4616,8 +4616,7 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr SizedArray deferredArgs; int argIdx = 0; - //for (int argIdx = 0; argIdx < argCount ; argIdx++) - + while (true) { //printf("Args: %p %p %d\n", resolvedArgs.mArguments, resolvedArgs.mArguments->mVals, resolvedArgs.mArguments->mSize); @@ -4683,6 +4682,15 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr } } + if (auto unaryOpExpr = BfNodeDynCastExact(argExpr)) + { + if (unaryOpExpr->mOp == BfUnaryOp_Cascade) + { + resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_Cascade); + argExpr = unaryOpExpr->mExpression; + } + } + bool deferParamEval = false; if ((flags & BfResolveArgsFlag_DeferParamEval) != 0) { @@ -5899,6 +5907,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu BF_ASSERT(returnType->IsInterface()); }*/ + Array argCascades; BfTypedValue target = inTarget; if (!skipThis) @@ -6607,6 +6616,8 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu argValue = mModule->LoadValue(argValue); } + if ((argExprIdx != -1) && (argExprIdx < (int)argValues.size()) && ((argValues[argExprIdx].mArgFlags & BfArgFlag_Cascade) != 0)) + argCascades.Add(argValue); if (expandedParamsArray) { @@ -6773,8 +6784,14 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu } auto func = moduleMethodInstance.mFunc; - BfTypedValue result = CreateCall(targetSrc, methodInstance, func, bypassVirtual, irArgs); - return result; + BfTypedValue callResult = CreateCall(targetSrc, methodInstance, func, bypassVirtual, irArgs); + + if (argCascades.mSize == 1) + return argCascades[0]; + if (argCascades.mSize > 1) + return mModule->CreateTuple(argCascades, {}); + + return callResult; } BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBoundExpression* methodBoundExpr, BfTypedValue target, BfTypeInstance* targetType, BfResolvedArgs& argValues, bool callCtorBodyOnly, bool allowAppendAlloc, BfTypedValue* appendIndexValue) @@ -19376,7 +19393,11 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, } } break; - + case BfUnaryOp_Cascade: + { + mModule->Fail("Illegal use of argument cascade expression", opToken); + } + break; default: mModule->Fail("INTERNAL ERROR: Unhandled unary operator", unaryOpExpr); break; diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 95c18499..f3f884f1 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -20,7 +20,8 @@ enum BfArgFlags BfArgFlag_ParamsExpr = 0x200, BfArgFlag_UninitializedExpr = 0x400, BfArgFlag_StringInterpolateFormat = 0x800, - BfArgFlag_StringInterpolateArg = 0x1000 + BfArgFlag_StringInterpolateArg = 0x1000, + BfArgFlag_Cascade = 0x2000 }; enum BfResolveArgsFlags diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 87670cdd..4c3dbdf0 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -13830,6 +13830,29 @@ void BfModule::CreateDIRetVal() } } +BfTypedValue BfModule::CreateTuple(const Array& values, const Array& fieldNames) +{ + BfTypeVector fieldTypes; + for (auto arg : values) + fieldTypes.Add(arg.mType); + + auto tupleType = CreateTupleType(fieldTypes, fieldNames); + + auto tupleTypedValue = BfTypedValue(CreateAlloca(tupleType), tupleType, true); + for (int fieldIdx = 0; fieldIdx < tupleType->mFieldInstances.size(); fieldIdx++) + { + auto& fieldInstance = tupleType->mFieldInstances[fieldIdx]; + if (fieldInstance.mDataIdx <= 0) + continue; + + auto typedVal = values[fieldIdx]; + typedVal = LoadOrAggregateValue(typedVal); + mBfIRBuilder->CreateAlignedStore(typedVal.mValue, mBfIRBuilder->CreateInBoundsGEP(tupleTypedValue.mValue, 0, fieldInstance.mDataIdx), typedVal.mType->mAlign); + } + + return tupleTypedValue; +} + void BfModule::CheckVariableDef(BfLocalVariable* variableDef) { if (variableDef->mName.IsEmpty()) diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 835d1377..0a35157e 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1737,6 +1737,7 @@ public: bool TryLocalVariableInit(BfLocalVariable* localVar); void LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit); void CreateDIRetVal(); + BfTypedValue CreateTuple(const Array& values, const Array& fieldNames); void CheckTupleVariableDeclaration(BfTupleExpression* tupleExpr, BfType* initType); void HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl, BfTupleExpression* tupleExpr, BfTypedValue initTupleValue, bool isReadOnly, bool isConst, bool forceAddr, BfIRBlock* declBlock = NULL); void HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl); diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 20aad098..1aff4c71 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -2250,8 +2250,15 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat unaryOpExpr->mOp = unaryOp; unaryOpExpr->mOpToken = tokenNode; ReplaceNode(tokenNode, unaryOpExpr); + + CreateExprFlags innerFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_EarlyExit); + if (unaryOp == BfUnaryOp_Cascade) + { + innerFlags = (CreateExprFlags)(innerFlags | (createExprFlags & CreateExprFlags_AllowVariableDecl)); + } + // Don't attempt binary or unary operations- they will always be lower precedence - unaryOpExpr->mExpression = CreateExpressionAfter(unaryOpExpr, (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_EarlyExit)); + unaryOpExpr->mExpression = CreateExpressionAfter(unaryOpExpr, innerFlags); if (unaryOpExpr->mExpression == NULL) return NULL; MoveNode(unaryOpExpr->mExpression, unaryOpExpr); diff --git a/IDEHelper/Tests/src/Cascades.bf b/IDEHelper/Tests/src/Cascades.bf new file mode 100644 index 00000000..91adc58d --- /dev/null +++ b/IDEHelper/Tests/src/Cascades.bf @@ -0,0 +1,33 @@ +using System; + +namespace Tests +{ + class Cascades + { + public static void MethodA(int a, float b) + { + + } + + public static void MethodB(int a, out float b) + { + b = 100; + } + + [Test] + public static void TestBasics() + { + int a = MethodA(.. 12, 2.3f); + Test.Assert(a == 12); + var (b, c) = MethodA(.. 12, .. 2.3f); + Test.Assert(b == 12); + Test.Assert(c == 2.3f); + var d = MethodA(.. 12, .. 2.3f); + Test.Assert(d == (12, 2.3f)); + var f = ref MethodB(12, .. var e); + e += 23; + Test.Assert(e == (int)123); + Test.Assert(f == (int)123); + } + } +}