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

Partial explicit generic method arguments with ... or ?

Brian Fiete 3 жил өмнө
parent
commit
f4f10fce99

+ 83 - 38
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -137,7 +137,7 @@ BfBaseClassWalker::Entry BfBaseClassWalker::Next()
 
 //////////////////////////////////////////////////////////////////////////
 
-BfMethodMatcher::BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, const StringImpl& methodName, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments) :
+BfMethodMatcher::BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, const StringImpl& methodName, SizedArrayImpl<BfResolvedArg>& arguments, const BfMethodGenericArguments& methodGenericArguments) :
 	mArguments(arguments)
 {
 	mTargetSrc = targetSrc;
@@ -146,7 +146,7 @@ BfMethodMatcher::BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, const S
 	Init(/*arguments, */methodGenericArguments);
 }
 
-BfMethodMatcher::BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* interfaceMethodInstance, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments) :
+BfMethodMatcher::BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* interfaceMethodInstance, SizedArrayImpl<BfResolvedArg>& arguments, const BfMethodGenericArguments& methodGenericArguments) :
 	mArguments(arguments)
 {
 	mTargetSrc = targetSrc;
@@ -156,7 +156,7 @@ BfMethodMatcher::BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMetho
 	mMethodName = mInterfaceMethodInstance->mMethodDef->mName;
 }
 
-void BfMethodMatcher::Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments)
+void BfMethodMatcher::Init(const BfMethodGenericArguments& methodGenericArguments)
 {
 	//mArguments = arguments;	
 	mActiveTypeDef = NULL;
@@ -170,6 +170,8 @@ void BfMethodMatcher::Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSized
 	mMethodType = BfMethodType_Normal;
 	mCheckReturnType = NULL;
 	mHadExplicitGenericArguments = false;
+	mHadOpenGenericArguments = methodGenericArguments.mIsOpen;
+	mHadPartialGenericArguments = methodGenericArguments.mIsPartial;
 	mHasVarArguments = false;
 	mInterfaceMethodInstance = NULL;
 	mFakeConcreteTarget = false;
@@ -202,16 +204,25 @@ void BfMethodMatcher::Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSized
 		}
 	}
 
-	if (methodGenericArguments != NULL)
+	if (methodGenericArguments.mArguments != NULL)
 	{
-		for (BfAstNode* genericArg : *methodGenericArguments)
+		for (BfAstNode* genericArg : *(methodGenericArguments.mArguments))
 		{
-			auto genericArgType = mModule->ResolveTypeRef(genericArg, NULL, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowImplicitConstExpr);
-			if ((genericArgType != NULL) && (genericArgType->IsGenericParam()))
+			BfType* genericArgType = NULL;
+			if (BfNodeIsA<BfUninitializedExpression>(genericArg))
 			{
-				auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)genericArgType);
-				if ((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
-					mHasVarArguments = true;
+				// Allow a null here
+				BF_ASSERT(mHadPartialGenericArguments);
+			}
+			else
+			{
+				genericArgType = mModule->ResolveTypeRef(genericArg, NULL, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowImplicitConstExpr);
+				if ((genericArgType != NULL) && (genericArgType->IsGenericParam()))
+				{
+					auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)genericArgType);
+					if ((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
+						mHasVarArguments = true;
+				}
 			}
 			mExplicitMethodGenericArguments.push_back(genericArgType);
 		}
@@ -1705,7 +1716,8 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
 	int argIdx = 0;
 	int argMatchCount = 0;
 	
-	bool needInferGenericParams = (checkMethod->mGenericParams.size() != 0) && (!mHadExplicitGenericArguments);
+	bool needInferGenericParams = (checkMethod->mGenericParams.size() != 0) &&
+		((!mHadExplicitGenericArguments) || (mHadPartialGenericArguments));
 	int paramIdx = 0;
 	BfType* paramsElementType = NULL;
 	if (checkMethod->mHasAppend)
@@ -1713,8 +1725,20 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
 
 	int uniqueGenericStartIdx = mModule->GetLocalInferrableGenericArgCount(checkMethod);
 
-	if ((mHadExplicitGenericArguments) && (checkMethod->mGenericParams.size() != mExplicitMethodGenericArguments.size() + uniqueGenericStartIdx))
-		goto NoMatch;
+	if (mHadExplicitGenericArguments)
+	{
+		int genericArgDelta = (int)(checkMethod->mGenericParams.size() - (mExplicitMethodGenericArguments.size() + uniqueGenericStartIdx));
+		if (mHadOpenGenericArguments)
+		{
+			if (genericArgDelta <= 0)
+				goto NoMatch;
+		}
+		else
+		{
+			if (genericArgDelta != 0)
+				goto NoMatch;
+		}
+	}
 	
 	for (auto& checkGenericArgRef : mCheckMethodGenericArguments)
 		checkGenericArgRef = NULL;
@@ -1740,7 +1764,14 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
 		else
 		{
 			genericArgumentsSubstitute = &mExplicitMethodGenericArguments;
-		}		
+		}
+
+		if ((mHadPartialGenericArguments) && (needInferGenericParams))
+		{
+			genericArgumentsSubstitute = &mCheckMethodGenericArguments;
+			for (int i = 0; i < (int)mExplicitMethodGenericArguments.mSize; i++)
+				mCheckMethodGenericArguments[i] = mExplicitMethodGenericArguments[i];
+		}
 	}
 	else if (needInferGenericParams)
 		genericArgumentsSubstitute = &mCheckMethodGenericArguments;
