mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-10 04:22:20 +02:00
Added proper support for explicit 'this' in functions
This commit is contained in:
parent
4a08a9702e
commit
7f726ef9ba
8 changed files with 438 additions and 142 deletions
|
@ -7307,7 +7307,7 @@ void BfCompiler::GenerateAutocompleteInfo()
|
|||
int dispParamIdx = 0;
|
||||
|
||||
StringT<64> paramName;
|
||||
for (int paramIdx = 0; paramIdx < (int)methodInstance->GetParamCount(); paramIdx++)
|
||||
for (int paramIdx = methodInstance->HasExplicitThis() ? -1 : 0; paramIdx < (int)methodInstance->GetParamCount(); paramIdx++)
|
||||
{
|
||||
auto paramKind = methodInstance->GetParamKind(paramIdx);
|
||||
if ((paramKind == BfParamKind_ImplicitCapture) || (paramKind == BfParamKind_AppendIdx))
|
||||
|
|
|
@ -5359,8 +5359,16 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
|
|||
|
||||
if (!skipThis)
|
||||
{
|
||||
if (!methodDef->mIsStatic)
|
||||
if ((target) && (target.mType->IsFunction()))
|
||||
{
|
||||
CheckResultForReading(target);
|
||||
target = mModule->LoadValue(target);
|
||||
auto funcType = mModule->mBfIRBuilder->MapMethod(moduleMethodInstance.mMethodInstance);
|
||||
auto funcPtrType = mModule->mBfIRBuilder->GetPointerTo(funcType);
|
||||
moduleMethodInstance.mFunc = mModule->mBfIRBuilder->CreateIntToPtr(target.mValue, funcPtrType);
|
||||
}
|
||||
else if (!methodDef->mIsStatic)
|
||||
{
|
||||
if (!target)
|
||||
{
|
||||
FinishDeferredEvals(argValues);
|
||||
|
@ -5407,15 +5415,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
|
|||
else
|
||||
PushThis(targetSrc, target, moduleMethodInstance.mMethodInstance, irArgs, skipMutCheck);
|
||||
}
|
||||
}
|
||||
else if ((target) && (target.mType->IsFunction()))
|
||||
{
|
||||
CheckResultForReading(target);
|
||||
target = mModule->LoadValue(target);
|
||||
auto funcType = mModule->mBfIRBuilder->MapMethod(moduleMethodInstance.mMethodInstance);
|
||||
auto funcPtrType = mModule->mBfIRBuilder->GetPointerTo(funcType);
|
||||
moduleMethodInstance.mFunc = mModule->mBfIRBuilder->CreateIntToPtr(target.mValue, funcPtrType);
|
||||
}
|
||||
}
|
||||
else if (methodDef->mMethodType == BfMethodType_Extension)
|
||||
{
|
||||
// Handled in args
|
||||
|
@ -5462,6 +5462,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
|
|||
if ((boxScopeData == NULL) && (mModule->mCurMethodState != NULL))
|
||||
boxScopeData = mModule->mCurMethodState->mCurScope;
|
||||
|
||||
if (methodInstance->HasExplicitThis())
|
||||
paramIdx = -1;
|
||||
|
||||
bool failed = false;
|
||||
while (true)
|
||||
{
|
||||
|
@ -5500,7 +5503,11 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
|
|||
if (errorRef == NULL)
|
||||
errorRef = targetSrc;
|
||||
|
||||
BfError* error = mModule->Fail(StrFormat("Too many arguments, expected %d fewer.", (int)argValues.size() - argIdx), errorRef);
|
||||
BfError* error;
|
||||
if ((prevBindResult.mPrevVal != NULL) && (prevBindResult.mPrevVal->mBindType != NULL))
|
||||
error = mModule->Fail(StrFormat("Method '%s' has too many parameters to bind to '%s'.", mModule->MethodToString(methodInstance).c_str(), mModule->TypeToString(prevBindResult.mPrevVal->mBindType).c_str()), errorRef);
|
||||
else
|
||||
error = mModule->Fail(StrFormat("Too many arguments, expected %d fewer.", (int)argValues.size() - argIdx), errorRef);
|
||||
if ((error != NULL) && (methodInstance->mMethodDef->mMethodDeclaration != NULL))
|
||||
mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("See method declaration"), methodInstance->mMethodDef->GetRefNode());
|
||||
failed = true;
|
||||
|
@ -5833,6 +5840,12 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
|
|||
|
||||
if (argValue)
|
||||
{
|
||||
if ((paramIdx == -1) && (argValue.mType->IsRef()))
|
||||
{
|
||||
// Convert a 'ref this' to a 'this*'
|
||||
argValue.mType = mModule->CreatePointerType(argValue.mType->GetUnderlyingType());
|
||||
}
|
||||
|
||||
BfAstNode* refNode = arg;
|
||||
if (refNode == NULL)
|
||||
refNode = targetSrc;
|
||||
|
@ -5903,7 +5916,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
|
|||
|
||||
if (argValue)
|
||||
{
|
||||
if (wantsSplat)
|
||||
if (paramIdx == -1)
|
||||
PushThis(targetSrc, argValue, methodInstance, irArgs);
|
||||
else if (wantsSplat)
|
||||
SplatArgs(argValue, irArgs);
|
||||
else
|
||||
PushArg(argValue, irArgs, true, false);
|
||||
|
@ -9719,6 +9734,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr)
|
|||
|
||||
BfFunctionBindResult bindResult;
|
||||
bindResult.mSkipMutCheck = true; // Allow operating on copies
|
||||
bindResult.mBindType = delegateTypeInstance;
|
||||
//
|
||||
{
|
||||
SetAndRestoreValue<BfType*> prevExpectingType(mExpectingType, methodInstance->mReturnType);
|
||||
|
@ -12322,7 +12338,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
|
|||
else
|
||||
autoComplete->mIsCapturingMethodMatchInfo = false;
|
||||
}
|
||||
else
|
||||
else if (!resolvedTypeRef->IsFunction())
|
||||
{
|
||||
MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInstance, argValues, false, true);
|
||||
}
|
||||
|
|
|
@ -238,6 +238,7 @@ public:
|
|||
BfTypedValue mTarget;
|
||||
BfIRValue mFunc;
|
||||
BfMethodInstance* mMethodInstance;
|
||||
BfType* mBindType;
|
||||
bool mSkipThis;
|
||||
bool mSkipMutCheck;
|
||||
bool mWantsArgs;
|
||||
|
@ -248,6 +249,7 @@ public:
|
|||
BfFunctionBindResult()
|
||||
{
|
||||
mMethodInstance = NULL;
|
||||
mBindType = NULL;
|
||||
mSkipMutCheck = false;
|
||||
mWantsArgs = false;
|
||||
mSkipThis = false;
|
||||
|
|
|
@ -309,7 +309,7 @@ void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name
|
|||
name += "N8functionI";
|
||||
SizedArray<BfType*, 8> typeVec;
|
||||
typeVec.push_back(invokeMethodInst->mReturnType);
|
||||
for (int paramIdx = 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++)
|
||||
for (int paramIdx = invokeMethodInst->HasExplicitThis() ? -1 : 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++)
|
||||
{
|
||||
name += "_";
|
||||
name += invokeMethodInst->GetParamName(paramIdx);
|
||||
|
@ -1156,44 +1156,45 @@ bool BfMSMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl&
|
|||
{
|
||||
BF_ASSERT(newNameSub.mTypeInst->mTypeDef->mMethods[0]->mName == "Invoke");
|
||||
|
||||
// Why did we have this in here? It appears it was to fix some sort of bug...
|
||||
//
|
||||
{
|
||||
auto methodDef = newNameSub.mTypeInst->mTypeDef->mMethods[0];
|
||||
if (newNameSub.mTypeInst->IsDelegate())
|
||||
name += "?$delegate";
|
||||
else
|
||||
name += "?$function";
|
||||
SizedArray<BfType*, 8> typeVec;
|
||||
typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mReturnTypeRef)->mType);
|
||||
for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++)
|
||||
{
|
||||
name += "_";
|
||||
name += methodDef->mParams[paramIdx]->mName;
|
||||
typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mParams[paramIdx]->mTypeRef)->mType);
|
||||
}
|
||||
name += '@';
|
||||
if (!typeVec.empty())
|
||||
AddGenericArgs(mangleContext, name, typeVec);
|
||||
name += '@';
|
||||
}
|
||||
// {
|
||||
// auto methodDef = newNameSub.mTypeInst->mTypeDef->mMethods[0];
|
||||
// if (newNameSub.mTypeInst->IsDelegate())
|
||||
// name += "?$delegate";
|
||||
// else
|
||||
// name += "?$function";
|
||||
// SizedArray<BfType*, 8> typeVec;
|
||||
// typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mReturnTypeRef)->mType);
|
||||
// for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++)
|
||||
// {
|
||||
// name += "_";
|
||||
// name += methodDef->mParams[paramIdx]->mName;
|
||||
// typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mParams[paramIdx]->mTypeRef)->mType);
|
||||
// }
|
||||
// name += '@';
|
||||
// if (!typeVec.empty())
|
||||
// AddGenericArgs(mangleContext, name, typeVec);
|
||||
// name += '@';
|
||||
// }
|
||||
|
||||
// auto invokeMethodInst = mangleContext.mModule->GetDelegateInvokeMethod(newNameSub.mTypeInst);
|
||||
// if (newNameSub.mTypeInst->IsDelegate())
|
||||
// name += "?$delegate";
|
||||
// else
|
||||
// name += "?$function";
|
||||
// SizedArray<BfType*, 8> typeVec;
|
||||
// typeVec.push_back(invokeMethodInst->mReturnType);
|
||||
// for (int paramIdx = 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++)
|
||||
// {
|
||||
// name += "_";
|
||||
// name += invokeMethodInst->GetParamName(paramIdx);
|
||||
// typeVec.push_back(invokeMethodInst->GetParamType(paramIdx));
|
||||
// }
|
||||
// name += '@';
|
||||
// if (!typeVec.empty())
|
||||
// AddGenericArgs(mangleContext, name, typeVec);
|
||||
// name += '@';
|
||||
auto invokeMethodInst = mangleContext.mModule->GetDelegateInvokeMethod(newNameSub.mTypeInst);
|
||||
if (newNameSub.mTypeInst->IsDelegate())
|
||||
name += "?$delegate";
|
||||
else
|
||||
name += "?$function";
|
||||
SizedArray<BfType*, 8> typeVec;
|
||||
typeVec.push_back(invokeMethodInst->mReturnType);
|
||||
for (int paramIdx = invokeMethodInst->HasExplicitThis() ? -1 : 0; paramIdx < (int)invokeMethodInst->mParams.size(); paramIdx++)
|
||||
{
|
||||
name += "_";
|
||||
name += invokeMethodInst->GetParamName(paramIdx);
|
||||
typeVec.push_back(invokeMethodInst->GetParamType(paramIdx));
|
||||
}
|
||||
name += '@';
|
||||
if (!typeVec.empty())
|
||||
AddGenericArgs(mangleContext, name, typeVec);
|
||||
name += '@';
|
||||
}
|
||||
else if (newNameSub.mTypeInst->IsBoxed())
|
||||
{
|
||||
|
|
|
@ -7774,7 +7774,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
if ((resolvedType->IsValueType()) || (resolvedType->IsGenericParam()))
|
||||
needsRefWrap = true;
|
||||
|
||||
if ((InDefinitionSection()) && (!resolvedType->IsGenericParam()))
|
||||
if ((InDefinitionSection()) && (!resolvedType->IsGenericParam()) && ((resolveFlags & BfResolveTypeRefFlag_NoWarnOnMut) == 0))
|
||||
{
|
||||
if (!resolvedType->IsValueType())
|
||||
Warn(0, StrFormat("Specified 'mut' has no effect on '%s' since reference types are always mutable", TypeToString(resolvedType).c_str()), refTypeRef->mRefToken);
|
||||
|
@ -8364,7 +8364,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
return ResolveTypeResult(typeRef, refType, populateType, resolveFlags);
|
||||
}
|
||||
else if (auto delegateTypeRef = BfNodeDynCast<BfDelegateTypeRef>(typeRef))
|
||||
{
|
||||
{
|
||||
bool wantGeneric = false;
|
||||
bool isUnspecialized = false;
|
||||
auto _CheckType = [&](BfType* type)
|
||||
|
@ -8375,19 +8375,56 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
isUnspecialized = true;
|
||||
};
|
||||
|
||||
bool failed = false;
|
||||
|
||||
auto returnType = ResolveTypeRef(delegateTypeRef->mReturnType);
|
||||
if (returnType == NULL)
|
||||
returnType = GetPrimitiveType(BfTypeCode_Var);
|
||||
_CheckType(returnType);
|
||||
|
||||
BfType* functionThisType = NULL;
|
||||
bool hasMutSpecifier = false;
|
||||
bool isFirst = true;
|
||||
bool isDelegate = delegateTypeRef->mTypeToken->GetToken() == BfToken_Delegate;
|
||||
|
||||
Array<BfType*> paramTypes;
|
||||
for (auto param : delegateTypeRef->mParams)
|
||||
{
|
||||
auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowRef);
|
||||
BfResolveTypeRefFlags resolveTypeFlags = BfResolveTypeRefFlag_AllowRef;
|
||||
if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
|
||||
resolveTypeFlags = (BfResolveTypeRefFlags)(resolveTypeFlags | BfResolveTypeRefFlag_NoWarnOnMut);
|
||||
auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, resolveTypeFlags);
|
||||
if (paramType == NULL)
|
||||
{
|
||||
failed = true;
|
||||
paramType = GetPrimitiveType(BfTypeCode_Var);
|
||||
paramTypes.Add(paramType);
|
||||
_CheckType(paramType);
|
||||
}
|
||||
|
||||
if ((!isDelegate) && (isFirst) && (param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
|
||||
{
|
||||
functionThisType = paramType;
|
||||
if (functionThisType->IsRef())
|
||||
{
|
||||
auto refType = (BfRefType*)functionThisType;
|
||||
if (refType->mRefKind != BfRefType::RefKind_Mut)
|
||||
{
|
||||
if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(param->mTypeRef))
|
||||
{
|
||||
failed = true;
|
||||
Fail("Only 'mut' is allowed here", refTypeRef->mRefToken);
|
||||
}
|
||||
}
|
||||
hasMutSpecifier = true;
|
||||
functionThisType = refType->mElementType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
paramTypes.Add(paramType);
|
||||
_CheckType(paramType);
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
if ((mCurTypeInstance == NULL) || (!mCurTypeInstance->IsGenericTypeInstance()))
|
||||
|
@ -8414,25 +8451,6 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
|
||||
CheckUnspecializedGenericType(genericTypeInst, populateType);
|
||||
|
||||
// for (auto paramType : paramTypes)
|
||||
// {
|
||||
// if (paramType->IsUnspecializedType())
|
||||
// genericTypeInst->mGenericTypeInfo->mIsUnspecialized = true;
|
||||
// if (paramType->IsUnspecializedTypeVariation())
|
||||
// genericTypeInst->mGenericTypeInfo->mIsUnspecializedVariation = true;
|
||||
// if (paramType->IsGenericParam())
|
||||
// {
|
||||
// BfGenericParamType* genericParamType = new BfGenericParamType();
|
||||
// if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
|
||||
// {
|
||||
// if (genericParamType->mGenericParamIdx >= genericTypeInst->mGenericTypeInfo->mGenericParams.size())
|
||||
// genericTypeInst->mGenericTypeInfo->mIsUnspecializedVariation = true;
|
||||
// }
|
||||
// else
|
||||
// genericTypeInst->mGenericTypeInfo->mIsUnspecializedVariation = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// We don't ever need to do an actual pass over generic delegate methods, so it's safe to set the 'unspecialized variation' flag
|
||||
if (isUnspecialized)
|
||||
{
|
||||
|
@ -8468,13 +8486,17 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
typeDef->mIsFunction = true;
|
||||
typeDef->mTypeCode = BfTypeCode_Struct;
|
||||
}
|
||||
|
||||
|
||||
BfMethodDef* methodDef = new BfMethodDef();
|
||||
methodDef->mDeclaringType = typeDef;
|
||||
methodDef->mName = "Invoke";
|
||||
methodDef->mProtection = BfProtection_Public;
|
||||
methodDef->mIdx = 0;
|
||||
methodDef->mIsStatic = !typeDef->mIsDelegate;
|
||||
methodDef->mIsStatic = !typeDef->mIsDelegate && (functionThisType == NULL);
|
||||
methodDef->mIsMutating = true;
|
||||
|
||||
if ((functionThisType != NULL) && (functionThisType->IsValueType()) && (!hasMutSpecifier))
|
||||
methodDef->mIsMutating = false;
|
||||
|
||||
auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>();
|
||||
delegateInfo->mDirectAllocNodes.push_back(directTypeRef);
|
||||
|
@ -8489,12 +8511,14 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
directTypeRef->Init(returnType);
|
||||
methodDef->mReturnTypeRef = directTypeRef;
|
||||
delegateInfo->mReturnType = returnType;
|
||||
delegateInfo->mFunctionThisType = functionThisType;
|
||||
|
||||
auto hashVal = mContext->mResolvedTypes.Hash(typeRef, &lookupCtx);
|
||||
|
||||
int paramSrcOfs = (functionThisType != NULL) ? 1 : 0;
|
||||
for (int paramIdx = 0; paramIdx < (int)paramTypes.size(); paramIdx++)
|
||||
{
|
||||
auto param = delegateTypeRef->mParams[paramIdx];
|
||||
auto param = delegateTypeRef->mParams[paramIdx + paramSrcOfs];
|
||||
auto paramType = paramTypes[paramIdx];
|
||||
if (paramType == NULL)
|
||||
paramType = GetPrimitiveType(BfTypeCode_Var);
|
||||
|
@ -8504,8 +8528,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
paramName = param->mNameNode->ToString();
|
||||
|
||||
if (!paramType->IsReified())
|
||||
delegateType->mIsReified = false;
|
||||
|
||||
delegateType->mIsReified = false;
|
||||
|
||||
auto directTypeRef = BfAstNode::ZeroedAlloc<BfDirectTypeReference>();
|
||||
delegateInfo->mDirectAllocNodes.push_back(directTypeRef);
|
||||
|
@ -8522,6 +8545,13 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
|
||||
typeDef->mMethods.push_back(methodDef);
|
||||
|
||||
if (failed)
|
||||
{
|
||||
delete delegateType;
|
||||
mContext->mResolvedTypes.RemoveEntry(resolvedEntry);
|
||||
return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
BfDefBuilder::AddMethod(typeDef, BfMethodType_Ctor, BfProtection_Public, false, "");
|
||||
|
@ -8531,20 +8561,21 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula
|
|||
delegateType->mContext = mContext;
|
||||
delegateType->mTypeDef = typeDef;
|
||||
populateModule->InitType(delegateType, populateType);
|
||||
resolvedEntry->mValue = delegateType;
|
||||
resolvedEntry->mValue = delegateType;
|
||||
|
||||
AddDependency(directTypeRef->mType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
|
||||
for (auto paramType : paramTypes)
|
||||
AddDependency(paramType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue);
|
||||
|
||||
// #ifdef _DEBUG
|
||||
// if (BfResolvedTypeSet::Hash(delegateType, &lookupCtx) != resolvedEntry->mHash)
|
||||
// {
|
||||
// int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx);
|
||||
// int typeHash = BfResolvedTypeSet::Hash(delegateType, &lookupCtx);
|
||||
// BF_ASSERT(refHash == typeHash);
|
||||
// }
|
||||
// #endif
|
||||
#ifdef _DEBUG
|
||||
if (BfResolvedTypeSet::Hash(delegateType, &lookupCtx) != resolvedEntry->mHash)
|
||||
{
|
||||
int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx);
|
||||
int typeHash = BfResolvedTypeSet::Hash(delegateType, &lookupCtx);
|
||||
BF_ASSERT(refHash == typeHash);
|
||||
}
|
||||
BF_ASSERT(BfResolvedTypeSet::Equals(delegateType, typeRef, &lookupCtx));
|
||||
#endif
|
||||
|
||||
BF_ASSERT(BfResolvedTypeSet::Hash(delegateType, &lookupCtx) == resolvedEntry->mHash);
|
||||
|
||||
|
@ -8847,7 +8878,56 @@ BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, BfMethodInstance* methodI
|
|||
}
|
||||
else if (invokeMethodInstance->IsExactMatch(methodInstance, false, false))
|
||||
{
|
||||
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider adding '%s this' to the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(methodInstance->GetParamType(-1)).c_str()), srcNode);
|
||||
bool handled = false;
|
||||
|
||||
auto thisType = methodInstance->GetParamType(-1);
|
||||
if (thisType != NULL)
|
||||
{
|
||||
if (invokeMethodInstance->HasExplicitThis())
|
||||
{
|
||||
auto invokeThisType = invokeMethodInstance->GetParamType(-1);
|
||||
|
||||
bool thisWasPtr = false;
|
||||
if (thisType->IsPointer())
|
||||
{
|
||||
thisType = thisType->GetUnderlyingType();
|
||||
thisWasPtr = true;
|
||||
}
|
||||
|
||||
bool invokeThisWasPtr = false;
|
||||
if (invokeThisType->IsPointer())
|
||||
{
|
||||
invokeThisType = invokeThisType->GetUnderlyingType();
|
||||
invokeThisWasPtr = true;
|
||||
}
|
||||
|
||||
if (invokeThisType == thisType)
|
||||
{
|
||||
if (invokeThisWasPtr != thisWasPtr)
|
||||
{
|
||||
if (invokeThisWasPtr)
|
||||
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider removing 'mut' from 'mut %s this' in the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(thisType).c_str()), srcNode);
|
||||
else
|
||||
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider adding 'mut' specifier to '%s this' in the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(thisType).c_str()), srcNode);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((!methodInstance->mMethodDef->mIsStatic) && (!invokeMethodInstance->HasExplicitThis()))
|
||||
{
|
||||
handled = true;
|
||||
Fail(StrFormat("Non-static method '%s' cannot match '%s', consider adding '%s this' to the function parameters", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str(), TypeToString(thisType).c_str()), srcNode);
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
if (invokeMethodInstance->mMethodDef->mIsStatic)
|
||||
Fail(StrFormat("Static method '%s' cannot match '%s'", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str()).c_str(), srcNode);
|
||||
else
|
||||
Fail(StrFormat("Non-static method '%s' cannot match '%s'", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str()).c_str(), srcNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return BfIRValue();
|
||||
|
@ -10386,34 +10466,41 @@ BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, Bf
|
|||
(fromMethodInst->mReturnType == toMethodInst->mReturnType) &&
|
||||
(fromMethodInst->GetParamCount() == toMethodInst->GetParamCount()))
|
||||
{
|
||||
bool matched = true;
|
||||
bool matched = true;
|
||||
|
||||
StringT<64> fromParamName;
|
||||
StringT<64> toParamName;
|
||||
|
||||
for (int paramIdx = 0; paramIdx < (int)fromMethodInst->GetParamCount(); paramIdx++)
|
||||
if (fromMethodInst->HasExplicitThis() != toMethodInst->HasExplicitThis())
|
||||
{
|
||||
bool nameMatches = true;
|
||||
|
||||
if (!explicitCast)
|
||||
matched = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int paramIdx = fromMethodInst->HasExplicitThis() ? -1 : 0; paramIdx < (int)fromMethodInst->GetParamCount(); paramIdx++)
|
||||
{
|
||||
fromMethodInst->GetParamName(paramIdx, fromParamName);
|
||||
toMethodInst->GetParamName(paramIdx, toParamName);
|
||||
if ((!fromParamName.IsEmpty()) && (!toParamName.IsEmpty()))
|
||||
nameMatches = fromParamName == toParamName;
|
||||
bool nameMatches = true;
|
||||
|
||||
if (!explicitCast)
|
||||
{
|
||||
fromMethodInst->GetParamName(paramIdx, fromParamName);
|
||||
toMethodInst->GetParamName(paramIdx, toParamName);
|
||||
if ((!fromParamName.IsEmpty()) && (!toParamName.IsEmpty()))
|
||||
nameMatches = fromParamName == toParamName;
|
||||
}
|
||||
|
||||
if ((fromMethodInst->GetParamKind(paramIdx) == toMethodInst->GetParamKind(paramIdx)) &&
|
||||
(fromMethodInst->GetParamType(paramIdx) == toMethodInst->GetParamType(paramIdx)) &&
|
||||
(nameMatches))
|
||||
{
|
||||
// Matched, required for implicit/explicit
|
||||
}
|
||||
else
|
||||
{
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fromMethodInst->GetParamKind(paramIdx) == toMethodInst->GetParamKind(paramIdx)) &&
|
||||
(fromMethodInst->GetParamType(paramIdx) == toMethodInst->GetParamType(paramIdx)) &&
|
||||
(nameMatches))
|
||||
{
|
||||
// Matched, required for implicit/explicit
|
||||
}
|
||||
else
|
||||
{
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched)
|
||||
|
@ -10939,9 +11026,20 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF
|
|||
str += "function ";
|
||||
DoTypeToString(str, delegateInfo->mReturnType, typeNameFlags, genericMethodNameOverrides);
|
||||
str += "(";
|
||||
|
||||
bool isFirstParam = true;
|
||||
if (delegateInfo->mFunctionThisType != NULL)
|
||||
{
|
||||
if ((methodDef->mIsMutating) && (delegateInfo->mFunctionThisType->IsValueType()))
|
||||
str += "mut ";
|
||||
DoTypeToString(str, delegateInfo->mFunctionThisType, typeNameFlags, genericMethodNameOverrides);
|
||||
str += " this";
|
||||
isFirstParam = false;
|
||||
}
|
||||
|
||||
for (int paramIdx = 0; paramIdx < methodDef->mParams.size(); paramIdx++)
|
||||
{
|
||||
if (paramIdx > 0)
|
||||
if (!isFirstParam)
|
||||
str += ", ";
|
||||
auto paramDef = methodDef->mParams[paramIdx];
|
||||
BfTypeNameFlags innerFlags = (BfTypeNameFlags)(typeNameFlags & ~(BfTypeNameFlag_OmitNamespace | BfTypeNameFlag_OmitOuterType));
|
||||
|
@ -10953,6 +11051,8 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF
|
|||
str += " ";
|
||||
str += paramDef->mName;
|
||||
}
|
||||
|
||||
isFirstParam = false;
|
||||
}
|
||||
|
||||
str += ")";
|
||||
|
|
|
@ -714,6 +714,13 @@ bool BfMethodInstance::HasThis()
|
|||
return (!mMethodInstanceGroup->mOwner->IsValuelessType());
|
||||
}
|
||||
|
||||
bool BfMethodInstance::HasExplicitThis()
|
||||
{
|
||||
if (mMethodDef->mIsStatic)
|
||||
return false;
|
||||
return mMethodInstanceGroup->mOwner->IsFunction();
|
||||
}
|
||||
|
||||
int BfMethodInstance::GetParamCount()
|
||||
{
|
||||
return (int)mParams.size();
|
||||
|
@ -773,9 +780,13 @@ BfType* BfMethodInstance::GetParamType(int paramIdx, bool useResolvedType)
|
|||
return mMethodInfoEx->mClosureInstanceInfo->mThisOverride;
|
||||
BF_ASSERT(!mMethodDef->mIsStatic);
|
||||
auto owner = mMethodInstanceGroup->mOwner;
|
||||
if ((owner->IsValueType()) && ((mMethodDef->mIsMutating) || (!AllowsSplatting())) && (!owner->GetLoweredType(BfTypeUsage_Parameter)))
|
||||
return owner->mModule->CreatePointerType(owner);
|
||||
return owner;
|
||||
auto delegateInfo = owner->GetDelegateInfo();
|
||||
BfType* thisType = owner;
|
||||
if ((delegateInfo != NULL) && (delegateInfo->mFunctionThisType != NULL))
|
||||
thisType = delegateInfo->mFunctionThisType;
|
||||
if ((thisType->IsValueType()) && ((mMethodDef->mIsMutating) || (!AllowsSplatting())) && (!thisType->GetLoweredType(BfTypeUsage_Parameter)))
|
||||
return owner->mModule->CreatePointerType(thisType);
|
||||
return thisType;
|
||||
}
|
||||
|
||||
BfMethodParam* methodParam = &mParams[paramIdx];
|
||||
|
@ -809,6 +820,8 @@ bool BfMethodInstance::GetParamIsSplat(int paramIdx)
|
|||
|
||||
BfParamKind BfMethodInstance::GetParamKind(int paramIdx)
|
||||
{
|
||||
if (paramIdx == -1)
|
||||
return BfParamKind_Normal;
|
||||
BfMethodParam* methodParam = &mParams[paramIdx];
|
||||
if (methodParam->mParamDefIdx == -1)
|
||||
return BfParamKind_ImplicitCapture;
|
||||
|
@ -820,12 +833,16 @@ BfParamKind BfMethodInstance::GetParamKind(int paramIdx)
|
|||
|
||||
bool BfMethodInstance::WasGenericParam(int paramIdx)
|
||||
{
|
||||
if (paramIdx == -1)
|
||||
return false;
|
||||
BfMethodParam* methodParam = &mParams[paramIdx];
|
||||
return methodParam->mWasGenericParam;
|
||||
}
|
||||
|
||||
bool BfMethodInstance::IsParamSkipped(int paramIdx)
|
||||
{
|
||||
if (paramIdx == -1)
|
||||
return false;
|
||||
BfType* paramType = GetParamType(paramIdx);
|
||||
if ((paramType->CanBeValuelessType()) && (paramType->IsDataIncomplete()))
|
||||
GetModule()->PopulateType(paramType, BfPopulateType_Data);
|
||||
|
@ -836,6 +853,8 @@ bool BfMethodInstance::IsParamSkipped(int paramIdx)
|
|||
|
||||
bool BfMethodInstance::IsImplicitCapture(int paramIdx)
|
||||
{
|
||||
if (paramIdx == -1)
|
||||
return false;
|
||||
BfMethodParam* methodParam = &mParams[paramIdx];
|
||||
if (methodParam->mParamDefIdx == -1)
|
||||
return true;
|
||||
|
@ -844,6 +863,8 @@ bool BfMethodInstance::IsImplicitCapture(int paramIdx)
|
|||
|
||||
BfExpression* BfMethodInstance::GetParamInitializer(int paramIdx)
|
||||
{
|
||||
if (paramIdx == -1)
|
||||
return NULL;
|
||||
BfMethodParam* methodParam = &mParams[paramIdx];
|
||||
if (methodParam->mParamDefIdx == -1)
|
||||
return NULL;
|
||||
|
@ -855,6 +876,8 @@ BfExpression* BfMethodInstance::GetParamInitializer(int paramIdx)
|
|||
|
||||
BfTypeReference* BfMethodInstance::GetParamTypeRef(int paramIdx)
|
||||
{
|
||||
if (paramIdx == -1)
|
||||
return NULL;
|
||||
BfMethodParam* methodParam = &mParams[paramIdx];
|
||||
if (methodParam->mParamDefIdx == -1)
|
||||
return NULL;
|
||||
|
@ -969,13 +992,22 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType,
|
|||
checkType = module->mCurMethodState->mClosureState->mClosureType;
|
||||
}
|
||||
else
|
||||
checkType = GetOwner();
|
||||
{
|
||||
if (HasExplicitThis())
|
||||
checkType = GetParamType(-1);
|
||||
else
|
||||
checkType = GetOwner();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
checkType = GetParamType(paramIdx);
|
||||
}
|
||||
|
||||
/*if (GetParamName(paramIdx) == "this")
|
||||
{
|
||||
NOP;
|
||||
}*/
|
||||
|
||||
bool checkLowered = false;
|
||||
bool doSplat = false;
|
||||
|
@ -1098,29 +1130,52 @@ bool BfMethodInstance::IsExactMatch(BfMethodInstance* other, bool ignoreImplicit
|
|||
int implicitParamCountA = ignoreImplicitParams ? GetImplicitParamCount() : 0;
|
||||
int implicitParamCountB = ignoreImplicitParams ? other->GetImplicitParamCount() : 0;
|
||||
|
||||
if (HasExplicitThis())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// if (other->HasExplicitThis())
|
||||
// {
|
||||
// if (!HasExplicitThis())
|
||||
// return false;
|
||||
// if (GetParamType(-1) != other->GetParamType(-1))
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if (checkThis)
|
||||
{
|
||||
if (other->mMethodDef->mIsStatic != mMethodDef->mIsStatic)
|
||||
return false;
|
||||
|
||||
// {
|
||||
// // If we are static and we have to match a non-static method, allow us to do so if we have an explicitly defined 'this' param that matches
|
||||
//
|
||||
// if (other->mMethodDef->mIsStatic)
|
||||
// return false;
|
||||
//
|
||||
// if ((GetParamCount() > 0) && (GetParamName(0) == "this"))
|
||||
// {
|
||||
// auto thisType = GetParamType(0);
|
||||
// auto otherThisType = other->GetParamType(-1);
|
||||
// if (thisType != otherThisType)
|
||||
// return false;
|
||||
//
|
||||
// implicitParamCountA++;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // Valueless types don't actually pass a 'this' anyway
|
||||
// if (!other->GetOwner()->IsValuelessType())
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (!mMethodDef->mIsStatic)
|
||||
{
|
||||
// If we are static and we have to match a non-static method, allow us to do so if we have an explicitly defined 'this' param that matches
|
||||
|
||||
if (other->mMethodDef->mIsStatic)
|
||||
if (GetParamType(-1) != other->GetParamType(-1))
|
||||
{
|
||||
return false;
|
||||
|
||||
if ((GetParamCount() > 0) && (GetParamName(0) == "this"))
|
||||
{
|
||||
auto thisType = GetParamType(0);
|
||||
auto otherThisType = other->GetParamType(-1);
|
||||
if (thisType != otherThisType)
|
||||
return false;
|
||||
|
||||
implicitParamCountA++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Valueless types don't actually pass a 'this' anyway
|
||||
if (!other->GetOwner()->IsValuelessType())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2413,6 +2468,14 @@ int BfResolvedTypeSet::Hash(BfType* type, LookupContext* ctx, bool allowRef)
|
|||
BF_ASSERT(methodDef->mName == "Invoke");
|
||||
BF_ASSERT(delegateInfo->mParams.size() == methodDef->mParams.size());
|
||||
|
||||
if (delegateInfo->mFunctionThisType != NULL)
|
||||
{
|
||||
hashVal = ((hashVal ^ (Hash(delegateInfo->mFunctionThisType, ctx))) << 5) - hashVal;
|
||||
String paramName = "this";
|
||||
int nameHash = (int)Hash64(paramName.c_str(), (int)paramName.length());
|
||||
hashVal = ((hashVal ^ (nameHash)) << 5) - hashVal;
|
||||
}
|
||||
|
||||
for (int paramIdx = 0; paramIdx < delegateInfo->mParams.size(); paramIdx++)
|
||||
{
|
||||
// Parse attributes?
|
||||
|
@ -2989,12 +3052,26 @@ int BfResolvedTypeSet::Hash(BfTypeReference* typeRef, LookupContext* ctx, BfHash
|
|||
else
|
||||
ctx->mFailed = true;
|
||||
|
||||
|
||||
bool isFirstParam = true;
|
||||
|
||||
for (auto param : delegateTypeRef->mParams)
|
||||
{
|
||||
// Parse attributes?
|
||||
BfTypeReference* fieldType = param->mTypeRef;
|
||||
|
||||
if (isFirstParam)
|
||||
{
|
||||
if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
|
||||
{
|
||||
if (auto refNode = BfNodeDynCast<BfRefTypeRef>(fieldType))
|
||||
fieldType = refNode->mElementType;
|
||||
}
|
||||
}
|
||||
|
||||
hashVal = ((hashVal ^ (Hash(fieldType, ctx, BfHashFlag_AllowRef))) << 5) - hashVal;
|
||||
hashVal = ((hashVal ^ (HashNode(param->mNameNode))) << 5) - hashVal;
|
||||
isFirstParam = true;
|
||||
}
|
||||
|
||||
return hashVal;
|
||||
|
@ -3498,20 +3575,57 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext*
|
|||
|
||||
BfMethodInstance* lhsInvokeMethodInstance = ctx->mModule->GetRawMethodInstanceAtIdx(lhs->ToTypeInstance(), 0, "Invoke");
|
||||
|
||||
if ((lhs->IsDelegate()) != (rhsDelegateType->mTypeToken->GetToken() == BfToken_Delegate))
|
||||
bool rhsIsDelegate = rhsDelegateType->mTypeToken->GetToken() == BfToken_Delegate;
|
||||
|
||||
if ((lhs->IsDelegate()) != rhsIsDelegate)
|
||||
return false;
|
||||
if (!Equals(lhsInvokeMethodInstance->mReturnType, rhsDelegateType->mReturnType, ctx))
|
||||
return false;
|
||||
if (lhsInvokeMethodInstance->GetParamCount() != (int)rhsDelegateType->mParams.size())
|
||||
|
||||
bool isMutating = true;
|
||||
|
||||
int paramRefOfs = 0;
|
||||
if (!rhsDelegateType->mParams.IsEmpty())
|
||||
{
|
||||
auto param0 = rhsDelegateType->mParams[0];
|
||||
if ((param0->mNameNode != NULL) && (param0->mNameNode->Equals("this")))
|
||||
{
|
||||
bool handled = false;
|
||||
auto lhsThisType = lhsInvokeMethodInstance->GetParamType(-1);
|
||||
|
||||
auto rhsThisType = ctx->mModule->ResolveTypeRef(param0->mTypeRef, BfPopulateType_Identity, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoWarnOnMut | BfResolveTypeRefFlag_AllowRef));
|
||||
if (rhsThisType->IsRef())
|
||||
{
|
||||
if (!lhsThisType->IsPointer())
|
||||
return false;
|
||||
if (lhsThisType->GetUnderlyingType() != rhsThisType->GetUnderlyingType())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lhsThisType != rhsThisType)
|
||||
return false;
|
||||
}
|
||||
|
||||
paramRefOfs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rhsIsDelegate)
|
||||
{
|
||||
if (lhsInvokeMethodInstance->mMethodDef->mIsStatic != (paramRefOfs == 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhsInvokeMethodInstance->GetParamCount() != (int)rhsDelegateType->mParams.size() - paramRefOfs)
|
||||
return false;
|
||||
for (int paramIdx = 0; paramIdx < lhsInvokeMethodInstance->GetParamCount(); paramIdx++)
|
||||
{
|
||||
if (!Equals(lhsInvokeMethodInstance->GetParamType(paramIdx), rhsDelegateType->mParams[paramIdx]->mTypeRef, ctx))
|
||||
if (!Equals(lhsInvokeMethodInstance->GetParamType(paramIdx), rhsDelegateType->mParams[paramIdx + paramRefOfs]->mTypeRef, ctx))
|
||||
return false;
|
||||
StringView rhsParamName;
|
||||
if (rhsDelegateType->mParams[paramIdx]->mNameNode != NULL)
|
||||
rhsParamName = rhsDelegateType->mParams[paramIdx]->mNameNode->ToStringView();
|
||||
|
||||
if (rhsDelegateType->mParams[paramIdx + paramRefOfs]->mNameNode != NULL)
|
||||
rhsParamName = rhsDelegateType->mParams[paramIdx + paramRefOfs]->mNameNode->ToStringView();
|
||||
if (lhsInvokeMethodInstance->GetParamName(paramIdx) != rhsParamName)
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ enum BfResolveTypeRefFlags
|
|||
BfResolveTypeRefFlag_FromIndirectSource = 0x80, // Such as a type alias or a generic parameter
|
||||
BfResolveTypeRefFlag_Attribute = 0x100,
|
||||
BfResolveTypeRefFlag_NoReify = 0x200,
|
||||
BfResolveTypeRefFlag_NoCreate = 0x400
|
||||
BfResolveTypeRefFlag_NoCreate = 0x400,
|
||||
BfResolveTypeRefFlag_NoWarnOnMut = 0x800
|
||||
};
|
||||
|
||||
enum BfTypeNameFlags : uint16
|
||||
|
@ -413,12 +414,14 @@ class BfDelegateInfo
|
|||
public:
|
||||
Array<BfAstNode*> mDirectAllocNodes;
|
||||
BfType* mReturnType;
|
||||
Array<BfType*> mParams;
|
||||
BfType* mFunctionThisType;
|
||||
Array<BfType*> mParams;
|
||||
|
||||
public:
|
||||
BfDelegateInfo()
|
||||
{
|
||||
mReturnType = NULL;
|
||||
mFunctionThisType = NULL;
|
||||
}
|
||||
|
||||
~BfDelegateInfo()
|
||||
|
@ -860,6 +863,7 @@ public:
|
|||
bool IsSpecializedGenericMethodOrType();
|
||||
bool IsSpecializedByAutoCompleteMethod();
|
||||
bool HasThis();
|
||||
bool HasExplicitThis();
|
||||
bool HasParamsArray();
|
||||
int GetStructRetIdx();
|
||||
bool HasSelf();
|
||||
|
|
59
IDEHelper/Tests/src/Functions.bf
Normal file
59
IDEHelper/Tests/src/Functions.bf
Normal file
|
@ -0,0 +1,59 @@
|
|||
#pragma warning disable 168
|
||||
|
||||
using System;
|
||||
|
||||
namespace Tests
|
||||
{
|
||||
class Functions
|
||||
{
|
||||
class ClassA
|
||||
{
|
||||
int mA = 123;
|
||||
|
||||
public int GetA(float f)
|
||||
{
|
||||
return mA + (int)f;
|
||||
}
|
||||
|
||||
public int GetT<T>(T val) where T : var
|
||||
{
|
||||
return mA + (int)val;
|
||||
}
|
||||
}
|
||||
|
||||
struct StructA
|
||||
{
|
||||
int mA = 123;
|
||||
int mB = 234;
|
||||
|
||||
public int GetA(float f)
|
||||
{
|
||||
return mA + mB*100 + (int)f;
|
||||
}
|
||||
|
||||
public int GetA2(float f) mut
|
||||
{
|
||||
return mA + mB*100 + (int)f;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestBasics()
|
||||
{
|
||||
ClassA ca = scope .();
|
||||
StructA sa = .();
|
||||
|
||||
function int (ClassA this, float f) func0 = => ca.GetA;
|
||||
function int (ClassA this, float) func0b = func0;
|
||||
Test.Assert(func0(ca, 100.0f) == 223);
|
||||
func0 = => ca.GetT<float>;
|
||||
Test.Assert(func0(ca, 100.0f) == 223);
|
||||
|
||||
function int (StructA this, float f) func1 = => sa.GetA;
|
||||
Test.Assert(func1(sa, 100.0f) == 23623);
|
||||
|
||||
function int (mut StructA this, float f) func2 = => sa.GetA2;
|
||||
Test.Assert(func2(mut sa, 100.0f) == 23623);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue