mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
Warn on 'this Foo*', make [CRepr] always pass 'this' as address
This commit is contained in:
parent
176947189b
commit
445fc0e982
5 changed files with 93 additions and 11 deletions
|
@ -7698,7 +7698,7 @@ void BfExprEvaluator::PushThis(BfAstNode* targetSrc, BfTypedValue argVal, BfMeth
|
||||||
else
|
else
|
||||||
allowThisSplatting = methodInstance->AllowsSplatting(-1);
|
allowThisSplatting = methodInstance->AllowsSplatting(-1);
|
||||||
|
|
||||||
if ((!allowThisSplatting) || (methodDef->mIsMutating) || (methodInstance->mCallingConvention == BfCallingConvention_Cdecl))
|
if ((!allowThisSplatting) || (methodDef->mIsMutating) || (methodInstance->ForcingThisPtr()))
|
||||||
{
|
{
|
||||||
argVal = mModule->MakeAddressable(argVal);
|
argVal = mModule->MakeAddressable(argVal);
|
||||||
irArgs.push_back(argVal.mValue);
|
irArgs.push_back(argVal.mValue);
|
||||||
|
|
|
@ -16003,7 +16003,7 @@ BfTypedValue BfModule::GetThis(bool markUsing)
|
||||||
auto curMethodOwner = mCurMethodInstance->mMethodInstanceGroup->mOwner;
|
auto curMethodOwner = mCurMethodInstance->mMethodInstanceGroup->mOwner;
|
||||||
if ((curMethodOwner->IsStruct()) || (curMethodOwner->IsTypedPrimitive()))
|
if ((curMethodOwner->IsStruct()) || (curMethodOwner->IsTypedPrimitive()))
|
||||||
{
|
{
|
||||||
if ((localDef->mResolvedType->IsTypedPrimitive()) && (!mCurMethodInstance->mMethodDef->mIsMutating) && (mCurMethodInstance->mCallingConvention != BfCallingConvention_Cdecl))
|
if ((localDef->mResolvedType->IsTypedPrimitive()) && (!mCurMethodInstance->mMethodDef->mIsMutating) && (!mCurMethodInstance->ForcingThisPtr()))
|
||||||
{
|
{
|
||||||
return BfTypedValue(thisValue, useMethodState->mLocals[0]->mResolvedType, BfTypedValueKind_ReadOnlyThisValue);
|
return BfTypedValue(thisValue, useMethodState->mLocals[0]->mResolvedType, BfTypedValueKind_ReadOnlyThisValue);
|
||||||
}
|
}
|
||||||
|
@ -19981,7 +19981,7 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp
|
||||||
if (!thisType->IsTypedPrimitive())
|
if (!thisType->IsTypedPrimitive())
|
||||||
paramVar->mIsSplat = true;
|
paramVar->mIsSplat = true;
|
||||||
}
|
}
|
||||||
else if ((!mIsComptimeModule) && (!methodDef->mIsMutating) && (methodInstance->mCallingConvention == BfCallingConvention_Unspecified))
|
else if ((!mIsComptimeModule) && (!methodDef->mIsMutating) && (!methodInstance->ForcingThisPtr()))
|
||||||
paramVar->mIsLowered = thisType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2) != BfTypeCode_None;
|
paramVar->mIsLowered = thisType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2) != BfTypeCode_None;
|
||||||
|
|
||||||
auto thisTypeInst = thisType->ToTypeInstance();
|
auto thisTypeInst = thisType->ToTypeInstance();
|
||||||
|
@ -21754,7 +21754,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
|
||||||
{
|
{
|
||||||
bool wantPtr = (thisType->IsComposite()) && (!paramVar->mIsLowered);
|
bool wantPtr = (thisType->IsComposite()) && (!paramVar->mIsLowered);
|
||||||
if ((thisType->IsTypedPrimitive()) &&
|
if ((thisType->IsTypedPrimitive()) &&
|
||||||
((methodDef->HasNoThisSplat()) || (methodInstance->mCallingConvention == BfCallingConvention_Cdecl)))
|
((methodDef->HasNoThisSplat()) || (methodInstance->ForcingThisPtr())))
|
||||||
wantPtr = true;
|
wantPtr = true;
|
||||||
|
|
||||||
if (wantPtr)
|
if (wantPtr)
|
||||||
|
@ -21885,7 +21885,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
|
||||||
diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType);
|
diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType);
|
||||||
bool wantRef = paramVar->mResolvedType->IsComposite();
|
bool wantRef = paramVar->mResolvedType->IsComposite();
|
||||||
if ((paramVar->mResolvedType->IsTypedPrimitive()) &&
|
if ((paramVar->mResolvedType->IsTypedPrimitive()) &&
|
||||||
((methodDef->HasNoThisSplat()) || (methodInstance->mCallingConvention == BfCallingConvention_Cdecl)))
|
((methodDef->HasNoThisSplat()) || (methodInstance->ForcingThisPtr())))
|
||||||
wantRef = true;
|
wantRef = true;
|
||||||
|
|
||||||
if (wantRef)
|
if (wantRef)
|
||||||
|
@ -22199,7 +22199,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
|
||||||
}
|
}
|
||||||
else if ((isThis) && (paramVar->mResolvedType->IsOpaque()))
|
else if ((isThis) && (paramVar->mResolvedType->IsOpaque()))
|
||||||
{
|
{
|
||||||
if ((methodDef->mIsMutating) || (methodInstance->mCallingConvention == BfCallingConvention_Cdecl))
|
if ((methodDef->mIsMutating) || (methodInstance->ForcingThisPtr()))
|
||||||
argIdx++;
|
argIdx++;
|
||||||
}
|
}
|
||||||
else if (!paramVar->mResolvedType->IsValuelessNonOpaqueType())
|
else if (!paramVar->mResolvedType->IsValuelessNonOpaqueType())
|
||||||
|
|
|
@ -5963,6 +5963,11 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
|
||||||
// Align size to alignment
|
// Align size to alignment
|
||||||
if (alignSize >= 1)
|
if (alignSize >= 1)
|
||||||
typeInstance->mInstSize = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
typeInstance->mInstSize = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
||||||
|
if (typeInstance->mInstSize == 0)
|
||||||
|
{
|
||||||
|
// CRepr doesn't allow valueless types
|
||||||
|
typeInstance->mInstSize = 1;
|
||||||
|
}
|
||||||
typeInstance->mIsCRepr = true;
|
typeInstance->mIsCRepr = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -12649,6 +12654,13 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(param->mTypeRef))
|
||||||
|
{
|
||||||
|
// This catches `ref Foo*` cases (which generate warnings)
|
||||||
|
if ((refTypeRef->mRefToken != NULL) && (refTypeRef->mRefToken->mToken == BfToken_Mut))
|
||||||
|
hasMutSpecifier = true;
|
||||||
|
}
|
||||||
|
|
||||||
auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, resolveTypeFlags);
|
auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, resolveTypeFlags);
|
||||||
if (paramType == NULL)
|
if (paramType == NULL)
|
||||||
{
|
{
|
||||||
|
@ -12673,6 +12685,13 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po
|
||||||
hasMutSpecifier = true;
|
hasMutSpecifier = true;
|
||||||
functionThisType = refType->mElementType;
|
functionThisType = refType->mElementType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((functionThisType != NULL) && (functionThisType->IsPointer()))
|
||||||
|
{
|
||||||
|
// We should have already warned against pointer types during hashing
|
||||||
|
functionThisType = functionThisType->GetUnderlyingType();
|
||||||
|
}
|
||||||
|
|
||||||
paramTypes.Add(functionThisType);
|
paramTypes.Add(functionThisType);
|
||||||
_CheckType(functionThisType);
|
_CheckType(functionThisType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -900,6 +900,20 @@ BfModule * BfMethodInstance::GetModule()
|
||||||
return mMethodInstanceGroup->mOwner->mModule;
|
return mMethodInstanceGroup->mOwner->mModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BfMethodInstance::ForcingThisPtr()
|
||||||
|
{
|
||||||
|
if (mMethodDef->mHasExplicitThis)
|
||||||
|
{
|
||||||
|
auto thisType = mParams[0].mResolvedType;
|
||||||
|
if (thisType->IsCRepr())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (mMethodInstanceGroup->mOwner->IsCRepr())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return (mCallingConvention == BfCallingConvention_Cdecl);
|
||||||
|
}
|
||||||
|
|
||||||
bool Beefy::BfMethodInstance::IsSpecializedGenericMethod()
|
bool Beefy::BfMethodInstance::IsSpecializedGenericMethod()
|
||||||
{
|
{
|
||||||
return (mMethodInfoEx != NULL) && (mMethodInfoEx->mGenericParams.size() != 0) && (!mIsUnspecialized);
|
return (mMethodInfoEx != NULL) && (mMethodInfoEx->mGenericParams.size() != 0) && (!mIsUnspecialized);
|
||||||
|
@ -1071,6 +1085,8 @@ bool BfMethodInstance::AllowsSplatting(int paramIdx)
|
||||||
{
|
{
|
||||||
if (mCallingConvention != BfCallingConvention_Unspecified)
|
if (mCallingConvention != BfCallingConvention_Unspecified)
|
||||||
return false;
|
return false;
|
||||||
|
if (ForcingThisPtr())
|
||||||
|
return false;
|
||||||
if (mMethodDef->mIsNoSplat)
|
if (mMethodDef->mIsNoSplat)
|
||||||
return false;
|
return false;
|
||||||
return !mMethodDef->HasNoThisSplat();
|
return !mMethodDef->HasNoThisSplat();
|
||||||
|
@ -1474,7 +1490,7 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType,
|
||||||
bool doSplat = false;
|
bool doSplat = false;
|
||||||
if (paramIdx == -1)
|
if (paramIdx == -1)
|
||||||
{
|
{
|
||||||
if (mCallingConvention == BfCallingConvention_Cdecl)
|
if (ForcingThisPtr())
|
||||||
{
|
{
|
||||||
// Pass by pointer even for typed primitives
|
// Pass by pointer even for typed primitives
|
||||||
}
|
}
|
||||||
|
@ -1840,6 +1856,11 @@ int BfTypeInstance::GetSplatCount(bool force)
|
||||||
return splatCount;
|
return splatCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BfTypeInstance::IsCRepr()
|
||||||
|
{
|
||||||
|
return mIsCRepr;
|
||||||
|
}
|
||||||
|
|
||||||
bool BfTypeInstance::IsString()
|
bool BfTypeInstance::IsString()
|
||||||
{
|
{
|
||||||
return IsInstanceOf(mContext->mCompiler->mStringTypeDef);
|
return IsInstanceOf(mContext->mCompiler->mStringTypeDef);
|
||||||
|
@ -3609,9 +3630,20 @@ int BfResolvedTypeSet::DirectHash(BfTypeReference* typeRef, LookupContext* ctx,
|
||||||
ctx->mFailed = true;
|
ctx->mFailed = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (((flags & BfHashFlag_DisallowPointer) != 0) && (resolvedType->IsPointer()))
|
||||||
|
{
|
||||||
|
ShowThisPointerWarning(ctx, typeRef);
|
||||||
|
resolvedType = resolvedType->GetUnderlyingType();
|
||||||
|
}
|
||||||
|
|
||||||
return Hash(resolvedType, ctx, BfHashFlag_None, hashSeed);
|
return Hash(resolvedType, ctx, BfHashFlag_None, hashSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BfResolvedTypeSet::ShowThisPointerWarning(LookupContext* ctx, BfTypeReference* typeRef)
|
||||||
|
{
|
||||||
|
ctx->mModule->Warn(0, "Pointer types cannot be used as 'this'. If 'this' address is required, use 'mut' or [CRepr]", typeRef);
|
||||||
|
}
|
||||||
|
|
||||||
BfTypeDef* BfResolvedTypeSet::FindRootCommonOuterType(BfTypeDef* outerType, LookupContext* ctx, BfTypeInstance*& outOuterTypeInstance)
|
BfTypeDef* BfResolvedTypeSet::FindRootCommonOuterType(BfTypeDef* outerType, LookupContext* ctx, BfTypeInstance*& outOuterTypeInstance)
|
||||||
{
|
{
|
||||||
if (ctx->mModule->mCurTypeInstance == NULL)
|
if (ctx->mModule->mCurTypeInstance == NULL)
|
||||||
|
@ -4142,12 +4174,22 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa
|
||||||
// Parse attributes?
|
// Parse attributes?
|
||||||
BfTypeReference* fieldType = param->mTypeRef;
|
BfTypeReference* fieldType = param->mTypeRef;
|
||||||
|
|
||||||
|
auto hashFlags = (BfHashFlags)(BfHashFlag_AllowRef);
|
||||||
|
|
||||||
if (isFirstParam)
|
if (isFirstParam)
|
||||||
{
|
{
|
||||||
if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
|
if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this")))
|
||||||
{
|
{
|
||||||
|
hashFlags = (BfHashFlags)(hashFlags | BfHashFlag_DisallowPointer);
|
||||||
|
|
||||||
if (auto refNode = BfNodeDynCast<BfRefTypeRef>(fieldType))
|
if (auto refNode = BfNodeDynCast<BfRefTypeRef>(fieldType))
|
||||||
fieldType = refNode->mElementType;
|
fieldType = refNode->mElementType;
|
||||||
|
|
||||||
|
if (auto pointerType = BfNodeDynCast<BfPointerTypeRef>(fieldType))
|
||||||
|
{
|
||||||
|
ShowThisPointerWarning(ctx, pointerType);
|
||||||
|
fieldType = pointerType->mElementType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4161,10 +4203,10 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldType != NULL)
|
if (fieldType != NULL)
|
||||||
hashVal = HASH_MIX(hashVal, Hash(fieldType, ctx, (BfHashFlags)(BfHashFlag_AllowRef), hashSeed + 1));
|
hashVal = HASH_MIX(hashVal, Hash(fieldType, ctx, hashFlags, hashSeed + 1));
|
||||||
hashVal = HASH_MIX(hashVal, HashNode(param->mNameNode));
|
hashVal = HASH_MIX(hashVal, HashNode(param->mNameNode));
|
||||||
isFirstParam = true;
|
isFirstParam = true;
|
||||||
}
|
}
|
||||||
|
@ -4981,17 +5023,33 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext*
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
auto lhsThisType = lhsDelegateInfo->mParams[0];
|
auto lhsThisType = lhsDelegateInfo->mParams[0];
|
||||||
|
|
||||||
auto rhsThisType = ctx->mModule->ResolveTypeRef(param0->mTypeRef, BfPopulateType_Identity, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoWarnOnMut | BfResolveTypeRefFlag_AllowRef));
|
|
||||||
bool wantsMutating = false;
|
bool wantsMutating = false;
|
||||||
|
if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(param0->mTypeRef))
|
||||||
|
{
|
||||||
|
// This catches `ref Foo*` cases (which generate warnings)
|
||||||
|
if ((refTypeRef->mRefToken != NULL) && (refTypeRef->mRefToken->mToken == BfToken_Mut))
|
||||||
|
wantsMutating = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rhsThisType = ctx->mModule->ResolveTypeRef(param0->mTypeRef, BfPopulateType_Identity, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoWarnOnMut | BfResolveTypeRefFlag_AllowRef));
|
||||||
|
|
||||||
|
if (rhsThisType == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (rhsThisType->IsRef())
|
if (rhsThisType->IsRef())
|
||||||
{
|
{
|
||||||
if (lhsThisType != rhsThisType->GetUnderlyingType())
|
rhsThisType = rhsThisType->GetUnderlyingType();
|
||||||
|
if (rhsThisType->IsPointer())
|
||||||
|
rhsThisType = rhsThisType->GetUnderlyingType();
|
||||||
|
|
||||||
|
if (lhsThisType != rhsThisType)
|
||||||
return false;
|
return false;
|
||||||
wantsMutating = (lhsThisType->IsValueType()) || (lhsThisType->IsGenericParam());
|
wantsMutating = (lhsThisType->IsValueType()) || (lhsThisType->IsGenericParam());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (rhsThisType->IsPointer())
|
||||||
|
rhsThisType = rhsThisType->GetUnderlyingType();
|
||||||
if (lhsThisType != rhsThisType)
|
if (lhsThisType != rhsThisType)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -559,6 +559,7 @@ public:
|
||||||
virtual bool IsUnspecializedTypeVariation() { return false; }
|
virtual bool IsUnspecializedTypeVariation() { return false; }
|
||||||
virtual bool IsSplattable() { return false; }
|
virtual bool IsSplattable() { return false; }
|
||||||
virtual int GetSplatCount(bool force = false) { return 1; }
|
virtual int GetSplatCount(bool force = false) { return 1; }
|
||||||
|
virtual bool IsCRepr() { return false; }
|
||||||
virtual bool IsVoid() { return false; }
|
virtual bool IsVoid() { return false; }
|
||||||
virtual bool IsVoidPtr() { return false; }
|
virtual bool IsVoidPtr() { return false; }
|
||||||
virtual bool CanBeValuelessType() { return false; }
|
virtual bool CanBeValuelessType() { return false; }
|
||||||
|
@ -987,6 +988,7 @@ public:
|
||||||
void UndoDeclaration(bool keepIRFunction = false);
|
void UndoDeclaration(bool keepIRFunction = false);
|
||||||
BfTypeInstance* GetOwner();
|
BfTypeInstance* GetOwner();
|
||||||
BfModule* GetModule();
|
BfModule* GetModule();
|
||||||
|
bool ForcingThisPtr();
|
||||||
bool IsSpecializedGenericMethod();
|
bool IsSpecializedGenericMethod();
|
||||||
bool IsSpecializedGenericMethodOrType();
|
bool IsSpecializedGenericMethodOrType();
|
||||||
bool IsSpecializedByAutoCompleteMethod();
|
bool IsSpecializedByAutoCompleteMethod();
|
||||||
|
@ -2148,6 +2150,7 @@ public:
|
||||||
virtual bool IsIncomplete() override { return (mTypeIncomplete) || (mBaseTypeMayBeIncomplete); }
|
virtual bool IsIncomplete() override { return (mTypeIncomplete) || (mBaseTypeMayBeIncomplete); }
|
||||||
virtual bool IsSplattable() override { BF_ASSERT((mInstSize >= 0) || (!IsComposite())); return mIsSplattable; }
|
virtual bool IsSplattable() override { BF_ASSERT((mInstSize >= 0) || (!IsComposite())); return mIsSplattable; }
|
||||||
virtual int GetSplatCount(bool force = false) override;
|
virtual int GetSplatCount(bool force = false) override;
|
||||||
|
virtual bool IsCRepr() override;
|
||||||
virtual bool IsTypeInstance() override { return true; }
|
virtual bool IsTypeInstance() override { return true; }
|
||||||
virtual BfTypeCode GetTypeCode() override { return mTypeDef->mTypeCode; }
|
virtual BfTypeCode GetTypeCode() override { return mTypeDef->mTypeCode; }
|
||||||
virtual bool IsInterface() override { return mTypeDef->mTypeCode == BfTypeCode_Interface; }
|
virtual bool IsInterface() override { return mTypeDef->mTypeCode == BfTypeCode_Interface; }
|
||||||
|
@ -2719,6 +2722,7 @@ public:
|
||||||
BfHashFlag_AllowRef = 1,
|
BfHashFlag_AllowRef = 1,
|
||||||
BfHashFlag_AllowGenericParamConstValue = 2,
|
BfHashFlag_AllowGenericParamConstValue = 2,
|
||||||
BfHashFlag_AllowDotDotDot = 4,
|
BfHashFlag_AllowDotDotDot = 4,
|
||||||
|
BfHashFlag_DisallowPointer = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BfExprResult
|
struct BfExprResult
|
||||||
|
@ -2817,6 +2821,7 @@ public:
|
||||||
static int DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHashFlags flags, int& hashSeed);
|
static int DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHashFlags flags, int& hashSeed);
|
||||||
static int Hash(BfTypeReference* typeRef, LookupContext* ctx, BfHashFlags flags = BfHashFlag_None, int hashSeed = 0);
|
static int Hash(BfTypeReference* typeRef, LookupContext* ctx, BfHashFlags flags = BfHashFlag_None, int hashSeed = 0);
|
||||||
static int Hash(BfAstNode* typeRefNode, LookupContext* ctx, BfHashFlags flags = BfHashFlag_None, int hashSeed = 0);
|
static int Hash(BfAstNode* typeRefNode, LookupContext* ctx, BfHashFlags flags = BfHashFlag_None, int hashSeed = 0);
|
||||||
|
static void ShowThisPointerWarning(LookupContext* ctx, BfTypeReference* typeRef);
|
||||||
|
|
||||||
static bool Equals(BfType* lhs, BfType* rhs, LookupContext* ctx);
|
static bool Equals(BfType* lhs, BfType* rhs, LookupContext* ctx);
|
||||||
static bool Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* ctx);
|
static bool Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* ctx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue