2
0
Эх сурвалжийг харах

Expanding support for `params` in delegates, `params` tuple support

Brian Fiete 5 сар өмнө
parent
commit
421cace017

+ 5 - 0
BeefLibs/corlib/src/Tuple.bf

@@ -0,0 +1,5 @@
+namespace System;
+
+struct Tuple
+{
+}

+ 5 - 3
BeefySysLib/util/String.h

@@ -551,10 +551,12 @@ protected:
 		if ((mAllocSizeAndFlags & AttrFlags) == StrPtrFlag)
 		{
 			// It's a reference
-			char* newPtr = AllocPtr(this->mLength);
-			memcpy(newPtr, this->mPtr, this->mLength + 1);
+			int allocSize = (int)BF_MAX(GetAllocSize(), this->mLength + 1);			
+			char* newPtr = AllocPtr(allocSize);
+			memcpy(newPtr, this->mPtr, this->mLength);
+			newPtr[this->mLength] = 0;
 			this->mPtr = newPtr;
-			mAllocSizeAndFlags = this->mLength | DynAllocFlag | StrPtrFlag;
+			mAllocSizeAndFlags = allocSize | DynAllocFlag | StrPtrFlag;
 		}
 	}
 

+ 5 - 0
IDE/mintest/minlib/src/System/Object.bf

@@ -213,6 +213,11 @@ namespace System
 		public static extern bool Equals<T>(T val1, T val2);
 	}
 
+	struct Tuple
+	{
+
+	}
+
 	struct Function : int
 	{
 

+ 2 - 0
IDEHelper/Compiler/BfCompiler.cpp

@@ -478,6 +478,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly)
 	mTypeTypeDef = NULL;
 	mUnboundAttributeTypeDef = NULL;
 	mValueTypeTypeDef = NULL;
+	mTupleTypeDef = NULL;
 	mResultTypeDef = NULL;
 	mObsoleteAttributeTypeDef = NULL;
 	mErrorAttributeTypeDef = NULL;
@@ -7334,6 +7335,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
 	mTypeTypeDef = _GetRequiredType("System.Type");
 	mUnboundAttributeTypeDef = _GetRequiredType("System.UnboundAttribute");
 	mValueTypeTypeDef = _GetRequiredType("System.ValueType");
+	mTupleTypeDef = _GetRequiredType("System.Tuple");
 	mObsoleteAttributeTypeDef = _GetRequiredType("System.ObsoleteAttribute");
 	mErrorAttributeTypeDef = _GetRequiredType("System.ErrorAttribute");
 	mWarnAttributeTypeDef = _GetRequiredType("System.WarnAttribute");

+ 1 - 0
IDEHelper/Compiler/BfCompiler.h

@@ -379,6 +379,7 @@ public:
 	BfTypeDef* mTypeTypeDeclDef;
 	BfTypeDef* mTypeTypeDef;
 	BfTypeDef* mValueTypeTypeDef;
+	BfTypeDef* mTupleTypeDef;
 	BfTypeDef* mResultTypeDef;
 	BfTypeDef* mGCTypeDef;
 	BfTypeDef* mGenericIEnumerableTypeDef;

+ 41 - 21
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -14741,30 +14741,19 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
 
 	if (invokeMethodInstance != NULL)
 	{
-		for (int paramIdx = 0; paramIdx < (int)invokeMethodInstance->mMethodDef->mParams.size(); paramIdx++)
+		for (int paramIdx = 0; paramIdx < (int)invokeMethodInstance->GetParamCount(); paramIdx++)
 		{
-			auto invokeParamDef = invokeMethodInstance->mMethodDef->mParams[paramIdx];
-
-			BfParameterDef* paramDef = new BfParameterDef();
-			paramDef->mParamDeclaration = tempParamDecls.Alloc();
-			BfAstNode::Zero(paramDef->mParamDeclaration);
-
-			BfLocalVariable* localVar = new BfLocalVariable();
+			BfLocalVariable* localVar = new BfLocalVariable();			
 			if (paramIdx < (int)lambdaBindExpr->mParams.size())
 			{
 				localVar->mName = lambdaBindExpr->mParams[paramIdx]->ToString();
-				localVar->mNameNode = lambdaBindExpr->mParams[paramIdx];
-				paramDef->mParamDeclaration->mNameNode = lambdaBindExpr->mParams[paramIdx];
+				localVar->mNameNode = lambdaBindExpr->mParams[paramIdx];				
 			}
 			else
 			{
 				mModule->AssertErrorState();
-				localVar->mName = invokeParamDef->mName;
-				paramDef->mParamDeclaration->mNameNode = NULL;
-			}
-			paramDef->mName = localVar->mName;
-			methodDef->mParams.push_back(paramDef);
-
+				localVar->mName = invokeMethodInstance->GetParamName(paramIdx);				
+			}						
 			localVar->mResolvedType = invokeMethodInstance->GetParamType(paramIdx);
 			mModule->PopulateType(localVar->mResolvedType);
 			localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
@@ -14773,15 +14762,41 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
 			auto rootMethodState = methodState.GetRootMethodState();
 			localVar->mLocalVarId = rootMethodState->mCurLocalVarId++;
 
-			mModule->DoAddLocalVariable(localVar);
+			mModule->DoAddLocalVariable(localVar);			
 
-			if (autoComplete != NULL)
-				autoComplete->CheckLocalDef(BfNodeDynCast<BfIdentifierNode>(paramDef->mParamDeclaration->mNameNode), methodState.mLocals.back());
 			auto resolvePassData = mModule->mCompiler->mResolvePassData;
 			if (resolvePassData != NULL)
-				resolvePassData->HandleLocalReference(BfNodeDynCast<BfIdentifierNode>(paramDef->mParamDeclaration->mNameNode), mModule->mCurTypeInstance->mTypeDef,
+				resolvePassData->HandleLocalReference(BfNodeDynCast<BfIdentifierNode>(localVar->mNameNode), mModule->mCurTypeInstance->mTypeDef,
 					mModule->mCurMethodInstance->mMethodDef, localVar->mLocalVarId);
 		}
+
+		for (int paramIdx = 0; paramIdx < (int)invokeMethodInstance->mMethodDef->mParams.size(); paramIdx++)
+		{
+			auto invokeParamDef = invokeMethodInstance->mMethodDef->mParams[paramIdx];
+
+			BfParameterDef* paramDef = new BfParameterDef();
+			paramDef->mParamDeclaration = tempParamDecls.Alloc();
+			BfAstNode::Zero(paramDef->mParamDeclaration);						
+			paramDef->mTypeRef = invokeParamDef->mTypeRef;
+			paramDef->mParamKind = invokeParamDef->mParamKind;
+						
+			if ((paramIdx < (int)lambdaBindExpr->mParams.size()) && (invokeMethodInstance->GetParamKind(paramIdx) != BfParamKind_DelegateParam))
+			{				
+				//TODO: Not always correct if we have a 'params'
+				paramDef->mParamDeclaration->mNameNode = lambdaBindExpr->mParams[paramIdx];
+				paramDef->mName = paramDef->mParamDeclaration->mNameNode->ToString();
+			}
+			else
+			{				
+				paramDef->mParamDeclaration->mNameNode = NULL;
+				paramDef->mName = invokeParamDef->mName;
+			}
+			
+			methodDef->mParams.push_back(paramDef);
+			
+			if (autoComplete != NULL)
+				autoComplete->CheckLocalDef(BfNodeDynCast<BfIdentifierNode>(paramDef->mParamDeclaration->mNameNode), methodState.mLocals.back());			
+		}
 	}
 
 	bool isAutocomplete = mModule->mCompiler->IsAutocomplete();
@@ -24091,12 +24106,17 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp
 			rightTypedValueExpr.mRefNode = opToken;
 
 			auto valueTypeEmpty = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mBaseType->mBaseType), {});
+
+			SizedArray<BfIRValue, 8> tupleEmptyMembers;
+			tupleEmptyMembers.Add(valueTypeEmpty);
+			auto tupleTypeEmpty = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(mModule->ResolveTypeDef(mModule->mCompiler->mTupleTypeDef)), tupleEmptyMembers);
+
 			SizedArray<BfIRValue, 8> enumMembers;
 			enumMembers.Add(valueTypeEmpty);
 			auto enumValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mBaseType), enumMembers);
 
 			SizedArray<BfIRValue, 8> tupleMembers;
-			tupleMembers.Add(valueTypeEmpty);
+			tupleMembers.Add(tupleTypeEmpty);
 			tupleMembers.Add(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1));
 			auto tupleValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mFieldInstances[0].mResolvedType), tupleMembers);
 

+ 78 - 29
IDEHelper/Compiler/BfModule.cpp

@@ -3617,25 +3617,22 @@ void BfModule::FatalError(const StringImpl& error, const char* file, int line, i
 	BfpSystem_FatalError(fullError.c_str(), "FATAL MODULE ERROR");
 }
 
-void BfModule::FatalError(const StringImpl& error, BfAstNode* refNode)
+void BfModule::FatalError(const StringImpl& error, BfFailHandleKind failHandleKind)
 {
-	if (refNode != NULL)
-	{
-		auto parser = refNode->GetParserData();
-		if (parser != NULL)
-		{
-			int line = -1;
-			int lineChar = -1;
-			parser->GetLineCharAtIdx(refNode->mSrcStart, line, lineChar);
-			if (line != -1)
-				FatalError(error, parser->mFileName.c_str(), line, lineChar);
-		}
-	}
-	FatalError(error);
+	if (failHandleKind == BfFailHandleKind_Normal)
+		FatalError(error);
+	else if (failHandleKind == BfFailHandleKind_Soft)
+		InternalError(error);
 }
 
 void BfModule::InternalError(const StringImpl& error, BfAstNode* refNode, const char* file, int line)
 {
+	static bool isInside = false;
+	if (isInside)
+		return;
+
+	isInside = true;
+
 	String fullError = error;
 
 	if (file != NULL)
@@ -3652,6 +3649,8 @@ void BfModule::InternalError(const StringImpl& error, BfAstNode* refNode, const
 		fullError += StrFormat("\nSource Location: %s:%d", mCurFilePosition.mFileInstance->mParser->mFileName.c_str(), mCurFilePosition.mCurLine + 1);
 
 	Fail(String("INTERNAL ERROR: ") + fullError, refNode);
+
+	isInside = false;
 }
 
 void BfModule::NotImpl(BfAstNode* astNode)
@@ -16018,6 +16017,8 @@ BfIRValue BfModule::AllocLocalVariable(BfType* type, const StringImpl& name, boo
 
 void BfModule::DoAddLocalVariable(BfLocalVariable* localVar)
 {
+	BF_ASSERT(localVar->mResolvedType != NULL);
+
 	while (localVar->mName.StartsWith('@'))
 	{
 		localVar->mNamePrefixCount++;
@@ -17064,7 +17065,7 @@ void BfModule::AssertErrorState()
 	if (mCompiler->mPassInstance->HasFailed())
 		return;
 
-	InternalError("Compiler in invalid state but AssertErrorState failed to prior error");
+	InternalError("Compiler in invalid state but AssertErrorState failed to detect prior error");
 }
 
 void BfModule::AssertParseErrorState()
@@ -20008,14 +20009,17 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
 		{
 			if (compositeVariableIdx == -1)
 			{
-				compositeVariableIdx = (int)mCurMethodState->mLocals.size();
-
-				BfLocalVariable* localVar = new BfLocalVariable();
 				auto paramInst = &methodInstance->mParams[paramIdx];
 				auto paramDef = methodDef->mParams[paramInst->mParamDefIdx];
+
+				compositeVariableIdx = (int)mCurMethodState->mLocals.size();
+				BfLocalVariable* localVar = new BfLocalVariable();
 				localVar->mName = paramDef->mName;
 				localVar->mNamePrefixCount = paramDef->mNamePrefixCount;
-				localVar->mResolvedType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam);
+				if (paramDef->mTypeRef != NULL)
+					localVar->mResolvedType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam);
+				if (localVar->mResolvedType == NULL)
+					localVar->mResolvedType = GetPrimitiveType(BfTypeCode_None);
 				localVar->mCompositeCount = 0;
 				DoAddLocalVariable(localVar);
 			}
@@ -20064,9 +20068,9 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
 				if (genericParamInstance->mTypeConstraint != NULL)
 				{
 					auto typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance();
-					if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) ||
+					if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) ||
 						((typeInstConstraint != NULL) &&
-						 ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)))))
+						 ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef)))))
 					{
 						BfLocalVariable* localVar = new BfLocalVariable();
 						localVar->mName = paramDef->mName;
@@ -24965,10 +24969,43 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
 				isValid = true;
 				addParams = false;
 			}
+			else if (resolvedParamType->IsTuple())
+			{				
+				auto tupleType = (BfTupleType*)resolvedParamType;
+				PopulateType(tupleType);
+			
+				hadDelegateParams = true;
+
+				// This means we copy the params from a delegate
+				BfMethodParam methodParam;
+				methodParam.mResolvedType = resolvedParamType;
+				methodParam.mParamDefIdx = paramDefIdx;
+				methodParam.mDelegateParamIdx = 0;
+				auto invokeMethodInstance = methodParam.GetDelegateParamInvoke();
+				for (int delegateParamIdx = 0; delegateParamIdx < tupleType->mFieldInstances.mSize; delegateParamIdx++)	
+				{
+					auto& fieldInstance = tupleType->mFieldInstances[delegateParamIdx];
+
+					methodParam.mDelegateParamIdx = delegateParamIdx;
+					mCurMethodInstance->mParams.Add(methodParam);
+					
+					if (!methodInstance->IsSpecializedGenericMethod())
+						AddDependency(fieldInstance.mResolvedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
+				}
+				isValid = true;
+				addParams = false;
+			}
 			else if (resolvedParamType->IsGenericParam())
 			{
-				auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType);
-				if (genericParamInstance->mTypeConstraint != NULL)
+				auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType, false, BfFailHandleKind_Ignore);
+				if (genericParamInstance == NULL)
+				{
+					// Delegate case with a 'params T'?
+					mCurMethodInstance->mHadGenericDelegateParams = true;
+					isValid = true;
+					addParams = false;
+				}
+				else if (genericParamInstance->mTypeConstraint != NULL)
 				{
 					auto typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance();
 					if ((genericParamInstance->mTypeConstraint->IsArray()) || (genericParamInstance->mTypeConstraint->IsSizedArray()))
@@ -24979,9 +25016,9 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
 						mCurMethodInstance->mParams.Add(methodParam);
 						isValid = true;
 					}
-					else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) ||
+					else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) ||
 						((genericParamInstance != NULL) && (typeInstConstraint != NULL) &&
-						 ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)))))
+						 ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef)))))
 					{
 						mCurMethodInstance->mHadGenericDelegateParams = true;
 						isValid = true;
@@ -25055,10 +25092,22 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
 			{
 				if (usedParamDefIdx[methodParam.mParamDefIdx] > 1)
 					methodParam.mDelegateParamNameCombine = true;
-				BfMethodInstance* invokeMethodInstance = methodParam.GetDelegateParamInvoke();
-				String paramName = invokeMethodInstance->GetParamName(methodParam.mDelegateParamIdx);
-				if (usedNames.Contains(paramName))
-					methodParam.mDelegateParamNameCombine = true;
+
+				if (methodParam.mResolvedType->IsTuple())
+				{
+					auto tupleType = (BfTupleType*)methodParam.mResolvedType;
+					auto& fieldInstance = tupleType->mFieldInstances[methodParam.mDelegateParamIdx];
+					auto paramName = fieldInstance.GetFieldDef()->mName;
+					if (!usedNames.Add(paramName))
+						methodParam.mDelegateParamNameCombine = true;
+				}
+				else
+				{
+					BfMethodInstance* invokeMethodInstance = methodParam.GetDelegateParamInvoke();
+					String paramName = invokeMethodInstance->GetParamName(methodParam.mDelegateParamIdx);
+					if (!usedNames.Add(paramName))
+						methodParam.mDelegateParamNameCombine = true;
+				}
 			}
 		}
 	}

