1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00

Adding Obsolete/Error/Warn attributes, devirt fixes for struct ptr

This commit is contained in:
Brian Fiete 2019-10-09 16:11:49 -07:00
parent 1aded38e36
commit ad2542eba6
4 changed files with 261 additions and 22 deletions

View file

@ -319,7 +319,6 @@ namespace System
{
}
/// Generally used as a per-method optimization, [DisableObjectAccessChecks] will avoid the runtime per-object-access
/// checks which by default are only applied in debug builds anyway.
[AttributeUsage(AttributeTargets.Method/*, AlwaysIncludeTarget=true*/)]
@ -327,6 +326,38 @@ namespace System
{
}
[AttributeUsage(.Method | .Constructor)]
public struct ObsoleteAttribute : Attribute
{
public this(bool isError)
{
}
public this(String error, bool isError)
{
}
}
[AttributeUsage(.Method | .Constructor)]
public struct ErrorAttribute : Attribute
{
public this(String error)
{
}
}
[AttributeUsage(.Method | .Constructor)]
public struct WarnAttribute : Attribute
{
public this(String error)
{
}
}
/// If [NoDiscard] is used on a method, the the compiler will show a warning if the result is discarded.
/// If used on a type, the compiler will show an warning if any method returns that type and the caller discards the result.
[AttributeUsage(.Method | .Class | .Struct)]

View file

