From 75333a092836dce1ff2b01032c188b84e79c88d0 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Mon, 30 May 2022 11:40:49 -0700 Subject: [PATCH] Improved circular mixin check, isconst(expr), [ConstSkip] --- BeefLibs/corlib/src/Attribute.bf | 8 +- BeefLibs/corlib/src/String.bf | 2 +- IDE/mintest/minlib/src/System/Attribute.bf | 5 + IDEHelper/Compiler/BfAst.cpp | 7 + IDEHelper/Compiler/BfAst.h | 17 ++- IDEHelper/Compiler/BfAutoComplete.cpp | 4 +- IDEHelper/Compiler/BfCompiler.cpp | 2 + IDEHelper/Compiler/BfCompiler.h | 1 + IDEHelper/Compiler/BfElementVisitor.cpp | 10 ++ IDEHelper/Compiler/BfElementVisitor.h | 1 + IDEHelper/Compiler/BfExprEvaluator.cpp | 156 ++++++++++++++------- IDEHelper/Compiler/BfExprEvaluator.h | 3 +- IDEHelper/Compiler/BfModule.h | 20 ++- IDEHelper/Compiler/BfParser.cpp | 4 + IDEHelper/Compiler/BfPrinter.cpp | 10 ++ IDEHelper/Compiler/BfPrinter.h | 1 + IDEHelper/Compiler/BfReducer.cpp | 13 ++ IDEHelper/Compiler/BfResolvedTypeUtils.cpp | 3 +- IDEHelper/Compiler/BfStmtEvaluator.cpp | 34 ++++- 19 files changed, 241 insertions(+), 60 deletions(-) diff --git a/BeefLibs/corlib/src/Attribute.bf b/BeefLibs/corlib/src/Attribute.bf index eae8dc13..e0eb24ae 100644 --- a/BeefLibs/corlib/src/Attribute.bf +++ b/BeefLibs/corlib/src/Attribute.bf @@ -172,6 +172,12 @@ namespace System } + + [AttributeUsage(.Block)] + public struct ConstSkipAttribute : Attribute + { + } + [AttributeUsage(.Block)] public struct IgnoreErrorsAttribute : Attribute { @@ -316,7 +322,7 @@ namespace System { } - } + } [AttributeUsage(.Method /*2*/)] public struct IntrinsicAttribute : Attribute diff --git a/BeefLibs/corlib/src/String.bf b/BeefLibs/corlib/src/String.bf index 2c58aca1..514981be 100644 --- a/BeefLibs/corlib/src/String.bf +++ b/BeefLibs/corlib/src/String.bf @@ -1042,7 +1042,7 @@ namespace System { if (object == null) return; - Append(object.ToString(.. scope .())); + Append(object.ToString(.. scope .(128))); } public void operator+=(String str) diff --git a/IDE/mintest/minlib/src/System/Attribute.bf b/IDE/mintest/minlib/src/System/Attribute.bf index 79ec09af..d05dc12e 100644 --- a/IDE/mintest/minlib/src/System/Attribute.bf +++ b/IDE/mintest/minlib/src/System/Attribute.bf @@ -145,6 +145,11 @@ namespace System } + [AttributeUsage(.Block)] + public struct ConstSkipAttribute : Attribute + { + } + [AttributeUsage(.Block)] public struct IgnoreErrorsAttribute : Attribute { diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index e7128ef2..24e4e955 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -356,6 +356,11 @@ void BfStructuralVisitor::Visit(BfOffsetOfExpression* offsetOfExpr) Visit(offsetOfExpr->ToBase()); } +void BfStructuralVisitor::Visit(BfIsConstExpression* isConstExpr) +{ + Visit(isConstExpr->ToBase()); +} + void BfStructuralVisitor::Visit(BfDefaultExpression* defaultExpr) { Visit(defaultExpr->ToBase()); @@ -1398,6 +1403,8 @@ const char* Beefy::BfTokenToString(BfToken token) return "internal"; case BfToken_Is: return "is"; + case BfToken_IsConst: + return "isconst"; case BfToken_Let: return "let"; case BfToken_Mixin: diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index ef7567c5..4e8dacf0 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -158,6 +158,7 @@ enum BfToken : uint8 BfToken_Interface, BfToken_Internal, BfToken_Is, + BfToken_IsConst, BfToken_Let, BfToken_Mixin, BfToken_Mut, @@ -380,6 +381,7 @@ class BfSizeOfExpression; class BfAlignOfExpression; class BfOffsetOfExpression; class BfStrideOfExpression; +class BfIsConstExpression; class BfDefaultExpression; class BfUninitializedExpression; class BfConditionalExpression; @@ -504,6 +506,7 @@ public: virtual void Visit(BfAlignOfExpression* alignOfExpr); virtual void Visit(BfStrideOfExpression* strideOfExpr); virtual void Visit(BfOffsetOfExpression* offsetOfExpr); + virtual void Visit(BfIsConstExpression* isConstExpr); virtual void Visit(BfDefaultExpression* defaultExpr); virtual void Visit(BfUninitializedExpression* uninitializedExpr); virtual void Visit(BfCheckTypeExpression* checkTypeExpr); @@ -2688,9 +2691,10 @@ public: class BfStrideOfExpression : public BfTypeAttrExpression { public: - BF_AST_TYPE(BfStrideOfExpression, BfTypeAttrExpression); + BF_AST_TYPE(BfStrideOfExpression, BfTypeAttrExpression); }; BF_AST_DECL(BfStrideOfExpression, BfTypeAttrExpression); + class BfOffsetOfExpression : public BfTypeAttrExpression { public: @@ -2700,6 +2704,17 @@ public: BfIdentifierNode* mMemberName; }; BF_AST_DECL(BfOffsetOfExpression, BfTypeAttrExpression); +class BfIsConstExpression : public BfExpression +{ +public: + BF_AST_TYPE(BfIsConstExpression, BfTypeAttrExpression); + + BfTokenNode* mIsConstToken; + BfTokenNode* mOpenParen; + BfExpression* mExpression; + BfTokenNode* mCloseParen; +}; BF_AST_DECL(BfIsConstExpression, BfTypeAttrExpression); + class BfDefaultExpression : public BfExpression { public: diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 2101c33b..9a38d9f1 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -1718,7 +1718,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress { "alignof", "as", "asm", "base", "break", "case", "catch", "checked", "continue", "default", "defer", "delegate", "delete", "do", "else", "false", "finally", - "fixed", "for", "function", "if", "implicit", "in", "internal", "is", "new", "mixin", "null", + "fixed", "for", "function", "if", "implicit", "in", "internal", "is", "isconst", "new", "mixin", "null", "offsetof", "out", "params", "ref", "rettype", "return", "sealed", "sizeof", "scope", "static", "strideof", "struct", "switch", /*"this",*/ "try", "true", "typeof", "unchecked", "using", "var", "virtual", "volatile", "where", "while", @@ -3570,6 +3570,8 @@ String BfAutoComplete::ConstantToString(BfIRConstHolder* constHolder, BfIRValue auto constant = constHolder->GetConstant(id); switch (constant->mTypeCode) { + case BfTypeCode_Boolean: + return StrFormat(":(bool) %s", constant->mBool ? "true" : "false"); case BfTypeCode_UInt8: return StrFormat(":(uint8) %llu", constant->mUInt64); case BfTypeCode_UInt16: diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 498a0e85..d495581f 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -474,6 +474,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mObsoleteAttributeTypeDef = NULL; mErrorAttributeTypeDef = NULL; mWarnAttributeTypeDef = NULL; + mConstSkipAttributeTypeDef = NULL; mIgnoreErrorsAttributeTypeDef = NULL; mReflectAttributeTypeDef = NULL; mOnCompileAttributeTypeDef = NULL; @@ -7104,6 +7105,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mObsoleteAttributeTypeDef = _GetRequiredType("System.ObsoleteAttribute"); mErrorAttributeTypeDef = _GetRequiredType("System.ErrorAttribute"); mWarnAttributeTypeDef = _GetRequiredType("System.WarnAttribute"); + mConstSkipAttributeTypeDef = _GetRequiredType("System.ConstSkipAttribute"); mIgnoreErrorsAttributeTypeDef = _GetRequiredType("System.IgnoreErrorsAttribute"); mReflectAttributeTypeDef = _GetRequiredType("System.ReflectAttribute"); mOnCompileAttributeTypeDef = _GetRequiredType("System.OnCompileAttribute"); diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index 643a332c..c980a0bf 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -454,6 +454,7 @@ public: BfTypeDef* mObsoleteAttributeTypeDef; BfTypeDef* mErrorAttributeTypeDef; BfTypeDef* mWarnAttributeTypeDef; + BfTypeDef* mConstSkipAttributeTypeDef; BfTypeDef* mIgnoreErrorsAttributeTypeDef; BfTypeDef* mReflectAttributeTypeDef; BfTypeDef* mOnCompileAttributeTypeDef; diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index a405415b..c5ad402b 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -528,6 +528,16 @@ void BfElementVisitor::Visit(BfDefaultExpression* defaultExpr) VisitChild(defaultExpr->mCloseParen); } +void BfElementVisitor::Visit(BfIsConstExpression* isConstExpr) +{ + Visit(isConstExpr->ToBase()); + + VisitChild(isConstExpr->mIsConstToken); + VisitChild(isConstExpr->mOpenParen); + VisitChild(isConstExpr->mExpression); + VisitChild(isConstExpr->mCloseParen); +} + void BfElementVisitor::Visit(BfUninitializedExpression* uninitializedExpr) { Visit(uninitializedExpr->ToBase()); diff --git a/IDEHelper/Compiler/BfElementVisitor.h b/IDEHelper/Compiler/BfElementVisitor.h index b5b1177d..4d08b799 100644 --- a/IDEHelper/Compiler/BfElementVisitor.h +++ b/IDEHelper/Compiler/BfElementVisitor.h @@ -72,6 +72,7 @@ public: virtual void Visit(BfTypeAttrExpression* typeAttrExpr); virtual void Visit(BfOffsetOfExpression* offsetOfExpr); virtual void Visit(BfDefaultExpression* defaultExpr); + virtual void Visit(BfIsConstExpression* isConstExpr); virtual void Visit(BfUninitializedExpression* uninitializedExpr); virtual void Visit(BfCheckTypeExpression* checkTypeExpr); virtual void Visit(BfDynamicCastExpression* dynCastExpr); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index c761dfae..209d6b6e 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -3317,7 +3317,7 @@ void BfExprEvaluator::Visit(BfAttributedExpression* attribExpr) attributeState.mSrc = attribExpr->mAttributes; attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_Invocation | BfAttributeTargets_MemberAccess); if (auto block = BfNodeDynCast(attribExpr->mExpression)) - attributeState.mTarget = BfAttributeTargets_Block; + attributeState.mTarget = BfAttributeTargets_Block; attributeState.mCustomAttributes = mModule->GetCustomAttributes(attribExpr->mAttributes, attributeState.mTarget); SetAndRestoreValue prevAttributeState(mModule->mAttributeState, &attributeState); @@ -3344,6 +3344,19 @@ void BfExprEvaluator::Visit(BfAttributedExpression* attribExpr) mResult = mModule->GetDefaultTypedValue(mModule->GetPrimitiveType(BfTypeCode_Boolean)); } } + else if (attributeState.mCustomAttributes->Contains(mModule->mCompiler->mConstSkipAttributeTypeDef)) + { + if ((mModule->mCurMethodState == NULL) || (mModule->mCurMethodState->mCurScope == NULL) || (!mModule->mCurMethodState->mCurScope->mInConstIgnore)) + { + VisitChild(attribExpr->mExpression); + } + else + { + BF_ASSERT(mModule->mBfIRBuilder->mIgnoreWrites); + mResult = mModule->GetDefaultTypedValue(mModule->GetPrimitiveType(BfTypeCode_Var)); + } + attributeState.mUsed = true; + } else { VisitChild(attribExpr->mExpression); @@ -11480,7 +11493,6 @@ void BfExprEvaluator::DoTypeIntAttr(BfTypeReference* typeRef, BfTokenNode* comma { auto autoComplete = GetAutoComplete(); - auto type = mModule->ResolveTypeRef(typeRef, BfPopulateType_Data, BfResolveTypeRefFlag_AutoComplete); if (type == NULL) return; @@ -11612,6 +11624,30 @@ void BfExprEvaluator::Visit(BfOffsetOfExpression* offsetOfExpr) DoTypeIntAttr(offsetOfExpr->mTypeRef, offsetOfExpr->mCommaToken, offsetOfExpr->mMemberName, BfToken_OffsetOf); } +void BfExprEvaluator::Visit(BfIsConstExpression* isConstExpr) +{ + if (isConstExpr->mExpression == NULL) + { + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), mModule->GetPrimitiveType(BfTypeCode_Boolean)); + return; + } + + BfMethodState methodState; + SetAndRestoreValue prevMethodState(mModule->mCurMethodState, &methodState, false); + if (mModule->mCurMethodState == NULL) + prevMethodState.Set(); + methodState.mTempKind = BfMethodState::TempKind_NonStatic; + + SetAndRestoreValue ignoreWrites(mModule->mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue allowUninitReads(mModule->mCurMethodState->mAllowUinitReads, true); + + BfEvalExprFlags exprFlags = BfEvalExprFlags_None; + + auto result = mModule->CreateValueFromExpression(isConstExpr->mExpression, NULL, BfEvalExprFlags_DeclType); + bool isConst = mModule->mBfIRBuilder->IsConstValue(result.mValue); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Boolean, isConst ? 1 : 0), mModule->GetPrimitiveType(BfTypeCode_Boolean)); +} + void BfExprEvaluator::Visit(BfDefaultExpression* defaultExpr) { auto autoComplete = GetAutoComplete(); @@ -11669,7 +11705,7 @@ void BfExprEvaluator::Visit(BfCheckTypeExpression* checkTypeExpr) if (autoComplete != NULL) autoComplete->CheckTypeRef(checkTypeExpr->mTypeRef, false, true); - auto targetType = mModule->ResolveTypeRef(checkTypeExpr->mTypeRef); + auto targetType = mModule->ResolveTypeRef(checkTypeExpr->mTypeRef, BfPopulateType_Declaration); if (!targetType) { mModule->AssertErrorState(); @@ -16442,41 +16478,6 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo } auto curMethodState = mModule->mCurMethodState; - // - - // Why was this required? It doesn't check for matching generic args (we only want to throw an error if we call back into a mixin with the same generic args as before) -// { -// bool hasCircularRef = false; -// -// auto checkMethodState = curMethodState; -// while (checkMethodState != NULL) -// { -// auto curMixinState = checkMethodState->mMixinState; -// while (curMixinState != NULL) -// { -// if (curMixinState->mSource == targetSrc) -// hasCircularRef = true; -// curMixinState = curMixinState->mPrevMixinState; -// } -// -// if ((checkMethodState->mClosureState != NULL) && (checkMethodState->mClosureState->mActiveDeferredLocalMethod != NULL)) -// { -// for (auto& mixinRecord : checkMethodState->mClosureState->mActiveDeferredLocalMethod->mMixinStateRecords) -// { -// if (mixinRecord.mSource == targetSrc) -// hasCircularRef = true; -// } -// } -// -// checkMethodState = checkMethodState->mPrevMethodState; -// } -// -// if (hasCircularRef) -// { -// mModule->Fail("Circular reference detected between mixins", targetSrc); -// return; -// } -// } auto moduleMethodInstance = GetSelectedMethod(targetSrc, methodMatcher.mBestMethodTypeInstance, methodMatcher.mBestMethodDef, methodMatcher); if (!moduleMethodInstance) @@ -16522,30 +16523,78 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo } } - // Check circular ref based on methodInstance + if (curMethodState->mCurScope->mMixinDepth >= 128) + { + mModule->Fail("Maximum nested mixin depth exceeded", targetSrc); + return; + } + + // Check circular ref based on methodInstance. We must check the arg types since we could be making progress in mixin evaluation + // based on method selection within the mixin dependent on args { bool hasCircularRef = false; + BfMixinState* checkMixinState = NULL; + auto checkMethodState = curMethodState; while (checkMethodState != NULL) - { - if (checkMethodState->mMethodInstance == methodInstance) - hasCircularRef = true; - + { auto curMixinState = checkMethodState->mMixinState; while (curMixinState != NULL) { - if (curMixinState->mMixinMethodInstance == methodInstance) - hasCircularRef = true; + if ((curMixinState->mDoCircularVarResult) && (curMixinState->mMixinMethodInstance == methodInstance)) + { + mResult = mModule->GetDefaultTypedValue(mModule->GetPrimitiveType(BfTypeCode_Var)); + return; + } + + if ((!curMixinState->mCheckedCircularRef) && (curMixinState->mMixinMethodInstance == methodInstance)) + { + checkMixinState = curMixinState; + checkMixinState->mCheckedCircularRef = true; + } + else if (checkMixinState != NULL) + { + if ((curMixinState->mMixinMethodInstance == checkMixinState->mMixinMethodInstance) && + (curMixinState->mArgTypes == checkMixinState->mArgTypes) && + (curMixinState->mArgConsts.mSize == checkMixinState->mArgConsts.mSize)) + { + bool constsMatch = true; + + for (int i = 0; i < curMixinState->mArgConsts.mSize; i++) + { + if (!mModule->mBfIRBuilder->CheckConstEquality(curMixinState->mArgConsts[i], checkMixinState->mArgConsts[i])) + { + constsMatch = false; + break; + } + } + + if (constsMatch) + hasCircularRef = true; + } + } + curMixinState = curMixinState->mPrevMixinState; } - checkMethodState = checkMethodState->mPrevMethodState; - } + } if (hasCircularRef) { + for (auto argType : checkMixinState->mArgTypes) + { + if (argType->IsVar()) + checkMixinState->mDoCircularVarResult = true; + } + + if (checkMixinState->mDoCircularVarResult) + { + mResult = mModule->GetDefaultTypedValue(mModule->GetPrimitiveType(BfTypeCode_Var)); + return; + } + mModule->Fail("Circular reference detected between mixins", targetSrc); return; } @@ -16981,6 +17030,9 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo } newLocalVar->mParamIdx = -3; + mixinState->mArgTypes.Add(newLocalVar->mResolvedType); + if (mModule->mBfIRBuilder->IsConstValue(newLocalVar->mConstValue)) + mixinState->mArgConsts.Add(newLocalVar->mConstValue); }; argExprEvaluatorItr = argExprEvaluators.begin(); @@ -18993,13 +19045,14 @@ void BfExprEvaluator::Visit(BfConditionalExpression* condExpr) { auto curBlock = mModule->mBfIRBuilder->GetInsertBlock(); SetAndRestoreValue ignoreWrites(mModule->mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue prevInConstIgnore(mModule->mCurMethodState->mCurScope->mInConstIgnore, true); ignoredValue = mModule->CreateValueFromExpression(ignoredExpr, mExpectingType, (BfEvalExprFlags)((mBfEvalExprFlags & BfEvalExprFlags_InheritFlags) | BfEvalExprFlags_NoCast)); mModule->mBfIRBuilder->SetInsertPoint(curBlock); } if (!actualValue) return; - if ((ignoredValue) && (ignoredValue.mType != actualValue.mType)) + if ((ignoredValue) && (ignoredValue.mType != actualValue.mType) && (!ignoredValue.mType->IsVar())) { // Cast to more specific 'ignored' type if applicable if (mModule->CanCast(actualValue, ignoredValue.mType)) @@ -19078,7 +19131,7 @@ void BfExprEvaluator::Visit(BfConditionalExpression* condExpr) { BfTypedValue trueToFalse; { - SetAndRestoreValue prevIgnoreError(mModule->mIgnoreErrors, true); + SetAndRestoreValue prevIgnoreError(mModule->mIgnoreErrors, true); mModule->mBfIRBuilder->SetInsertPoint(trueBlockPos); trueToFalse = mModule->Cast(condExpr->mTrueExpression, trueValue, falseValue.mType); } @@ -22078,7 +22131,8 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp else { // Always false - SetAndRestoreValue prevIgnoreWrites(mModule->mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue prevIgnoreWrites(mModule->mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue prevInConstIgnore(mModule->mCurMethodState->mCurScope->mInConstIgnore, true); rightValue = mModule->CreateValueFromExpression(rightExpression, boolType, (BfEvalExprFlags)(mBfEvalExprFlags & BfEvalExprFlags_InheritFlags)); mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), boolType); } @@ -22131,6 +22185,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp { // Always true SetAndRestoreValue prevIgnoreWrites(mModule->mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue prevInConstIgnore(mModule->mCurMethodState->mCurScope->mInConstIgnore, true); rightValue = mModule->CreateValueFromExpression(rightExpression, boolType, (BfEvalExprFlags)(mBfEvalExprFlags & BfEvalExprFlags_InheritFlags)); mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1), boolType); } @@ -22208,6 +22263,7 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, // Already have a value, we don't need the right side SetAndRestoreValue prevIgnoreWrites(mModule->mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue prevInConstIgnore(mModule->mCurMethodState->mCurScope->mInConstIgnore, true); mModule->CreateValueFromExpression(rightExpression, wantType, (BfEvalExprFlags)((mBfEvalExprFlags & BfEvalExprFlags_InheritFlags) | BfEvalExprFlags_CreateConditionalScope)); mResult = leftValue; return true; diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 3ce29c08..e2fafeea 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -547,8 +547,9 @@ public: virtual void Visit(BfTypeOfExpression* typeOfExpr) override; virtual void Visit(BfSizeOfExpression* sizeOfExpr) override; virtual void Visit(BfAlignOfExpression* alignOfExpr) override; - virtual void Visit(BfStrideOfExpression* strideOfExpr) override; + virtual void Visit(BfStrideOfExpression* strideOfExpr) override; virtual void Visit(BfOffsetOfExpression* offsetOfExpr) override; + virtual void Visit(BfIsConstExpression* isConstExpr) override; virtual void Visit(BfDefaultExpression* defaultExpr) override; virtual void Visit(BfUninitializedExpression* uninitialziedExpr) override; virtual void Visit(BfCheckTypeExpression* checkTypeExpr) override; diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index a2a1c9f0..f4fc6582 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -445,6 +445,7 @@ public: bool mAllowVariableDeclarations; bool mInInitBlock; bool mSupressNextUnreachable; + bool mInConstIgnore; BfMixinState* mMixinState; BfBlock* mAstBlock; BfAstNode* mCloseNode; @@ -484,6 +485,7 @@ public: mAllowTargeting = true; mAllowVariableDeclarations = true; mInInitBlock = false; + mInConstIgnore = false; mMixinDepth = 0; mScopeDepth = 0; mScopeLocalId = -1; @@ -794,7 +796,7 @@ public: class BfMixinState { -public: +public: BfMixinState* mPrevMixinState; BfAstNode* mSource; BfScopeData* mCallerScope; @@ -802,15 +804,30 @@ public: BfFilePosition mInjectFilePosition; BfMethodInstance* mMixinMethodInstance; BfAstNode* mResultExpr; + SizedArray mArgTypes; + SizedArray mArgConsts; int mLocalsStartIdx; bool mUsedInvocationScope; bool mHasDeferredUsage; + bool mCheckedCircularRef; + bool mDoCircularVarResult; BfTypedValue mTarget; int mLastTargetAccessId; public: BfMixinState() { + mPrevMixinState = NULL; + mSource = NULL; + mCallerScope = NULL; + mTarget = NULL; + mMixinMethodInstance = NULL; + mResultExpr = NULL; + mLocalsStartIdx = 0; + mUsedInvocationScope = false; + mHasDeferredUsage = false; + mCheckedCircularRef = false; + mDoCircularVarResult = false; mLastTargetAccessId = -1; } @@ -1126,6 +1143,7 @@ public: newScopeData->mPrevScope = mCurScope; newScopeData->mMixinDepth = mCurScope->mMixinDepth; newScopeData->mScopeDepth = mCurScope->mScopeDepth + 1; + newScopeData->mInConstIgnore = mCurScope->mInConstIgnore; mCurScope = newScopeData; mTailScope = mCurScope; } diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index eb16bb4b..f836998b 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -3143,6 +3143,10 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate, bool disablePrepro if ((!mCompatMode) && (SrcPtrHasToken("is"))) mToken = BfToken_Is; break; + case TOKEN_HASH('i', 's', 'c', 'o'): + if ((!mCompatMode) && (SrcPtrHasToken("isconst"))) + mToken = BfToken_IsConst; + break; case TOKEN_HASH('l', 'e', 't', 0): if ((!mCompatMode) && (SrcPtrHasToken("let"))) mToken = BfToken_Let; diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index 4d3e5ef6..03368a79 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -1764,6 +1764,16 @@ void BfPrinter::Visit(BfDefaultExpression* defaultExpr) VisitChild(defaultExpr->mCloseParen); } +void BfPrinter::Visit(BfIsConstExpression* isConstExpr) +{ + Visit(isConstExpr->ToBase()); + + VisitChild(isConstExpr->mIsConstToken); + VisitChild(isConstExpr->mOpenParen); + VisitChild(isConstExpr->mExpression); + VisitChild(isConstExpr->mCloseParen); +} + void BfPrinter::Visit(BfCheckTypeExpression* checkTypeExpr) { Visit(checkTypeExpr->ToBase()); diff --git a/IDEHelper/Compiler/BfPrinter.h b/IDEHelper/Compiler/BfPrinter.h index 3231fa88..7601e6f1 100644 --- a/IDEHelper/Compiler/BfPrinter.h +++ b/IDEHelper/Compiler/BfPrinter.h @@ -175,6 +175,7 @@ public: virtual void Visit(BfSizeOfExpression* sizeOfExpr) override; virtual void Visit(BfOffsetOfExpression* offsetOfExpr) override; virtual void Visit(BfDefaultExpression* defaultExpr) override; + virtual void Visit(BfIsConstExpression* isConstExpr) override; virtual void Visit(BfCheckTypeExpression* checkTypeExpr) override; virtual void Visit(BfDynamicCastExpression* dynCastExpr) override; virtual void Visit(BfCastExpression* castExpr) override; diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index d08cd438..a9f62ae0 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -1998,6 +1998,19 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat exprLeft = defaultExpr; } + else if (token == BfToken_IsConst) + { + auto isConstExpr = mAlloc->Alloc(); + ReplaceNode(tokenNode, isConstExpr); + isConstExpr->mIsConstToken = tokenNode; + tokenNode = ExpectTokenAfter(isConstExpr, BfToken_LParen); + MEMBER_SET_CHECKED(isConstExpr, mOpenParen, tokenNode); + auto expr = CreateExpressionAfter(isConstExpr); + MEMBER_SET_CHECKED(isConstExpr, mExpression, expr); + tokenNode = ExpectTokenAfter(isConstExpr, BfToken_RParen); + MEMBER_SET_CHECKED(isConstExpr, mCloseParen, tokenNode); + exprLeft = isConstExpr; + } else if (token == BfToken_Question) { auto uninitExpr = mAlloc->Alloc(); diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 177b30f6..e9df8de2 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -3221,6 +3221,7 @@ void BfResolvedTypeSet::HashGenericArguments(BfTypeReference* typeRef, LookupCon } else { + ctx->mModule->Fail("Generic argument expected", genericTypeRef->mOpenChevron); ctx->mFailed = true; return; } @@ -3807,7 +3808,7 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa else { result = ctx->mModule->CreateValueFromExpression(exprModTypeRef->mTarget, NULL, BfEvalExprFlags_DeclType); - } + } } if ((result) && (exprModTypeRef->mToken->mToken == BfToken_Comptype)) diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index 2be0680b..0c8cbad0 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -3742,11 +3742,16 @@ void BfModule::DoIfStatement(BfIfStatement* ifStmt, bool includeTrueStmt, bool i if (includeTrueStmt) { - SetAndRestoreValue ignoreWrites(mBfIRBuilder->mIgnoreWrites); + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites); + SetAndRestoreValue prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore); + if (trueBB) mBfIRBuilder->SetInsertPoint(trueBB); if ((isConstBranch) && (constResult != true)) + { mBfIRBuilder->mIgnoreWrites = true; + mCurMethodState->mCurScope->mInConstIgnore = true; + } else ignoredLastBlock = false; VisitEmbeddedStatement(ifStmt->mTrueStatement); @@ -3785,9 +3790,14 @@ void BfModule::DoIfStatement(BfIfStatement* ifStmt, bool includeTrueStmt, bool i ignoredLastBlock = true; // { - SetAndRestoreValue ignoreWrites(mBfIRBuilder->mIgnoreWrites); + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites); + SetAndRestoreValue prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore); + if ((isConstBranch) && (constResult != false)) + { mBfIRBuilder->mIgnoreWrites = true; + mCurMethodState->mCurScope->mInConstIgnore = true; + } else ignoredLastBlock = false; falseDeferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData); @@ -3898,6 +3908,18 @@ void BfModule::Visit(BfAttributedStatement* attribStmt) VisitChild(attribStmt->mStatement); attributeState.mUsed = true; } + else if (attributeState.mCustomAttributes->Contains(mCompiler->mConstSkipAttributeTypeDef)) + { + if ((mCurMethodState == NULL) || (mCurMethodState->mCurScope == NULL) || (!mCurMethodState->mCurScope->mInConstIgnore)) + { + VisitChild(attribStmt->mStatement); + } + else + { + BF_ASSERT(mBfIRBuilder->mIgnoreWrites); + } + attributeState.mUsed = true; + } else { VisitChild(attribStmt->mStatement); @@ -4755,7 +4777,9 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) auto prevInsertBlock = mBfIRBuilder->GetInsertBlock(); - SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true, !mayHaveMatch && !prevHadFallthrough); + bool isConstIgnore = !mayHaveMatch && !prevHadFallthrough; + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true, isConstIgnore); + SetAndRestoreValue prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore, true, isConstIgnore); mBfIRBuilder->AddBlock(caseBlock); mBfIRBuilder->SetInsertPoint(caseBlock); @@ -4921,6 +4945,7 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) if (switchStmt->mDefaultCase != NULL) { SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true, hadConstMatch); + SetAndRestoreValue prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore, true, hadConstMatch); mBfIRBuilder->AddBlock(defaultBlock); mBfIRBuilder->SetInsertPoint(defaultBlock); @@ -5742,6 +5767,7 @@ void BfModule::Visit(BfWhileStatement* whileStmt) if (isFalseLoop) { SetAndRestoreValue ignoreWrites(mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore, true); VisitEmbeddedStatement(whileStmt->mEmbeddedStatement); } else @@ -6693,6 +6719,8 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) { if (!isVarEnumerator) AssertErrorState(); + + mBfIRBuilder->CreateBr(endBB); } else {