+ 2 - 1
IDEHelper/Compiler/BfModule.h

@@ -1617,6 +1617,7 @@ public:
 public:
 	void FatalError(const StringImpl& error, const char* file = NULL, int line = -1, int column = -1);
 	void FatalError(const StringImpl& error, BfAstNode* refNode);
+	void FatalError(const StringImpl& error, BfFailHandleKind failHandleKind);
 	void InternalError(const StringImpl& error, BfAstNode* refNode = NULL, const char* file = NULL, int line = -1);
 	void NotImpl(BfAstNode* astNode);
 	void AddMethodReference(const BfMethodRef& methodRef, BfGetMethodInstanceFlags flags = BfGetMethodInstanceFlag_None);
@@ -1959,7 +1960,7 @@ public:
 	BfType* ResolveGenericType(BfType* unspecializedType, BfTypeVector* typeGenericArguments, BfTypeVector* methodGenericArguments, BfType* selfType, bool allowFail = false);
 	BfType* ResolveSelfType(BfType* type, BfType* selfType);
 	bool IsUnboundGeneric(BfType* type);
-	BfGenericParamInstance* GetGenericTypeParamInstance(int paramIdx);
+	BfGenericParamInstance* GetGenericTypeParamInstance(int paramIdx, BfFailHandleKind failHandleKind = BfFailHandleKind_Normal);
 	BfGenericParamInstance* GetGenericParamInstance(BfGenericParamType* type, bool checkMixinBind = false, BfFailHandleKind failHandleKind = BfFailHandleKind_Normal);
 	void GetActiveTypeGenericParamInstances(SizedArray<BfGenericParamInstance*, 4>& genericParamInstance);
 	BfGenericParamInstance* GetMergedGenericParamData(BfType* type, BfGenericParamFlags& outFlags, BfType*& outTypeConstraint);

+ 8 - 4
IDEHelper/Compiler/BfModuleTypeUtils.cpp

@@ -4209,6 +4209,10 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 	{
 		baseType = ResolveTypeDef(mCompiler->mPointerTTypeDef, BfPopulateType_Data);
 	}
+	else if (resolvedTypeRef->IsTuple())
+	{
+		baseType = ResolveTypeDef(mCompiler->mTupleTypeDef, BfPopulateType_Data);
+	}
 	else if ((resolvedTypeRef->IsValueType()) && (typeDef != mCompiler->mValueTypeTypeDef))
 	{
 		baseType = ResolveTypeDef(mCompiler->mValueTypeTypeDef, BfPopulateType_Data)->ToTypeInstance();
@@ -9565,7 +9569,7 @@ bool BfModule::IsUnboundGeneric(BfType* type)
 	return (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0;
 }
 
-BfGenericParamInstance* BfModule::GetGenericTypeParamInstance(int genericParamIdx)
+BfGenericParamInstance* BfModule::GetGenericTypeParamInstance(int genericParamIdx, BfFailHandleKind failHandleKind)
 {
 	// When we're evaluating a method, make sure the params refer back to that method context
 	auto curTypeInstance = mCurTypeInstance;
@@ -9576,8 +9580,8 @@ BfGenericParamInstance* BfModule::GetGenericTypeParamInstance(int genericParamId
 	BfTypeInstance* genericTypeInst = curTypeInstance->ToGenericTypeInstance();
 
 	if (genericTypeInst == NULL)
-	{
-		FatalError("Invalid mCurTypeInstance for GetGenericTypeParamInstance");
+	{		
+		FatalError("Invalid mCurTypeInstance for GetGenericTypeParamInstance", failHandleKind);
 		return NULL;
 	}
 
@@ -9756,7 +9760,7 @@ BfGenericParamInstance* BfModule::GetGenericParamInstance(BfGenericParamType* ty
 		return curGenericMethodInstance->mMethodInfoEx->mGenericParams[type->mGenericParamIdx];
 	}
 
-	return GetGenericTypeParamInstance(type->mGenericParamIdx);
+	return GetGenericTypeParamInstance(type->mGenericParamIdx, failHandleKind);
 }
 
 bool BfModule::ResolveTypeResult_Validate(BfAstNode* typeRef, BfType* resolvedTypeRef)

+ 27 - 1
IDEHelper/Compiler/BfResolvedTypeUtils.cpp

@@ -698,7 +698,8 @@ BfMethodInstance* BfMethodParam::GetDelegateParamInvoke()
 		auto methodRefType = (BfMethodRefType*)mResolvedType;
 		return methodRefType->mMethodRef;
 	}
-
+	else if (mResolvedType->IsTuple())
+		return NULL;
 	BF_ASSERT(mResolvedType->IsDelegate() || mResolvedType->IsFunction());
 	auto bfModule = BfModule::GetModuleFor(mResolvedType);
 	BfMethodInstance* invokeMethodInstance = bfModule->GetRawMethodInstanceAtIdx(mResolvedType->ToTypeInstance(), 0, "Invoke");
@@ -1166,6 +1167,23 @@ void BfMethodInstance::GetParamName(int paramIdx, StringImpl& name, int& namePre
 	BfParameterDef* paramDef = mMethodDef->mParams[methodParam->mParamDefIdx];
 	if (methodParam->mDelegateParamIdx != -1)
 	{
+		if (methodParam->mResolvedType->IsTuple())
+		{
+			auto tupleType = (BfTupleType*)methodParam->mResolvedType;
+			auto& fieldInstance = tupleType->mFieldInstances[methodParam->mDelegateParamIdx];
+			if (methodParam->mDelegateParamNameCombine)
+				name = paramDef->mName + "__" + fieldInstance.GetFieldDef()->mName;
+			else
+				name = fieldInstance.GetFieldDef()->mName;
+
+			if (name == "p__a__a")
+			{
+				NOP;
+			}
+
+			return;
+		}
+
 		BfMethodInstance* invokeMethodInstance = methodParam->GetDelegateParamInvoke();
 		if (methodParam->mDelegateParamNameCombine)
 			name = paramDef->mName + "__" + invokeMethodInstance->GetParamName(methodParam->mDelegateParamIdx);
@@ -1213,6 +1231,12 @@ BfType* BfMethodInstance::GetParamType(int paramIdx, bool returnUnderlyingParams
 	BfMethodParam* methodParam = &mParams[paramIdx];
 	if (methodParam->mDelegateParamIdx != -1)
 	{
+		if (methodParam->mResolvedType->IsTuple())
+		{
+			auto tupleType = (BfTupleType*)methodParam->mResolvedType;
+			return tupleType->mFieldInstances[methodParam->mDelegateParamIdx].mResolvedType;
+		}
+
 		BfMethodInstance* invokeMethodInstance = methodParam->GetDelegateParamInvoke();
 		return invokeMethodInstance->GetParamType(methodParam->mDelegateParamIdx, true);
 	}
@@ -1247,6 +1271,8 @@ bool BfMethodInstance::GetParamIsSplat(int paramIdx)
 	if (methodParam->mDelegateParamIdx != -1)
 	{
 		BfMethodInstance* invokeMethodInstance = methodParam->GetDelegateParamInvoke();
+		if (invokeMethodInstance == NULL)
+			return false;
 		return invokeMethodInstance->GetParamIsSplat(methodParam->mDelegateParamIdx);
 	}
 	return methodParam->mIsSplat;

+ 29 - 0
IDEHelper/Tests/src/Params.bf

@@ -0,0 +1,29 @@
+using System;
+
+namespace Tests;
+
+class Params
+{
+	class ClassA<T> where T : Tuple
+	{
+		public static int Test(delegate int(char8 a, params T) dlg, params T par)
+		{
+			return dlg('A', params par);
+		}
+	}
+
+	class ClassB : ClassA<(int a, float b)>
+	{
+
+	}
+
+	[Test]
+	public static void TestBasics()
+	{
+		int val = ClassB.Test(scope (a, __a, b) =>
+			{
+				return (.)a + (.)__a + (.)b;
+			}, 10, 2.3f);
+		Test.Assert(val == 65+10+2);
+	}
+}