@@ -3780,7 +3811,7 @@ void BfExprEvaluator::Visit(BfStringInterpolationExpression* stringInterpolation
 		BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
 		BfResolvedArgs argValues(&sizedArgExprs);
 		ResolveArgValues(argValues, BfResolveArgsFlag_InsideStringInterpolationAlloc);
-		MatchMethod(stringInterpolationExpression, NULL, newString, false, false, "AppendF", argValues, NULL);
+		MatchMethod(stringInterpolationExpression, NULL, newString, false, false, "AppendF", argValues, BfMethodGenericArguments());
 		mResult = newString;
 
 		return;
@@ -7666,7 +7697,7 @@ BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBou
 	static int sCtorCount = 0;
 	sCtorCount++;			
 	
-	BfMethodMatcher methodMatcher(targetSrc, mModule, "", argValues.mResolvedArgs, NULL);
+	BfMethodMatcher methodMatcher(targetSrc, mModule, "", argValues.mResolvedArgs, BfMethodGenericArguments());
 	methodMatcher.mBfEvalExprFlags = mBfEvalExprFlags;
 		
 	BfTypeVector typeGenericArguments;
@@ -8251,10 +8282,12 @@ bool BfExprEvaluator::CheckGenericCtor(BfGenericParamType* genericParamType, BfR
 }
 
 BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExpression* methodBoundExpr, BfTypedValue target, bool allowImplicitThis, bool bypassVirtual, const StringImpl& methodName, 
-	BfResolvedArgs& argValues, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments, BfCheckedKind checkedKind)
+	BfResolvedArgs& argValues, const BfMethodGenericArguments& methodGenericArgs, BfCheckedKind checkedKind)
 {
 	BP_ZONE("MatchMethod");
 
+	auto methodGenericArguments = methodGenericArgs.mArguments;
+
 	if (bypassVirtual)
 	{
 		// "bypassVirtual" means that we know for sure that the target is EXACTLY the specified target type,
@@ -8534,7 +8567,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 
 	BfTypeInstance* curTypeInst = targetTypeInst;
 
-	BfMethodMatcher methodMatcher(targetSrc, mModule, methodName, argValues.mResolvedArgs, methodGenericArguments);
+	BfMethodMatcher methodMatcher(targetSrc, mModule, methodName, argValues.mResolvedArgs, methodGenericArgs);
 	methodMatcher.mOrigTarget = origTarget;
 	methodMatcher.mTarget = target;
 	methodMatcher.mCheckedKind = checkedKind;
@@ -9053,7 +9086,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 				{
 					mFunctionBindResult->mOrigTarget = BfTypedValue();
 				}
-				return MatchMethod(targetSrc, NULL, fieldVal, false, false, "Invoke", argValues, methodGenericArguments, checkedKind);
+				return MatchMethod(targetSrc, NULL, fieldVal, false, false, "Invoke", argValues, methodGenericArgs, checkedKind);
 			}
 			if (IsVar(fieldVal.mType))
 			{				
@@ -9634,7 +9667,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 						autoComplete->mIsGetDefinition = false;
 					result = mModule->MakeAddressable(result);
 					BfResolvedArgs resolvedArgs;
-					MatchMethod(targetSrc, NULL, result, false, false, "ReturnValueDiscarded", resolvedArgs, NULL);
+					MatchMethod(targetSrc, NULL, result, false, false, "ReturnValueDiscarded", resolvedArgs, BfMethodGenericArguments());
 					if (wasGetDefinition)
 						autoComplete->mIsGetDefinition = true;
 				}
@@ -10534,7 +10567,7 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr)
 				BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
 				BfResolvedArgs argValues(&sizedArgExprs);
 				exprEvaluator.ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval);
-				exprEvaluator.MatchMethod(elementExpr, NULL, initValue, false, false, "Add", argValues, NULL);
+				exprEvaluator.MatchMethod(elementExpr, NULL, initValue, false, false, "Add", argValues, BfMethodGenericArguments());
 
 				if (addFunctionBindResult.mMethodInstance != NULL)
 					CreateCall(initExpr, addFunctionBindResult.mMethodInstance, addFunctionBindResult.mFunc, true, addFunctionBindResult.mIRArgs);
@@ -11598,16 +11631,16 @@ bool BfExprEvaluator::CanBindDelegate(BfDelegateBindExpression* delegateBindExpr
 		args[i] = typedValueExpr;
 	}
 
-	BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments = NULL;
+	BfMethodGenericArguments methodGenericArgs;
 	if (delegateBindExpr->mGenericArgs != NULL)
-		methodGenericArguments = &delegateBindExpr->mGenericArgs->mGenericArgs;
+		methodGenericArgs.mArguments = &delegateBindExpr->mGenericArgs->mGenericArgs;
 	
 	BfFunctionBindResult bindResult;
 	bindResult.mSkipMutCheck = true; // Allow operating on copies
 	bindResult.mBindType = expectingType;
 	mFunctionBindResult = &bindResult;
 	SetAndRestoreValue<bool> ignoreError(mModule->mIgnoreErrors, true);
-	DoInvocation(delegateBindExpr->mTarget, delegateBindExpr, args, methodGenericArguments);
+	DoInvocation(delegateBindExpr->mTarget, delegateBindExpr, args, methodGenericArgs);
 	mFunctionBindResult = NULL;
 	if (bindResult.mMethodInstance == NULL)
 		return false;	
@@ -12002,9 +12035,9 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr)
 		args[i] = typedValueExpr;
 	}
 
-	BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments = NULL;
+	BfMethodGenericArguments methodGenericArgs;
 	if (delegateBindExpr->mGenericArgs != NULL)
-		methodGenericArguments = &delegateBindExpr->mGenericArgs->mGenericArgs;
+		methodGenericArgs.mArguments = &delegateBindExpr->mGenericArgs->mGenericArgs;
 
 	if (delegateBindExpr->mTarget == NULL)
 	{
@@ -12034,7 +12067,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr)
 	{
 		SetAndRestoreValue<BfType*> prevExpectingType(mExpectingType, methodInstance->mReturnType);
 		mFunctionBindResult = &bindResult;
-		DoInvocation(delegateBindExpr->mTarget, delegateBindExpr, args, methodGenericArguments);
+		DoInvocation(delegateBindExpr->mTarget, delegateBindExpr, args, methodGenericArgs);
 		mFunctionBindResult = NULL;
 	}	
 	
@@ -15289,7 +15322,7 @@ BfModuleMethodInstance BfExprEvaluator::GetSelectedMethod(BfAstNode* targetSrc,
 			}
 			mModule->Fail(StrFormat("Too many generic arguments, expected %d fewer", genericArgCountDiff), errorNode);
 		}
-		else if (genericArgCountDiff < 0)
+		else if ((genericArgCountDiff < 0) && (!methodMatcher.mHadOpenGenericArguments))
 		{	
 			BfAstNode* errorNode = targetSrc;
 			if ((invocationExpr != NULL) && (invocationExpr->mGenericArgs != NULL) && (invocationExpr->mGenericArgs->mCloseChevron != NULL))
@@ -15646,7 +15679,7 @@ void BfExprEvaluator::CheckLocalMethods(BfAstNode* targetSrc, BfTypeInstance* ty
 	}	
 }
 
-void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, bool allowImplicitThis, const StringImpl& name, const BfSizedArray<BfExpression*>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArgs)
+void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, bool allowImplicitThis, const StringImpl& name, const BfSizedArray<BfExpression*>& arguments, const BfMethodGenericArguments& methodGenericArgs)
 {
 	if (mModule->mCurMethodState == NULL)
 		return;
@@ -16512,8 +16545,10 @@ void BfExprEvaluator::SetMethodElementType(BfAstNode* target)
 		mModule->SetElementType(target, BfSourceElementType_Method);
 }
 
-void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* methodBoundExpr, const BfSizedArray<BfExpression*>& args, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments, BfTypedValue* outCascadeValue)
+void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* methodBoundExpr, const BfSizedArray<BfExpression*>& args, const BfMethodGenericArguments& methodGenericArgs, BfTypedValue* outCascadeValue)
 {
+	auto methodGenericArguments = methodGenericArgs.mArguments;
+
 	// Just a check
 	mModule->mBfIRBuilder->GetInsertBlock();
 
@@ -17066,7 +17101,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 	if ((targetFunctionName != "") && (targetFunctionName[targetFunctionName.length() - 1] == '!'))
 	{						
 		targetFunctionName = targetFunctionName.Substring(0, targetFunctionName.length() - 1);
-		InjectMixin(methodNodeSrc, thisValue, allowImplicitThis, targetFunctionName, args, methodGenericArguments);
+		InjectMixin(methodNodeSrc, thisValue, allowImplicitThis, targetFunctionName, args, methodGenericArgs);
 
 		return;
 	}		
@@ -17161,7 +17196,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 	if (isCascade)
 		mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_InCascade);
 	ResolveArgValues(argValues, resolveArgsFlags);
-	mResult = MatchMethod(methodNodeSrc, methodBoundExpr, thisValue, allowImplicitThis, bypassVirtual, targetFunctionName, argValues, methodGenericArguments, checkedKind);		
+	mResult = MatchMethod(methodNodeSrc, methodBoundExpr, thisValue, allowImplicitThis, bypassVirtual, targetFunctionName, argValues, methodGenericArgs, checkedKind);		
 	argValues.HandleFixits(mModule);
 
 	if (mModule->mAttributeState == &attributeState)
@@ -17253,15 +17288,25 @@ void BfExprEvaluator::Visit(BfInvocationExpression* invocationExpr)
 		autoComplete->CheckInvocation(invocationExpr, invocationExpr->mOpenParen, invocationExpr->mCloseParen, invocationExpr->mCommas);	
 
 	mModule->UpdateExprSrcPos(invocationExpr);
-	BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments = NULL;
+	BfMethodGenericArguments methodGenericArgs;
 	if (invocationExpr->mGenericArgs != NULL)
-		methodGenericArguments = &invocationExpr->mGenericArgs->mGenericArgs;
+	{
+		methodGenericArgs.mArguments = &invocationExpr->mGenericArgs->mGenericArgs;
+		if ((!invocationExpr->mGenericArgs->mCommas.IsEmpty()) && (invocationExpr->mGenericArgs->mCommas.back()->mToken == BfToken_DotDotDot))
+		{
+			methodGenericArgs.mIsOpen = true;
+			methodGenericArgs.mIsPartial = true;
+		}
+		for (int i = 0; i < (int)methodGenericArgs.mArguments->mSize; i++)
+			if (BfNodeIsA<BfUninitializedExpression>((*methodGenericArgs.mArguments)[i]))
+				methodGenericArgs.mIsPartial = true;
+	}
 	SizedArray<BfExpression*, 8> copiedArgs;
 	for (BfExpression* arg : invocationExpr->mArguments)
 		copiedArgs.push_back(arg);			
 
 	BfTypedValue cascadeValue;
-	DoInvocation(invocationExpr->mTarget, invocationExpr, copiedArgs, methodGenericArguments, &cascadeValue);
+	DoInvocation(invocationExpr->mTarget, invocationExpr, copiedArgs, methodGenericArgs, &cascadeValue);
 
 	if (autoComplete != NULL)
 	{
@@ -19916,7 +19961,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr)
 			if (!val.mTypedValue)
 				val.mTypedValue = mModule->GetDefaultTypedValue(mModule->mContext->mBfObjectType);
 
-		BfMethodMatcher methodMatcher(indexerExpr->mTarget, mModule, "[]", mIndexerValues, NULL);
+		BfMethodMatcher methodMatcher(indexerExpr->mTarget, mModule, "[]", mIndexerValues, BfMethodGenericArguments());
 		methodMatcher.mCheckedKind = checkedKind;
 		//methodMatcher.CheckType(target.mType->ToTypeInstance(), target, false);
 		
@@ -20306,7 +20351,7 @@ BfTypedValue BfExprEvaluator::PerformUnaryOperation_TryOperator(const BfTypedVal
 	BfResolvedArg resolvedArg;
 	resolvedArg.mTypedValue = inValue;
 	args.push_back(resolvedArg);
-	BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL);
+	BfMethodMatcher methodMatcher(opToken, mModule, "", args, BfMethodGenericArguments());
 	methodMatcher.mBfEvalExprFlags = BfEvalExprFlags_NoAutoComplete;
 	methodMatcher.mAllowImplicitRef = true;
 	BfBaseClassWalker baseClassWalker(inValue.mType, NULL, mModule);
@@ -21947,7 +21992,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 				auto checkLeftType = leftValue.mType;				
 				auto checkRightType = rightValue.mType;
 
-				BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL);
+				BfMethodMatcher methodMatcher(opToken, mModule, "", args, BfMethodGenericArguments());
 				methodMatcher.mAllowImplicitRef = true;
 				methodMatcher.mBfEvalExprFlags = BfEvalExprFlags_NoAutoComplete;
 				BfBaseClassWalker baseClassWalker(checkLeftType, checkRightType, mModule);

+ 23 - 7
IDEHelper/Compiler/BfExprEvaluator.h

@@ -157,6 +157,20 @@ public:
 	void InferGenericArguments(BfMethodInstance* methodInstance);
 };
 
+struct BfMethodGenericArguments
+{
+	BfSizedArray<BfAstNode*>* mArguments;	
+	bool mIsPartial;
+	bool mIsOpen; // Ends with ...
+
+	BfMethodGenericArguments()
+	{
+		mArguments = NULL;		
+		mIsPartial = false;
+		mIsOpen = false;
+	}
+};
+
 class BfMethodMatcher
 {
 public:
@@ -180,7 +194,7 @@ public:
 		BackupMatchKind_EarlyMismatch,		
 		BackupMatchKind_PartialLastArgMatch
 	};
-
+	
 public:
 	BfAstNode* mTargetSrc;
 	BfTypedValue mTarget;
@@ -194,6 +208,8 @@ public:
 	BfMethodType mMethodType;
 	BfCheckedKind mCheckedKind;
 	bool mHadExplicitGenericArguments;	
+	bool mHadOpenGenericArguments;
+	bool mHadPartialGenericArguments;
 	bool mHasVarArguments;	
 	bool mHadVarConflictingReturnType;
 	bool mBypassVirtual;
@@ -236,9 +252,9 @@ public:
 	int GetMostSpecificType(BfType* lhs, BfType* rhs); // 0, 1, or -1
 
 public:
-	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, const StringImpl& methodName, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments);
-	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* interfaceMethodInstance, SizedArrayImpl<BfResolvedArg>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments = NULL);
-	void Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments);
+	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, const StringImpl& methodName, SizedArrayImpl<BfResolvedArg>& arguments, const BfMethodGenericArguments& methodGenericArguments);
+	BfMethodMatcher(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* interfaceMethodInstance, SizedArrayImpl<BfResolvedArg>& arguments, const BfMethodGenericArguments& methodGenericArguments);
+	void Init(const BfMethodGenericArguments& methodGenericArguments);
 	bool IsMemberAccessible(BfTypeInstance* typeInst, BfTypeDef* declaringType);
 	bool CheckType(BfTypeInstance* typeInstance, BfTypedValue target, bool isFailurePass, bool forceOuterCheck = false);
 	void CheckOuterTypeStaticMethods(BfTypeInstance* typeInstance, bool isFailurePass);
@@ -462,16 +478,16 @@ public:
 		BfResolvedArgs& argValues, bool callCtorBodyOnly, bool allowAppendAlloc, BfTypedValue* appendIndexValue = NULL);
 	BfTypedValue CheckEnumCreation(BfAstNode* targetSrc, BfTypeInstance* enumType, const StringImpl& caseName, BfResolvedArgs& argValues);
 	BfTypedValue MatchMethod(BfAstNode* targetSrc, BfMethodBoundExpression* methodBoundExpr, BfTypedValue target, bool allowImplicitThis, bool bypassVirtual, const StringImpl& name, 
-		BfResolvedArgs& argValue, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments, BfCheckedKind checkedKind = BfCheckedKind_NotSet);
+		BfResolvedArgs& argValue, const BfMethodGenericArguments& methodGenericArguments, BfCheckedKind checkedKind = BfCheckedKind_NotSet);
 	BfTypedValue MakeCallableTarget(BfAstNode* targetSrc, BfTypedValue target);		
 	BfModuleMethodInstance GetSelectedMethod(BfAstNode* targetSrc, BfTypeInstance* curTypeInst, BfMethodDef* methodDef, BfMethodMatcher& methodMatcher, BfType** overrideReturnType = NULL);
 	BfModuleMethodInstance GetSelectedMethod(BfMethodMatcher& methodMatcher);
 	bool CheckVariableDeclaration(BfAstNode* checkNode, bool requireSimpleIfExpr, bool exprMustBeTrue, bool silentFail);
 	bool HasVariableDeclaration(BfAstNode* checkNode);
-	void DoInvocation(BfAstNode* target, BfMethodBoundExpression* methodBoundExpr, const BfSizedArray<BfExpression*>& args, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArgs, BfTypedValue* outCascadeValue = NULL);	
+	void DoInvocation(BfAstNode* target, BfMethodBoundExpression* methodBoundExpr, const BfSizedArray<BfExpression*>& args, const BfMethodGenericArguments& methodGenericArgs, BfTypedValue* outCascadeValue = NULL);	
 	int GetMixinVariable();	
 	void CheckLocalMethods(BfAstNode* targetSrc, BfTypeInstance* typeInstance, const StringImpl& methodName, BfMethodMatcher& methodMatcher, BfMethodType methodType);
-	void InjectMixin(BfAstNode* targetSrc, BfTypedValue target, bool allowImplicitThis, const StringImpl& name, const BfSizedArray<BfExpression*>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArgs);
+	void InjectMixin(BfAstNode* targetSrc, BfTypedValue target, bool allowImplicitThis, const StringImpl& name, const BfSizedArray<BfExpression*>& arguments, const BfMethodGenericArguments& methodGenericArgs);
 	void SetMethodElementType(BfAstNode* target);
 	BfTypedValue DoImplicitArgCapture(BfAstNode* refNode, BfIdentifierNode* identifierNode, int shadowIdx);
 	BfTypedValue DoImplicitArgCapture(BfAstNode* refNode, BfMethodInstance* methodInstance, int paramIdx, bool& failed, BfImplicitParamKind paramKind = BfImplicitParamKind_General, const BfTypedValue& methodRefTarget = BfTypedValue());

+ 7 - 7
IDEHelper/Compiler/BfModule.cpp

@@ -8733,7 +8733,7 @@ BfIRValue BfModule::AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTar
 		if (allocTarget.mScopedInvocationTarget != NULL)
 		{			
 			SizedArray<BfTypeReference*, 2> genericArgs;
-			exprEvaluator.DoInvocation(allocTarget.mScopedInvocationTarget, NULL, argExprs, NULL);
+			exprEvaluator.DoInvocation(allocTarget.mScopedInvocationTarget, NULL, argExprs, BfMethodGenericArguments());
 			allocResult = LoadValue(exprEvaluator.mResult);
 		}
 		else if (allocTarget.mCustomAllocator)
@@ -8779,7 +8779,7 @@ BfIRValue BfModule::AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTar
 					BfResolvedArgs argValues(&sizedArgExprs);
 					exprEvaluator.ResolveArgValues(argValues);
 					SetAndRestoreValue<bool> prevNoBind(mCurMethodState->mNoBind, true);
-					allocResult = exprEvaluator.MatchMethod(refNode, NULL, allocTarget.mCustomAllocator, false, false, allocMethodName, argValues, NULL);
+					allocResult = exprEvaluator.MatchMethod(refNode, NULL, allocTarget.mCustomAllocator, false, false, allocMethodName, argValues, BfMethodGenericArguments());
 				}
 			}			
 		}
@@ -8854,7 +8854,7 @@ BfIRValue BfModule::GetMarkFuncPtr(BfType* type)
 		BfResolvedArg resolvedArg;
 		resolvedArg.mTypedValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), type, type->IsComposite());
 		resolvedArgs.Add(resolvedArg);
-		BfMethodMatcher methodMatcher(NULL, this, "Mark", resolvedArgs, NULL);
+		BfMethodMatcher methodMatcher(NULL, this, "Mark", resolvedArgs, BfMethodGenericArguments());
 		methodMatcher.CheckType(gcType, BfTypedValue(), false);
 
 		BfModuleMethodInstance moduleMethodInst = exprEvaluator.GetSelectedMethod(NULL, methodMatcher.mBestMethodTypeInstance, methodMatcher.mBestMethodDef, methodMatcher);
@@ -9457,7 +9457,7 @@ BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget
 				BfResolvedArgs argValues(&sizedArgExprs);
 				exprEvaluator.ResolveArgValues(argValues);
 				exprEvaluator.mNoBind = true;
-				BfTypedValue allocResult = exprEvaluator.MatchMethod(allocTarget.mRefNode, NULL, allocTarget.mCustomAllocator, false, false, "AllocObject", argValues, NULL);
+				BfTypedValue allocResult = exprEvaluator.MatchMethod(allocTarget.mRefNode, NULL, allocTarget.mCustomAllocator, false, false, "AllocObject", argValues, BfMethodGenericArguments());
 				if (allocResult)
 				{					
 					if ((allocResult.mType->IsVoidPtr()) || (allocResult.mType == mContext->mBfObjectType))
@@ -11734,7 +11734,7 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri
 				autoComplete->CheckInvocation(attributesDirective, attributesDirective->mCtorOpenParen, attributesDirective->mCtorCloseParen, attributesDirective->mCommas);
 		}			
 					
-		BfMethodMatcher methodMatcher(attributesDirective, this, "", argValues, NULL);
+		BfMethodMatcher methodMatcher(attributesDirective, this, "", argValues, BfMethodGenericArguments());
 		attrTypeDef = attrTypeInst->mTypeDef;
 
 		bool success = true;
@@ -17688,7 +17688,7 @@ void BfModule::EmitTupleToStringBody()
 		{
 			BfExprEvaluator exprEvaluator(this);			
 			SizedArray<BfResolvedArg, 0> resolvedArgs;
-			BfMethodMatcher methodMatcher(NULL, this, printModuleMethodInstance.mMethodInstance, resolvedArgs);
+			BfMethodMatcher methodMatcher(NULL, this, printModuleMethodInstance.mMethodInstance, resolvedArgs, BfMethodGenericArguments());
 			methodMatcher.mBestMethodDef = printModuleMethodInstance.mMethodInstance->mMethodDef;
 			methodMatcher.mBestMethodTypeInstance = iPrintableType;
 			methodMatcher.TryDevirtualizeCall(fieldValue);
@@ -17720,7 +17720,7 @@ void BfModule::EmitTupleToStringBody()
 
 		BfExprEvaluator exprEvaluator(this);		
 		SizedArray<BfResolvedArg, 0> resolvedArgs;		
-		BfMethodMatcher methodMatcher(NULL, this, toStringModuleMethodInstance.mMethodInstance, resolvedArgs);
+		BfMethodMatcher methodMatcher(NULL, this, toStringModuleMethodInstance.mMethodInstance, resolvedArgs, BfMethodGenericArguments());
 		methodMatcher.mBestMethodDef = toStringModuleMethodInstance.mMethodInstance->mMethodDef;
 		methodMatcher.mBestMethodTypeInstance = mContext->mBfObjectType;		
 		methodMatcher.TryDevirtualizeCall(fieldValue);

+ 1 - 1
IDEHelper/Compiler/BfModuleTypeUtils.cpp

@@ -12739,7 +12739,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
 			resolvedArg.mTypedValue.mKind = BfTypedValueKind_Value;
 		}
 		args.push_back(resolvedArg);
-		BfMethodMatcher methodMatcher(srcNode, this, "", args, NULL);
+		BfMethodMatcher methodMatcher(srcNode, this, "", args, BfMethodGenericArguments());
 		methodMatcher.mCheckReturnType = toType;
 		methodMatcher.mBfEvalExprFlags = (BfEvalExprFlags)(BfEvalExprFlags_NoAutoComplete | BfEvalExprFlags_FromConversionOp);
 		if ((castFlags & BfCastFlags_Explicit) != 0)

+ 24 - 4
IDEHelper/Compiler/BfReducer.cpp

