瀏覽代碼

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

Brian Fiete 6 年之前
父節點
當前提交
ad2542eba6

+ 32 - 1
BeefLibs/corlib/src/Attribute.bf

@@ -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)]

+ 32 - 0
IDE/mintest/minlib/src/System/Attribute.bf

@@ -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
 	{

+ 195 - 20
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -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())
 		{

+ 2 - 1
IDEHelper/Compiler/BfExprEvaluator.h

@@ -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);