mirror of
https://github.com/verilator/verilator.git
synced 2026-09-07 01:52:17 +02:00
@@ -150,6 +150,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
bool m_inRestrict = false; // True inside restrict assertion
|
||||
AstNode* m_passsp = nullptr; // Current pass statement
|
||||
AstNode* m_failsp = nullptr; // Current fail statement
|
||||
AstFinal* m_finalp = nullptr; // Current final block
|
||||
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||
m_modExpr2Sen2DelayedAlwaysp;
|
||||
@@ -302,6 +303,14 @@ class AssertVisitor final : public VNVisitor {
|
||||
if (AstPExpr* const pexprp = VN_CAST(propp, PExpr)) {
|
||||
AstFork* const forkp = new AstFork{nodep->fileline(), VJoinType::JOIN_NONE};
|
||||
forkp->addForksp(pexprp->bodyp()->unlinkFrBack());
|
||||
if (AstNodeStmt* const finalp = pexprp->finalp()) {
|
||||
if (!m_finalp) {
|
||||
m_finalp = new AstFinal{m_modp->fileline(), finalp->unlinkFrBack()};
|
||||
m_modp->addStmtsp(m_finalp);
|
||||
} else {
|
||||
m_finalp->addStmtsp(finalp->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING2(pushDeletep(pexprp), pexprp, propp);
|
||||
bodyp = forkp;
|
||||
} else {
|
||||
|
||||
@@ -67,9 +67,11 @@ private:
|
||||
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator
|
||||
V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator
|
||||
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator
|
||||
V3UniqueNames m_activeNames{"__VassertsActive"}; // Active asserts map name generator
|
||||
bool m_inAssign = false; // True if in an AssignNode
|
||||
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
|
||||
bool m_inSynchDrive = false; // True if in synchronous drive
|
||||
bool m_hasCycleDelay = false; // True if node has cycle delay beneath
|
||||
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
|
||||
std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal
|
||||
|
||||
@@ -411,6 +413,7 @@ private:
|
||||
}
|
||||
}
|
||||
void visit(AstDelay* nodep) override {
|
||||
m_hasCycleDelay = true;
|
||||
// Only cycle delays are relevant in this stage; also only process once
|
||||
if (!nodep->isCycleDelay()) {
|
||||
if (m_inSynchDrive) {
|
||||
@@ -792,6 +795,121 @@ private:
|
||||
nodep->replaceWith(exprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
static AstAssocArrayDType* getProcessAssocArrayType(FileLine* const flp) {
|
||||
// Type of __VassertsActive___x[std::process]
|
||||
AstNodeDType* valp
|
||||
= v3Global.rootp()->typeTablep()->findBasicDType(flp, VBasicDTypeKwd::BIT);
|
||||
AstClassRefDType* keyp
|
||||
= new AstClassRefDType{flp, v3Global.rootp()->stdPackageClassp(), nullptr};
|
||||
keyp->classOrPackagep(v3Global.rootp()->stdPackageClassp());
|
||||
v3Global.rootp()->typeTablep()->addTypesp(keyp);
|
||||
AstAssocArrayDType* const typep = new AstAssocArrayDType{flp, valp, keyp};
|
||||
typep->dtypep(typep);
|
||||
v3Global.rootp()->typeTablep()->addTypesp(typep);
|
||||
return typep;
|
||||
}
|
||||
static AstStmtExpr* getProcessAssocArrayDelete(AstVarRef* const refp) {
|
||||
// Constructs refp.delete(std::process::self()) statement
|
||||
FileLine* const flp = refp->fileline();
|
||||
refp->classOrPackagep(v3Global.rootp()->stdPackageClassp());
|
||||
AstCMethodHard* const deletep = new AstCMethodHard{
|
||||
flp, refp, VCMethod::ASSOC_ERASE, v3Global.rootp()->stdPackageProcessSelfp(flp)};
|
||||
deletep->dtypep(refp->findVoidDType());
|
||||
return new AstStmtExpr{flp, deletep};
|
||||
}
|
||||
static AstNodeExpr* getProcessAssocArraySize(AstVarRef* const refp) {
|
||||
// Constructs refp.size() statement
|
||||
refp->classOrPackagep(v3Global.rootp()->stdPackageClassp());
|
||||
AstCMethodHard* const sizep
|
||||
= new AstCMethodHard{refp->fileline(), refp, VCMethod::ASSOC_SIZE};
|
||||
sizep->dtypep(refp->findBasicDType(VBasicDTypeKwd::UINT32));
|
||||
return sizep;
|
||||
}
|
||||
void visit(AstSEventually* nodep) override {
|
||||
UASSERT(v3Global.rootp()->stdPackagep(), "Should be imported");
|
||||
|
||||
AstSenTree* const sentreep = newSenTree(nodep);
|
||||
if (!sentreep->sensesp()) {
|
||||
VL_DO_DANGLING(pushDeletep(sentreep), sentreep);
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
||||
// Track active assertions
|
||||
AstVar* const activep = new AstVar{flp, VVarType::MODULETEMP, m_activeNames.get(""),
|
||||
getProcessAssocArrayType(flp)};
|
||||
activep->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(activep);
|
||||
|
||||
// Assertion condition check
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
AstNodeExpr* const condp = new AstSampled{flp, nodep->exprp()->unlinkFrBack()};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLogNot{flp, condp}});
|
||||
loopp->addStmtsp(new AstEventControl{flp, sentreep, nullptr});
|
||||
|
||||
// Add assertion to the active set
|
||||
AstAssocSel* const selp = new AstAssocSel{flp, new AstVarRef{flp, activep, VAccess::WRITE},
|
||||
v3Global.rootp()->stdPackageProcessSelfp(flp)};
|
||||
AstAssign* const incrementp = new AstAssign{flp, selp, new AstConst{flp, 1}};
|
||||
AstPExprClause* const clausep = new AstPExprClause{flp};
|
||||
AstStmtExpr* const deletep
|
||||
= getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE});
|
||||
|
||||
// Main assertion block
|
||||
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
|
||||
bodyp->addStmtsp(incrementp);
|
||||
bodyp->addStmtsp(loopp);
|
||||
bodyp->addStmtsp(clausep);
|
||||
bodyp->addStmtsp(deletep);
|
||||
|
||||
// Validate assertion condition for each active assert
|
||||
AstVar* const activeCountp = new AstVar{flp, VVarType::BLOCKTEMP, "__VassertCount",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
activeCountp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
|
||||
AstAssign* const initActiveCountp
|
||||
= new AstAssign{flp, new AstVarRef{flp, activeCountp, VAccess::WRITE},
|
||||
getProcessAssocArraySize(new AstVarRef{flp, activep, VAccess::READ})};
|
||||
AstLoop* const finalLoopp = new AstLoop{flp};
|
||||
AstIf* const finalBodypCondp
|
||||
= new AstIf{flp, condp->cloneTreePure(false), new AstPExprClause{flp},
|
||||
new AstPExprClause{flp, false}};
|
||||
finalLoopp->addStmtsp(
|
||||
new AstLoopTest{flp, finalLoopp,
|
||||
new AstNeq{flp, new AstVarRef{flp, activeCountp, VAccess::READ},
|
||||
new AstConst{flp, 0}}});
|
||||
finalLoopp->addStmtsp(finalBodypCondp);
|
||||
finalLoopp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, activeCountp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, activeCountp, VAccess::READ},
|
||||
new AstConst{flp, 1}}});
|
||||
|
||||
// Final assertion block
|
||||
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
|
||||
finalp->addStmtsp(activeCountp);
|
||||
finalp->addStmtsp(initActiveCountp);
|
||||
finalp->addStmtsp(finalLoopp);
|
||||
|
||||
m_pexprp = new AstPExpr{flp, bodyp, finalp, nodep->dtypep()};
|
||||
VL_RESTORER(m_hasCycleDelay);
|
||||
m_hasCycleDelay = false;
|
||||
iterate(bodyp);
|
||||
iterate(finalp);
|
||||
|
||||
if (m_hasCycleDelay) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in s_eventually");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
VL_DO_DANGLING(m_pexprp->deleteTree(), m_pexprp);
|
||||
return;
|
||||
}
|
||||
|
||||
nodep->replaceWith(m_pexprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstStable* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
@@ -1272,6 +1390,17 @@ private:
|
||||
m_pexprp = nodep;
|
||||
|
||||
if (m_disablep) {
|
||||
const AstSampled* sampledp = nullptr;
|
||||
if (m_disablep->exists([&sampledp](const AstSampled* const sp) {
|
||||
sampledp = sp;
|
||||
return true;
|
||||
})) {
|
||||
sampledp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: $sampled inside disabled condition of a sequence");
|
||||
m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}};
|
||||
// always a copy is used, so remove it now
|
||||
pushDeletep(m_disablep);
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
// Add counter which counts times the condition turned true
|
||||
AstVar* const disableCntp
|
||||
|
||||
@@ -1898,10 +1898,18 @@ public:
|
||||
class AstPExpr final : public AstNodeExpr {
|
||||
// Property expression
|
||||
// @astgen op1 := bodyp : AstBegin
|
||||
// @astgen op2 := finalp : Optional[AstNodeStmt]
|
||||
public:
|
||||
explicit AstPExpr(FileLine* fl, AstBegin* bodyp, AstNodeDType* dtypep)
|
||||
: ASTGEN_SUPER_PExpr(fl) {
|
||||
this->bodyp(bodyp);
|
||||
this->finalp(nullptr);
|
||||
this->dtypep(dtypep);
|
||||
}
|
||||
explicit AstPExpr(FileLine* fl, AstBegin* bodyp, AstNodeStmt* finalp, AstNodeDType* dtypep)
|
||||
: ASTGEN_SUPER_PExpr(fl) {
|
||||
this->bodyp(bodyp);
|
||||
this->finalp(finalp);
|
||||
this->dtypep(dtypep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstPExpr;
|
||||
@@ -2226,6 +2234,19 @@ public:
|
||||
bool unbounded() const { return m_unbounded; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSEventually final : public AstNodeExpr {
|
||||
// s_eventually
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
public:
|
||||
explicit AstSEventually(FileLine* fl, AstNodeExpr* exprp)
|
||||
: ASTGEN_SUPER_SEventually(fl) {
|
||||
this->exprp(exprp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSEventually;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
};
|
||||
class AstSExpr final : public AstNodeExpr {
|
||||
// Sequence expression
|
||||
// @astgen op1 := preExprp: Optional[AstNodeExpr]
|
||||
|
||||
@@ -1263,6 +1263,7 @@ class AstNetlist final : public AstNode {
|
||||
// @astgen ptr := m_constPoolp : AstConstPool // Reference to constant pool, for faster lookup
|
||||
// @astgen ptr := m_dollarUnitPkgp : Optional[AstPackage] // $unit
|
||||
// @astgen ptr := m_stdPackagep : Optional[AstPackage] // SystemVerilog std package
|
||||
// @astgen ptr := m_stdPackageClassp : Optional[AstClass] // SystemVerilog std process class
|
||||
// @astgen ptr := m_evalp : Optional[AstCFunc] // The '_eval' function
|
||||
// @astgen ptr := m_evalNbap : Optional[AstCFunc] // The '_eval__nba' function
|
||||
// @astgen ptr := m_dpiExportTriggerp : Optional[AstVarScope] // DPI export trigger variable
|
||||
@@ -1304,6 +1305,9 @@ public:
|
||||
void nbaEventTriggerp(AstVarScope* const varScopep) { m_nbaEventTriggerp = varScopep; }
|
||||
void stdPackagep(AstPackage* const packagep) { m_stdPackagep = packagep; }
|
||||
AstPackage* stdPackagep() const { return m_stdPackagep; }
|
||||
void stdPackageClassp(AstClass* const classp) { m_stdPackageClassp = classp; }
|
||||
AstClass* stdPackageClassp() const { return m_stdPackageClassp; }
|
||||
AstFuncRef* stdPackageProcessSelfp(FileLine*) const;
|
||||
AstTopScope* topScopep() const { return m_topScopep; }
|
||||
void createTopScope(AstScope* scopep);
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
|
||||
@@ -2703,6 +2703,18 @@ AstVarScope* AstNetlist::stlFirstIterationp() {
|
||||
AstVarScope* const vscp = m_stlFirstIterationp;
|
||||
return vscp;
|
||||
}
|
||||
AstFuncRef* AstNetlist::stdPackageProcessSelfp(FileLine* flp) const {
|
||||
UASSERT(v3Global.rootp()->stdPackageClassp(), "'std' should be imported");
|
||||
AstFunc* selfp = nullptr;
|
||||
for (AstNode* itemp = v3Global.rootp()->stdPackageClassp()->stmtsp(); itemp;
|
||||
itemp = itemp->nextp()) {
|
||||
if (itemp->name() == "self") selfp = VN_AS(itemp, Func);
|
||||
}
|
||||
UASSERT(selfp, "'std::process::self' should be found");
|
||||
AstFuncRef* const processSelfp = new AstFuncRef{flp, selfp};
|
||||
processSelfp->classOrPackagep(v3Global.rootp()->stdPackageClassp());
|
||||
return processSelfp;
|
||||
}
|
||||
void AstNodeModule::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " L" << level();
|
||||
|
||||
@@ -157,6 +157,7 @@ void V3Global::removeStd() {
|
||||
UINFO(3, "Removing unused std:: package");
|
||||
if (AstNodeModule* stdp = v3Global.rootp()->stdPackagep()) {
|
||||
v3Global.rootp()->stdPackagep(nullptr);
|
||||
v3Global.rootp()->stdPackageClassp(nullptr);
|
||||
VL_DO_DANGLING(stdp->unlinkFrBack()->deleteTree(), stdp);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-23
@@ -161,12 +161,6 @@ class LinkJumpVisitor final : public VNVisitor {
|
||||
if (AstNode* const refp = nodep->op4p()) addPrefixToBlocksRecurse(prefix, refp);
|
||||
if (AstNode* const refp = nodep->nextp()) addPrefixToBlocksRecurse(prefix, refp);
|
||||
}
|
||||
static AstNode* getMemberp(const AstNodeModule* const nodep, const std::string& name) {
|
||||
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp->name() == name) return itemp;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
bool existsBlockAbove(const std::string& name) const {
|
||||
for (const AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) {
|
||||
if (stackp->name() == name) return true;
|
||||
@@ -175,14 +169,11 @@ class LinkJumpVisitor final : public VNVisitor {
|
||||
}
|
||||
static AstStmtExpr* getQueuePushProcessSelfp(AstVarRef* const queueRefp) {
|
||||
// Constructs queue.push_back(std::process::self()) statement
|
||||
FileLine* const fl = queueRefp->fileline();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstFunc* const selfMethodp = VN_AS(getMemberp(processClassp, "self"), Func);
|
||||
AstFuncRef* const processSelfp = new AstFuncRef{fl, selfMethodp};
|
||||
processSelfp->classOrPackagep(processClassp);
|
||||
FileLine* const flp = queueRefp->fileline();
|
||||
return new AstStmtExpr{
|
||||
fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}};
|
||||
flp,
|
||||
new AstMethodCall{flp, queueRefp, "push_back",
|
||||
new AstArg{flp, "", v3Global.rootp()->stdPackageProcessSelfp(flp)}}};
|
||||
}
|
||||
static AstStmtExpr* getQueuePushProcessSelfp(FileLine* const fl, AstVar* const processQueuep) {
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
@@ -192,13 +183,18 @@ class LinkJumpVisitor final : public VNVisitor {
|
||||
}
|
||||
static AstStmtExpr* getQueueKillStmtp(FileLine* const fl, AstVar* const processQueuep) {
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE};
|
||||
AstTaskRef* const killQueueCall
|
||||
= new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task),
|
||||
new AstArg{fl, "", queueRefp}};
|
||||
killQueueCall->classOrPackagep(processClassp);
|
||||
AstTaskRef* killQueueCall = nullptr;
|
||||
for (AstNode* itemp = v3Global.rootp()->stdPackageClassp()->stmtsp(); itemp;
|
||||
itemp = itemp->nextp()) {
|
||||
if (itemp->name() == "killQueue") {
|
||||
killQueueCall
|
||||
= new AstTaskRef{fl, VN_AS(itemp, Task), new AstArg{fl, "", queueRefp}};
|
||||
break;
|
||||
}
|
||||
}
|
||||
UASSERT(killQueueCall, "Should be found");
|
||||
killQueueCall->classOrPackagep(v3Global.rootp()->stdPackageClassp());
|
||||
return new AstStmtExpr{fl, killQueueCall};
|
||||
}
|
||||
static void prependStmtsp(AstNodeFTask* const nodep, AstNode* const stmtp) {
|
||||
@@ -238,12 +234,11 @@ class LinkJumpVisitor final : public VNVisitor {
|
||||
}
|
||||
AstVar* getProcessQueuep(AstNode* const nodep, FileLine* const fl) {
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstVar* const processQueuep = new AstVar{
|
||||
fl, VVarType::VAR, m_queueNames.get(nodep->name()), VFlagChildDType{},
|
||||
new AstQueueDType{fl, VFlagChildDType{},
|
||||
new AstClassRefDType{fl, processClassp, nullptr}, nullptr}};
|
||||
new AstQueueDType{
|
||||
fl, VFlagChildDType{},
|
||||
new AstClassRefDType{fl, v3Global.rootp()->stdPackageClassp(), nullptr}, nullptr}};
|
||||
processQueuep->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
processQueuep->processQueue(true);
|
||||
topPkgp->addStmtsp(processQueuep);
|
||||
|
||||
@@ -80,6 +80,15 @@ void V3ParseImp::importIfInStd(FileLine* fileline, const string& id, bool doImpo
|
||||
if (doImport) {
|
||||
AstPackageImport* const impp = new AstPackageImport{stdpkgp->fileline(), stdpkgp, "*"};
|
||||
unitPackage(stdpkgp->fileline())->addStmtsp(impp);
|
||||
for (AstNode* itemp = v3Global.rootp()->stdPackagep()->stmtsp(); itemp;
|
||||
itemp = itemp->nextp()) {
|
||||
if (itemp->name() == "process") {
|
||||
v3Global.rootp()->stdPackageClassp(VN_AS(itemp, Class));
|
||||
UASSERT_OBJ(v3Global.rootp()->stdPackageClassp(), v3Global.rootp(),
|
||||
"'std' package class should be found");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1728,6 +1728,18 @@ class WidthVisitor final : public VNVisitor {
|
||||
if (nodep->seedp()) iterateCheckSigned32(nodep, "seed", nodep->seedp(), BOTH);
|
||||
}
|
||||
}
|
||||
void visit(AstSEventually* nodep) override {
|
||||
if (v3Global.opt.timing().isSetFalse() || !v3Global.opt.timing().isSetTrue()) {
|
||||
nodep->v3warn(E_NOTIMING, "s_eventually requires --timing");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 1, 0});
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
if (m_vup->prelim()) {
|
||||
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
|
||||
nodep->dtypeSetBit();
|
||||
}
|
||||
}
|
||||
void visit(AstSGotoRep* nodep) override {
|
||||
assertAtExpr(nodep);
|
||||
if (m_vup->prelim()) {
|
||||
|
||||
+4
-1
@@ -6778,7 +6778,10 @@ pexpr<nodeExprp>: // IEEE: property_expr (The name pexpr is important as regex
|
||||
| yS_ALWAYS pexpr
|
||||
{ $$ = new AstPropAlways{$1, $2, new AstUnbounded{$1}, new AstUnbounded{$1}, true}; }
|
||||
| yS_EVENTUALLY pexpr
|
||||
{ $$ = $2; BBUNSUP($1, "Unsupported: s_eventually (in property expression)"); }
|
||||
{
|
||||
$$ = new AstSEventually{$1, $2};
|
||||
PARSEP->importIfInStd($1, "process", true);
|
||||
}
|
||||
| yS_EVENTUALLY anyrange pexpr %prec yS_EVENTUALLY
|
||||
{ $$ = $3; BBUNSUP($1, "Unsupported: s_eventually[] (in property expression)"); DEL($2); }
|
||||
| yEVENTUALLY anyrange pexpr %prec yS_EVENTUALLY
|
||||
|
||||
Reference in New Issue
Block a user