@@ -742,7 +742,7 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int*
 						if (auto prevToken = BfNodeDynCast<BfTokenNode>(prevNode))
 						{
 							// If this is just a 'loose' comma then it can't be part of a nullable
-							if ((prevToken->GetToken() == BfToken_Comma) ||
+							if (((prevToken->GetToken() == BfToken_Comma) && (chevronDepth == 0)) ||
 								(prevToken->GetToken() == BfToken_LParen))
 							{
 								return false;
@@ -936,6 +936,11 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int*
 			{
 				if (checkNode->IsExact<BfLiteralExpression>())
 					mayBeExprPart = true;
+				else if (auto tokenNode = BfNodeDynCast<BfTokenNode>(checkNode))
+				{
+					if (tokenNode->mToken == BfToken_Question)
+						mayBeExprPart = true;
+				}
 			}
 
 			if (!mayBeExprPart)
@@ -2726,7 +2731,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
 				if (auto outToken = BfNodeDynCast<BfTokenNode>(outNode))
 				{
 					int endNodeIdx = -1;
-					if ((outToken->GetToken() == BfToken_LParen) && (IsTypeReference(exprLeft, BfToken_LParen, &endNodeIdx)))
+					if (((outToken->mToken == BfToken_LParen) && (IsTypeReference(exprLeft, BfToken_LParen, &endNodeIdx))) ||
+						(outToken->mToken == BfToken_DotDotDot))
 					{
 						exprLeft = CreateInvocationExpression(exprLeft);
 						if (exprLeft == NULL)
@@ -7352,7 +7358,7 @@ BfInvocationExpression* BfReducer::CreateInvocationExpression(BfAstNode* target,
 
 	if (tokenNode->GetToken() == BfToken_LChevron)
 	{
-		auto genericParamsDecl = CreateGenericArguments(tokenNode);
+		auto genericParamsDecl = CreateGenericArguments(tokenNode, true);
 		MEMBER_SET_CHECKED(invocationExpr, mGenericArgs, genericParamsDecl);
 		tokenNode = ExpectTokenAfter(invocationExpr, BfToken_LParen);
 	}
@@ -9726,7 +9732,7 @@ bool BfReducer::ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayIm
 	return true;
 }
 
-BfGenericArgumentsNode* BfReducer::CreateGenericArguments(BfTokenNode* tokenNode)
+BfGenericArgumentsNode* BfReducer::CreateGenericArguments(BfTokenNode* tokenNode, bool allowPartial)
 {
 	auto genericArgs = mAlloc->Alloc<BfGenericArgumentsNode>();
 	BfDeferredAstSizedArray<BfAstNode*> genericArgsArray(genericArgs->mGenericArgs, mAlloc);
@@ -9740,6 +9746,11 @@ BfGenericArgumentsNode* BfReducer::CreateGenericArguments(BfTokenNode* tokenNode
 		auto nextNode = mVisitorPos.GetNext();
 		if (BfNodeIsA<BfLiteralExpression>(nextNode))
 			doAsExpr = true;
+		else if (auto tokenNode = BfNodeDynCast<BfTokenNode>(nextNode))
+		{
+			if (tokenNode->mToken == BfToken_Question)
+				doAsExpr = true;
+		}
 
 		BfAstNode* genericArg = NULL;
 		if (doAsExpr)
@@ -9782,6 +9793,15 @@ BfGenericArgumentsNode* BfReducer::CreateGenericArguments(BfTokenNode* tokenNode
 		if (token == BfToken_RDblChevron)
 			tokenNode = BreakDoubleChevron(tokenNode);
 
+		if ((token == BfToken_DotDotDot) && (allowPartial))
+		{
+			commas.push_back(tokenNode);
+			mVisitorPos.MoveNext();
+			nextNode = mVisitorPos.GetNext();
+			tokenNode = BfNodeDynCast<BfTokenNode>(nextNode);
+			token = tokenNode->GetToken();
+		}
+
 		if (token == BfToken_RChevron)
 		{
 			MoveNode(tokenNode, genericArgs);

+ 1 - 1
IDEHelper/Compiler/BfReducer.h

@@ -226,7 +226,7 @@ public:
 	BfTypeReference* CreateTypeRefAfter(BfAstNode* astNode, CreateTypeRefFlags createTypeRefFlags = CreateTypeRefFlags_None);
 	BfTypeReference* CreateRefTypeRef(BfTypeReference* elementType, BfTokenNode* refToken);	
 	bool ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayImpl<BfParameterDeclaration*>* params, SizedArrayImpl<BfTokenNode*>* commas, bool alwaysIncludeBlock = false);
-	BfGenericArgumentsNode* CreateGenericArguments(BfTokenNode* tokenNode);
+	BfGenericArgumentsNode* CreateGenericArguments(BfTokenNode* tokenNode, bool allowPartial = false);
 	BfGenericParamsDeclaration* CreateGenericParamsDeclaration(BfTokenNode* tokenNode);
 	BfGenericConstraintsDeclaration* CreateGenericConstraintsDeclaration(BfTokenNode* tokenNode);
 	BfForEachStatement* CreateForEachStatement(BfAstNode* node, bool hasTypeDecl);

+ 8 - 8
IDEHelper/Compiler/BfStmtEvaluator.cpp

@@ -4079,7 +4079,7 @@ void BfModule::Visit(BfDeleteStatement* deleteStmt)
 							BfResolvedArgs argValues(&sizedArgExprs);
 							exprEvaluator.ResolveArgValues(argValues);
 							exprEvaluator.mNoBind = true;
-							exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, true, "FreeObject", argValues, NULL);
+							exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, true, "FreeObject", argValues, BfMethodGenericArguments());
 							customAllocator = BfTypedValue();
 						}
 					}
@@ -4137,7 +4137,7 @@ void BfModule::Visit(BfDeleteStatement* deleteStmt)
 		BfResolvedArgs argValues(&sizedArgExprs);
 		exprEvaluator.ResolveArgValues(argValues);
 		exprEvaluator.mNoBind = true;
-		exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "Free", argValues, NULL);
+		exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "Free", argValues, BfMethodGenericArguments());
 	}
 	
 	mBfIRBuilder->CreateBr(endBB);
@@ -5444,7 +5444,7 @@ void BfModule::Visit(BfUsingStatement* usingStmt)
 
 			exprEvaluator.mFunctionBindResult = &functionBindResult;			
 			SizedArray<BfResolvedArg, 0> resolvedArgs;
-			BfMethodMatcher methodMatcher(usingStmt->mVariableDeclaration, this, dispMethod.mMethodInstance, resolvedArgs);
+			BfMethodMatcher methodMatcher(usingStmt->mVariableDeclaration, this, dispMethod.mMethodInstance, resolvedArgs, BfMethodGenericArguments());
 			methodMatcher.CheckType(iDisposableType, embeddedValue, false);
 			methodMatcher.TryDevirtualizeCall(embeddedValue);
 			auto retVal = exprEvaluator.CreateCall(&methodMatcher, embeddedValue);
@@ -6528,7 +6528,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
 			SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true); 
 			BfResolvedArgs resolvedArgs;
 			exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_NoAutoComplete);
-			exprEvaluator.MatchMethod(forEachStmt->mCollectionExpression, NULL, itr, false, false, "Dispose", resolvedArgs, NULL);
+			exprEvaluator.MatchMethod(forEachStmt->mCollectionExpression, NULL, itr, false, false, "Dispose", resolvedArgs, BfMethodGenericArguments());
 			if (functionBindResult.mMethodInstance != NULL)
 			{
 				BfModuleMethodInstance moduleMethodInstance;
@@ -6624,7 +6624,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
 			BfExprEvaluator exprEvaluator(this);			
 			auto itrTypeInstance = itr.mType->ToTypeInstance();
 			SizedArray<BfResolvedArg, 0> resolvedArgs;			
-			BfMethodMatcher methodMatcher(forEachStmt->mCollectionExpression, this, getNextMethodInst.mMethodInstance, resolvedArgs);
+			BfMethodMatcher methodMatcher(forEachStmt->mCollectionExpression, this, getNextMethodInst.mMethodInstance, resolvedArgs, BfMethodGenericArguments());
 			if (isRefExpression)
 				methodMatcher.CheckType(refItrInterface, itr, false);
 			else
@@ -6704,7 +6704,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
 		if (typeOptions != NULL)
 			boundsCheck = typeOptions->Apply(boundsCheck, BfOptionFlags_RuntimeChecks);
 		
-		BfMethodMatcher methodMatcher(forEachStmt->mVariableName, this, "get__", argValues.mResolvedArgs, NULL);
+		BfMethodMatcher methodMatcher(forEachStmt->mVariableName, this, "get__", argValues.mResolvedArgs, BfMethodGenericArguments());
 		methodMatcher.mMethodType = BfMethodType_PropertyGetter;
 		methodMatcher.CheckType(target.mType->ToTypeInstance(), target, false);
 		if (methodMatcher.mBestMethodDef == NULL)
@@ -6957,7 +6957,7 @@ void BfModule::Visit(BfDeferStatement* deferStmt)
 				exprEvaluator.ResolveArgValues(argValues);
 				exprEvaluator.mNoBind = true;
 				exprEvaluator.mFunctionBindResult = &functionBindResult;
-				exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "FreeObject", argValues, NULL);
+				exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "FreeObject", argValues, BfMethodGenericArguments());
 			}
 			else
 			{
@@ -6973,7 +6973,7 @@ void BfModule::Visit(BfDeferStatement* deferStmt)
 				exprEvaluator.ResolveArgValues(argValues);
 				exprEvaluator.mNoBind = true;
 				exprEvaluator.mFunctionBindResult = &functionBindResult;
-				exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "Free", argValues, NULL);
+				exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "Free", argValues, BfMethodGenericArguments());
 			}
 
 			if (functionBindResult.mMethodInstance != NULL)

+ 8 - 0
IDEHelper/Tests/src/Generics.bf

@@ -326,6 +326,11 @@ namespace Tests
 			TestGen(a);
 		}
 
+		public static TOut Conv<TOut, TIn>(TIn val) where TOut : operator explicit TIn
+		{
+			return (TOut)val;
+		}
+
 		[Test]
 		public static void TestBasics()
 		{
@@ -396,6 +401,9 @@ namespace Tests
 					true
 				} == false);
 			MethodG<ClassG, ClassF>();
+
+			Test.Assert(Conv<int...>(12.34f) == 12);
+			Test.Assert(Conv<int,?>(12.34f) == 12);
 		}
 	}