diff --git a/BeefLibs/corlib/src/Attribute.bf b/BeefLibs/corlib/src/Attribute.bf index 51e50def..782fe5db 100644 --- a/BeefLibs/corlib/src/Attribute.bf +++ b/BeefLibs/corlib/src/Attribute.bf @@ -230,6 +230,12 @@ namespace System } + [AttributeUsage(.Method /*2*/)] + public struct NoSplatAttribute : Attribute + { + + } + [AttributeUsage(.Method /*2*/)] public struct SkipCallAttribute : Attribute { diff --git a/IDEHelper/Compiler/BfDefBuilder.cpp b/IDEHelper/Compiler/BfDefBuilder.cpp index 44d1e660..ce3ca80c 100644 --- a/IDEHelper/Compiler/BfDefBuilder.cpp +++ b/IDEHelper/Compiler/BfDefBuilder.cpp @@ -795,13 +795,15 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef } } else if (typeRefName == "NoReturn") - methodDef->mNoReturn = true; + methodDef->mIsNoReturn = true; else if (typeRefName == "SkipCall") methodDef->mIsSkipCall = true; else if (typeRefName == "NoShow") methodDef->mIsNoShow = true; else if (typeRefName == "NoDiscard") methodDef->mIsNoDiscard = true; + else if (typeRefName == "NoSplat") + methodDef->mIsNoSplat = true; else if (typeRefName == "Commutable") { if (methodDef->mParams.size() != 2) @@ -1122,12 +1124,12 @@ BfMethodDef* BfDefBuilder::AddMethod(BfTypeDef* typeDef, BfMethodType methodType else if (methodType == BfMethodType_CtorNoBody) { methodDef->mName = "__BfCtorNoBody"; - methodDef->mNoReflect = true; + methodDef->mIsNoReflect = true; } else if (methodType == BfMethodType_CtorClear) { methodDef->mName = "__BfCtorClear"; - methodDef->mNoReflect = true; + methodDef->mIsNoReflect = true; } else if (methodType == BfMethodType_Dtor) { @@ -1191,7 +1193,7 @@ void BfDefBuilder::AddDynamicCastMethods(BfTypeDef* typeDef) paramDef->mTypeRef = typeDef->mSystem->mDirectInt32TypeRef; methodDef->mParams.push_back(paramDef); methodDef->mReturnTypeRef = typeDef->mSystem->mDirectObjectTypeRef; - methodDef->mNoReflect = true; + methodDef->mIsNoReflect = true; } // @@ -1212,7 +1214,7 @@ void BfDefBuilder::AddDynamicCastMethods(BfTypeDef* typeDef) paramDef->mTypeRef = typeDef->mSystem->mDirectInt32TypeRef; methodDef->mParams.push_back(paramDef); methodDef->mReturnTypeRef = typeDef->mSystem->mDirectObjectTypeRef; - methodDef->mNoReflect = true; + methodDef->mIsNoReflect = true; } } @@ -2048,13 +2050,13 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if ((hasStaticField) && (!hasStaticMarkMethod)) { auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Normal, BfProtection_Protected, true, BF_METHODNAME_MARKMEMBERS_STATIC); - methodDef->mNoReflect = true; + methodDef->mIsNoReflect = true; } if (hasThreadStatics) { auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Normal, BfProtection_Protected, true, BF_METHODNAME_FIND_TLS_MEMBERS); - methodDef->mNoReflect = true; + methodDef->mIsNoReflect = true; } if ((hasNonStaticField) && (!hasMarkMethod)) @@ -2062,7 +2064,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Normal, BfProtection_Protected, false, BF_METHODNAME_MARKMEMBERS); methodDef->mIsVirtual = true; methodDef->mIsOverride = true; - methodDef->mNoReflect = true; + methodDef->mIsNoReflect = true; methodDef->mCallingConvention = BfCallingConvention_Cdecl; mCurTypeDef->mHasOverrideMethods = true; } diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 69ef0032..98f99684 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -4638,7 +4638,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfMethodInstance* methodInstance, BfIRV mModule->mCurMethodState->mCancelledDeferredCall = true; } - if (methodDef->mNoReturn) + if (methodDef->mIsNoReturn) { mModule->mCurMethodState->SetHadReturn(true); mModule->mCurMethodState->mLeftBlockUncond = true; @@ -4984,7 +4984,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfMethodInstance* methodInstance, BfIRV if ((expectCallingConvention != BfIRCallingConv_CDecl) && (!methodInstance->mIsIntrinsic)) mModule->mBfIRBuilder->SetCallCallingConv(callInst, expectCallingConvention); - if ((methodDef->mNoReturn) && (!methodInstance->mIsIntrinsic)) + if ((methodDef->mIsNoReturn) && (!methodInstance->mIsIntrinsic)) mModule->mBfIRBuilder->Call_AddAttribute(callInst, -1, BfIRAttribute_NoReturn); bool hadAttrs = false; @@ -5146,7 +5146,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfMethodInstance* methodInstance, BfIRV if (isTailCall) mModule->mBfIRBuilder->SetTailCall(callInst); - if (methodDef->mNoReturn) + if (methodDef->mIsNoReturn) { mModule->mBfIRBuilder->CreateUnreachable(); // For debuggability when looking back at stack trace @@ -15737,7 +15737,7 @@ bool BfExprEvaluator::CheckIsBase(BfAstNode* checkNode) return true; } -bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNode, const char* modifyType, bool onlyNeedsMut) +bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNode, const char* modifyType, bool onlyNeedsMut, bool emitWarning) { BfLocalVariable* localVar = NULL; bool isCapturedLocal = false; @@ -15774,10 +15774,18 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod bool canModify = typedVal.CanModify(); + auto _Fail = [&](const StringImpl& error, BfAstNode* refNode) + { + if (emitWarning) + return mModule->Warn(BfWarning_BF4204_AddressOfReadOnly, error, refNode); + else + return mModule->Fail(error, refNode); + }; + if (localVar != NULL) { if (!canModify) - { + { BfError* error = NULL; if (localVar->mIsThis) { @@ -15807,18 +15815,18 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod if (isClosure) { if (!mModule->mCurMethodState->mClosureState->mDeclaringMethodIsMutating) - error = mModule->Fail(StrFormat("Cannot %s 'this' within struct lambda. Consider adding 'mut' specifier to this method.", modifyType), refNode); + error = _Fail(StrFormat("Cannot %s 'this' within struct lambda. Consider adding 'mut' specifier to this method.", modifyType), refNode); else - error = mModule->Fail(StrFormat("Cannot %s 'this' within struct lambda. Consider adding by-reference capture specifier [&] to lambda.", modifyType), refNode); + error = _Fail(StrFormat("Cannot %s 'this' within struct lambda. Consider adding by-reference capture specifier [&] to lambda.", modifyType), refNode); } else if (localVar->mResolvedType->IsValueType()) { - error = mModule->Fail(StrFormat("Cannot %s 'this' within struct method '%s'. Consider adding 'mut' specifier to this method.", modifyType, + error = _Fail(StrFormat("Cannot %s 'this' within struct method '%s'. Consider adding 'mut' specifier to this method.", modifyType, mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); } else { - error = mModule->Fail(StrFormat("Cannot %s 'this' because '%s' is a reference type.", modifyType, + error = _Fail(StrFormat("Cannot %s 'this' because '%s' is a reference type.", modifyType, mModule->TypeToString(localVar->mResolvedType).c_str()), refNode); } return false; @@ -15827,21 +15835,21 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod { if (isCapturedLocal) { - error = mModule->Fail(StrFormat("Cannot %s read-only captured local variable '%s'. Consider adding by-reference capture specifier [&] to lambda and ensuring that captured value is not read-only.", modifyType, + error = _Fail(StrFormat("Cannot %s read-only captured local variable '%s'. Consider adding by-reference capture specifier [&] to lambda and ensuring that captured value is not read-only.", modifyType, mResultFieldInstance->GetFieldDef()->mName.c_str()), refNode); } else if (isClosure) { if (!mModule->mCurMethodState->mClosureState->mDeclaringMethodIsMutating) - error = mModule->Fail(StrFormat("Cannot %s field '%s.%s' within struct lambda. Consider adding 'mut' specifier to this method.", modifyType, + error = _Fail(StrFormat("Cannot %s field '%s.%s' within struct lambda. Consider adding 'mut' specifier to this method.", modifyType, mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str()), refNode); else - error = mModule->Fail(StrFormat("Cannot %s field '%s.%s' within struct lambda. Consider adding by-reference capture specifier [&] to lambda.", modifyType, + error = _Fail(StrFormat("Cannot %s field '%s.%s' within struct lambda. Consider adding by-reference capture specifier [&] to lambda.", modifyType, mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str()), refNode); } else if (mResultFieldInstance->GetFieldDef()->mIsReadOnly) { - error = mModule->Fail(StrFormat("Cannot %s readonly field '%s.%s' within method '%s'", modifyType, + error = _Fail(StrFormat("Cannot %s readonly field '%s.%s' within method '%s'", modifyType, mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); } @@ -15851,12 +15859,12 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod if (propertyDeclaration->mNameNode != NULL) propertyDeclaration->mNameNode->ToString(propNam); - error = mModule->Fail(StrFormat("Cannot %s auto-implemented property '%s.%s' without set accessor", modifyType, + error = _Fail(StrFormat("Cannot %s auto-implemented property '%s.%s' without set accessor", modifyType, mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), propNam.c_str()), refNode); } else { - error = mModule->Fail(StrFormat("Cannot %s field '%s.%s' within struct method '%s'. Consider adding 'mut' specifier to this method.", modifyType, + error = _Fail(StrFormat("Cannot %s field '%s.%s' within struct method '%s'. Consider adding 'mut' specifier to this method.", modifyType, mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); } @@ -15868,13 +15876,13 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod if (!mModule->mCurMethodInstance->IsMixin()) { if (mModule->mCurMethodState->mMixinState != NULL) - error = mModule->Fail(StrFormat("Cannot %s mixin parameter '%s'", modifyType, + error = _Fail(StrFormat("Cannot %s mixin parameter '%s'", modifyType, localVar->mName.c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); else if ((localVar->mResolvedType->IsGenericParam()) && (onlyNeedsMut)) - error = mModule->Fail(StrFormat("Cannot %s parameter '%s'. Consider adding 'mut' or 'ref' specifier to parameter or copying to a local variable.", modifyType, + error = _Fail(StrFormat("Cannot %s parameter '%s'. Consider adding 'mut' or 'ref' specifier to parameter or copying to a local variable.", modifyType, localVar->mName.c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); else - error = mModule->Fail(StrFormat("Cannot %s parameter '%s'. Consider adding 'ref' specifier to parameter or copying to a local variable.", modifyType, + error = _Fail(StrFormat("Cannot %s parameter '%s'. Consider adding 'ref' specifier to parameter or copying to a local variable.", modifyType, localVar->mName.c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); return false; } @@ -15891,7 +15899,7 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod { if (field.mDataIdx == dataIdx) { - error = mModule->Fail(StrFormat("Cannot %s readonly field '%s.%s'.", modifyType, + error = _Fail(StrFormat("Cannot %s readonly field '%s.%s'.", modifyType, mModule->TypeToString(typeInst).c_str(), field.GetFieldDef()->mName.c_str()), refNode); break; @@ -15904,7 +15912,7 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod if (error == NULL) { - error = mModule->Fail(StrFormat("Cannot %s read-only local variable '%s'.", modifyType, + error = _Fail(StrFormat("Cannot %s read-only local variable '%s'.", modifyType, localVar->mName.c_str()), refNode); } return false; @@ -15919,14 +15927,13 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue typedVal, BfAstNode* refNod if ((mResultFieldInstance != NULL) && (mResultFieldInstance->GetFieldDef()->mIsReadOnly) && (!canModify)) { - auto error = mModule->Fail(StrFormat("Cannot %s static readonly field '%s.%s' within method '%s'", modifyType, + auto error = _Fail(StrFormat("Cannot %s static readonly field '%s.%s' within method '%s'", modifyType, mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); return false; } - return mModule->CheckModifyValue(typedVal, refNode, modifyType); } @@ -18367,12 +18374,18 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, mModule->FixIntUnknown(mResult); mModule->PopulateType(mResult.mType); auto ptrType = mModule->CreatePointerType(mResult.mType); - if ((!CheckModifyResult(mResult, unaryOpExpr, "take address of")) || (mResult.mType->IsValuelessType())) + if (mResult.mType->IsValuelessType()) { // Sentinel value auto val = mModule->mBfIRBuilder->CreateIntToPtr(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1), mModule->mBfIRBuilder->MapType(ptrType)); mResult = BfTypedValue(val, ptrType); } + else if (!CheckModifyResult(mResult, unaryOpExpr, "take address of", false, true)) + { + if (!mResult.IsAddr()) + mResult = mModule->MakeAddressable(mResult); + mResult = BfTypedValue(mResult.mValue, ptrType, false); + } else mResult = BfTypedValue(mResult.mValue, ptrType, false); } diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 063af729..b08f1827 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -377,7 +377,7 @@ public: void MarkResultAssigned(); void MakeResultAsValue(); bool CheckIsBase(BfAstNode* checkNode); - bool CheckModifyResult(BfTypedValue typeValue, BfAstNode* refNode, const char* modifyType, bool onlyNeedsMut = false); + bool CheckModifyResult(BfTypedValue typeValue, BfAstNode* refNode, const char* modifyType, bool onlyNeedsMut = false, bool emitWarning = false); bool CheckGenericCtor(BfGenericParamType* genericParamType, BfResolvedArgs& argValues, BfAstNode* targetSrc); BfTypedValue LookupField(BfAstNode* targetSrc, BfTypedValue target, const StringImpl& fieldName, BfLookupFieldFlags flags = BfLookupFieldFlag_None); void CheckObjectCreateTypeRef(BfType* expectingType, BfAstNode* afterNode); diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index b144362b..0cebaeb6 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -6113,7 +6113,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin if (!methodInstanceGroup->IsImplemented()) continue; auto methodDef = typeDef->mMethods[methodIdx]; - if (methodDef->mNoReflect) + if (methodDef->mIsNoReflect) continue; auto defaultMethod = methodInstanceGroup->mDefault; @@ -15146,7 +15146,7 @@ void BfModule::SetupIRMethod(BfMethodInstance* methodInstance, BfIRFunction func if (methodDef->mImportKind == BfImportKind_Export) mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_DllExport); - if (methodDef->mNoReturn) + if (methodDef->mIsNoReturn) mBfIRBuilder->Func_AddAttribute(func, -1, BfIRAttribute_NoReturn); auto callingConv = GetIRCallingConvention(methodInstance); if (callingConv != BfIRCallingConv_CDecl) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 6fa92d59..fa0ae208 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -764,6 +764,8 @@ bool BfMethodInstance::AllowsSplatting() { if (mCallingConvention != BfCallingConvention_Unspecified) return false; + if (mMethodDef->mIsNoSplat) + return false; return true; } @@ -771,6 +773,8 @@ bool BfMethodInstance::AllowsThisSplatting() { if (mCallingConvention != BfCallingConvention_Unspecified) return false; + if (mMethodDef->mIsNoSplat) + return false; return !mMethodDef->HasNoThisSplat(); } diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index 1846f7d6..6296e98a 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -720,9 +720,10 @@ public: bool mCLink; bool mHasAppend; bool mAlwaysInline; - bool mNoReturn; - bool mIsMutating; - bool mNoReflect; + bool mIsNoReturn; + bool mIsMutating; + bool mIsNoSplat; + bool mIsNoReflect; bool mIsSkipCall; bool mIsOperator; bool mIsExtern; @@ -747,9 +748,10 @@ public: mIsNew = false; mIsPartial = false; mCLink = false; - mNoReturn = false; - mIsMutating = false; - mNoReflect = false; + mIsNoReturn = false; + mIsMutating = false; + mIsNoSplat = false; + mIsNoReflect = false; mIsSkipCall = false; mIsOperator = false; mIsExtern = false; @@ -774,7 +776,7 @@ public: virtual ~BfMethodDef(); static BfImportKind GetImportKindFromPath(const StringImpl& filePath); - bool HasNoThisSplat() { return mIsMutating; } + bool HasNoThisSplat() { return mIsMutating || mIsNoSplat; } void Reset(); void FreeMembers(); BfMethodDeclaration* GetMethodDeclaration(); @@ -1103,6 +1105,7 @@ enum BfWarning BfWarning_BF4201_Only7Hex = 4201, BfWarning_BF4202_TooManyHexForInt = 4202, BfWarning_BF4203_UnnecessaryDynamicCast = 4203, + BfWarning_BF4204_AddressOfReadOnly = 4204, BfWarning_C4554_PossiblePrecedenceError = 4554 };