@ -332,6 +332,38 @@ namespace System
{
}
[AttributeUsage(.Method | .Constructor)]
public struct ObsoleteAttribute : Attribute
{
public this(bool isError)
{
}
public this(String error, bool isError)
{
}
}
[AttributeUsage(.Method | .Constructor)]
public struct ErrorAttribute : Attribute
{
public this(String error)
{
}
}
[AttributeUsage(.Method | .Constructor)]
public struct WarnAttribute : Attribute
{
public this(String error)
{
}
}
[AttributeUsage(.Method | .Class | .Struct)]
public struct NoDiscardAttribute : Attribute
{

View file

@ -1660,7 +1660,7 @@ bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue targe
return mBestMethodDef != NULL;
}
void BfMethodMatcher::TryDevirtualizeCall(BfTypedValue target)
void BfMethodMatcher::TryDevirtualizeCall(BfTypedValue target, BfTypedValue* origTarget, BfTypedValue* staticResult)
{
if ((mBestMethodDef == NULL) || (target.mType == NULL))
return;
@ -1684,14 +1684,25 @@ void BfMethodMatcher::TryDevirtualizeCall(BfTypedValue target)
if (checkType->IsWrappableType())
checkType = mModule->GetWrappedStructType(checkType);
if ((checkType != NULL) && (checkType->IsTypeInstance()) && (!checkType->IsInterface()))
{
{
BfTypeInterfaceEntry* bestIFaceEntry = NULL;
auto checkTypeInst = checkType->ToTypeInstance();
if (mBestMethodTypeInstance->mTypeDef == mModule->mCompiler->mIHashableTypeDef)
{
if ((origTarget != NULL) && (origTarget->mType->IsPointer()) && (staticResult != NULL))
{
BfTypedValue ptrVal = mModule->LoadValue(*origTarget);
*staticResult = BfTypedValue(mModule->mBfIRBuilder->CreatePtrToInt(ptrVal.mValue, BfTypeCode_IntPtr), mModule->GetPrimitiveType(BfTypeCode_IntPtr));
return;
}
}
while (checkTypeInst != NULL)
{
mModule->PopulateType(checkTypeInst, BfPopulateType_DataAndMethods);
for (auto&& iface : checkTypeInst->mInterfaces)
{
{
//TODO: Why did we have this check? This caused Dictionary to not be able to devirtualize
// calls to TKey GetHashCode when TKey was from a user's project...
/*if (!checkTypeInst->IsTypeMemberAccessible(iface.mDeclaringType, activeTypeDef))
@ -1723,6 +1734,13 @@ void BfMethodMatcher::TryDevirtualizeCall(BfTypedValue target)
if (bestIFaceEntry != NULL)
break;
checkTypeInst = checkTypeInst->mBaseType;
if ((checkTypeInst == NULL) && (checkType->HasWrappedRepresentation()))
{
auto underlyingType = checkType->GetUnderlyingType();
if ((underlyingType != NULL) && (underlyingType->IsWrappableType()))
checkTypeInst = mModule->GetWrappedStructType(underlyingType);
}
}
if (bestIFaceEntry != NULL)
@ -3689,6 +3707,82 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
autoComplete->mIgnoreFixits = hadIgnoredFixits;
}
void BfExprEvaluator::PerformCallChecks(BfMethodInstance* methodInstance, BfAstNode* targetSrc)
{
BfCustomAttributes* customAttributes = methodInstance->GetCustomAttributes();
if (customAttributes != NULL)
{
auto _AddMethodDeclarationMoreInfo = [&]()
{
if (methodInstance->mMethodDef->mMethodDeclaration != NULL)
mModule->mCompiler->mPassInstance->MoreInfo(
StrFormat("See method declaration '%s'", mModule->MethodToString(methodInstance).c_str()),
methodInstance->mMethodDef->GetRefNode());
};
BfIRConstHolder* constHolder = methodInstance->GetOwner()->mConstHolder;
auto customAttribute = customAttributes->Get(mModule->mCompiler->mObsoleteAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()))
{
String err;
err = StrFormat("'%s' is obsolete", mModule->MethodToString(methodInstance).c_str());
bool isError = false;
auto constant = constHolder->GetConstant(customAttribute->mCtorArgs[0]);
if (constant->mTypeCode == BfTypeCode_Boolean)
{
isError = constant->mBool;
}
else if (customAttribute->mCtorArgs.size() >= 2)
{
String* str = mModule->GetStringPoolString(customAttribute->mCtorArgs[0], constHolder);
if (str != NULL)
{
err += ":\n '";
err += *str;
err += "'";
}
constant = constHolder->GetConstant(customAttribute->mCtorArgs[1]);
isError = constant->mBool;
}
BfError* error = NULL;
if (isError)
error = mModule->Fail(err, targetSrc);
else
error = mModule->Warn(0, err, targetSrc);
if (error != NULL)
_AddMethodDeclarationMoreInfo();
}
customAttribute = customAttributes->Get(mModule->mCompiler->mErrorAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()))
{
String err = StrFormat("Method error: '", mModule->MethodToString(methodInstance).c_str());
String* str = mModule->GetStringPoolString(customAttribute->mCtorArgs[0], constHolder);
if (str != NULL)
err += *str;
err += "'";
if (mModule->Fail(err, targetSrc) != NULL)
_AddMethodDeclarationMoreInfo();
}
customAttribute = customAttributes->Get(mModule->mCompiler->mWarnAttributeTypeDef);
if ((customAttribute != NULL) && (!customAttribute->mCtorArgs.IsEmpty()))
{
String err = StrFormat("Method warning: '", mModule->MethodToString(methodInstance).c_str());
String* str = mModule->GetStringPoolString(customAttribute->mCtorArgs[0], constHolder);
if (str != NULL)
err += *str;
err += "'";
if (mModule->Warn(0, err, targetSrc) != NULL)
_AddMethodDeclarationMoreInfo();
}
}
}
BfTypedValue BfExprEvaluator::CreateCall(BfMethodInstance* methodInstance, BfIRValue func, bool bypassVirtual, SizedArrayImpl<BfIRValue>& irArgs, BfTypedValue* sret, bool isTailCall)
{
// static int sCallIdx = 0;
@ -5926,7 +6020,10 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
methodMatcher.CheckType(lookupTypeInst, target, true);
}
methodMatcher.TryDevirtualizeCall(target);
BfTypedValue staticResult;
methodMatcher.TryDevirtualizeCall(target, &origTarget, &staticResult);
if (staticResult)
return staticResult;
bypassVirtual |= methodMatcher.mBypassVirtual;
if (methodMatcher.mBestMethodDef != NULL)
@ -6660,6 +6757,8 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
}
}
PerformCallChecks(moduleMethodInstance.mMethodInstance, targetSrc);
if (result)
{
bool discardedReturnValue = mUsedAsStatement;
@ -6672,16 +6771,12 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
auto customAttribute = customAttributes->Get(mModule->mCompiler->mNoDiscardAttributeTypeDef);
if (!customAttribute->mCtorArgs.IsEmpty())
{
int strId = mModule->GetStringPoolIdx(customAttribute->mCtorArgs[0], constHolder);
if (strId != -1)
String* str = mModule->GetStringPoolString(customAttribute->mCtorArgs[0], constHolder);
if ((str != NULL) && (!str->IsEmpty()))
{
auto& entry = mModule->mContext->mStringObjectIdMap[strId];
if (!entry.mString.IsEmpty())
{
mModule->Warn(0, text + ": " + entry.mString, targetSrc);
return;
}
}
mModule->Warn(0, text + ": " + *str, targetSrc);
return;
}
}
}
@ -7505,7 +7600,7 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie
mModule->AddDependency(type, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference);
mModule->PopulateType(type);
auto typeInstance = type->ToTypeInstance();
auto typeInstance = type->ToTypeInstance();
auto _BoolResult = [&](bool val)
{
@ -7562,6 +7657,70 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie
_Int32Result((typeInstance != NULL) ? typeInstance->mInstAlign : type->mSize);
else if (memberName == "InstanceStride")
_Int32Result((typeInstance != NULL) ? typeInstance->GetInstStride() : type->GetStride());
else if ((memberName == "MinValue") || (memberName == "MaxValue"))
{
bool isMin = memberName == "MinValue";
BfType* checkType = typeInstance;
if (checkType->IsTypedPrimitive())
checkType = checkType->GetUnderlyingType();
if (checkType->IsPrimitiveType())
{
auto primType = (BfPrimitiveType*)checkType;
if (typeInstance->IsEnum())
{
if (typeInstance->mTypeInfoEx != NULL)
{
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)typeInstance->mTypeInfoEx->mMinValue : (uint64)typeInstance->mTypeInfoEx->mMaxValue), typeInstance);
return true;
}
}
else
{
switch (primType->mTypeDef->mTypeCode)
{
case BfTypeCode_Int8:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x80 : 0x7F), typeInstance);
return true;
case BfTypeCode_Int16:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x8000 : 0x7FFF), typeInstance);
return true;
case BfTypeCode_Int32:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), typeInstance);
return true;
case BfTypeCode_Int64:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), typeInstance);
return true;
case BfTypeCode_UInt8:
case BfTypeCode_Char8:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFF), typeInstance);
return true;
case BfTypeCode_UInt16:
case BfTypeCode_Char16:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFFFF), typeInstance);
return true;
case BfTypeCode_UInt32:
case BfTypeCode_Char32:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), typeInstance);
return true;
case BfTypeCode_UInt64:
mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), typeInstance);
return true;
}
}
}
if (typeInstance->IsEnum())
{
mModule->Fail("'MinValue' cannot be used on enums with payloads", propName);
}
else
{
mModule->Fail(StrFormat("'%s' cannot be used on type '%s'", memberName.c_str(), mModule->TypeToString(typeInstance).c_str()), propName);
}
}
else
return false;
@ -11287,10 +11446,24 @@ BfTypedValue BfExprEvaluator::MakeCallableTarget(BfAstNode* targetSrc, BfTypedVa
}
if (target.mType->IsWrappableType())
{
{
auto primStructType = mModule->GetWrappedStructType(target.mType);
if (primStructType != NULL)
{
mModule->PopulateType(primStructType);
target.mType = primStructType;
if ((primStructType->IsSplattable()) && (!primStructType->IsTypedPrimitive()))
{
if (target.IsAddr())
{
auto ptrType = mModule->CreatePointerType(primStructType);
target = BfTypedValue(mModule->mBfIRBuilder->CreateBitCast(target.mValue, mModule->mBfIRBuilder->MapType(ptrType)), primStructType, true);
}
else
target.mKind = BfTypedValueKind_SplatHead;
}
}
return target;
}
@ -12981,7 +13154,7 @@ void BfExprEvaluator::Visit(BfInvocationExpression* invocationExpr)
checkTarget = indexerExpr->mTarget;
if (indexerExpr->mCommas.size() != 0)
mModule->Fail("Only one value expected. Consider adding an allocation specifier such as 'new' if construction of a dynamic multidimentional was intended.", indexerExpr->mCommas[0]);
mModule->Fail("Only one value expected. Consider adding an allocation specifier such as 'new' if construction of a dynamic multidimensional was intended.", indexerExpr->mCommas[0]);
int arrSize = 0;
@ -13161,10 +13334,11 @@ BfModuleMethodInstance BfExprEvaluator::GetPropertyMethodInstance(BfMethodDef* m
if (!checkedUnderlying)
{
checkedUnderlying = true;
if (checkType->IsTypedPrimitive())
if (checkType->HasWrappedRepresentation())
{
auto underlyingType = checkType->GetUnderlyingType();
checkTypeInst = mModule->GetWrappedStructType(underlyingType);
if (underlyingType != NULL)
checkTypeInst = mModule->GetWrappedStructType(underlyingType);
}
}
}
@ -13251,12 +13425,13 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp
if (!mModule->mBfIRBuilder->mIgnoreWrites)
{
BF_ASSERT(!methodInstance.mFunc.IsFake());
}
}
if (mPropSrc != NULL)
mModule->UpdateExprSrcPos(mPropSrc);
CheckPropFail(matchedMethod, methodInstance.mMethodInstance);
PerformCallChecks(methodInstance.mMethodInstance, mPropSrc);
if (methodInstance.mMethodInstance->IsSkipCall())
{

View file

@ -167,7 +167,7 @@ public:
void CheckOuterTypeStaticMethods(BfTypeInstance* typeInstance, bool isFailurePass);
bool WantsCheckMethod(BfProtectionCheckFlags& flags, BfTypeInstance* startTypeInstance, BfTypeInstance* checkTypeInstance, BfMethodDef* methodDef);
bool CheckMethod(BfTypeInstance* typeInstance, BfMethodDef* checkMethod, bool isFailurePass);
void TryDevirtualizeCall(BfTypedValue target);
void TryDevirtualizeCall(BfTypedValue target, BfTypedValue* origTarget = NULL, BfTypedValue* staticResult = NULL);
};
@ -345,6 +345,7 @@ public:
BfTypedValue LookupIdentifier(BfAstNode* identifierNode, const StringImpl& findName, bool ignoreInitialError = false, bool* hadError = NULL);
BfTypedValue LookupIdentifier(BfIdentifierNode* identifierNode, bool ignoreInitialError = false, bool* hadError = NULL);
void AddCallDependencies(BfMethodInstance* methodInstance);
void PerformCallChecks(BfMethodInstance* methodInstance, BfAstNode* targetSrc);
BfTypedValue CreateCall(BfMethodInstance* methodInstance, BfIRValue func, bool bypassVirtual, SizedArrayImpl<BfIRValue>& irArgs, BfTypedValue* sret = NULL, bool isTailCall = false);
BfTypedValue CreateCall(BfAstNode* targetSrc, const BfTypedValue& target, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance methodInstance, bool bypassVirtual, SizedArrayImpl<BfResolvedArg>& argValues, bool skipThis = false);
BfTypedValue CreateCall(BfMethodMatcher* methodMatcher, BfTypedValue target);