1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

'not case' expression, case variable scope change

This commit is contained in:
Brian Fiete 2025-02-22 09:56:39 -08:00
parent 471897a150
commit 5feb0c044b
15 changed files with 133 additions and 19 deletions

View file

@ -2423,8 +2423,8 @@ namespace System {
if (dynamicKey.GetValue(c_firstEntryValue) case .Ok(let val)) if (dynamicKey.GetValue(c_firstEntryValue) case .Ok(let val))
first = val.Get<int32>(); first = val.Get<int32>();
int32 last = -1; int32 last = -1;
if (dynamicKey.GetValue(c_lastEntryValue) case .Ok(let val)) if (dynamicKey.GetValue(c_lastEntryValue) case .Ok(let val2))
last = val.Get<int32>(); last = val2.Get<int32>();
if ((first == -1) || (last == -1) || (first > last)) { if ((first == -1) || (last == -1) || (first > last)) {
rules = null; rules = null;

View file

@ -1461,6 +1461,8 @@ const char* Beefy::BfTokenToString(BfToken token)
return "namespace"; return "namespace";
case BfToken_New: case BfToken_New:
return "new"; return "new";
case BfToken_Not:
return "not";
case BfToken_Null: case BfToken_Null:
return "null"; return "null";
case BfToken_Nullable: case BfToken_Nullable:

View file

@ -229,6 +229,7 @@ enum BfToken : uint8
BfToken_NameOf, BfToken_NameOf,
BfToken_Namespace, BfToken_Namespace,
BfToken_New, BfToken_New,
BfToken_Not,
BfToken_Null, BfToken_Null,
BfToken_Nullable, BfToken_Nullable,
BfToken_OffsetOf, BfToken_OffsetOf,
@ -2365,6 +2366,7 @@ class BfCaseExpression : public BfExpression
public: public:
BF_AST_TYPE(BfCaseExpression, BfExpression); BF_AST_TYPE(BfCaseExpression, BfExpression);
BfAstNode* mNotToken;
BfTokenNode* mCaseToken; BfTokenNode* mCaseToken;
BfExpression* mCaseExpression; BfExpression* mCaseExpression;
BfTokenNode* mEqualsNode; BfTokenNode* mEqualsNode;

View file

@ -724,6 +724,7 @@ void BfElementVisitor::Visit(BfCaseExpression* caseExpr)
{ {
Visit(caseExpr->ToBase()); Visit(caseExpr->ToBase());
VisitChild(caseExpr->mNotToken);
VisitChild(caseExpr->mCaseToken); VisitChild(caseExpr->mCaseToken);
VisitChild(caseExpr->mCaseExpression); VisitChild(caseExpr->mCaseExpression);
VisitChild(caseExpr->mEqualsNode); VisitChild(caseExpr->mEqualsNode);

View file

@ -3884,7 +3884,7 @@ void BfExprEvaluator::DoCaseExpression(BfTypedValue caseValAddr, BfCaseExpressio
if (hasVariable) 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. // 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); mResult = BfTypedValue(phiValue, boolType);
} }
if (caseExpr->mNotToken != NULL)
mResult.mValue = mModule->mBfIRBuilder->CreateNot(mResult.mValue);
} }
void BfExprEvaluator::Visit(BfTypedValueExpression* typedValueExpr) void BfExprEvaluator::Visit(BfTypedValueExpression* typedValueExpr)
@ -4390,6 +4393,13 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef)
} }
else if (varDecl->mAddr) 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)) if ((varDecl->mResolvedType->IsRef()) && (!allowRef))
{ {
BfRefType* refType = (BfRefType*)varDecl->mResolvedType; BfRefType* refType = (BfRefType*)varDecl->mResolvedType;

View file

@ -16309,22 +16309,30 @@ void BfModule::CheckVariableDef(BfLocalVariable* variableDef)
auto checkLocal = localVarEntryPtr->mLocalVar; auto checkLocal = localVarEntryPtr->mLocalVar;
if ((checkLocal->mLocalVarIdx >= mCurMethodState->GetLocalStartIdx()) && (!checkLocal->mIsShadow)) 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) if (checkLocal->mIsImplicitParam)
return; // Ignore 'redefinition' return; // Ignore 'redefinition'
if (checkLocal->IsParam()) if (checkLocal->IsParam())
{ {
if (variableDef->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 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) else if (checkLocal->mLocalVarIdx < checkScope->mLocalVarStart)
error = Fail(StrFormat("A variable named '%s' has already been declared in this parent's scope", variableDef->mName.c_str()), variableDef->mNameNode); _Fail(4200, StrFormat("A variable named '%s' has already been declared in an outer scope", variableDef->mName.c_str()), variableDef->mNameNode);
else else
error = Fail(StrFormat("A variable named '%s' has already been declared in this scope", variableDef->mName.c_str()), variableDef->mNameNode); _Fail(4200, 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);
return; return;
} }
} }

View file

@ -1929,6 +1929,7 @@ public:
bool CheckGenericConstraints(const BfGenericParamSource& genericParamSource, BfType* checkArgType, BfAstNode* checkArgTypeRef, BfGenericParamInstance* genericParamInst, BfTypeVector* methodGenericArgs = NULL, BfError** errorOut = NULL); 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); BfIRValue AllocLocalVariable(BfType* type, const StringImpl& name, bool doLifetimeEnd = true);
void DoAddLocalVariable(BfLocalVariable* localVar); void DoAddLocalVariable(BfLocalVariable* localVar);
void FixLocalVariable(BfLocalVariable* localVar);
void DoLocalVariableDebugInfo(BfLocalVariable* localVar, bool doAliasValue = false, BfIRValue declareBefore = BfIRValue(), BfIRInitType initType = BfIRInitType_NotSet); 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); BfLocalVariable* AddLocalVariableDef(BfLocalVariable* localVarDef, bool addDebugInfo = false, bool doAliasValue = false, BfIRValue declareBefore = BfIRValue(), BfIRInitType initType = BfIRInitType_NotSet);
bool TryLocalVariableInit(BfLocalVariable* localVar); bool TryLocalVariableInit(BfLocalVariable* localVar);

View file

@ -3691,6 +3691,26 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth, bool isInterpolate)
Fail("Unexpected ending brace"); Fail("Unexpected ending brace");
break; break;
} }
else if (mToken == BfToken_Case)
{
if (childArr.mSize > 0)
{
auto prevNode = childArr[childArr.mSize - 1];
if (auto prevIdentifier = BfNodeDynCastExact<BfIdentifierNode>(prevNode))
{
if (prevIdentifier->Equals("not"))
{
auto bfTokenNode = mAlloc->Alloc<BfTokenNode>();
bfTokenNode->Init(prevIdentifier->mTriviaStart, prevIdentifier->mSrcStart, prevIdentifier->mSrcEnd);
bfTokenNode->SetToken(BfToken_Not);
childArr[childArr.mSize - 1] = bfTokenNode;
}
}
}
astNode->Add(childNode);
childArr.Add(childNode);
}
else else
{ {
if (mToken == BfToken_LParen) if (mToken == BfToken_LParen)

View file

@ -2080,6 +2080,8 @@ void BfPrinter::Visit(BfCaseExpression* caseExpr)
{ {
VisitChild(caseExpr->mValueExpression); VisitChild(caseExpr->mValueExpression);
ExpectSpace(); ExpectSpace();
VisitChild(caseExpr->mNotToken);
ExpectSpace();
VisitChild(caseExpr->mCaseToken); VisitChild(caseExpr->mCaseToken);
BF_ASSERT(caseExpr->mEqualsNode == NULL); BF_ASSERT(caseExpr->mEqualsNode == NULL);
ExpectSpace(); ExpectSpace();

View file

@ -1899,6 +1899,22 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
{ {
if (auto tokenNode = BfNodeDynCast<BfTokenNode>(node)) if (auto tokenNode = BfNodeDynCast<BfTokenNode>(node))
{ {
BfAstNode* notNode = NULL;
if (tokenNode->mToken == BfToken_Not)
{
auto nextNode = mVisitorPos.GetNext();
if (auto nextTokenNode = BfNodeDynCast<BfTokenNode>(nextNode))
{
if (nextTokenNode->mToken == BfToken_Case)
{
mVisitorPos.MoveNext();
notNode = tokenNode;
node = nextNode;
tokenNode = nextTokenNode;
}
}
}
BfToken token = tokenNode->GetToken(); BfToken token = tokenNode->GetToken();
auto nextNode = mVisitorPos.GetNext(); auto nextNode = mVisitorPos.GetNext();
@ -2112,8 +2128,18 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
else if (token == BfToken_Case) else if (token == BfToken_Case)
{ {
auto caseExpr = mAlloc->Alloc<BfCaseExpression>(); auto caseExpr = mAlloc->Alloc<BfCaseExpression>();
if (notNode != NULL)
{
ReplaceNode(notNode, caseExpr);
caseExpr->mNotToken = notNode;
MEMBER_SET(caseExpr, mCaseToken, tokenNode);
}
else
{
ReplaceNode(tokenNode, caseExpr); ReplaceNode(tokenNode, caseExpr);
caseExpr->mCaseToken = tokenNode; caseExpr->mCaseToken = tokenNode;
}
if (auto bindToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext())) if (auto bindToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext()))
{ {
@ -2694,7 +2720,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
continue; 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) if ((createExprFlags & CreateExprFlags_EarlyExit) != 0)
return exprLeft; return exprLeft;
@ -2710,8 +2737,20 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
auto caseExpr = mAlloc->Alloc<BfCaseExpression>(); auto caseExpr = mAlloc->Alloc<BfCaseExpression>();
ReplaceNode(exprLeft, caseExpr); ReplaceNode(exprLeft, caseExpr);
caseExpr->mValueExpression = exprLeft; caseExpr->mValueExpression = exprLeft;
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); MEMBER_SET(caseExpr, mCaseToken, tokenNode);
mVisitorPos.MoveNext(); mVisitorPos.MoveNext();
}
exprLeft = caseExpr; exprLeft = caseExpr;
if (auto bindTokenNode = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext())) if (auto bindTokenNode = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext()))

View file

@ -463,6 +463,12 @@ void BfSourceClassifier::Visit(BfTokenNode* tokenNode)
SetElementType(tokenNode, BfSourceElementType_Normal); SetElementType(tokenNode, BfSourceElementType_Normal);
} }
void BfSourceClassifier::Visit(BfCaseExpression* caseExpr)
{
BfElementVisitor::Visit(caseExpr);
SetElementType(caseExpr->mNotToken, BfSourceElementType_Keyword);
}
void BfSourceClassifier::Visit(BfInvocationExpression* invocationExpr) void BfSourceClassifier::Visit(BfInvocationExpression* invocationExpr)
{ {
//BfElementVisitor::Visit(invocationExpr); //BfElementVisitor::Visit(invocationExpr);

View file

@ -125,6 +125,7 @@ public:
virtual void Visit(BfLiteralExpression* literalExpr) override; virtual void Visit(BfLiteralExpression* literalExpr) override;
virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression) override; virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression) override;
virtual void Visit(BfTokenNode* tokenNode) override; virtual void Visit(BfTokenNode* tokenNode) override;
virtual void Visit(BfCaseExpression* caseExpr) override;
virtual void Visit(BfInvocationExpression* invocationExpr) override; virtual void Visit(BfInvocationExpression* invocationExpr) override;
virtual void Visit(BfIndexerExpression* indexerExpr) override; virtual void Visit(BfIndexerExpression* indexerExpr) override;
virtual void Visit(BfConstructorDeclaration* ctorDeclaration) override; virtual void Visit(BfConstructorDeclaration* ctorDeclaration) override;

View file

@ -2441,6 +2441,13 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr
auto localVar = HandleVariableDeclaration(varDecl, tupleElement, false, true); auto localVar = HandleVariableDeclaration(varDecl, tupleElement, false, true);
localVar->mReadFromId = 0; // Don't give usage errors for binds 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; continue;
} }
@ -2464,6 +2471,12 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr
auto localVar = HandleVariableDeclaration(resolvedType, binOpExpr->mRight, tupleElement, false, true); auto localVar = HandleVariableDeclaration(resolvedType, binOpExpr->mRight, tupleElement, false, true);
localVar->mReadFromId = 0; // Don't give usage errors for binds 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; continue;
} }
} }

View file

@ -128,7 +128,7 @@ namespace Tests
} }
irn = null; irn = null;
if (irn case .Ok(let val)) if (irn case .Ok(let val2))
{ {
Test.FatalError(); Test.FatalError();
} }

View file

@ -91,6 +91,15 @@ namespace Tests
bool eq = iResult case .Ok(ref result); bool eq = iResult case .Ok(ref result);
Test.Assert(result == 99); Test.Assert(result == 99);
if (iResult not case .Ok(var result2))
{
}
else
{
Test.FatalError();
}
Test.Assert(result2 == 0);
const ETest t = .B(234.5f); const ETest t = .B(234.5f);
switch (t) switch (t)
{ {