diff --git a/BeefLibs/corlib/src/Attribute.bf b/BeefLibs/corlib/src/Attribute.bf index 5d5fac7d..680499e4 100644 --- a/BeefLibs/corlib/src/Attribute.bf +++ b/BeefLibs/corlib/src/Attribute.bf @@ -28,11 +28,12 @@ namespace System Alloc = 0x40000, Delete = 0x80000, Alias = 0x100000, + Block = 0x200000, All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | StaticField | Interface | Parameter | Delegate | Function | ReturnValue | GenericParameter | Invocation | MemberAccess | - Alloc | Delete | Alias, + Alloc | Delete | Alias | Block, } public enum ReflectKind @@ -137,6 +138,14 @@ namespace System } + [AttributeUsage(.Block)] + public struct IgnoreErrorsAttribute : Attribute + { + public this(bool stopOnErrors = false) + { + } + } + [AttributeUsage(.Method | .Class | .Struct | .Enum)] public struct OptimizeAttribute : Attribute { diff --git a/IDE/mintest/minlib/src/System/Attribute.bf b/IDE/mintest/minlib/src/System/Attribute.bf index 6c12a829..680499e4 100644 --- a/IDE/mintest/minlib/src/System/Attribute.bf +++ b/IDE/mintest/minlib/src/System/Attribute.bf @@ -5,7 +5,7 @@ namespace System } public enum AttributeTargets - { + { Assembly = 0x0001, Module = 0x0002, Class = 0x0004, @@ -28,11 +28,12 @@ namespace System Alloc = 0x40000, Delete = 0x80000, Alias = 0x100000, + Block = 0x200000, All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | StaticField | Interface | Parameter | Delegate | Function | ReturnValue | GenericParameter | Invocation | MemberAccess | - Alloc | Delete | Alias, + Alloc | Delete | Alias | Block, } public enum ReflectKind @@ -120,33 +121,43 @@ namespace System [AttributeUsage(.Class | .Struct | .Interface | .Method | .Constructor)] public struct AlwaysIncludeAttribute : Attribute { - bool mAssumeInstantiated; - public bool AssumeInstantiated { - get { return mAssumeInstantiated; } - set mut { mAssumeInstantiated = value; } + set { } + } + + public bool IncludeAllMethods + { + set { } } } - [AttributeUsage(.MemberAccess | .Alloc | .Delete)] + [AttributeUsage(.MemberAccess | .Alloc)] public struct FriendAttribute : Attribute { } + [AttributeUsage(.Block)] + public struct IgnoreErrorsAttribute : Attribute + { + public this(bool stopOnErrors = false) + { + } + } + + [AttributeUsage(.Method | .Class | .Struct | .Enum)] + public struct OptimizeAttribute : Attribute + { + + } + [AttributeUsage(.Method | .Class | .Struct | .Enum)] public struct UseLLVMAttribute : Attribute { } - [AttributeUsage(.Method | .Class | .Struct | .Enum)] - public struct OptimizeAttribute : Attribute - { - - } - [AttributeUsage(.Method /*2*/ | .StaticField)] public struct CLinkAttribute : Attribute { @@ -165,7 +176,6 @@ namespace System public this(String linkName) { - } public this(MangleKind mangleKind) @@ -190,7 +200,7 @@ namespace System } } - + [Obsolete("Use [CallingConvention(.Stdcall)]", false)] [AttributeUsage(.Method | .Delegate | .Function)] public struct StdCallAttribute : Attribute { @@ -222,21 +232,6 @@ namespace System { } - - public this(String intrinName, Type t0) - { - - } - - public this(String intrinName, Type t0, Type t1) - { - - } - - public this(String intrinName, Type t0, Type t1, Type t2) - { - - } } [AttributeUsage(.Class | .Struct /*2*/)] @@ -268,6 +263,7 @@ namespace System } + /// This attribute is required on constructors that include 'append' allocations. [AttributeUsage(.Constructor)] public struct AllowAppendAttribute : Attribute { @@ -348,7 +344,7 @@ namespace System public struct ExportAttribute : Attribute { - + } [AttributeUsage(.StaticField | .Field, .NotInherited)] @@ -359,28 +355,36 @@ namespace System } } + /// The [Checked] attribute is used to mark a method or a method invocation as being "checked", meaning + /// that the method applies extra runtime checks such as bounds checking or other parameter or state validation. [AttributeUsage(.Invocation | .Method | .Property)] public struct CheckedAttribute : Attribute { } + /// The [Unchecked] attribute is used to mark a method or a method invocation as being "unchecked", meaning + /// that the method omits runtime checks such as bounds checking or other parameter or state validation. [AttributeUsage(.Invocation | .Method | .Property)] public struct UncheckedAttribute : Attribute { } + /// Generally used as a per-method optimization, [DisableChecks] will cause calls within this method to + /// call the unchecked version of methods. By default, only debug builds perform checked calls. [AttributeUsage(.Method | .Constructor)] public struct DisableChecksAttribute : Attribute { } + /// 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(.Method | .MemberAccess)] public struct DisableObjectAccessChecksAttribute : Attribute { } - [AttributeUsage(.Method | .Constructor | .Class | .Struct | .Alias)] + [AttributeUsage(.Method | .Constructor | .Class | .Struct | .Alias | .Interface)] public struct ObsoleteAttribute : Attribute { public this(bool isError) @@ -418,17 +422,17 @@ namespace System } } + /// 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)] public struct NoDiscardAttribute : Attribute { public this() { - } public this(String message) { - } } -} \ No newline at end of file +} diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index 45b8f984..8e2f6840 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -121,6 +121,11 @@ void BfStructuralVisitor::Visit(BfStatement* stmt) Visit(stmt->ToBase()); } +void BfStructuralVisitor::Visit(BfAttributedStatement* attribStmt) +{ + Visit(attribStmt->ToBase()); +} + void BfStructuralVisitor::Visit(BfLabelableStatement* labelableStmt) { Visit(labelableStmt->ToBase()); @@ -753,8 +758,11 @@ bool BfAstNode::IsMissingSemicolon() else return false; } + if (auto attribExpr = BfNodeDynCastExact(this)) + return (attribExpr->mStatement == NULL) || (attribExpr->mStatement->IsMissingSemicolon()); + if (auto stmt = BfNodeDynCast(this)) - return stmt->mTrailingSemicolon == NULL; + return stmt->mTrailingSemicolon == NULL; return false; } @@ -769,6 +777,7 @@ bool BfAstNode::IsExpression() return false; return block->mChildArr.GetLast()->IsExpression(); } + return IsA(); } diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 5b715e72..30756b8f 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -255,6 +255,7 @@ class BfLabelableStatement; class BfExpression; class BfExpressionStatement; class BfAttributedExpression; +class BfAttributedStatement; class BfLiteralExpression; class BfBlock; class BfBlockExtension; @@ -410,8 +411,9 @@ public: virtual void Visit(BfLabeledBlock* labeledBlock); virtual void Visit(BfExpression* expr); virtual void Visit(BfExpressionStatement* exprStmt); - virtual void Visit(BfAttributedExpression* attribExpr); + virtual void Visit(BfAttributedExpression* attribExpr); virtual void Visit(BfStatement* stmt); + virtual void Visit(BfAttributedStatement* attribStmt); virtual void Visit(BfLabelableStatement* labelableStmt); virtual void Visit(BfTypedValueExpression* typedValueExpr); @@ -2612,6 +2614,15 @@ public: BfExpression* mExpression; }; BF_AST_DECL(BfAttributedExpression, BfExpression); +class BfAttributedStatement : public BfStatement +{ +public: + BF_AST_TYPE(BfAttributedStatement, BfStatement); + + BfAttributeDirective* mAttributes; + BfAstNode* mStatement; +}; BF_AST_DECL(BfAttributedStatement, BfStatement); + class BfObjectCreateExpression : public BfMethodBoundExpression { public: diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index b2f8c82d..1fa73728 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -1499,7 +1499,11 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken BfAttributeState attributeState; attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_MemberAccess); attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); - isFriend = (attributeState.mCustomAttributes != NULL) && (attributeState.mCustomAttributes->Contains(mModule->mCompiler->mFriendAttributeTypeDef)); + if ((attributeState.mCustomAttributes != NULL) && (attributeState.mCustomAttributes->Contains(mModule->mCompiler->mFriendAttributeTypeDef))) + { + isFriend = true; + attributeState.mUsed = true; + } mInsertStartIdx = attrIdentifier->mAttributes->GetSrcEnd(); } diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 22b10bc9..8c157107 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -435,6 +435,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mObsoleteAttributeTypeDef = NULL; mErrorAttributeTypeDef = NULL; mWarnAttributeTypeDef = NULL; + mIgnoreErrorsAttributeTypeDef = NULL; mLastAutocompleteModule = NULL; } @@ -6309,6 +6310,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mObsoleteAttributeTypeDef = _GetRequiredType("System.ObsoleteAttribute"); mErrorAttributeTypeDef = _GetRequiredType("System.ErrorAttribute"); mWarnAttributeTypeDef = _GetRequiredType("System.WarnAttribute"); + mIgnoreErrorsAttributeTypeDef = _GetRequiredType("System.IgnoreErrorsAttribute"); for (int i = 0; i < BfTypeCode_Length; i++) mContext->mPrimitiveStructTypes[i] = NULL; diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index 010ccbf3..4243189f 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -401,6 +401,7 @@ public: BfTypeDef* mObsoleteAttributeTypeDef; BfTypeDef* mErrorAttributeTypeDef; BfTypeDef* mWarnAttributeTypeDef; + BfTypeDef* mIgnoreErrorsAttributeTypeDef; int mCurTypeId; diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index 38995d4b..170dd86b 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -114,6 +114,14 @@ void BfElementVisitor::Visit(BfStatement* stmt) VisitChild(stmt->mTrailingSemicolon); } +void BfElementVisitor::Visit(BfAttributedStatement* attribStmt) +{ + Visit(attribStmt->ToBase()); + + VisitChild(attribStmt->mAttributes); + VisitChild(attribStmt->mStatement); +} + void BfElementVisitor::Visit(BfLabelableStatement* labelableStmt) { Visit(labelableStmt->ToBase()); diff --git a/IDEHelper/Compiler/BfElementVisitor.h b/IDEHelper/Compiler/BfElementVisitor.h index badba377..98c70cfa 100644 --- a/IDEHelper/Compiler/BfElementVisitor.h +++ b/IDEHelper/Compiler/BfElementVisitor.h @@ -18,6 +18,7 @@ public: virtual void Visit(BfExpressionStatement* exprStmt); virtual void Visit(BfAttributedExpression* attribExpr); virtual void Visit(BfStatement* stmt); + virtual void Visit(BfAttributedStatement* attribStmt); virtual void Visit(BfLabelableStatement* labelableStmt); virtual void Visit(BfTypedValueExpression* typedValueExpr); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index c4a8679d..e676c988 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -213,16 +213,16 @@ bool BfMethodMatcher::IsMemberAccessible(BfTypeInstance* typeInst, BfTypeDef* de if (mActiveTypeDef == NULL) mActiveTypeDef = mModule->GetActiveTypeDef(); - // This needs to be outside the `IsInSpecializedSection`. Note that mActiveTypeDef does not pose a constraint here + // Note that mActiveTypeDef does not pose a constraint here if (!typeInst->IsTypeMemberIncluded(declaringType, mActiveTypeDef, mModule)) return false; - // This may not be completely correct - BUT if we don't have this then even Dictionary TKey's operator == won't be considered accessible - if ((!mModule->IsInSpecializedSection()) && (mActiveTypeDef->mTypeDeclaration != NULL)) - { - if (!typeInst->IsTypeMemberAccessible(declaringType, mActiveTypeDef)) - return false; + auto visibleProjectSet = mModule->GetVisibleProjectSet(); + if ((visibleProjectSet != NULL) && (!typeInst->IsTypeMemberAccessible(declaringType, visibleProjectSet))) + { + return false; } + return true; } @@ -2582,15 +2582,34 @@ void BfExprEvaluator::Visit(BfTypeReference* typeRef) void BfExprEvaluator::Visit(BfAttributedExpression* attribExpr) { BfAttributeState attributeState; - attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_Invocation | BfAttributeTargets_MemberAccess); - attributeState.mCustomAttributes = mModule->GetCustomAttributes(attribExpr->mAttributes, attributeState.mTarget); - + attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_Invocation | BfAttributeTargets_MemberAccess); + if (auto block = BfNodeDynCast(attribExpr->mExpression)) + attributeState.mTarget = BfAttributeTargets_Block; + + attributeState.mCustomAttributes = mModule->GetCustomAttributes(attribExpr->mAttributes, attributeState.mTarget); SetAndRestoreValue prevAttributeState(mModule->mAttributeState, &attributeState); - VisitChild(attribExpr->mExpression); + + if (auto ignoreErrorsAttrib = attributeState.mCustomAttributes->Get(mModule->mCompiler->mIgnoreErrorsAttributeTypeDef)) + { + SetAndRestoreValue ignoreErrors(mModule->mIgnoreErrors, true); + if (!ignoreErrorsAttrib->mCtorArgs.IsEmpty()) + { + auto constant = mModule->mCurTypeInstance->mConstHolder->GetConstant(ignoreErrorsAttrib->mCtorArgs[0]); + if (constant->mBool) + attributeState.mFlags = BfAttributeState::Flag_StopOnError; + } + VisitChild(attribExpr->mExpression); + attributeState.mUsed = true; + } + else + { + VisitChild(attribExpr->mExpression); + } + if (!attributeState.mUsed) { mModule->Fail("Unused attributes", attribExpr->mAttributes); - } + } } void BfExprEvaluator::Visit(BfBlock* blockExpr) @@ -4106,9 +4125,15 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar if ((mModule->mAttributeState != NULL) && (mModule->mAttributeState->mCustomAttributes != NULL)) { if (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mFriendAttributeTypeDef)) + { mPropGetMethodFlags = (BfGetMethodInstanceFlags)(mPropGetMethodFlags | BfGetMethodInstanceFlag_Friend); + mModule->mAttributeState->mUsed = true; + } if (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mDisableObjectAccessChecksAttributeTypeDef)) + { mPropGetMethodFlags = (BfGetMethodInstanceFlags)(mPropGetMethodFlags | BfGetMethodInstanceFlag_DisableObjectAccessChecks); + mModule->mAttributeState->mUsed = true; + } } if (mPropDef->mIsStatic) @@ -12740,8 +12765,8 @@ BfAllocTarget BfExprEvaluator::ResolveAllocTarget(BfAstNode* allocNode, BfTokenN } } } - else if (attrib.mType->mTypeDef == mModule->mCompiler->mFriendAttributeTypeDef) - allocTarget.mIsFriend = true; + else if (attrib.mType->mTypeDef == mModule->mCompiler->mFriendAttributeTypeDef) + allocTarget.mIsFriend = true; } if (outCustomAttributes != NULL) @@ -14059,7 +14084,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m bool allowImplicitThis = false; BfAstNode* methodNodeSrc = target; - + BfAttributeState attributeState; attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_Invocation | BfAttributeTargets_MemberAccess); @@ -14194,7 +14219,8 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m { if (attrIdentifier->mIdentifier != NULL) methodNodeSrc = attrIdentifier->mIdentifier; - attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); + attributeState.mSrc = attrIdentifier->mAttributes; + attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); if (attrIdentifier->mIdentifier != NULL) targetFunctionName = attrIdentifier->mIdentifier->ToString(); } @@ -14370,7 +14396,8 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m { if (attrIdentifier->mIdentifier != NULL) methodNodeSrc = attrIdentifier->mIdentifier; - attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); + attributeState.mSrc = attrIdentifier->mAttributes; + attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); targetFunctionName = attrIdentifier->mIdentifier->ToString(); } else @@ -14476,7 +14503,8 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m { if (attrIdentifier->mIdentifier != NULL) methodNodeSrc = attrIdentifier->mIdentifier; - attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); + attributeState.mSrc = attrIdentifier->mAttributes; + attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); } allowImplicitThis = true; @@ -14635,12 +14663,18 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m } BfCheckedKind checkedKind = BfCheckedKind_NotSet; - if (attributeState.mCustomAttributes != NULL) + if ((mModule->mAttributeState != NULL) && (mModule->mAttributeState->mCustomAttributes != NULL)) { - if (attributeState.mCustomAttributes->Contains(mModule->mCompiler->mCheckedAttributeTypeDef)) + if (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mCheckedAttributeTypeDef)) + { checkedKind = BfCheckedKind_Checked; - if (attributeState.mCustomAttributes->Contains(mModule->mCompiler->mUncheckedAttributeTypeDef)) + mModule->mAttributeState->mUsed = true; + } + if (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mUncheckedAttributeTypeDef)) + { checkedKind = BfCheckedKind_Unchecked; + mModule->mAttributeState->mUsed = true; + } } SetAndRestoreValue prevUsedAsStatement(mUsedAsStatement, mUsedAsStatement || isCascade); @@ -14648,6 +14682,11 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m mResult = MatchMethod(methodNodeSrc, methodBoundExpr, thisValue, allowImplicitThis, bypassVirtual, targetFunctionName, argValues, methodGenericArguments, checkedKind); argValues.HandleFixits(mModule); + if ((mModule->mAttributeState == &attributeState) && (!attributeState.mUsed)) + { + mModule->Fail("Unused attributes", attributeState.mSrc); + } + if (isCascade) { if (outCascadeValue != NULL) @@ -16910,7 +16949,7 @@ void BfExprEvaluator::DoMemberReference(BfMemberReferenceExpression* memberRefEx BfAutoComplete* autoComplete = GetAutoComplete(); if (autoComplete != NULL) - { + { SetAndRestoreValue prevFriendSet(autoComplete->mHasFriendSet, (attributeState.mCustomAttributes != NULL) && (attributeState.mCustomAttributes->Contains(mModule->mCompiler->mFriendAttributeTypeDef))); if (memberRefExpr->mTarget == NULL) @@ -19260,7 +19299,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod { auto convertedValue = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType, BfCastFlags_NoBox); if (!convertedValue) - return; + return; if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality)) mResult = BfTypedValue(mModule->mBfIRBuilder->CreateCmpEQ(resultTypedValue->mValue, convertedValue), mModule->GetPrimitiveType(BfTypeCode_Boolean)); else diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index d1c1b56f..d705633d 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -2226,15 +2226,27 @@ BfProjectSet* BfModule::GetVisibleProjectSet() { auto typeInstance = type->ToTypeInstance(); if (typeInstance == NULL) + { + if (type->IsSizedArray()) + _AddType(type->GetUnderlyingType()); return; + } + if (typeInstance->IsTuple()) + { + for (auto& fieldInst : typeInstance->mFieldInstances) + { + if (fieldInst.mDataIdx != -1) + _AddType(fieldInst.mResolvedType); + } + } _AddProject(typeInstance->mTypeDef->mProject); if (typeInstance->mGenericTypeInfo == NULL) return; for (auto type : typeInstance->mGenericTypeInfo->mTypeGenericArguments) { if (seenTypes.Add(type)) - _AddType(type); - } + _AddType(type); + } }; if (mCurTypeInstance != NULL) @@ -2460,7 +2472,10 @@ bool BfModule::CheckProtection(BfProtection protection, bool allowProtected, boo ((protection == BfProtection_Private) && (allowPrivate))) return true; if ((mAttributeState != NULL) && (mAttributeState->mCustomAttributes != NULL) && (mAttributeState->mCustomAttributes->Contains(mCompiler->mFriendAttributeTypeDef))) - return true; + { + mAttributeState->mUsed = true; + return true; + } return false; } @@ -2564,7 +2579,10 @@ bool BfModule::CheckProtection(BfProtectionCheckFlags& flags, BfTypeInstance* me } if ((mAttributeState != NULL) && (mAttributeState->mCustomAttributes != NULL) && (mAttributeState->mCustomAttributes->Contains(mCompiler->mFriendAttributeTypeDef))) + { + mAttributeState->mUsed = true; return true; + } return false; } @@ -2584,7 +2602,9 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers BP_ZONE("BfModule::Fail"); if (mIgnoreErrors) - { + { + if (mAttributeState != NULL) + mAttributeState->mFlags = (BfAttributeState::Flags)(mAttributeState->mFlags | BfAttributeState::Flag_HadError); return NULL; } @@ -4167,19 +4187,25 @@ void BfModule::CreateValueTypeEqualsMethod(bool strictEquals) auto _SizedIndex = [&](BfIRValue target, BfIRValue index) { + BfTypedValue result; if (sizedArrayType->mElementType->IsSizeAligned()) { auto ptrType = CreatePointerType(sizedArrayType->mElementType); auto ptrValue = mBfIRBuilder->CreateBitCast(target, mBfIRBuilder->MapType(ptrType)); auto gepResult = mBfIRBuilder->CreateInBoundsGEP(ptrValue, index); - return BfTypedValue(gepResult, sizedArrayType->mElementType, BfTypedValueKind_Addr); + result = BfTypedValue(gepResult, sizedArrayType->mElementType, BfTypedValueKind_Addr); } else { auto indexResult = CreateIndexedValue(sizedArrayType->mElementType, target, index); - return BfTypedValue(indexResult, sizedArrayType->mElementType, BfTypedValueKind_Addr); + result = BfTypedValue(indexResult, sizedArrayType->mElementType, BfTypedValueKind_Addr); } + + if (!result.mType->IsValueType()) + result = LoadValue(result); + + return result; }; if (sizedArrayType->mElementCount > 6) @@ -9874,6 +9900,7 @@ static String GetAttributesTargetListString(BfAttributeTargets attrTarget) AddAttributeTargetName(flagsLeft, BfAttributeTargets_MemberAccess, resultStr, "member access"); AddAttributeTargetName(flagsLeft, BfAttributeTargets_Alloc, resultStr, "allocations"); AddAttributeTargetName(flagsLeft, BfAttributeTargets_Alias, resultStr, "aliases"); + AddAttributeTargetName(flagsLeft, BfAttributeTargets_Block, resultStr, "blocks"); if (resultStr.IsEmpty()) return ""; return resultStr; diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 782402af..f505c1db 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -660,13 +660,25 @@ public: class BfAttributeState { +public: + enum Flags + { + Flag_None, + Flag_StopOnError = 1, + Flag_HadError = 2 + }; + public: + Flags mFlags; + BfAstNode* mSrc; BfAttributeTargets mTarget; - BfCustomAttributes* mCustomAttributes; + BfCustomAttributes* mCustomAttributes; bool mUsed; BfAttributeState() { + mSrc = NULL; + mFlags = Flag_None; mTarget = BfAttributeTargets_None; mCustomAttributes = NULL; mUsed = false; @@ -1553,6 +1565,7 @@ public: virtual void Visit(BfExpressionStatement* expressionStmt) override; virtual void Visit(BfVariableDeclaration* varDecl) override; virtual void Visit(BfLocalMethodDeclaration* methodDecl) override; + virtual void Visit(BfAttributedStatement* attribStmt) override; virtual void Visit(BfThrowStatement* throwStmt) override; virtual void Visit(BfDeleteStatement* deleteStmt) override; virtual void Visit(BfSwitchStatement* switchStmt) override; diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 105bc94c..7fd3b41f 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -3866,6 +3866,10 @@ BfAstNode* BfReducer::DoCreateStatement(BfAstNode* node, CreateStmtFlags createS return localMethodDecl; } + else if (token == BfToken_LBracket) + { + return CreateAttributedStatement(tokenNode); + } } if (auto identifier = BfNodeDynCast(node)) @@ -5311,7 +5315,10 @@ BfAttributeDirective* BfReducer::CreateAttributeDirective(BfTokenNode* startToke auto nextNode = mVisitorPos.GetNext(); tokenNode = BfNodeDynCast(nextNode); if ((tokenNode != NULL) && (tokenNode->GetToken() == BfToken_RBracket)) + { + mVisitorPos.MoveNext(); goto Do_RBracket; + } return attributeDirective; } MEMBER_SET(attributeDirective, mCtorCloseParen, tokenNode); @@ -5340,6 +5347,44 @@ Do_RBracket: return attributeDirective; } +BfStatement* BfReducer::CreateAttributedStatement(BfTokenNode* tokenNode) +{ + auto attrib = CreateAttributeDirective(tokenNode); + if (attrib == NULL) + return NULL; + + BfAstNode* stmt = CreateStatementAfter(attrib); + if (stmt != NULL) + { + bool isValid = true; + + auto checkNode = stmt; + if (auto exprStatement = BfNodeDynCast(checkNode)) + checkNode = exprStatement->mExpression; + + if ((checkNode->IsA()) || + (checkNode->IsA()) || + (checkNode->IsA()) || + (checkNode->IsA())) + { + BfAttributedStatement* attribStmt = mAlloc->Alloc(); + ReplaceNode(attrib, attribStmt); + attribStmt->mAttributes = attrib; + MEMBER_SET(attribStmt, mStatement, stmt); + return attribStmt; + } + } + + Fail("Prefixed attributes can only be used on allocations, invocations, blocks, or variable declarations", attrib); + + BfAttributedStatement* attribStmt = mAlloc->Alloc(); + ReplaceNode(attrib, attribStmt); + attribStmt->mAttributes = attrib; + if (stmt != NULL) + MEMBER_SET(attribStmt, mStatement, stmt); + return attribStmt; +} + BfExpression* BfReducer::CreateAttributedExpression(BfTokenNode* tokenNode, bool onlyAllowIdentifier) { auto attrib = CreateAttributeDirective(tokenNode); @@ -5347,8 +5392,8 @@ BfExpression* BfReducer::CreateAttributedExpression(BfTokenNode* tokenNode, bool return NULL; if (!onlyAllowIdentifier) - { - auto expr = CreateExpressionAfter(attrib); + { + BfExpression* expr = CreateExpressionAfter(attrib); if (expr != NULL) { if (auto identifier = BfNodeDynCast(expr)) @@ -5362,7 +5407,8 @@ BfExpression* BfReducer::CreateAttributedExpression(BfTokenNode* tokenNode, bool if ((expr->IsA()) || (expr->IsA()) || - (expr->IsA())) + (expr->IsA()) || + (expr->IsA())) { BfAttributedExpression* attribExpr = mAlloc->Alloc(); ReplaceNode(attrib, attribExpr); @@ -5370,9 +5416,9 @@ BfExpression* BfReducer::CreateAttributedExpression(BfTokenNode* tokenNode, bool MEMBER_SET(attribExpr, mExpression, expr); return attribExpr; } - } + } - Fail("Prefixed attributes can only be used on constructor calls, invocations, or variable declarations", attrib); + Fail("Prefixed attributes can only be used on allocations, invocations, blocks, or variable declarations", attrib); BfAttributedExpression* attribExpr = mAlloc->Alloc(); ReplaceNode(attrib, attribExpr); diff --git a/IDEHelper/Compiler/BfReducer.h b/IDEHelper/Compiler/BfReducer.h index d134ac2a..ff4ce6c1 100644 --- a/IDEHelper/Compiler/BfReducer.h +++ b/IDEHelper/Compiler/BfReducer.h @@ -192,6 +192,7 @@ public: BfFieldDtorDeclaration* CreateFieldDtorDeclaration(BfAstNode* srcNode); BfFieldDeclaration* CreateFieldDeclaration(BfTokenNode* tokenNode, BfTypeReference* typeRef, BfIdentifierNode* nameIdentifier, BfFieldDeclaration* prevFieldDeclaration); BfAttributeDirective* CreateAttributeDirective(BfTokenNode* startToken); + BfStatement* CreateAttributedStatement(BfTokenNode* tokenNode); BfExpression* CreateAttributedExpression(BfTokenNode* tokenNode, bool onlyAllowIdentifier); BfDelegateBindExpression* CreateDelegateBindExpression(BfAstNode* allocNode); BfLambdaBindExpression* CreateLambdaBindExpression(BfAstNode* allocNode, BfTokenNode* parenToken = NULL); diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index bbbf7087..36ae0fd8 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -1492,7 +1492,8 @@ enum BfAttributeTargets : int32 BfAttributeTargets_Alloc = 0x40000, BfAttributeTargets_Delete = 0x80000, BfAttributeTargets_Alias = 0x100000, - BfAttributeTargets_All = 0x1FFFFF + BfAttributeTargets_Block = 0x200000, + BfAttributeTargets_All = 0x3FFFFF }; class BfAttributeData diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index 3b4254e9..c7ba8d76 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -3394,9 +3394,19 @@ void BfModule::VisitCodeBlock(BfBlock* block) { if (mCurMethodState->mCurScope->mExprEvaluator != NULL) { - // Evaluate last child as an expression - mCurMethodState->mCurScope->mExprEvaluator->VisitChild(expr); - mCurMethodState->mCurScope->mExprEvaluator->FinishExpressionResult(); + if ((mAttributeState != NULL) && + ((mAttributeState->mFlags & (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)) == (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError))) + { + // Resolve as just 'false' + mCurMethodState->mCurScope->mExprEvaluator->mResult = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_Boolean)); + } + else + { + // Evaluate last child as an expression + mCurMethodState->mCurScope->mExprEvaluator->VisitChild(expr); + mCurMethodState->mCurScope->mExprEvaluator->FinishExpressionResult(); + } + break; } else if (mCurMethodState->InMainMixinScope()) @@ -3416,10 +3426,17 @@ void BfModule::VisitCodeBlock(BfBlock* block) } } } - + UpdateSrcPos(child); BfAutoParentNodeEntry autoParentNode(this, child); - child->Accept(this); + + if ((mAttributeState != NULL) && + ((mAttributeState->mFlags & (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)) == (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError))) + { + // Ignore child + } + else + child->Accept(this); mSystem->CheckLockYield(); @@ -3740,6 +3757,41 @@ void BfModule::Visit(BfLocalMethodDeclaration* methodDecl) Fail("Local method declarations must be wrapped in a block statement", methodDecl->mMethodDeclaration->mNameNode); } +void BfModule::Visit(BfAttributedStatement* attribStmt) +{ + BfAttributeState attributeState; + + attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_Invocation | BfAttributeTargets_MemberAccess); + if (auto block = BfNodeDynCast(attribStmt->mStatement)) + attributeState.mTarget = BfAttributeTargets_Block; + + attributeState.mCustomAttributes = GetCustomAttributes(attribStmt->mAttributes, attributeState.mTarget); + + SetAndRestoreValue prevAttributeState(mAttributeState, &attributeState); + + if (auto ignoreErrorsAttrib = attributeState.mCustomAttributes->Get(mCompiler->mIgnoreErrorsAttributeTypeDef)) + { + SetAndRestoreValue ignoreErrors(mIgnoreErrors, true); + if (!ignoreErrorsAttrib->mCtorArgs.IsEmpty()) + { + auto constant = mCurTypeInstance->mConstHolder->GetConstant(ignoreErrorsAttrib->mCtorArgs[0]); + if (constant->mBool) + attributeState.mFlags = BfAttributeState::Flag_StopOnError; + } + VisitChild(attribStmt->mStatement); + attributeState.mUsed = true; + } + else + { + VisitChild(attribStmt->mStatement); + } + + if (!attributeState.mUsed) + { + Fail("Unused attributes", attribStmt->mAttributes); + } +} + void BfModule::Visit(BfExpression* expression) { UpdateSrcPos(expression);