From 5feb0c044ba2de5bea7928d69694ddc9ee79b12f Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Sat, 22 Feb 2025 09:56:39 -0800 Subject: [PATCH] 'not case' expression, case variable scope change --- BeefLibs/corlib/src/TimeZoneInfo.bf | 4 +- IDEHelper/Compiler/BfAst.cpp | 2 + IDEHelper/Compiler/BfAst.h | 2 + IDEHelper/Compiler/BfElementVisitor.cpp | 1 + IDEHelper/Compiler/BfExprEvaluator.cpp | 16 ++++++-- IDEHelper/Compiler/BfModule.cpp | 24 +++++++---- IDEHelper/Compiler/BfModule.h | 1 + IDEHelper/Compiler/BfParser.cpp | 20 +++++++++ IDEHelper/Compiler/BfPrinter.cpp | 2 + IDEHelper/Compiler/BfReducer.cpp | 49 ++++++++++++++++++++--- IDEHelper/Compiler/BfSourceClassifier.cpp | 6 +++ IDEHelper/Compiler/BfSourceClassifier.h | 1 + IDEHelper/Compiler/BfStmtEvaluator.cpp | 13 ++++++ IDEHelper/Tests/src/Nullable.bf | 2 +- IDEHelper/Tests/src/Switches.bf | 9 +++++ 15 files changed, 133 insertions(+), 19 deletions(-) diff --git a/BeefLibs/corlib/src/TimeZoneInfo.bf b/BeefLibs/corlib/src/TimeZoneInfo.bf index 08a15b84..ac689966 100644 --- a/BeefLibs/corlib/src/TimeZoneInfo.bf +++ b/BeefLibs/corlib/src/TimeZoneInfo.bf @@ -2423,8 +2423,8 @@ namespace System { if (dynamicKey.GetValue(c_firstEntryValue) case .Ok(let val)) first = val.Get(); int32 last = -1; - if (dynamicKey.GetValue(c_lastEntryValue) case .Ok(let val)) - last = val.Get(); + if (dynamicKey.GetValue(c_lastEntryValue) case .Ok(let val2)) + last = val2.Get(); if ((first == -1) || (last == -1) || (first > last)) { rules = null; diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index 473f1379..a4210268 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -1461,6 +1461,8 @@ const char* Beefy::BfTokenToString(BfToken token) return "namespace"; case BfToken_New: return "new"; + case BfToken_Not: + return "not"; case BfToken_Null: return "null"; case BfToken_Nullable: diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 621a9759..16e70c5c 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -229,6 +229,7 @@ enum BfToken : uint8 BfToken_NameOf, BfToken_Namespace, BfToken_New, + BfToken_Not, BfToken_Null, BfToken_Nullable, BfToken_OffsetOf, @@ -2365,6 +2366,7 @@ class BfCaseExpression : public BfExpression public: BF_AST_TYPE(BfCaseExpression, BfExpression); + BfAstNode* mNotToken; BfTokenNode* mCaseToken; BfExpression* mCaseExpression; BfTokenNode* mEqualsNode; diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index 305739a3..79ea34b2 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -724,6 +724,7 @@ void BfElementVisitor::Visit(BfCaseExpression* caseExpr) { Visit(caseExpr->ToBase()); + VisitChild(caseExpr->mNotToken); VisitChild(caseExpr->mCaseToken); VisitChild(caseExpr->mCaseExpression); VisitChild(caseExpr->mEqualsNode); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index ef1924aa..b7cb5281 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -3884,7 +3884,7 @@ void BfExprEvaluator::DoCaseExpression(BfTypedValue caseValAddr, BfCaseExpressio if (hasVariable) { - CheckVariableDeclaration(caseExpr, false, true, false); + CheckVariableDeclaration(caseExpr, false, false, false); } // We can avoid clearing on mismatch if we can be sure we ONLY enter the true block on a match. @@ -4038,6 +4038,9 @@ void BfExprEvaluator::Visit(BfCaseExpression* caseExpr) mResult = BfTypedValue(phiValue, boolType); } + + if (caseExpr->mNotToken != NULL) + mResult.mValue = mModule->mBfIRBuilder->CreateNot(mResult.mValue); } void BfExprEvaluator::Visit(BfTypedValueExpression* typedValueExpr) @@ -4390,6 +4393,13 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef) } else if (varDecl->mAddr) { + if ((!mModule->mBfIRBuilder->mIgnoreWrites) && (varDecl->mAddr.IsFake()) && (!varDecl->mResolvedType->IsValuelessType())) + { + // In an ignore case match we can may need to create a fake "out" when someone tries to read it + auto defaultTypedValue = mModule->GetDefaultTypedValue(varDecl->mResolvedType, true, BfDefaultValueKind_Addr); + varDecl->mAddr = defaultTypedValue.mValue; + } + if ((varDecl->mResolvedType->IsRef()) && (!allowRef)) { BfRefType* refType = (BfRefType*)varDecl->mResolvedType; @@ -7744,7 +7754,7 @@ void BfExprEvaluator::FinishDeferredEvals(BfResolvedArgs& argValues) if (curScope->mScopeKind == BfScopeKind_StatementTarget) { // Move this variable into the parent scope - curScope->mLocalVarStart = (int)mModule->mCurMethodState->mLocals.size(); + curScope->mLocalVarStart = (int)mModule->mCurMethodState->mLocals.size(); } } } @@ -9360,7 +9370,7 @@ BfTypedValue BfExprEvaluator::ResolveArgValue(BfResolvedArg& resolvedArg, BfType if (curScope->mScopeKind == BfScopeKind_StatementTarget) { // Move this variable into the parent scope - curScope->mLocalVarStart = (int)mModule->mCurMethodState->mLocals.size(); + curScope->mLocalVarStart = (int)mModule->mCurMethodState->mLocals.size(); } } return argValue; diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index e45bf759..293ab76f 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -16309,22 +16309,30 @@ void BfModule::CheckVariableDef(BfLocalVariable* variableDef) auto checkLocal = localVarEntryPtr->mLocalVar; if ((checkLocal->mLocalVarIdx >= mCurMethodState->GetLocalStartIdx()) && (!checkLocal->mIsShadow)) { - BfError* error; + auto _Fail = [&](int warningNum, String str, BfAstNode* refNode) + { + BfError* error = Warn(warningNum, str, refNode); + if ((checkLocal->mNameNode != NULL) && (error != NULL)) + mCompiler->mPassInstance->MoreInfo("Previous declaration", checkLocal->mNameNode); + }; + + auto checkScope = mCurMethodState->mCurScope; + if (checkScope->mScopeKind == BfScopeKind_StatementTarget) + checkScope = checkScope->mPrevScope; + if (checkLocal->mIsImplicitParam) return; // Ignore 'redefinition' if (checkLocal->IsParam()) { if (variableDef->IsParam()) - error = Fail(StrFormat("A parameter named '%s' has already been declared", variableDef->mName.c_str()), variableDef->mNameNode); + _Fail(4200, StrFormat("A parameter named '%s' has already been declared", variableDef->mName.c_str()), variableDef->mNameNode); else - error = Fail(StrFormat("The name '%s' is already used by a parameter. Consider declaring 'var %s;' if you wish to make a mutable copy of that parameter.", variableDef->mName.c_str(), variableDef->mName.c_str()), variableDef->mNameNode); + _Fail(4200, StrFormat("The name '%s' is already used by a parameter. Consider declaring 'var %s;' if you wish to make a mutable copy of that parameter.", variableDef->mName.c_str(), variableDef->mName.c_str()), variableDef->mNameNode); } - else if (checkLocal->mLocalVarIdx < mCurMethodState->mCurScope->mLocalVarStart) - error = Fail(StrFormat("A variable named '%s' has already been declared in this parent's scope", variableDef->mName.c_str()), variableDef->mNameNode); + else if (checkLocal->mLocalVarIdx < checkScope->mLocalVarStart) + _Fail(4200, StrFormat("A variable named '%s' has already been declared in an outer scope", variableDef->mName.c_str()), variableDef->mNameNode); else - error = Fail(StrFormat("A variable named '%s' has already been declared in this scope", variableDef->mName.c_str()), variableDef->mNameNode); - if ((checkLocal->mNameNode != NULL) && (error != NULL)) - mCompiler->mPassInstance->MoreInfo("Previous declaration", checkLocal->mNameNode); + _Fail(4200, StrFormat("A variable named '%s' has already been declared in this scope", variableDef->mName.c_str()), variableDef->mNameNode); return; } } diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index ddf1a3e8..0f6b4aba 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1929,6 +1929,7 @@ public: bool CheckGenericConstraints(const BfGenericParamSource& genericParamSource, BfType* checkArgType, BfAstNode* checkArgTypeRef, BfGenericParamInstance* genericParamInst, BfTypeVector* methodGenericArgs = NULL, BfError** errorOut = NULL); BfIRValue AllocLocalVariable(BfType* type, const StringImpl& name, bool doLifetimeEnd = true); void DoAddLocalVariable(BfLocalVariable* localVar); + void FixLocalVariable(BfLocalVariable* localVar); void DoLocalVariableDebugInfo(BfLocalVariable* localVar, bool doAliasValue = false, BfIRValue declareBefore = BfIRValue(), BfIRInitType initType = BfIRInitType_NotSet); BfLocalVariable* AddLocalVariableDef(BfLocalVariable* localVarDef, bool addDebugInfo = false, bool doAliasValue = false, BfIRValue declareBefore = BfIRValue(), BfIRInitType initType = BfIRInitType_NotSet); bool TryLocalVariableInit(BfLocalVariable* localVar); diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index 7440383a..85fcf837 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -3691,6 +3691,26 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth, bool isInterpolate) Fail("Unexpected ending brace"); break; } + else if (mToken == BfToken_Case) + { + if (childArr.mSize > 0) + { + auto prevNode = childArr[childArr.mSize - 1]; + if (auto prevIdentifier = BfNodeDynCastExact(prevNode)) + { + if (prevIdentifier->Equals("not")) + { + auto bfTokenNode = mAlloc->Alloc(); + bfTokenNode->Init(prevIdentifier->mTriviaStart, prevIdentifier->mSrcStart, prevIdentifier->mSrcEnd); + bfTokenNode->SetToken(BfToken_Not); + childArr[childArr.mSize - 1] = bfTokenNode; + } + } + } + + astNode->Add(childNode); + childArr.Add(childNode); + } else { if (mToken == BfToken_LParen) diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index 9ccd1aa3..2c27ae6e 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -2080,6 +2080,8 @@ void BfPrinter::Visit(BfCaseExpression* caseExpr) { VisitChild(caseExpr->mValueExpression); ExpectSpace(); + VisitChild(caseExpr->mNotToken); + ExpectSpace(); VisitChild(caseExpr->mCaseToken); BF_ASSERT(caseExpr->mEqualsNode == NULL); ExpectSpace(); diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index b9035141..e17597a5 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -1899,6 +1899,22 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat { if (auto tokenNode = BfNodeDynCast(node)) { + BfAstNode* notNode = NULL; + if (tokenNode->mToken == BfToken_Not) + { + auto nextNode = mVisitorPos.GetNext(); + if (auto nextTokenNode = BfNodeDynCast(nextNode)) + { + if (nextTokenNode->mToken == BfToken_Case) + { + mVisitorPos.MoveNext(); + notNode = tokenNode; + node = nextNode; + tokenNode = nextTokenNode; + } + } + } + BfToken token = tokenNode->GetToken(); auto nextNode = mVisitorPos.GetNext(); @@ -2112,8 +2128,18 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat else if (token == BfToken_Case) { auto caseExpr = mAlloc->Alloc(); - ReplaceNode(tokenNode, caseExpr); - caseExpr->mCaseToken = tokenNode; + + if (notNode != NULL) + { + ReplaceNode(notNode, caseExpr); + caseExpr->mNotToken = notNode; + MEMBER_SET(caseExpr, mCaseToken, tokenNode); + } + else + { + ReplaceNode(tokenNode, caseExpr); + caseExpr->mCaseToken = tokenNode; + } if (auto bindToken = BfNodeDynCast(mVisitorPos.GetNext())) { @@ -2694,7 +2720,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat continue; } - if ((token == BfToken_Case) && ((createExprFlags & CreateStmtFlags_NoCaseExpr) == 0)) + if (((token == BfToken_Case) || (token == BfToken_Not)) + && ((createExprFlags & CreateStmtFlags_NoCaseExpr) == 0)) { if ((createExprFlags & CreateExprFlags_EarlyExit) != 0) return exprLeft; @@ -2710,8 +2737,20 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat auto caseExpr = mAlloc->Alloc(); ReplaceNode(exprLeft, caseExpr); caseExpr->mValueExpression = exprLeft; - MEMBER_SET(caseExpr, mCaseToken, tokenNode); - mVisitorPos.MoveNext(); + + if (token == BfToken_Not) + { + MEMBER_SET(caseExpr, mNotToken, tokenNode); + mVisitorPos.MoveNext(); + tokenNode = ExpectTokenAfter(caseExpr, BfToken_Case); + MEMBER_SET(caseExpr, mCaseToken, tokenNode); + } + else + { + MEMBER_SET(caseExpr, mCaseToken, tokenNode); + mVisitorPos.MoveNext(); + } + exprLeft = caseExpr; if (auto bindTokenNode = BfNodeDynCast(mVisitorPos.GetNext())) diff --git a/IDEHelper/Compiler/BfSourceClassifier.cpp b/IDEHelper/Compiler/BfSourceClassifier.cpp index 635d4d78..74b92df5 100644 --- a/IDEHelper/Compiler/BfSourceClassifier.cpp +++ b/IDEHelper/Compiler/BfSourceClassifier.cpp @@ -463,6 +463,12 @@ void BfSourceClassifier::Visit(BfTokenNode* tokenNode) SetElementType(tokenNode, BfSourceElementType_Normal); } +void BfSourceClassifier::Visit(BfCaseExpression* caseExpr) +{ + BfElementVisitor::Visit(caseExpr); + SetElementType(caseExpr->mNotToken, BfSourceElementType_Keyword); +} + void BfSourceClassifier::Visit(BfInvocationExpression* invocationExpr) { //BfElementVisitor::Visit(invocationExpr); diff --git a/IDEHelper/Compiler/BfSourceClassifier.h b/IDEHelper/Compiler/BfSourceClassifier.h index e3bb0c35..cb87afd7 100644 --- a/IDEHelper/Compiler/BfSourceClassifier.h +++ b/IDEHelper/Compiler/BfSourceClassifier.h @@ -125,6 +125,7 @@ public: virtual void Visit(BfLiteralExpression* literalExpr) override; virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression) override; virtual void Visit(BfTokenNode* tokenNode) override; + virtual void Visit(BfCaseExpression* caseExpr) override; virtual void Visit(BfInvocationExpression* invocationExpr) override; virtual void Visit(BfIndexerExpression* indexerExpr) override; virtual void Visit(BfConstructorDeclaration* ctorDeclaration) override; diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index 950602e3..22050623 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -2441,6 +2441,13 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr auto localVar = HandleVariableDeclaration(varDecl, tupleElement, false, true); localVar->mReadFromId = 0; // Don't give usage errors for binds + + auto curScope = mCurMethodState->mCurScope; + if (curScope->mScopeKind == BfScopeKind_StatementTarget) + { + // Move this variable into the parent scope + curScope->mLocalVarStart = localVar->mLocalVarIdx + 1; + } continue; } @@ -2464,6 +2471,12 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr auto localVar = HandleVariableDeclaration(resolvedType, binOpExpr->mRight, tupleElement, false, true); localVar->mReadFromId = 0; // Don't give usage errors for binds + auto curScope = mCurMethodState->mCurScope; + if (curScope->mScopeKind == BfScopeKind_StatementTarget) + { + // Move this variable into the parent scope + curScope->mLocalVarStart = localVar->mLocalVarIdx + 1; + } continue; } } diff --git a/IDEHelper/Tests/src/Nullable.bf b/IDEHelper/Tests/src/Nullable.bf index 120abbd4..df2df0da 100644 --- a/IDEHelper/Tests/src/Nullable.bf +++ b/IDEHelper/Tests/src/Nullable.bf @@ -128,7 +128,7 @@ namespace Tests } irn = null; - if (irn case .Ok(let val)) + if (irn case .Ok(let val2)) { Test.FatalError(); } diff --git a/IDEHelper/Tests/src/Switches.bf b/IDEHelper/Tests/src/Switches.bf index b6949317..e02ee4ae 100644 --- a/IDEHelper/Tests/src/Switches.bf +++ b/IDEHelper/Tests/src/Switches.bf @@ -91,6 +91,15 @@ namespace Tests bool eq = iResult case .Ok(ref result); Test.Assert(result == 99); + if (iResult not case .Ok(var result2)) + { + } + else + { + Test.FatalError(); + } + Test.Assert(result2 == 0); + const ETest t = .B(234.5f); switch (t) {