// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: AstNode sub-types representing expressions
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// This files contains all 'AstNode' sub-types that represent expressions,
// i.e.: those constructs that represent, or evaluate to [a possible void]
// value. The root of the hierarchy is 'AstNodeExpr'.
//
// Think of expressions in a very general sense as constructs that "name
// things". The "thing" can be considered the value, but can be highly
// structured. For example, an AstConst can name the value '1', which is
// hopefully familiar. On the opposite end of the spectrum of "things" named by
// expressions, consider AstClassOrPackageRef, that can name a collection of
// pairs (specifically the collection of ('member name', 'member thing')
// pairs). Nevertheless, that collection itself can be considered a value. The
// valid composition of expressions then defines the calculus of values in the
// language.
//
//*************************************************************************

#ifndef VERILATOR_V3ASTNODEEXPR_H_
#define VERILATOR_V3ASTNODEEXPR_H_

#ifndef VERILATOR_V3AST_H_
#error "Use V3Ast.h as the include"
#include "V3Ast.h"  // This helps code analysis tools pick up symbols in V3Ast.h
#define VL_NOT_FINAL  // This #define fixes broken code folding in the CLion IDE
#endif

// === Abstract base node types (AstNode*) =====================================

class AstNodeExpr VL_NOT_FINAL : public AstNode {
    // An expression tree node
protected:
    AstNodeExpr(VNType t, FileLine* fl)
        : AstNode{t, fl} {}

public:
    ASTGEN_MEMBERS_AstNodeExpr;
    // METHODS
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    // TODO: The only AstNodeExpr without dtype is AstArg. Otherwise this could be final.
    bool hasDType() const override VL_MT_SAFE { return true; }
    virtual string emitVerilog() = 0;  /// Format string for verilog writing; see V3EmitV
    // For documentation on emitC format see EmitCFunc::emitOpName
    virtual string emitC() = 0;
    virtual string emitSMT() const { return ""; };
    virtual string emitSimpleOperator() { return ""; }  // "" means not ok to use
    virtual bool emitCheckMaxWords() { return false; }  // Check VL_MULS_MAX_WORDS
    virtual bool cleanOut() const = 0;  // True if output has extra upper bits zero
    // Someday we will generically support data types on every expr node
    // Until then isOpaque indicates we shouldn't constant optimize this node type
    bool isOpaque() const { return VN_IS(this, CvtPackString); }

    // Wrap This expression into an AstStmtExpr to denote it occurs in statement position
    inline AstStmtExpr* makeStmt();
    // Returns an error message if widthMin() is not correct otherwise returns nullptr like
    // broken()
    virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; }
};
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
    // Binary expression
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    VIsCached m_purity;  // Pure state

protected:
    AstNodeBiop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : AstNodeExpr{t, fl} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }

public:
    ASTGEN_MEMBERS_AstNodeBiop;
    // METHODS
    // Set out to evaluation of a AstConst'ed
    virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) = 0;
    virtual bool cleanLhs() const = 0;  // True if LHS must have extra upper bits zero
    virtual bool cleanRhs() const = 0;  // True if RHS must have extra upper bits zero
    virtual bool sizeMattersLhs() const = 0;  // True if output result depends on lhs size
    virtual bool sizeMattersRhs() const = 0;  // True if output result depends on rhs size
    virtual bool doubleFlavor() const { return false; }  // D flavor of nodes with both flavors?
    // Signed flavor of nodes with both flavors?
    virtual bool signedFlavor() const { return false; }
    virtual bool stringFlavor() const { return false; }  // N flavor of nodes with both flavors?
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode*) const override { return true; }
    bool isPure() override;
    const char* broken() const override;

private:
    bool getPurityRecurse() const { return lhsp()->isPure() && rhsp()->isPure(); }
};
class AstNodeBiCom VL_NOT_FINAL : public AstNodeBiop {
    // Binary expr with commutative properties
protected:
    AstNodeBiCom(VNType t, FileLine* fl, AstNodeExpr* lhs, AstNodeExpr* rhs)
        : AstNodeBiop{t, fl, lhs, rhs} {}

public:
    ASTGEN_MEMBERS_AstNodeBiCom;
};
class AstNodeBiComAsv VL_NOT_FINAL : public AstNodeBiCom {
    // Binary expr with commutative & associative properties
protected:
    AstNodeBiComAsv(VNType t, FileLine* fl, AstNodeExpr* lhs, AstNodeExpr* rhs)
        : AstNodeBiCom{t, fl, lhs, rhs} {}

public:
    ASTGEN_MEMBERS_AstNodeBiComAsv;
};
class AstNodeDistBiop VL_NOT_FINAL : public AstNodeBiop {
public:
    AstNodeDistBiop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : AstNodeBiop{t, fl, lhsp, rhsp} {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstNodeDistBiop;
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
};
class AstNodeSel VL_NOT_FINAL : public AstNodeBiop {
    // Single bit range extraction, perhaps with non-constant selection or array selection
    // @astgen alias op1 := fromp // Expression we are indexing into
    // @astgen alias op2 := bitp // The index // TODO: rename to idxp
protected:
    AstNodeSel(VNType t, FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp)
        : AstNodeBiop{t, fl, fromp, bitp} {}

public:
    ASTGEN_MEMBERS_AstNodeSel;
    int bitConst() const;
};
class AstNodeStream VL_NOT_FINAL : public AstNodeBiop {
    // Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
protected:
    AstNodeStream(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : AstNodeBiop{t, fl, lhsp, rhsp} {
        if (lhsp->dtypep()) dtypeSetLogicSized(lhsp->dtypep()->width(), VSigning::UNSIGNED);
    }

public:
    ASTGEN_MEMBERS_AstNodeStream;
};
class AstNodeSystemBiopD VL_NOT_FINAL : public AstNodeBiop {
public:
    AstNodeSystemBiopD(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : AstNodeBiop{t, fl, lhsp, rhsp} {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstNodeSystemBiopD;
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
    bool doubleFlavor() const override { return true; }
};
class AstNodeCCall VL_NOT_FINAL : public AstNodeExpr {
    // A call of a C++ function, perhaps a AstCFunc or perhaps globally named
    // @astgen op2 := argsp : List[AstNodeExpr]  // Note: op1 used by some sub-types only
    //
    // @astgen ptr := m_funcp : AstCFunc  // Function being called
    string m_argTypes;
    bool m_superReference = false;  // Called with super reference

protected:
    AstNodeCCall(VNType t, FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
        : AstNodeExpr{t, fl}
        , m_funcp{funcp} {
        addArgsp(argsp);
    }

public:
    ASTGEN_MEMBERS_AstNodeCCall;
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    int instrCount() const override { return INSTR_COUNT_CALL; }
    bool sameNode(const AstNode* samep) const override {
        const AstNodeCCall* const asamep = VN_DBG_AS(samep, NodeCCall);
        return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
    }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override;
    bool isOutputter() override { return !isPure(); }
    AstCFunc* funcp() const { return m_funcp; }
    void funcp(AstCFunc* funcp) { m_funcp = funcp; }
    string argTypes() const { return m_argTypes; }
    void argTypes(const string& str) { m_argTypes = str; }

    string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
    string emitC() final override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const final override { return true; }
    bool superReference() const { return m_superReference; }
    void superReference(bool flag) { m_superReference = flag; }
};
class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
    // A reference to a task (or function)
    // @astgen op1 := namep : Optional[AstNode]
    // op2 used by some sub-types only
    // @astgen op3 := pinsp : List[AstNodeExpr]
    // @astgen op4 := scopeNamep : Optional[AstScopeName]
    //
    // @astgen ptr := m_taskp : Optional[AstNodeFTask]  // [AfterLink] Pointer to task referenced
    // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule]  // Class/package of the task
    ASTGEN_MEMBERS_AstNodeFTaskRef;  // Gen pointers before other members for performance
private:
    string m_name;  // Name of variable
    string m_dotted;  // Dotted part of scope the name()ed task/func is under or ""
    string m_inlinedDots;  // Dotted hierarchy flattened out
    bool m_pli = false;  // Pli system call ($name)
    VIsCached m_purity;  // Pure state

protected:
    AstNodeFTaskRef(VNType t, FileLine* fl, const string& name, AstNodeExpr* pinsp)
        : AstNodeExpr{t, fl}
        , m_name{name} {
        addPinsp(pinsp);
    }

public:
    const char* broken() const override;
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    string name() const override VL_MT_STABLE { return m_name; }  // * = Var name
    bool isGateOptimizable() const override;
    string dotted() const { return m_dotted; }  // * = Scope name or ""
    string inlinedDots() const { return m_inlinedDots; }
    void inlinedDots(const string& flag) { m_inlinedDots = flag; }
    AstNodeFTask* taskp() const { return m_taskp; }  // [After Link] Pointer to variable
    void taskp(AstNodeFTask* taskp) { m_taskp = taskp; }
    void name(const string& name) override { m_name = name; }
    void dotted(const string& name) { m_dotted = name; }
    AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
    void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
    bool pli() const { return m_pli; }
    void pli(bool flag) { m_pli = flag; }
    bool isPure() override;

    string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
    string emitC() final override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }

private:
    bool getPurityRecurse() const;
};
class AstNodePreSel VL_NOT_FINAL : public AstNodeExpr {
    // Something that becomes an AstSel
    // @astgen op1 := fromp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    // @astgen op3 := thsp : Optional[AstNodeExpr]
    // @astgen op4 := attrp : Optional[AstAttrOf]
    VIsCached m_purity;  // Pure state

protected:
    AstNodePreSel(VNType t, FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : AstNodeExpr{t, fl} {
        this->fromp(fromp);
        this->rhsp(rhsp);
        this->thsp(thsp);
    }

public:
    ASTGEN_MEMBERS_AstNodePreSel;
    // METHODS
    bool sameNode(const AstNode*) const override { return true; }

    string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
    string emitC() final override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
    bool isPure() override;
    const char* broken() const override;

private:
    bool getPurityRecurse() const {
        return fromp()->isPure() && rhsp()->isPure() && (!thsp() || thsp()->isPure());
    }
};
class AstNodeQuadop VL_NOT_FINAL : public AstNodeExpr {
    // 4-ary expression
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    // @astgen op3 := thsp : AstNodeExpr
    // @astgen op4 := fhsp : AstNodeExpr
    VIsCached m_purity;  // Pure state

protected:
    AstNodeQuadop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp,
                  AstNodeExpr* fhsp)
        : AstNodeExpr{t, fl} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
        this->thsp(thsp);
        this->fhsp(fhsp);
    }

public:
    ASTGEN_MEMBERS_AstNodeQuadop;
    // METHODS
    // Set out to evaluation of a AstConst'ed
    virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                               const V3Number& ths, const V3Number& fhs)
        = 0;
    virtual bool cleanLhs() const = 0;  // True if LHS must have extra upper bits zero
    virtual bool cleanRhs() const = 0;  // True if RHS must have extra upper bits zero
    virtual bool cleanThs() const = 0;  // True if THS must have extra upper bits zero
    virtual bool cleanFhs() const = 0;  // True if THS must have extra upper bits zero
    virtual bool sizeMattersLhs() const = 0;  // True if output result depends on lhs size
    virtual bool sizeMattersRhs() const = 0;  // True if output result depends on rhs size
    virtual bool sizeMattersThs() const = 0;  // True if output result depends on ths size
    virtual bool sizeMattersFhs() const = 0;  // True if output result depends on ths size
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode*) const override { return true; }
    bool isPure() override;
    const char* broken() const override;

private:
    bool getPurityRecurse() const {
        return lhsp()->isPure() && rhsp()->isPure() && thsp()->isPure() && fhsp()->isPure();
    }
};
class AstNodeTermop VL_NOT_FINAL : public AstNodeExpr {
    // Terminal operator -- an operator with no "inputs"
protected:
    AstNodeTermop(VNType t, FileLine* fl)
        : AstNodeExpr{t, fl} {}

public:
    ASTGEN_MEMBERS_AstNodeTermop;
    bool isSystemFunc() const override { return true; }
};
class AstNodeTriop VL_NOT_FINAL : public AstNodeExpr {
    // Ternary expression
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    // @astgen op3 := thsp : AstNodeExpr
    VIsCached m_purity;  // Pure state

protected:
    AstNodeTriop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : AstNodeExpr{t, fl} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
        this->thsp(thsp);
    }

public:
    ASTGEN_MEMBERS_AstNodeTriop;
    // METHODS
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    // Set out to evaluation of a AstConst'ed
    virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                               const V3Number& ths)
        = 0;
    virtual bool cleanLhs() const = 0;  // True if LHS must have extra upper bits zero
    virtual bool cleanRhs() const = 0;  // True if RHS must have extra upper bits zero
    virtual bool cleanThs() const = 0;  // True if THS must have extra upper bits zero
    virtual bool sizeMattersLhs() const = 0;  // True if output result depends on lhs size
    virtual bool sizeMattersRhs() const = 0;  // True if output result depends on rhs size
    virtual bool sizeMattersThs() const = 0;  // True if output result depends on ths size
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode*) const override { return true; }
    bool isPure() override;
    const char* broken() const override;

private:
    bool getPurityRecurse() const {
        return lhsp()->isPure() && rhsp()->isPure() && thsp()->isPure();
    }
};
class AstNodeDistTriop VL_NOT_FINAL : public AstNodeTriop {
public:
    AstNodeDistTriop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp,
                     AstNodeExpr* thsp)
        : AstNodeTriop{t, fl, lhsp, rhsp, thsp} {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstNodeDistTriop;
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool cleanThs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sizeMattersThs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        V3ERROR_NA;
    }
};
class AstNodeUniop VL_NOT_FINAL : public AstNodeExpr {
    // Unary expression
    // @astgen op1 := lhsp : AstNodeExpr
    VIsCached m_purity;  // Pure state

protected:
    AstNodeUniop(VNType t, FileLine* fl, AstNodeExpr* lhsp)
        : AstNodeExpr{t, fl} {
        dtypeFrom(lhsp);
        this->lhsp(lhsp);
    }

public:
    ASTGEN_MEMBERS_AstNodeUniop;
    // METHODS
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    // Set out to evaluation of a AstConst'ed lhs
    virtual void numberOperate(V3Number& out, const V3Number& lhs) = 0;
    virtual bool cleanLhs() const = 0;
    virtual bool sizeMattersLhs() const = 0;  // True if output result depends on lhs size
    virtual bool doubleFlavor() const { return false; }  // D flavor of nodes with both flavors?
    // Signed flavor of nodes with both flavors?
    virtual bool signedFlavor() const { return false; }
    virtual bool stringFlavor() const { return false; }  // N flavor of nodes with both flavors?
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode*) const override { return true; }
    bool isPure() override;
    const char* broken() const override;
};
class AstNodeSystemUniopD VL_NOT_FINAL : public AstNodeUniop {
public:
    AstNodeSystemUniopD(VNType t, FileLine* fl, AstNodeExpr* lhsp)
        : AstNodeUniop{t, fl, lhsp} {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstNodeSystemUniopD;
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
    bool doubleFlavor() const override { return true; }
};
class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
    // An AstVarRef or AstVarXRef
    // @astgen ptr := m_varp : Optional[AstVar]  // [AfterLink] Pointer to variable itself
    // @astgen ptr := m_varScopep : Optional[AstVarScope]  // Varscope for hierarchy
    // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule]  // Class/package of the variable
    ASTGEN_MEMBERS_AstNodeVarRef;  // Gen pointers before other members for performance
private:
    VAccess m_access;  // Left hand side assignment
    VSelfPointerText m_selfPointer
        = VSelfPointerText{VSelfPointerText::Empty()};  // Output code object
                                                        // pointer (e.g.: 'this')
protected:
    AstNodeVarRef(VNType t, FileLine* fl, AstVar* varp, const VAccess& access)
        : AstNodeExpr{t, fl}
        , m_access{access} {
        // May have varp==nullptr
        this->varp(varp);
    }

public:
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    int instrCount() const override { return widthInstrs(); }
    VAccess access() const { return m_access; }
    void access(const VAccess& flag) { m_access = flag; }  // Avoid using this; Set in constructor
    AstVar* varp() const VL_MT_STABLE { return m_varp; }  // [After Link] Pointer to variable
    void varp(AstVar* varp) {
        m_varp = varp;
        dtypeFrom(reinterpret_cast<AstNode*>(varp));
    }
    AstVarScope* varScopep() const { return m_varScopep; }
    void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
    const VSelfPointerText& selfPointer() const { return m_selfPointer; }
    void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
    string selfPointerProtect(bool useSelfForThis) const {
        return selfPointer().protect(useSelfForThis, protect());
    }
    AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
    void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
    static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
};

// === Concrete node types =====================================================

// === AstNodeExpr ===
class AstAddrOfCFunc final : public AstNodeExpr {
    // Get address of CFunc
    // @astgen ptr := m_funcp : AstCFunc  // Pointer to function itself

public:
    AstAddrOfCFunc(FileLine* fl, AstCFunc* funcp)
        : ASTGEN_SUPER_AddrOfCFunc(fl)
        , m_funcp{funcp} {
        dtypep(findCHandleDType());
    }

public:
    ASTGEN_MEMBERS_AstAddrOfCFunc;
    void dump(std::ostream& str) const override;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    AstCFunc* funcp() const { return m_funcp; }
};
class AstArg final : public AstNodeExpr {
    // An argument to a function/task, which is either an expression, or is a placeholder for an
    // omitted argument.
    // TODO: AstArg should not be AstNodeExpr, but is currently used as such widely. Fix later.
    // @astgen op1 := exprp : Optional[AstNodeExpr] // nullptr if omitted
    string m_name;  // Pin name, or "" for number based interconnect
public:
    AstArg(FileLine* fl, const string& name, AstNodeExpr* exprp)
        : ASTGEN_SUPER_Arg(fl)
        , m_name{name} {
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstArg;
    bool hasDType() const override VL_MT_SAFE { return false; }
    string name() const override VL_MT_STABLE { return m_name; }  // * = Pin name, ""=go by number
    void name(const string& name) override { m_name = name; }
    bool emptyConnectNoNext() const { return !exprp() && name() == "" && !nextp(); }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstAttrOf final : public AstNodeExpr {
    // Return a value of a attribute, for example a LSB or array LSB of a signal
    // @astgen op1 := fromp : Optional[AstNode<AstNodeExpr|AstNodeDType>]
    // @astgen op2 := dimp : Optional[AstNodeExpr]
    VAttrType m_attrType;  // What sort of extraction
    string m_name;  // Name for some attributes
public:
    AstAttrOf(FileLine* fl, VAttrType attrtype, AstNode* fromp = nullptr,
              AstNodeExpr* dimp = nullptr)
        : ASTGEN_SUPER_AttrOf(fl) {
        this->fromp(fromp);
        this->dimp(dimp);
        m_attrType = attrtype;
    }
    ASTGEN_MEMBERS_AstAttrOf;
    string name() const override VL_MT_STABLE { return m_name; }  // * = Var name
    void name(const string& name) override { m_name = name; }
    VAttrType attrType() const { return m_attrType; }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstCExpr final : public AstNodeExpr {
    // C expression emitted into output, with some arbitrary nodes interspersed
    // @astgen op1 := nodesp : List[AstNode<AstNodeExpr|AstText>]
    const bool m_pure;  // Pure optimizable

    void init(const string& text, int setwidth) {
        if (!text.empty()) add(text);
        if (setwidth) {
            dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
        } else {
            dtypeSetVoid();  // Caller to override if necessary
        }
    }

public:
    class Pure {};
    explicit AstCExpr(FileLine* fl, const string& text = "", int setwidth = 0)
        : ASTGEN_SUPER_CExpr(fl)
        , m_pure{false} {
        init(text, setwidth);
    }
    AstCExpr(FileLine* fl, Pure, const string& text = "", int setwidth = 0)
        : ASTGEN_SUPER_CExpr(fl)
        , m_pure{true} {
        init(text, setwidth);
    }
    ASTGEN_MEMBERS_AstCExpr;
    // METHODS
    bool cleanOut() const override { return true; }
    std::string emitC() override { V3ERROR_NA_RETURN(""); }
    std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return m_pure; }
    bool isOutputter() override { return true; }
    bool isPredictOptimizable() const override { return m_pure; }
    bool isPure() override { return m_pure; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    // Add some text, or a node to this expression
    void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
    void add(AstNode* nodep) { addNodesp(nodep); }
};
class AstCExprUser final : public AstNodeExpr {
    // User '$c' or '$cpure' expression - Like AstCStmt, with text tracking and optimizations
    // disabled.
    //
    // Use AstCExpr instead, unless the text is from user input.
    //
    // @astgen op1 := nodesp : List[AstNode<AstNodeExpr|AstText>]
    const bool m_pure;  // Whether the function is pure
public:
    class Pure {};
    explicit AstCExprUser(FileLine* fl)
        : ASTGEN_SUPER_CExprUser(fl)
        , m_pure{false} {}
    AstCExprUser(FileLine* fl, Pure)
        : ASTGEN_SUPER_CExprUser(fl)
        , m_pure{true} {}
    ASTGEN_MEMBERS_AstCExprUser;
    // METHODS
    bool cleanOut() const override { return false; }
    std::string emitC() override { V3ERROR_NA_RETURN(""); }
    std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return m_pure; }
    bool isOutputter() override { return true; }
    bool isPredictOptimizable() const override { return m_pure; }
    bool isPure() override { return m_pure; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    // Add some text, or a node to this expression
    void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
    void add(AstNode* nodep) { addNodesp(nodep); }
};
class AstCMethodHard final : public AstNodeExpr {
    // A reference to a "C" hardcoded member task (or function)
    // PARENTS: stmt/expr
    // @astgen op1 := fromp : AstNodeExpr // Subject of method call
    // @astgen op2 := pinsp : List[AstNodeExpr] // Arguments
    VCMethod m_method;  // Which method to call
    bool m_pure = false;  // Pure optimizable
    bool m_usePtr = false;  // Use '->' not '.'
public:
    AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr)
        : ASTGEN_SUPER_CMethodHard(fl)
        , m_method{method} {
        this->fromp(fromp);
        addPinsp(pinsp);
        setPurity();
    }
    ASTGEN_MEMBERS_AstCMethodHard;
    string name() const override VL_MT_STABLE { return method().ascii(); }
    bool sameNode(const AstNode* samep) const override {
        const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard);
        return (m_method == asamep->m_method);
    }
    bool isPure() override { return m_pure; }
    int instrCount() const override;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool usePtr() const { return m_usePtr; }
    void usePtr(bool flag) { m_usePtr = flag; }
    VCMethod method() const VL_MT_STABLE { return m_method; }
    void method(VCMethod value) { m_method = value; }

private:
    void setPurity();
};
class AstCast final : public AstNodeExpr {
    // Cast to appropriate data type
    // @astgen op1 := fromp : AstNodeExpr
    // @astgen op2 := childDTypep : Optional[AstNodeDType]
public:
    AstCast(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, AstNodeDType* dtp)
        : ASTGEN_SUPER_Cast(fl) {
        this->fromp(fromp);
        childDTypep(dtp);
        dtypeFrom(dtp);
    }
    // cppcheck-suppress constParameterPointer
    // cppcheck-suppress constParameterCallback
    AstCast(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
        : ASTGEN_SUPER_Cast(fl) {
        this->fromp(fromp);
        dtypeFrom(dtp);
    }
    ASTGEN_MEMBERS_AstCast;
    string emitVerilog() override { return "((%d)'(%l))"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
    AstNodeDType* getChildDTypep() const override { return childDTypep(); }
    AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); }
};
class AstCastParse final : public AstNodeExpr {
    // Cast to appropriate type, where we haven't determined yet what the data type is
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := dtp : AstNode
public:
    AstCastParse(FileLine* fl, AstNodeExpr* lhsp, AstNode* dtp)
        : ASTGEN_SUPER_CastParse(fl) {
        this->lhsp(lhsp);
        this->dtp(dtp);
    }
    ASTGEN_MEMBERS_AstCastParse;
    string emitVerilog() override { return "((%d)'(%l))"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstCastSize final : public AstNodeExpr {
    // Cast to specific size; signed/twostate inherited from lower element per IEEE
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstConst
public:
    AstCastSize(FileLine* fl, AstNodeExpr* lhsp, AstConst* rhsp)
        : ASTGEN_SUPER_CastSize(fl) {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstCastSize;
    // No hasDType because widthing removes this node before the hasDType check
    string emitVerilog() override { return "((%r)'(%l))"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstCellArrayRef final : public AstNodeExpr {
    // As-of-yet unlinkable reference into an array of cells
    // @astgen op1 := selp : List[AstNodeExpr] // Select expression
    string m_name;  // Array name
public:
    AstCellArrayRef(FileLine* fl, const string& name, AstNodeExpr* selp)
        : ASTGEN_SUPER_CellArrayRef(fl)
        , m_name{name} {
        addSelp(selp);
    }
    ASTGEN_MEMBERS_AstCellArrayRef;
    // ACCESSORS
    string name() const override VL_MT_STABLE { return m_name; }  // * = Array name

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstCellRef final : public AstNodeExpr {
    // As-of-yet unlinkable reference into a cell
    // @astgen op1 := cellp : AstNode
    // @astgen op2 := exprp : AstNode<AstNodeExpr|AstNodeDType>
    string m_name;  // Cell name
public:
    AstCellRef(FileLine* fl, const string& name, AstNode* cellp, AstNode* exprp)
        : ASTGEN_SUPER_CellRef(fl)
        , m_name{name} {
        this->cellp(cellp);
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstCellRef;
    // ACCESSORS
    string name() const override VL_MT_STABLE { return m_name; }  // * = Array name

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstClassOrPackageRef final : public AstNodeExpr {
    // @astgen op1 := paramsp : List[AstPin]
    string m_name;
    // Node not NodeModule to appease some early parser usage
    // @astgen ptr := m_classOrPackageNodep : Optional[AstNode]  // Class/package referenced
public:
    AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
                         AstPin* paramsp)
        : ASTGEN_SUPER_ClassOrPackageRef(fl)
        , m_name{name}
        , m_classOrPackageNodep{classOrPackageNodep} {
        addParamsp(paramsp);
    }
    ASTGEN_MEMBERS_AstClassOrPackageRef;
    // METHODS
    bool sameNode(const AstNode* samep) const override {
        return (m_classOrPackageNodep
                == VN_DBG_AS(samep, ClassOrPackageRef)->m_classOrPackageNodep);
    }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    string name() const override VL_MT_STABLE { return m_name; }  // * = Var name
    // There's no classOrPackagep(); use classOrPackageNodep() to get Node,
    // or iterating to package with classOrPackageSkipp()
    AstNodeModule* classOrPackageSkipp() const;
    AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
    void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
    void classOrPackagep(AstNodeModule* nodep) {
        m_classOrPackageNodep = reinterpret_cast<AstNode*>(nodep);
    }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstConsAssoc final : public AstNodeExpr {
    // Construct an assoc array and return object, '{}
    // @astgen op1 := defaultp : Optional[AstNode]
public:
    AstConsAssoc(FileLine* fl, AstNode* defaultp)
        : ASTGEN_SUPER_ConsAssoc(fl) {
        this->defaultp(defaultp);
    }
    ASTGEN_MEMBERS_AstConsAssoc;
    string emitVerilog() override { return "'{}"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstConsDynArray final : public AstNodeExpr {
    // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
    // @astgen op1 := lhsp : Optional[AstNode]
    // @astgen op2 := rhsp : Optional[AstNode]
    const bool m_lhsIsValue;  // LHS constructs value inside the queue, not concat
    const bool m_rhsIsValue;  // RHS constructs value inside the queue, not concat
public:
    explicit AstConsDynArray(FileLine* fl)
        : ASTGEN_SUPER_ConsDynArray(fl)
        , m_lhsIsValue{false}
        , m_rhsIsValue{false} {}
    explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue,
                             AstNode* rhsp)
        : ASTGEN_SUPER_ConsDynArray(fl)
        , m_lhsIsValue{lhsIsValue}
        , m_rhsIsValue{rhsIsValue} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstConsDynArray;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    string emitVerilog() override { return "'{%l, %r}"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* samep) const override {
        const AstConsDynArray* const sp = VN_DBG_AS(samep, ConsDynArray);
        return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue;
    }
    bool lhsIsValue() const { return m_lhsIsValue; }
    bool rhsIsValue() const { return m_rhsIsValue; }
};
class AstConsPackMember final : public AstNodeExpr {
    // Construct a packed array single element [member1: value1]
    // Don't need the member we are constructing, as the dtypep can get us to it
    // @astgen op2 := rhsp : AstNodeExpr
public:
    explicit AstConsPackMember(FileLine* fl, AstMemberDType* dtypep, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_ConsPackMember(fl) {
        this->dtypep(dtypep);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstConsPackMember;
    const char* broken() const override {
        BROKEN_RTN(dtypep() && !VN_IS(dtypep(), MemberDType));
        return nullptr;
    }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstConsPackUOrStruct final : public AstNodeExpr {
    // Construct a packed struct and return object, '{member1: value1, member2: value2}
    // Don't need the class we are constructing, as the dtypep can get us to it
    // @astgen op1 := membersp : List[AstConsPackMember]
public:
    explicit AstConsPackUOrStruct(FileLine* fl, AstNodeUOrStructDType* dtypep,
                                  AstConsPackMember* membersp = nullptr)
        : ASTGEN_SUPER_ConsPackUOrStruct(fl) {
        this->dtypep(dtypep);
        addMembersp(membersp);
    }
    ASTGEN_MEMBERS_AstConsPackUOrStruct;
    const char* broken() const override {
        BROKEN_RTN(dtypep() && !VN_IS(dtypep(), NodeUOrStructDType));
        return nullptr;
    }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstConsQueue final : public AstNodeExpr {
    // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
    // @astgen op1 := lhsp : Optional[AstNode]
    // @astgen op2 := rhsp : Optional[AstNode]
    const bool m_lhsIsValue;  // LHS constructs value inside the queue, not concat
    const bool m_rhsIsValue;  // RHS constructs value inside the queue, not concat
public:
    explicit AstConsQueue(FileLine* fl)
        : ASTGEN_SUPER_ConsQueue(fl)
        , m_lhsIsValue{false}
        , m_rhsIsValue{false} {}
    explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue,
                          AstNode* rhsp)
        : ASTGEN_SUPER_ConsQueue(fl)
        , m_lhsIsValue{lhsIsValue}
        , m_rhsIsValue{rhsIsValue} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstConsQueue;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    string emitVerilog() override { return "'{%l, %r}"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* samep) const override {
        const AstConsQueue* const sp = VN_DBG_AS(samep, ConsQueue);
        return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue;
    }
    bool lhsIsValue() const { return m_lhsIsValue; }
    bool rhsIsValue() const { return m_rhsIsValue; }
};
class AstConsWildcard final : public AstNodeExpr {
    // Construct a wildcard assoc array and return object, '{}
    // @astgen op1 := defaultp : Optional[AstNode]
public:
    AstConsWildcard(FileLine* fl, AstNode* defaultp)
        : ASTGEN_SUPER_ConsWildcard(fl) {
        this->defaultp(defaultp);
    }
    ASTGEN_MEMBERS_AstConsWildcard;
    string emitVerilog() override { return "'{}"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstConst final : public AstNodeExpr {
    // A constant
    V3Number m_num;  // Constant value
    void initWithNumber() {
        if (m_num.isDouble()) {
            dtypeSetDouble();
        } else if (m_num.isString()) {
            dtypeSetString();
        } else {
            dtypeSetLogicUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthToFit()),
                                 VSigning::fromBool(m_num.isSigned()));
        }
        m_num.nodep(this);
    }

public:
    AstConst(FileLine* fl, const V3Number& num)
        : ASTGEN_SUPER_Const(fl)
        , m_num{num} {
        initWithNumber();
    }
    class WidthedValue {};  // for creator type-overload selection
    AstConst(FileLine* fl, WidthedValue, int width, uint32_t value)
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, width, value) {  // Need () constructor
        initWithNumber();
    }
    class DTyped {};  // for creator type-overload selection
    // Zero/empty constant with a type matching nodetypep
    AstConst(FileLine* fl, DTyped, const AstNodeDType* nodedtypep)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, nodedtypep} {
        initWithNumber();
    }
    class StringToParse {};  // for creator type-overload selection
    AstConst(FileLine* fl, StringToParse, const char* sourcep)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, V3Number::VerilogNumberLiteral{}, sourcep} {
        initWithNumber();
    }
    class VerilogStringLiteral {};  // for creator type-overload selection
    AstConst(FileLine* fl, VerilogStringLiteral, const string& str)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, V3Number::VerilogStringLiteral{}, str} {
        initWithNumber();
    }
    AstConst(FileLine* fl, uint32_t num)
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 32, num) {  // Need () constructor
        dtypeSetLogicUnsized(m_num.width(), 0, VSigning::UNSIGNED);
    }
    class Unsized32 {};  // for creator type-overload selection
    AstConst(FileLine* fl, Unsized32, uint32_t num)  // Unsized 32-bit integer of specified value
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 32, num) {  // Need () constructor
        m_num.width(32, false);
        dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::UNSIGNED);
    }
    class Signed32 {};  // for creator type-overload selection
    AstConst(FileLine* fl, Signed32, int32_t num)  // Signed 32-bit integer of specified value
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 32, num) {  // Need () constructor
        m_num.width(32, true);
        dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::SIGNED);
    }
    class Unsized64 {};  // for creator type-overload selection
    AstConst(FileLine* fl, Unsized64, uint64_t num)
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 64, 0) {  // Need () constructor
        m_num.setQuad(num);
        dtypeSetLogicSized(64, VSigning::UNSIGNED);
    }
    class SizedEData {};  // for creator type-overload selection
    AstConst(FileLine* fl, SizedEData, uint64_t num)
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, VL_EDATASIZE, 0) {  // Need () constructor
        m_num.setQuad(num);
        dtypeSetLogicSized(VL_EDATASIZE, VSigning::UNSIGNED);
    }
    class RealDouble {};  // for creator type-overload selection
    AstConst(FileLine* fl, RealDouble, double num)
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 64) {  // Need () constructor
        m_num.setDouble(num);
        dtypeSetDouble();
    }
    class String {};  // for creator type-overload selection
    AstConst(FileLine* fl, String, const string& num)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, V3Number::String{}, num} {
        dtypeSetString();
    }
    class BitFalse {};
    AstConst(FileLine* fl, BitFalse)  // Shorthand const 0, dtype should be a logic of size 1
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 1, 0) {  // Need () constructor
        dtypeSetBit();
    }
    // Shorthand const 1 (or with argument 0/1), dtype should be a logic of size 1
    class BitTrue {};
    AstConst(FileLine* fl, BitTrue, bool on = true)
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 1, on) {  // Need () constructor
        dtypeSetBit();
    }
    class All0 {};
    AstConst(FileLine* fl, All0)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, V3Number::VerilogNumberLiteral{}, "'0"} {
        initWithNumber();
        fl->warnOff(V3ErrorCode::NEWERSTD, true);
    }
    class All1 {};
    AstConst(FileLine* fl, All1)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, V3Number::VerilogNumberLiteral{}, "'1"} {
        initWithNumber();
        fl->warnOff(V3ErrorCode::NEWERSTD, true);
    }
    class Null {};
    AstConst(FileLine* fl, Null)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, V3Number::Null{}} {
        dtypeSetBit();  // Events 1 bit, objects 64 bits, so autoExtend=1 and use bit here
        initWithNumber();
    }
    class OneStep {};
    AstConst(FileLine* fl, OneStep)
        : ASTGEN_SUPER_Const(fl)
        , m_num{this, V3Number::OneStep{}} {
        dtypeSetLogicSized(64, VSigning::UNSIGNED);
        initWithNumber();
    }
    ASTGEN_MEMBERS_AstConst;
    string name() const override VL_MT_STABLE { return num().ascii(); }  // * = Value
    const V3Number& num() const VL_MT_SAFE { return m_num; }  // * = Value
    V3Number& num() { return m_num; }  // * = Value
    uint32_t toUInt() const { return num().toUInt(); }
    int32_t toSInt() const VL_MT_SAFE { return num().toSInt(); }
    uint64_t toUQuad() const { return num().toUQuad(); }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* samep) const override {
        const AstConst* const sp = VN_DBG_AS(samep, Const);
        return num().isCaseEq(sp->num());
    }
    void cloneRelink() override { m_num.nodep(this); }
    const char* broken() const override {
        BROKEN_RTN(m_num.nodep() && m_num.nodep() != this);
        return nullptr;
    }
    int instrCount() const override { return widthInstrs(); }
    bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
    bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
    // Parse string and create appropriate type of AstConst.
    // May return nullptr on parse failure.
    static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
};
class AstConstraintRef final : public AstNodeExpr {
    // A reference to a constraint identifier
    // @astgen op1 := fromp : Optional[AstNodeExpr]
    //
    // @astgen ptr := m_constrp : AstConstraint  // The constraint pointed to
    // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule]  // Class/package of constraint

public:
    AstConstraintRef(FileLine* fl, AstNodeExpr* fromp, AstConstraint* constrp)
        : ASTGEN_SUPER_ConstraintRef(fl) {
        this->fromp(fromp);
        this->constrp(constrp);
        dtypep(findConstraintRefDType());
    }
    ASTGEN_MEMBERS_AstConstraintRef;
    string name() const override VL_MT_STABLE;
    AstConstraint* constrp() const VL_MT_STABLE { return m_constrp; }
    void constrp(AstConstraint* nodep) { m_constrp = nodep; }
    AstNodeModule* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
    // TODO: this setter is unused
    void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }

    string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
    string emitC() final override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
};
class AstCvtArrayToArray final : public AstNodeExpr {
    // Copy/Cast from dynamic/unpacked types to dynamic/unpacked types
    // @astgen op1 := fromp : AstNodeExpr
private:
    const bool m_reverse;  // whether ordering gets reversed in this operation (ex: {<<{expr}})
    const int m_blockSize;  // num bits per block in a streaming operation (ex: 4 in {<<4{expr}}))
    const int m_dstElementBits;  // num bits in lhs (ex: 8 if to byte-queue, 1 if to bit-queue)
    const int m_srcElementBits;  // num bits in rhs (ex 8 if from byte-queue, 1 if from bit-queue)

public:
    // cppcheck-suppress constParameterPointer
    // cppcheck-suppress constParameterCallback
    AstCvtArrayToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp, bool reverse,
                       int blockSize, int dstElementBits, int srcElementBits)
        : ASTGEN_SUPER_CvtArrayToArray(fl)
        , m_reverse{reverse}
        , m_blockSize{blockSize}
        , m_dstElementBits{dstElementBits}
        , m_srcElementBits{srcElementBits} {
        this->fromp(fromp);
        dtypeFrom(dtp);
    }
    ASTGEN_MEMBERS_AstCvtArrayToArray;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool reverse() const { return m_reverse; }
    int blockSize() const { return m_blockSize; }
    int dstElementBits() const { return m_dstElementBits; }
    int srcElementBits() const { return m_srcElementBits; }
};
class AstCvtArrayToPacked final : public AstNodeExpr {
    // Cast from dynamic queue data type to packed array
    // @astgen op1 := fromp : AstNodeExpr
public:
    // cppcheck-suppress constParameterPointer
    // cppcheck-suppress constParameterCallback
    AstCvtArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
        : ASTGEN_SUPER_CvtArrayToPacked(fl) {
        this->fromp(fromp);
        dtypeFrom(dtp);
    }
    ASTGEN_MEMBERS_AstCvtArrayToPacked;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
};
class AstCvtPackedToArray final : public AstNodeExpr {
    // Cast from packed array to dynamic/unpacked queue data type
    // @astgen op1 := fromp : AstNodeExpr
public:
    // cppcheck-suppress constParameterPointer
    // cppcheck-suppress constParameterCallback
    AstCvtPackedToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
        : ASTGEN_SUPER_CvtPackedToArray(fl) {
        this->fromp(fromp);
        dtypeFrom(dtp);
    }
    ASTGEN_MEMBERS_AstCvtPackedToArray;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
};
class AstCvtUnpackedToQueue final : public AstNodeExpr {
    // Cast from unpacked array to dynamic/unpacked queue data type
    // @astgen op1 := fromp : AstNodeExpr
public:
    // cppcheck-suppress constParameterPointer
    // cppcheck-suppress constParameterCallback
    AstCvtUnpackedToQueue(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
        : ASTGEN_SUPER_CvtUnpackedToQueue(fl) {
        this->fromp(fromp);
        dtypeFrom(dtp);
    }
    ASTGEN_MEMBERS_AstCvtUnpackedToQueue;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { return "VL_CVT_UNPACK_TO_Q(%P, %li)"; }
    bool cleanOut() const override { return true; }
};
class AstDist final : public AstNodeExpr {
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := itemsp : List[AstDistItem]
public:
    AstDist(FileLine* fl, AstNodeExpr* exprp, AstDistItem* itemsp)
        : ASTGEN_SUPER_Inside(fl) {
        this->exprp(exprp);
        addItemsp(itemsp);
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstDist;
    string emitVerilog() override { return "%l dist { %r }"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }  // NA
};
class AstDistItem final : public AstNodeExpr {
    // Constraint distribution item
    // @astgen op1 := rangep : AstNodeExpr
    // @astgen op2 := weightp : AstNodeExpr
    bool m_isWhole = false;  // True for weight ':/', false for ':='
public:
    AstDistItem(FileLine* fl, AstNodeExpr* rangep, AstNodeExpr* weightp)
        : ASTGEN_SUPER_DistItem(fl) {
        this->rangep(rangep);
        this->weightp(weightp);
    }
    ASTGEN_MEMBERS_AstDistItem;
    string emitVerilog() override { return "%l "s + (m_isWhole ? ":/" : ":=") + " %r"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }  // NA
    void isWhole(bool flag) { m_isWhole = flag; }
    bool isWhole() const { return m_isWhole; }
};
class AstDot final : public AstNodeExpr {
    // A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
    // These are eliminated in the link stage
    // @astgen op1 := lhsp : AstNode<AstNodeExpr|AstNodeDType>
    // @astgen op2 := rhsp : AstNode<AstNodeExpr|AstNodeDType>
    //
    // We don't have a list of elements as it's probably legal to do '(foo.bar).(baz.bap)'
    const bool m_colon;  // Is a "::" instead of a "." (lhs must be package/class)
public:
    AstDot(FileLine* fl, bool colon, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Dot(fl)
        , m_colon{colon} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstDot;
    // For parser, make only if non-null package
    static AstNodeExpr* newIfPkg(FileLine* fl, AstNodeExpr* packageOrClassp, AstNodeExpr* rhsp) {
        if (!packageOrClassp) return rhsp;
        return new AstDot{fl, true, packageOrClassp, rhsp};
    }
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    bool colon() const { return m_colon; }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstEmptyQueue final : public AstNodeExpr {
public:
    explicit AstEmptyQueue(FileLine* fl)
        : ASTGEN_SUPER_EmptyQueue(fl) {}
    ASTGEN_MEMBERS_AstEmptyQueue;
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitVerilog() override { return "{}"; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool cleanOut() const override { return true; }
};
class AstEnumItemRef final : public AstNodeExpr {
    // @astgen ptr := m_itemp : Optional[AstEnumItem]  // [AfterLink] Pointer to item
    // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule]  // Class/package defined in
    string m_name;  // Name of enum (for param relink)
public:
    AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
        : ASTGEN_SUPER_EnumItemRef(fl)
        , m_name{itemp->name()}
        , m_itemp{itemp}
        , m_classOrPackagep{classOrPackagep} {
        dtypeFrom(m_itemp);
    }
    ASTGEN_MEMBERS_AstEnumItemRef;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    const char* broken() const override;
    string name() const override VL_MT_STABLE { return itemp() ? itemp()->name() : m_name; }
    int instrCount() const override { return 0; }
    bool sameNode(const AstNode* samep) const override {
        const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef);
        return itemp() == sp->itemp();
    }
    AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; }
    void itemp(AstEnumItem* nodep) { m_itemp = nodep; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
    void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
};
class AstExprStmt final : public AstNodeExpr {
    // Perform a statement, often assignment inside an expression node,
    // the parent gets passed the 'resultp()'.
    // resultp is evaluated AFTER the statement(s).
    // @astgen op1 := stmtsp : List[AstNode]
    // @astgen op2 := resultp : AstNodeExpr
private:
    bool m_hasResult = true;

public:
    AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp)
        : ASTGEN_SUPER_ExprStmt(fl) {
        addStmtsp(stmtsp);
        this->resultp(resultp);
        dtypeFrom(resultp);
    }
    ASTGEN_MEMBERS_AstExprStmt;
    // METHODS
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool isPure() override {
        if (AstNode::afterCommentp(stmtsp())) return false;
        return resultp()->isPure();
    }
    bool sameNode(const AstNode*) const override { return true; }
    bool hasResult() { return m_hasResult; }
    void hasResult(bool flag) { m_hasResult = flag; }
};
class AstFError final : public AstNodeExpr {
    // @astgen op1 := filep : AstNode
    // @astgen op2 := strp : AstNode
public:
    AstFError(FileLine* fl, AstNode* filep, AstNode* strp)
        : ASTGEN_SUPER_FError(fl) {
        this->filep(filep);
        this->strp(strp);
    }
    ASTGEN_MEMBERS_AstFError;
    string emitVerilog() override { return "%f$ferror(%l, %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs() * 64; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: $display has 'visual' ordering
    bool isSystemFunc() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstFOpen final : public AstNodeExpr {
    // @astgen op2 := filenamep : AstNodeExpr
    // @astgen op3 := modep : AstNodeExpr
public:
    AstFOpen(FileLine* fl, AstNodeExpr* filenamep, AstNodeExpr* modep)
        : ASTGEN_SUPER_FOpen(fl) {
        this->filenamep(filenamep);
        this->modep(modep);
    }
    ASTGEN_MEMBERS_AstFOpen;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    string verilogKwd() const override { return "$fopen"; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }
    bool isOutputter() override { return true; }
    bool isUnlikely() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstFOpenMcd final : public AstNodeExpr {
    // @astgen op2 := filenamep : AstNodeExpr
public:
    AstFOpenMcd(FileLine* fl, AstNodeExpr* filenamep)
        : ASTGEN_SUPER_FOpenMcd(fl) {
        this->filenamep(filenamep);
    }
    ASTGEN_MEMBERS_AstFOpenMcd;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    string verilogKwd() const override { return "$fopen"; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }
    bool isOutputter() override { return true; }
    bool isUnlikely() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstFRead final : public AstNodeExpr {
    // @astgen op1 := memp : AstNode // VarRef for result
    // @astgen op2 := filep : AstNode // file (must be a VarRef)
    // @astgen op3 := startp : Optional[AstNode] // Offset
    // @astgen op4 := countp : Optional[AstNode] // Size
public:
    AstFRead(FileLine* fl, AstNode* memp, AstNode* filep, AstNode* startp, AstNode* countp)
        : ASTGEN_SUPER_FRead(fl) {
        this->memp(memp);
        this->filep(filep);
        this->startp(startp);
        this->countp(countp);
    }
    ASTGEN_MEMBERS_AstFRead;
    string verilogKwd() const override { return "$fread"; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstFRewind final : public AstNodeExpr {
    // @astgen op1 := filep : Optional[AstNode]
public:
    AstFRewind(FileLine* fl, AstNode* filep)
        : ASTGEN_SUPER_FRewind(fl) {
        this->filep(filep);
    }
    ASTGEN_MEMBERS_AstFRewind;
    string verilogKwd() const override { return "$frewind"; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }
    bool isOutputter() override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return false; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstFScanF final : public AstNodeExpr {
    // @astgen op1 := exprsp : List[AstNode] // VarRefs for results
    // @astgen op2 := filep : Optional[AstNode] // file (must be a VarRef)
    string m_text;

public:
    AstFScanF(FileLine* fl, const string& text, AstNode* filep, AstNode* exprsp)
        : ASTGEN_SUPER_FScanF(fl)
        , m_text{text} {
        addExprsp(exprsp);
        this->filep(filep);
    }
    ASTGEN_MEMBERS_AstFScanF;
    string name() const override VL_MT_STABLE { return m_text; }
    string verilogKwd() const override { return "$fscanf"; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool sameNode(const AstNode* samep) const override {
        return text() == VN_DBG_AS(samep, FScanF)->text();
    }
    string text() const { return m_text; }  // * = Text to display
    void text(const string& text) { m_text = text; }
    bool isSystemFunc() const override { return true; }
};
class AstFSeek final : public AstNodeExpr {
    // @astgen op1 := filep : AstNode // file (must be a VarRef)
    // @astgen op2 := offset : Optional[AstNode]
    // @astgen op3 := operation : Optional[AstNode]
public:
    AstFSeek(FileLine* fl, AstNode* filep, AstNode* offset, AstNode* operation)
        : ASTGEN_SUPER_FSeek(fl) {
        this->filep(filep);
        this->offset(offset);
        this->operation(operation);
    }
    ASTGEN_MEMBERS_AstFSeek;
    string verilogKwd() const override { return "$fseek"; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstFTell final : public AstNodeExpr {
    // @astgen op1 := filep : AstNode // file (must be a VarRef)
public:
    AstFTell(FileLine* fl, AstNode* filep)
        : ASTGEN_SUPER_FTell(fl) {
        this->filep(filep);
    }
    ASTGEN_MEMBERS_AstFTell;
    string verilogKwd() const override { return "$ftell"; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }
    bool isOutputter() override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return false; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstFalling final : public AstNodeExpr {
    // Verilog $falling_gclk
    // @astgen op1 := exprp : AstNodeExpr
public:
    AstFalling(FileLine* fl, AstNodeExpr* exprp)
        : ASTGEN_SUPER_Falling(fl) {
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstFalling;
    string emitVerilog() override { return "$falling_gclk(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstFell final : public AstNodeExpr {
    // Verilog $fell
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := sentreep : Optional[AstSenTree]
public:
    AstFell(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep)
        : ASTGEN_SUPER_Fell(fl) {
        this->exprp(exprp);
        this->sentreep(sentreep);
    }
    ASTGEN_MEMBERS_AstFell;
    string emitVerilog() override { return "$fell(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstFuture final : public AstNodeExpr {
    // Verilog $future_gclk
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := sentreep : Optional[AstSenTree]
public:
    AstFuture(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep)
        : ASTGEN_SUPER_Future(fl) {
        this->exprp(exprp);
        this->sentreep(sentreep);
    }
    ASTGEN_MEMBERS_AstFuture;
    string emitVerilog() override { return "$future_gclk(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstGatePin final : public AstNodeExpr {
    // Possibly expand a gate primitive input pin value to match the range of the gate primitive
    // @astgen op1 := exprp : AstNodeExpr // Pin expression
    // @astgen op2 := rangep : AstRange // Range of pin
public:
    AstGatePin(FileLine* fl, AstNodeExpr* exprp, AstRange* rangep)
        : ASTGEN_SUPER_GatePin(fl) {
        this->exprp(exprp);
        this->rangep(rangep);
    }
    ASTGEN_MEMBERS_AstGatePin;
    string emitVerilog() override { return "%l"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
};
class AstImplication final : public AstNodeExpr {
    // Verilog Implication Operator
    // Nonoverlapped "|=>"
    // Overlapped "|->"
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    // @astgen op3 := sentreep : Optional[AstSenTree]

private:
    const bool m_isOverlapped;  // True if overlapped

public:
    AstImplication(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool isOverlapped)
        : ASTGEN_SUPER_Implication(fl)
        , m_isOverlapped{isOverlapped} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstImplication;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isOverlapped() const { return m_isOverlapped; }
};
class AstInitArray final : public AstNodeExpr {
    // This is also used as an array value in V3Simulate/const prop.
    // Would be better called as 'AstConstArray'
    // Set a var to a map of values
    // The list of initsp() is not relevant
    // If default is specified, the vector may be sparse, and not provide each value.
    // Key values are C++ array style, with lo() at index 0
    // Parents: ASTVAR::init()
    // @astgen op1 := defaultp : Optional[AstNodeExpr] // Default, if sparse
    // @astgen op2 := initsp : List[AstNode] // Initial value expressions
    //
public:
    using KeyItemMap = std::map<uint64_t, AstInitItem*>;

private:
    KeyItemMap m_map;  // Node value for each array index
    // METHODS
    void dumpInitList(std::ostream& str) const;

public:
    AstInitArray(FileLine* fl, AstNodeDType* newDTypep, AstNodeExpr* defaultp)
        : ASTGEN_SUPER_InitArray(fl) {
        dtypep(newDTypep);
        this->defaultp(defaultp);
    }
    ASTGEN_MEMBERS_AstInitArray;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    const char* broken() const override;
    void cloneRelink() override;
    bool sameNode(const AstNode* samep) const override {
        // Only works if exact same children, instead should override comparison
        // of children list, and instead use map-vs-map key/value compare
        return m_map == VN_DBG_AS(samep, InitArray)->m_map;
    }
    void addValuep(AstNodeExpr* newp) { addIndexValuep(m_map.size(), newp); }
    const KeyItemMap& map() const { return m_map; }
    void addIndexValuep(uint64_t index, AstNodeExpr* newp);
    AstNodeExpr* getIndexValuep(uint64_t index) const;
    AstNodeExpr* getIndexDefaultedValuep(uint64_t index) const;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
};
class AstInside final : public AstNodeExpr {
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := itemsp : List[AstNodeExpr]
public:
    AstInside(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* itemsp)
        : ASTGEN_SUPER_Inside(fl) {
        this->exprp(exprp);
        addItemsp(itemsp);
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstInside;
    string emitVerilog() override { return "%l inside { %r }"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }  // NA
};
class AstInsideRange final : public AstNodeExpr {
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
public:
    AstInsideRange(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_InsideRange(fl) {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstInsideRange;
    string emitVerilog() override { return "[%l:%r]"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }  // NA
    // Create AstAnd(AstGte(...), AstLte(...))
    AstNodeExpr* newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* lhsp, AstNodeExpr* rhsp);
};
class AstLambdaArgRef final : public AstNodeExpr {
    // Lambda argument usage
    // These are not AstVarRefs because we need to be able to delete/clone lambdas during
    // optimizations and AstVar's are painful to remove.
    string m_name;  // Name of variable
    bool m_index;  // Index, not value

public:
    AstLambdaArgRef(FileLine* fl, const string& name, bool index)
        : ASTGEN_SUPER_LambdaArgRef(fl)
        , m_name{name}
        , m_index{index} {}
    ASTGEN_MEMBERS_AstLambdaArgRef;
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    string emitVerilog() override { return name(); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    string name() const override VL_MT_STABLE { return m_name; }  // * = Var name
    void name(const string& name) override { m_name = name; }
    bool index() const { return m_index; }
    bool isExprCoverageEligible() const override { return false; }
};
class AstMemberSel final : public AstNodeExpr {
    // @astgen op1 := fromp : AstNodeExpr
    //
    // @astgen ptr := m_varp : Optional[AstVar]  // Post link, variable in class that is selecting
    // Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
    string m_name;
    VAccess m_access;  // Read or write, as in AstNodeVarRef
public:
    AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
        : ASTGEN_SUPER_MemberSel(fl)
        , m_name{name} {
        this->fromp(fromp);
        dtypep(nullptr);  // V3Width will resolve
    }
    AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstVar* varp);
    ASTGEN_MEMBERS_AstMemberSel;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    string name() const override VL_MT_STABLE { return m_name; }
    void name(const string& name) override { m_name = name; }
    VAccess access() const { return m_access; }
    void access(const VAccess& flag) { m_access = flag; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* samep) const override;
    int instrCount() const override { return widthInstrs(); }
    AstVar* varp() const { return m_varp; }
    void varp(AstVar* nodep) { m_varp = nodep; }
};
class AstNewCopy final : public AstNodeExpr {
    // New as shallow copy
    // @astgen op1 := rhsp : AstNodeExpr
public:
    AstNewCopy(FileLine* fl, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_NewCopy(fl) {
        dtypeFrom(rhsp);  // otherwise V3Width will resolve
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstNewCopy;
    string emitVerilog() override { return "new"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
};
class AstNewDynamic final : public AstNodeExpr {
    // New for dynamic array
    // @astgen op1 := sizep : AstNodeExpr
    // @astgen op2 := rhsp : Optional[AstNodeExpr]
public:
    AstNewDynamic(FileLine* fl, AstNodeExpr* sizep, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_NewDynamic(fl) {
        dtypeFrom(rhsp);  // otherwise V3Width will resolve
        this->sizep(sizep);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstNewDynamic;
    string emitVerilog() override { return "new"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
};
class AstPExpr final : public AstNodeExpr {
    // Property expression
    // @astgen op1 := bodyp : AstBegin
public:
    explicit AstPExpr(FileLine* fl, AstBegin* bodyp, AstNodeDType* dtypep)
        : ASTGEN_SUPER_PExpr(fl) {
        this->bodyp(bodyp);
        this->dtypep(dtypep);
    }
    ASTGEN_MEMBERS_AstPExpr;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
};
class AstParseHolder final : public AstNodeExpr {
    // A reference to something soon to replace, used in a select at parse time
    // that needs conversion to pull the upper lvalue later
public:
    explicit AstParseHolder(FileLine* fl)
        : ASTGEN_SUPER_ParseHolder(fl) {}
    ASTGEN_MEMBERS_AstParseHolder;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstParseRef final : public AstNodeExpr {
    // A reference to a variable, function or task
    // We don't know which at parse time due to bison constraints
    // The link stages will replace this with AstVarRef, or AstTaskRef, etc.
    // @astgen op1 := lhsp : Optional[AstNode]
    // @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef]

    string m_name;

public:
    AstParseRef(FileLine* fl, const string& name, AstNode* lhsp = nullptr,
                AstNodeFTaskRef* ftaskrefp = nullptr)
        : ASTGEN_SUPER_ParseRef(fl)
        , m_name{name} {
        this->lhsp(lhsp);
        this->ftaskrefp(ftaskrefp);
    }
    ASTGEN_MEMBERS_AstParseRef;
    string name() const override VL_MT_STABLE { return m_name; }  // * = Var name
    void name(const string& name) override { m_name = name; }
    bool sameNode(const AstNode* samep) const override {
        const AstParseRef* const asamep = VN_DBG_AS(samep, ParseRef);
        return m_name == asamep->m_name;
    }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstPast final : public AstNodeExpr {
    // Verilog $past
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := ticksp : Optional[AstNode]
    // @astgen op3 := sentreep : Optional[AstSenTree]
public:
    AstPast(FileLine* fl, AstNodeExpr* exprp, AstNode* ticksp = nullptr,
            AstSenTree* sentreep = nullptr)
        : ASTGEN_SUPER_Past(fl) {
        this->exprp(exprp);
        this->ticksp(ticksp);
        this->sentreep(sentreep);
    }
    ASTGEN_MEMBERS_AstPast;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstPatMember final : public AstNodeExpr {
    // Verilog '{a} or '{a{b}}
    // Parents: AstPattern
    // Children: expression, AstPattern, replication count, decoded nodep if TEXT
    // Expression to assign or another AstPattern (list if replicated)
    // @astgen op1 := lhssp : List[AstNodeExpr]
    // @astgen op2 := keyp : Optional[AstNode]
    // @astgen op3 := repp : Optional[AstNodeExpr]  // replication count, or nullptr for count 1
    // @astgen op4 := varrefp : Optional[AstNodeExpr]  // Decoded variable if TEXT
    bool m_isDefault = false;  // Has default
    bool m_isConcat = false;  // From concatenate

public:
    AstPatMember(FileLine* fl, AstNodeExpr* lhssp, AstNode* keyp, AstNodeExpr* repp)
        : ASTGEN_SUPER_PatMember(fl) {
        addLhssp(lhssp);
        this->keyp(keyp);
        this->repp(repp);
    }
    ASTGEN_MEMBERS_AstPatMember;
    string emitVerilog() override { return lhssp() ? "%f{%r{%k%l}}" : "%l"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs() * 2; }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    bool isConcat() const { return m_isConcat; }
    void isConcat(bool flag) { m_isConcat = flag; }
    bool isDefault() const { return m_isDefault; }
    void isDefault(bool flag) { m_isDefault = flag; }
};
class AstPattern final : public AstNodeExpr {
    // Verilog '{a,b,c,d...}
    // Parents: AstNodeAssign, AstPattern, ...
    // Children: expression, AstPattern, AstPatReplicate
    // @astgen op1 := childDTypep : Optional[AstNodeDType]
    // @astgen op2 := itemsp : List[AstNode] // AstPatReplicate, AstPatMember, etc
public:
    AstPattern(FileLine* fl, AstNode* itemsp)
        : ASTGEN_SUPER_Pattern(fl) {
        addItemsp(itemsp);
    }
    ASTGEN_MEMBERS_AstPattern;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    AstNodeDType* getChildDTypep() const override { return childDTypep(); }
    AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); }
};
class AstRand final : public AstNodeExpr {
    // $random/$random(seed) or $urandom/$urandom(seed)
    // Return a random number, based upon width()
    // @astgen op1 := seedp : Optional[AstNode]
    const bool m_urandom;  // $urandom vs $random
public:
    class Reset {};
    AstRand(FileLine* fl, AstNode* seedp, bool urandom)
        : ASTGEN_SUPER_Rand(fl)
        , m_urandom{urandom} {
        this->seedp(seedp);
    }
    class SRandomU32 {};
    AstRand(FileLine* fl, SRandomU32)
        : ASTGEN_SUPER_Rand(fl)
        , m_urandom{true} {
        dtypeSetUInt32();
    }
    ASTGEN_MEMBERS_AstRand;
    string emitVerilog() override {
        return seedp() ? (m_urandom ? "%f$urandom(%l)" : "%f$random(%l)")
                       : (m_urandom ? "%f$urandom()" : "%f$random()");
    }
    string emitC() override {
        if (seedp()) {
            if (urandom()) {
                return "VL_URANDOM_SEEDED_%nq%lq(%li)";
            } else {
                return "VL_RANDOM_SEEDED_%nq%lq(%li)";
            }
        }
        if (isWide()) {
            return "VL_RANDOM_%nq(%nw, %P)";
        } else {
            return "VL_RANDOM_%nq()";
        }
    }
    bool cleanOut() const override { return false; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return !seedp(); }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_PLI; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool combinable(const AstRand* samep) const {
        return !seedp() && !samep->seedp() && urandom() == samep->urandom();
    }
    bool urandom() const { return m_urandom; }
};
class AstRandRNG final : public AstNodeExpr {
    // Random used in a class using VlRNG
    // Return a random number, based upon width()
public:
    AstRandRNG(FileLine* fl, AstNodeDType* dtp)
        : ASTGEN_SUPER_RandRNG(fl) {
        dtypep(dtp);
    }
    ASTGEN_MEMBERS_AstRandRNG;
    string emitVerilog() override { return "%f$rngrandom()"; }
    string emitC() override {
        return isWide() ? "VL_RANDOM_RNG_%nq(__Vm_rng, %nw, %P)"  //
                        : "VL_RANDOM_RNG_%nq(__Vm_rng)";
    }
    bool cleanOut() const override { return false; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_PLI; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstRising final : public AstNodeExpr {
    // Verilog $rising_gclk
    // @astgen op1 := exprp : AstNodeExpr
public:
    AstRising(FileLine* fl, AstNodeExpr* exprp)
        : ASTGEN_SUPER_Rising(fl) {
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstRising;
    string emitVerilog() override { return "$rising_gclk(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstRose final : public AstNodeExpr {
    // Verilog $rose
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := sentreep : Optional[AstSenTree]
public:
    AstRose(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep)
        : ASTGEN_SUPER_Rose(fl) {
        this->exprp(exprp);
        this->sentreep(sentreep);
    }
    ASTGEN_MEMBERS_AstRose;
    string emitVerilog() override { return "$rose(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstSExpr final : public AstNodeExpr {
    // Sequence expression
    // @astgen op1 := preExprp: Optional[AstNodeExpr]
    // @astgen op2 := delayp : AstNodeStmt<AstDelay|AstBegin>
    // @astgen op3 := exprp : AstNodeExpr
public:
    explicit AstSExpr(FileLine* fl, AstNodeExpr* preExprp, AstNodeStmt* delayp, AstNodeExpr* exprp)
        : ASTGEN_SUPER_SExpr(fl) {
        this->preExprp(preExprp);
        this->delayp(delayp);
        this->exprp(exprp);
    }
    explicit AstSExpr(FileLine* fl, AstNodeStmt* delayp, AstNodeExpr* exprp)
        : ASTGEN_SUPER_SExpr(fl) {
        this->preExprp(nullptr);
        this->delayp(delayp);
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstSExpr;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
};
class AstSFormatF final : public AstNodeExpr {
    // Convert format to string, generally under an AstDisplay or AstSFormat
    // Also used as "real" function for /*verilator sformat*/ functions
    // @astgen op1 := exprsp : List[AstNodeExpr]
    // @astgen op2 := scopeNamep : Optional[AstScopeName]
    string m_text;
    const bool m_hidden;  // Under display, etc
    bool m_hasFormat;  // Has format code
    const char m_missingArgChar;  // Format code when argument without format, 'h'/'o'/'b'
    VTimescale m_timeunit;  // Parent module time unit
public:
    class NoFormat {};
    AstSFormatF(FileLine* fl, const string& text, bool hidden, AstNodeExpr* exprsp,
                char missingArgChar = 'd')
        : ASTGEN_SUPER_SFormatF(fl)
        , m_text{text}
        , m_hidden{hidden}
        , m_hasFormat{true}
        , m_missingArgChar{missingArgChar} {
        dtypeSetString();
        addExprsp(exprsp);
    }
    AstSFormatF(FileLine* fl, NoFormat, AstNodeExpr* exprsp, char missingArgChar = 'd',
                bool hidden = true)
        : ASTGEN_SUPER_SFormatF(fl)
        , m_text{""}
        , m_hidden{hidden}
        , m_hasFormat{false}
        , m_missingArgChar{missingArgChar} {
        dtypeSetString();
        addExprsp(exprsp);
    }
    ASTGEN_MEMBERS_AstSFormatF;
    string name() const override VL_MT_STABLE { return m_text; }
    void name(const string& name) override { m_text = name; }
    int instrCount() const override { return INSTR_COUNT_PLI; }
    bool sameNode(const AstNode* samep) const override {
        return text() == VN_DBG_AS(samep, SFormatF)->text();
    }
    string verilogKwd() const override { return "$sformatf"; }
    string text() const { return m_text; }  // * = Text to display
    void text(const string& text) { m_text = text; }
    bool formatScopeTracking() const {  // Track scopeNamep();  Ok if false positive
        return (name().find("%m") != string::npos || name().find("%M") != string::npos);
    }
    bool hidden() const { return m_hidden; }
    bool hasFormat() const { return m_hasFormat; }
    void hasFormat(bool flag) { m_hasFormat = flag; }
    char missingArgChar() const { return m_missingArgChar; }
    VTimescale timeunit() const { return m_timeunit; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
    bool isSystemFunc() const override { return true; }
};
class AstSScanF final : public AstNodeExpr {
    // @astgen op1 := exprsp : List[AstNode] // VarRefs for results
    // @astgen op2 := fromp : AstNode
    string m_text;
    VTimescale m_timeunit;  // Parent module time unit

public:
    AstSScanF(FileLine* fl, const string& text, AstNode* fromp, AstNode* exprsp)
        : ASTGEN_SUPER_SScanF(fl)
        , m_text{text} {
        addExprsp(exprsp);
        this->fromp(fromp);
    }
    ASTGEN_MEMBERS_AstSScanF;
    string name() const override VL_MT_STABLE { return m_text; }
    string verilogKwd() const override { return "$sscanf"; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool sameNode(const AstNode* samep) const override {
        const AstSScanF* const asamep = VN_DBG_AS(samep, SScanF);
        return text() == asamep->text() && timeunit() == asamep->timeunit();
    }
    string text() const { return m_text; }  // * = Text to display
    VTimescale timeunit() const { return m_timeunit; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
    bool isSystemFunc() const override { return true; }
};
class AstSampled final : public AstNodeExpr {
    // Verilog $sampled
    // @astgen op1 := exprp : AstNode // AstNodeExpr or AstPropSpec
public:
    AstSampled(FileLine* fl, AstNode* exprp)
        : ASTGEN_SUPER_Sampled(fl) {
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstSampled;
    string emitVerilog() override { return "$sampled(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return 0; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstScopeName final : public AstNodeExpr {
    // For display %m and DPI context imports
    // Parents:  AstSFormatF, AstNodeFTaskRef, AstNodeFTask
    std::string m_scopeAttr;
    std::string m_scopeEntr;
    bool m_dpiExport = false;  // Is for dpiExport
    const bool m_forFormat;  // Is for a format %m
    static std::string scopeNameFormatter(const std::string& text);
    static std::string scopePrettyNameFormatter(const std::string& text);

public:
    class ForFormat {};
    AstScopeName(FileLine* fl, bool forFormat)
        : ASTGEN_SUPER_ScopeName(fl)
        , m_forFormat{forFormat} {
        dtypeSetUInt64();
    }
    ASTGEN_MEMBERS_AstScopeName;
    bool sameNode(const AstNode* samep) const override {
        const AstScopeName* const sp = VN_DBG_AS(samep, ScopeName);
        return m_scopeAttr == sp->m_scopeAttr  //
               && m_scopeEntr == sp->m_scopeEntr  //
               && m_dpiExport == sp->m_dpiExport  //
               && m_forFormat == sp->m_forFormat;
    }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    // ACCESSORS
    const std::string& scopeAttr() const { return m_scopeAttr; }
    void scopeAttr(const std::string& val) { m_scopeAttr = val; }
    const std::string& scopeEntr() const { return m_scopeEntr; }
    void scopeEntr(const std::string& val) { m_scopeEntr = val; }
    bool dpiExport() const { return m_dpiExport; }
    void dpiExport(bool flag) { m_dpiExport = flag; }
    bool forFormat() const { return m_forFormat; }
    // Name for __Vscopep variable including children
    string scopeSymName() const { return scopeNameFormatter(m_scopeAttr); }
    // Name for DPI import scope
    string scopeDpiName() const { return scopeNameFormatter(m_scopeEntr); }
    // Name for __Vscopep variable including children
    string scopePrettySymName() const { return scopePrettyNameFormatter(m_scopeAttr); }
    // Name for __Vscopep variable including children
    string scopePrettyDpiName() const { return scopePrettyNameFormatter(m_scopeEntr); }
};
class AstSelLoopVars final : public AstNodeExpr {
    // Parser only concept "[id, id, id]" for a foreach statement
    // Unlike normal selects elements is a list
    // TODO: Should not be an AstNodeExpr, model foreach better
    // @astgen op1 := fromp : AstNodeExpr
    // @astgen op2 := elementsp : List[AstNode]
public:
    AstSelLoopVars(FileLine* fl, AstNodeExpr* fromp, AstNode* elementsp)
        : ASTGEN_SUPER_SelLoopVars(fl) {
        this->fromp(fromp);
        addElementsp(elementsp);
    }
    ASTGEN_MEMBERS_AstSelLoopVars;
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool maybePointedTo() const override VL_MT_SAFE { return false; }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
    bool hasDType() const override VL_MT_SAFE { return false; }
};
class AstSetAssoc final : public AstNodeExpr {
    // Set an assoc array element and return object, '{}
    // @astgen op1 := lhsp : AstNode
    // @astgen op2 := keyp : Optional[AstNode]
    // @astgen op3 := valuep : AstNodeExpr
public:
    AstSetAssoc(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNodeExpr* valuep)
        : ASTGEN_SUPER_SetAssoc(fl) {
        this->lhsp(lhsp);
        this->keyp(keyp);
        this->valuep(valuep);
    }
    ASTGEN_MEMBERS_AstSetAssoc;
    string emitVerilog() override { return "'{}"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstSetWildcard final : public AstNodeExpr {
    // Set a wildcard assoc array element and return object, '{}
    // @astgen op1 := lhsp : AstNode
    // @astgen op2 := keyp : Optional[AstNode]
    // @astgen op3 := valuep : AstNodeExpr
public:
    AstSetWildcard(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNodeExpr* valuep)
        : ASTGEN_SUPER_SetWildcard(fl) {
        this->lhsp(lhsp);
        this->keyp(keyp);
        this->valuep(valuep);
    }
    ASTGEN_MEMBERS_AstSetWildcard;
    string emitVerilog() override { return "'{}"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstStable final : public AstNodeExpr {
    // Verilog $stable
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := sentreep : Optional[AstSenTree]
public:
    AstStable(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep)
        : ASTGEN_SUPER_Stable(fl) {
        this->exprp(exprp);
        this->sentreep(sentreep);
    }
    ASTGEN_MEMBERS_AstStable;
    string emitVerilog() override { return "$stable(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstStackTraceF final : public AstNodeExpr {
    // $stacktrace used as function
public:
    explicit AstStackTraceF(FileLine* fl)
        : ASTGEN_SUPER_StackTraceF(fl) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstStackTraceF;
    string verilogKwd() const override { return "$stacktrace"; }
    string emitVerilog() override { return verilogKwd(); }
    string emitC() override { return "VL_STACKTRACE_N()"; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }
    bool isOutputter() override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstSteady final : public AstNodeExpr {
    // Verilog $steady_gclk
    // @astgen op1 := exprp : AstNodeExpr
public:
    AstSteady(FileLine* fl, AstNodeExpr* exprp)
        : ASTGEN_SUPER_Steady(fl) {
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstSteady;
    string emitVerilog() override { return "$steady_gclk(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstStructSel final : public AstNodeExpr {
    // Unpacked struct/union member access
    // Parents: math|stmt
    // Children: varref, math
    // @astgen op1 := fromp : AstNodeExpr
    string m_name;  // Name of the member
public:
    AstStructSel(FileLine* fl, AstNodeExpr* fromp, const string& name)
        : ASTGEN_SUPER_StructSel(fl)
        , m_name{name} {
        this->fromp(fromp);
        dtypep(nullptr);  // V3Width will resolve
    }
    ASTGEN_MEMBERS_AstStructSel;
    string name() const override VL_MT_STABLE { return m_name; }
    void name(const string& name) override { m_name = name; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override {
        // Not a union
        return VN_IS(fromp()->dtypep()->skipRefp(), StructDType);
    }
    bool sameNode(const AstNode* samep) const override {
        const AstStructSel* const sp = VN_DBG_AS(samep, StructSel);
        return m_name == sp->m_name;
    }
    int instrCount() const override { return widthInstrs(); }
};
class AstSysIgnore final : public AstNodeExpr {
    // @astgen op1 := exprsp : List[AstNode] // Expressions to output (???)
public:
    AstSysIgnore(FileLine* fl, AstNode* exprsp)
        : ASTGEN_SUPER_SysIgnore(fl) {
        addExprsp(exprsp);
    }
    ASTGEN_MEMBERS_AstSysIgnore;
    string verilogKwd() const override { return "$ignored"; }
    bool isGateOptimizable() const override { return false; }  // Though deleted before opt
    bool isPredictOptimizable() const override { return false; }  // Though deleted before opt
    bool isPure() override { return false; }  // Though deleted before opt
    bool isOutputter() override { return true; }  // Though deleted before opt
    int instrCount() const override { return INSTR_COUNT_PLI; }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstSystemF final : public AstNodeExpr {
    // $system used as function
    // @astgen op1 := lhsp : AstNode
public:
    AstSystemF(FileLine* fl, AstNode* lhsp)
        : ASTGEN_SUPER_SystemF(fl) {
        this->lhsp(lhsp);
    }
    ASTGEN_MEMBERS_AstSystemF;
    string verilogKwd() const override { return "$system"; }
    string emitVerilog() override { return verilogKwd(); }
    string emitC() override { return "VL_SYSTEM_%nq(%lw, %P)"; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }
    bool isOutputter() override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool isSystemFunc() const override { return true; }
};
class AstTestPlusArgs final : public AstNodeExpr {
    // Search expression. If nullptr then this is a $test$plusargs instead of $value$plusargs.
    // @astgen op1 := searchp : Optional[AstNode]
public:
    AstTestPlusArgs(FileLine* fl, AstNode* searchp)
        : ASTGEN_SUPER_TestPlusArgs(fl) {
        this->searchp(searchp);
    }
    ASTGEN_MEMBERS_AstTestPlusArgs;
    string verilogKwd() const override { return "$test$plusargs"; }
    string emitVerilog() override { return verilogKwd(); }
    string emitC() override { return "VL_VALUEPLUSARGS_%nq(%lw, %P, nullptr)"; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    // but isPure() true
    bool isSystemFunc() const override { return true; }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstThisRef final : public AstNodeExpr {
    // Reference to 'this'.
    // @astgen op1 := childDTypep : Optional[AstClassRefDType] // dtype of the node
public:
    AstThisRef(FileLine* fl, VFlagChildDType, AstClassRefDType* dtypep)
        : ASTGEN_SUPER_ThisRef(fl) {
        childDTypep(dtypep);
    }
    AstThisRef(FileLine* fl, AstClassRefDType* dtypep)
        : ASTGEN_SUPER_ThisRef(fl) {
        this->dtypep(dtypep);
    }
    ASTGEN_MEMBERS_AstThisRef;
    string emitC() override { return "this"; }
    string emitVerilog() override { return "this"; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    bool cleanOut() const override { return true; }
    AstNodeDType* getChildDTypep() const override { return childDTypep(); }
};
class AstTimePrecision final : public AstNodeExpr {
    // Verilog $timeprecision
public:
    explicit AstTimePrecision(FileLine* fl)
        : ASTGEN_SUPER_TimePrecision(fl) {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstTimePrecision;
    string emitVerilog() override { return "$timeprecision"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstTimeUnit final : public AstNodeExpr {
    VTimescale m_timeunit;  // Parent module time unit
    // Verilog $timeunit
public:
    explicit AstTimeUnit(FileLine* fl)
        : ASTGEN_SUPER_TimeUnit(fl) {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstTimeUnit;
    string emitVerilog() override { return "$timeunit"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    VTimescale timeunit() const { return m_timeunit; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
class AstUnbounded final : public AstNodeExpr {
    // A $ in the parser, used for unbounded and queues
    // Due to where is used, treated as Signed32
public:
    explicit AstUnbounded(FileLine* fl)
        : ASTGEN_SUPER_Unbounded(fl) {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstUnbounded;
    string emitVerilog() override { return "$"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
};
class AstUnlinkedRef final : public AstNodeExpr {
    // As-of-yet unlinkable Ref
    // @astgen op1 := refp : AstNode
    // @astgen op2 := cellrefp : AstNode

    string m_name;  // Var name // TODO: There is no way to access this, fix or remove
public:
    AstUnlinkedRef(FileLine* fl, AstNode* refp, const string& name, AstNode* cellrefp)
        : ASTGEN_SUPER_UnlinkedRef(fl)
        , m_name{name} {
        this->refp(refp);
        this->cellrefp(cellrefp);
    }
    ASTGEN_MEMBERS_AstUnlinkedRef;

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstValuePlusArgs final : public AstNodeExpr {
    // Search expression. If nullptr then this is a $test$plusargs instead of $value$plusargs.
    // @astgen op1 := searchp : Optional[AstNode]
    // @astgen op2 := outp : AstNode // VarRef for result
public:
    AstValuePlusArgs(FileLine* fl, AstNode* searchp, AstNode* outp)
        : ASTGEN_SUPER_ValuePlusArgs(fl) {
        this->searchp(searchp);
        this->outp(outp);
    }
    ASTGEN_MEMBERS_AstValuePlusArgs;
    string verilogKwd() const override { return "$value$plusargs"; }
    string emitVerilog() override { return "%f$value$plusargs(%l, %k%r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return !outp(); }
    bool isSystemFunc() const override { return true; }
    bool cleanOut() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstWith final : public AstNodeExpr {
    // Used as argument to method, then to AstCMethodHard
    // dtypep() contains the with lambda's return dtype
    // Parents: funcref (similar to AstArg)
    // Children: LambdaArgRef that declares the item variable
    // Children: LambdaArgRef that declares the item.index variable
    // Children: expression (equation establishing the with)
    // @astgen op1 := indexArgRefp : AstLambdaArgRef
    // @astgen op2 := valueArgRefp : AstLambdaArgRef
    // @astgen op3 := exprp : List[AstNode]  // Pins, expression and constraints
    // TODO: Separate expression and constraints
public:
    AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
            AstNode* exprp)
        : ASTGEN_SUPER_With(fl) {
        this->indexArgRefp(indexArgRefp);
        this->valueArgRefp(valueArgRefp);
        addExprp(exprp);
    }
    ASTGEN_MEMBERS_AstWith;
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    const char* broken() const override {
        BROKEN_RTN(!indexArgRefp());  // varp needed to know lambda's arg dtype
        BROKEN_RTN(!valueArgRefp());  // varp needed to know lambda's arg dtype
        return nullptr;
    }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstWithParse final : public AstNodeExpr {
    // In early parse, FUNC(index) WITH equation-using-index
    // Replaced with AstWith
    // Parents: expr|stmt
    // Children: funcref, expr
    // @astgen op1 := funcrefp : AstNodeExpr
    // @astgen op3 := exprsp : List[AstNode]  // With's parenthesis part
    // @astgen op4 := constraintsp : List[AstNode]  // With's braces part
public:
    AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp,
                 AstNode* constraintsp = nullptr)
        : ASTGEN_SUPER_WithParse(fl) {
        this->funcrefp(funcrefp);
        addExprsp(exprsp);
        addConstraintsp(constraintsp);
    }
    ASTGEN_MEMBERS_AstWithParse;
    bool sameNode(const AstNode* /*samep*/) const override { return true; }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};

// === AstNodeBiop ===
class AstBufIf1 final : public AstNodeBiop {
    // lhs is enable, rhs is data to drive
    // Note unlike the Verilog bufif1() UDP, this allows any width; each lhsp
    // bit enables respective rhsp bit
public:
    AstBufIf1(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_BufIf1(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstBufIf1;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opBufIf1(lhs, rhs);
    }
    string emitVerilog() override { return "bufif(%r,%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }  // Lclean || Rclean
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }  // Lclean || Rclean
    bool cleanOut() const override { V3ERROR_NA_RETURN(""); }  // Lclean || Rclean
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstCastDynamic final : public AstNodeBiop {
    // Verilog $cast used as a function
    // Task usage of $cast is converted during parse to assert($cast(...))
    // lhsp() is value (we are converting FROM) to match AstCCast etc, this
    // is opposite of $cast's order, because the first access is to the
    // value reading from.  Suggest use fromp()/top() instead of lhsp/rhsp().
    // @astgen alias op1 := fromp
    // @astgen alias op2 := top
public:
    AstCastDynamic(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_CastDynamic(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstCastDynamic;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        V3ERROR_NA;
    }
    string emitVerilog() override { return "%f$cast(%r, %l)"; }
    string emitC() override { return "VL_DYNAMIC_CAST(%r, %l)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return widthInstrs() * 20; }
    bool isPure() override { return false; }
};
class AstCompareNN final : public AstNodeBiop {
    // Verilog str.compare() and str.icompare()
    const bool m_ignoreCase;  // True for str.icompare()
public:
    AstCompareNN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool ignoreCase)
        : ASTGEN_SUPER_CompareNN(fl, lhsp, rhsp)
        , m_ignoreCase{ignoreCase} {
        dtypeSetUInt32();
    }
    ASTGEN_MEMBERS_AstCompareNN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opCompareNN(lhs, rhs, m_ignoreCase);
    }
    string name() const override VL_MT_STABLE { return m_ignoreCase ? "icompare" : "compare"; }
    string emitVerilog() override {
        return m_ignoreCase ? "%k(%l.icompare(%r))" : "%k(%l.compare(%r))";
    }
    string emitC() override {
        return m_ignoreCase ? "VL_CMP_NN(%li,%ri,true)" : "VL_CMP_NN(%li,%ri,false)";
    }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstConcat final : public AstNodeBiop {
    // If you're looking for {#{}}, see AstReplicate
public:
    AstConcat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Concat(fl, lhsp, rhsp) {
        if (lhsp->dtypep() && rhsp->dtypep()) {
            dtypeSetLogicSized(lhsp->dtypep()->width() + rhsp->dtypep()->width(),
                               VSigning::UNSIGNED);
        }
    }
    ASTGEN_MEMBERS_AstConcat;
    string emitVerilog() override { return "%f{%l, %k%r}"; }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opConcat(lhs, rhs);
    }
    string emitC() override { return "VL_CONCAT_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(concat %l %r)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 2; }
};
class AstConcatN final : public AstNodeBiop {
    // String concatenate
public:
    AstConcatN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_ConcatN(fl, lhsp, rhsp) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstConcatN;
    string emitVerilog() override { return "%f{%l, %k%r}"; }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opConcatN(lhs, rhs);
    }
    string emitC() override { return "VL_CONCATN_NNN(%li, %ri)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
    bool stringFlavor() const override { return true; }
};
class AstDiv final : public AstNodeBiop {
public:
    AstDiv(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Div(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstDiv;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opDiv(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f/ %r)"; }
    string emitC() override { return "VL_DIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvudiv %l %r)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
};
class AstDivD final : public AstNodeBiop {
public:
    AstDivD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_DivD(fl, lhsp, rhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstDivD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opDivD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f/ %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "/"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL_DIV; }
    bool doubleFlavor() const override { return true; }
};
class AstDivS final : public AstNodeBiop {
public:
    AstDivS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_DivS(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstDivS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opDivS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f/ %r)"; }
    string emitC() override { return "VL_DIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvsdiv %l %r)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
    bool signedFlavor() const override { return true; }
};
class AstEqWild final : public AstNodeBiop {
    // Note wildcard operator rhs differs from lhs
public:
    AstEqWild(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_EqWild(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstEqWild;
    // Return AstEqWild/AstEqD
    static AstNodeBiop* newTyped(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp);
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opWildEq(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f==? %r)"; }
    string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (= %l %r))"; }
    string emitSimpleOperator() override { return "=="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstFGetS final : public AstNodeBiop {
public:
    AstFGetS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_FGetS(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstFGetS;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%f$fgets(%l,%r)"; }
    string emitC() override {
        return strgp()->dtypep()->basicp()->isString() ? "VL_FGETS_NI(%li, %ri)"
                                                       : "VL_FGETS_%nqX%rq(%lw, %P, &(%li), %ri)";
    }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return widthInstrs() * 64; }
    AstNode* strgp() const { return lhsp(); }
    AstNode* filep() const { return rhsp(); }
};
class AstFUngetC final : public AstNodeBiop {
public:
    AstFUngetC(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_FUngetC(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstFUngetC;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%f$ungetc(%r, %l)"; }
    // Non-existent filehandle returns EOF
    string emitC() override {
        return "(%li ? (ungetc(%ri, VL_CVT_I_FP(%li)) >= 0 ? 0 : -1) : -1)";
    }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return widthInstrs() * 64; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: $display has 'visual' ordering
    AstNode* filep() const { return lhsp(); }
    AstNode* charp() const { return rhsp(); }
};
class AstGetcN final : public AstNodeBiop {
    // Verilog string.getc()
public:
    AstGetcN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GetcN(fl, lhsp, rhsp) {
        dtypeSetBitSized(8, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstGetcN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGetcN(lhs, rhs);
    }
    string name() const override VL_MT_STABLE { return "getc"; }
    string emitVerilog() override { return "%k(%l.getc(%r))"; }
    string emitC() override { return "VL_GETC_N(%li,%ri)"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstGetcRefN final : public AstNodeBiop {
    // Verilog string[#] on the left-hand-side of assignment
    // Spec says is of type byte (not string of single character)
public:
    AstGetcRefN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GetcRefN(fl, lhsp, rhsp) {
        dtypeSetBitSized(8, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstGetcRefN;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%k%l[%r]"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstGt final : public AstNodeBiop {
public:
    AstGt(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Gt(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGt;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGt(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f> %r)"; }
    string emitC() override { return "VL_GT_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvugt %l %r))"; }
    string emitSimpleOperator() override { return ">"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstGtD final : public AstNodeBiop {
public:
    AstGtD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GtD(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGtD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGtD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f> %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return ">"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstGtN final : public AstNodeBiop {
public:
    AstGtN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GtN(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGtN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGtN(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f> %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return ">"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
    bool stringFlavor() const override { return true; }
};
class AstGtS final : public AstNodeBiop {
public:
    AstGtS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GtS(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGtS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGtS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f> %r)"; }
    string emitC() override { return "VL_GTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvsgt %l %r))"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool signedFlavor() const override { return true; }
};
class AstGte final : public AstNodeBiop {
public:
    AstGte(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Gte(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGte;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGte(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f>= %r)"; }
    string emitC() override { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvuge %l %r))"; }
    string emitSimpleOperator() override { return ">="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstGteD final : public AstNodeBiop {
public:
    AstGteD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GteD(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGteD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGteD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f>= %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return ">="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstGteN final : public AstNodeBiop {
public:
    AstGteN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GteN(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGteN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGteN(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f>= %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return ">="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
    bool stringFlavor() const override { return true; }
};
class AstGteS final : public AstNodeBiop {
public:
    AstGteS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_GteS(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstGteS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGteS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f>= %r)"; }
    string emitC() override { return "VL_GTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvsge %l %r))"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool signedFlavor() const override { return true; }
};
class AstLogAnd final : public AstNodeBiop {
public:
    AstLogAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LogAnd(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLogAnd;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLogAnd(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f&& %r)"; }
    string emitC() override { return "VL_LOGAND_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvand %l %r)"; }
    string emitSimpleOperator() override { return "&&"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogIf final : public AstNodeBiop {
public:
    AstLogIf(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LogIf(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLogIf;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLogIf(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f-> %r)"; }
    string emitC() override { return "VL_LOGIF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (=> (__Vbool %l) (__Vbool %r)))"; }
    string emitSimpleOperator() override { return "->"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogOr final : public AstNodeBiop {
public:
    AstLogOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LogOr(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLogOr;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLogOr(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f|| %r)"; }
    string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvor %l %r)"; }
    string emitSimpleOperator() override { return "||"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLt final : public AstNodeBiop {
public:
    AstLt(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Lt(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLt;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLt(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f< %r)"; }
    string emitC() override { return "VL_LT_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvult %l %r))"; }
    string emitSimpleOperator() override { return "<"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstLtD final : public AstNodeBiop {
public:
    AstLtD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LtD(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLtD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLtD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f< %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "<"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstLtN final : public AstNodeBiop {
public:
    AstLtN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LtN(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLtN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLtN(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f< %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "<"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
    bool stringFlavor() const override { return true; }
};
class AstLtS final : public AstNodeBiop {
public:
    AstLtS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LtS(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLtS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLtS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f< %r)"; }
    string emitC() override { return "VL_LTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvslt %l %r))"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool signedFlavor() const override { return true; }
};
class AstLte final : public AstNodeBiop {
public:
    AstLte(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Lte(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLte;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLte(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f<= %r)"; }
    string emitC() override { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvule %l %r))"; }
    string emitSimpleOperator() override { return "<="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstLteD final : public AstNodeBiop {
public:
    AstLteD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LteD(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLteD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLteD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f<= %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "<="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstLteN final : public AstNodeBiop {
public:
    AstLteN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LteN(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLteN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLteN(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f<= %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "<="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
    bool stringFlavor() const override { return true; }
};
class AstLteS final : public AstNodeBiop {
public:
    AstLteS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LteS(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLteS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLteS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f<= %r)"; }
    string emitC() override { return "VL_LTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (bvsle %l %r))"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool signedFlavor() const override { return true; }
};
class AstModDiv final : public AstNodeBiop {
public:
    AstModDiv(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_ModDiv(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstModDiv;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opModDiv(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f%% %r)"; }
    string emitC() override { return "VL_MODDIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvurem %l %r)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
};
class AstModDivS final : public AstNodeBiop {
public:
    AstModDivS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_ModDivS(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstModDivS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opModDivS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f%% %r)"; }
    string emitC() override { return "VL_MODDIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvsmod %l %r)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
    bool signedFlavor() const override { return true; }
};
class AstNeqWild final : public AstNodeBiop {
public:
    AstNeqWild(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_NeqWild(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstNeqWild;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opWildNeq(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f!=? %r)"; }
    string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return "!="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstPow final : public AstNodeBiop {
public:
    AstPow(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Pow(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstPow;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opPow(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f** %r)"; }
    string emitC() override { return "VL_POW_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
};
class AstPowD final : public AstNodeBiop {
public:
    AstPowD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_PowD(fl, lhsp, rhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstPowD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opPowD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f** %r)"; }
    string emitC() override { return "pow(%li,%ri)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL_DIV * 5; }
    bool doubleFlavor() const override { return true; }
};
class AstPowSS final : public AstNodeBiop {
public:
    AstPowSS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_PowSS(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstPowSS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opPowSS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f** %r)"; }
    string emitC() override { return "VL_POWSS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri, 1,1)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
    bool signedFlavor() const override { return true; }
};
class AstPowSU final : public AstNodeBiop {
public:
    AstPowSU(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_PowSU(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstPowSU;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opPowSU(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f** %r)"; }
    string emitC() override { return "VL_POWSS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri, 1,0)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
    bool signedFlavor() const override { return true; }
};
class AstPowUS final : public AstNodeBiop {
public:
    AstPowUS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_PowUS(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstPowUS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opPowUS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f** %r)"; }
    string emitC() override { return "VL_POWSS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri, 0,1)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
    bool signedFlavor() const override { return true; }
};
class AstReplicate final : public AstNodeBiop {
    // Also used as a "Uniop" flavor of Concat, e.g. "{a}"
    // Verilog {rhs{lhs}} - Note rhsp() is the replicate value, not the lhsp()
    // @astgen alias op1 := srcp
    // @astgen alias op2 := countp
public:
    AstReplicate(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Replicate(fl, lhsp, rhsp) {
        if (lhsp) {
            if (const AstConst* const constp = VN_CAST(rhsp, Const)) {
                if (constp->num().isFourState()
                    || (constp->dtypep()->isSigned() && constp->num().isNegative())) {
                    dtypeSetLogicSized(lhsp->width(), VSigning::UNSIGNED);  // V3Width warns
                } else {
                    dtypeSetLogicSized(lhsp->width() * constp->toSInt(), VSigning::UNSIGNED);
                }
            }
        }
    }
    AstReplicate(FileLine* fl, AstNodeExpr* lhsp, uint32_t repCount)
        : AstReplicate{fl, lhsp, new AstConst{fl, repCount}} {}
    ASTGEN_MEMBERS_AstReplicate;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opRepl(lhs, rhs);
    }
    string emitVerilog() override { return "%f{%r{%k%l}}"; }
    string emitC() override { return "VL_REPLICATE_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSMT() const override {
        return "((_ repeat " + cvtToStr(width() / lhsp()->width()) + ") %l)";
    }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 2; }
};
class AstReplicateN final : public AstNodeBiop {
    // String replicate
public:
    AstReplicateN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_ReplicateN(fl, lhsp, rhsp) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstReplicateN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opReplN(lhs, rhs);
    }
    string emitVerilog() override { return "%f{%r{%k%l}}"; }
    string emitC() override { return "VL_REPLICATEN_NN%rq(%li, %ri)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 2; }
    bool stringFlavor() const override { return true; }
};
class AstSel final : public AstNodeBiop {
    // *Resolved* (tyep checked) multiple bit range extraction. Always const width
    // @astgen alias op1 := fromp
    // @astgen alias op2 := lsbp
    VNumRange m_declRange;  // Range of the 'from' array if isRanged() is set, else invalid
    int m_declElWidth;  // If a packed array, the number of bits per element
    // Selection width (!= this->width() from V3Clean onwards, != this->widthMin() in V3Width)
    int m_widthConst;

public:
    AstSel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* lsbp, int bitwidth)
        : ASTGEN_SUPER_Sel(fl, fromp, lsbp)
        , m_declElWidth{1}
        , m_widthConst{bitwidth} {
        dtypeSetLogicSized(bitwidth, VSigning::UNSIGNED);
    }
    AstSel(FileLine* fl, AstNodeExpr* fromp, int lsb, int bitwidth)
        : ASTGEN_SUPER_Sel(fl, fromp, new AstConst(fl, lsb))  // Need () constructor
        , m_declElWidth{1}
        , m_widthConst{bitwidth} {
        dtypeSetLogicSized(bitwidth, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstSel;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit) override {
        out.opSel(from, bit.toUInt() + widthConst() - 1, bit.toUInt());
    }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override {
        return widthConst() == 1 ? "VL_BITSEL_%nq%lq%rqI(%lw, %P, %li, %ri)"
               : isWide()        ? "VL_SEL_%nq%lq%rqI(%nw, %lw, %P, %li, %ri, %nw)"
                                 : "VL_SEL_%nq%lq%rqI(%lw, %P, %li, %ri, %nw)";
    }
    string emitSMT() const override { return "((_ extract %t %r) %l)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sameNode(const AstNode* otherp) const override {
        return widthConst() == VN_DBG_AS(otherp, Sel)->widthConst();
    }
    int instrCount() const override {
        return widthInstrs() * (VN_CAST(lsbp(), Const) ? 3 : 10) + 1;
    }
    int widthConst() const { return m_widthConst; }
    void widthConst(int value) { m_widthConst = value; }
    int lsbConst() const { return VN_AS(lsbp(), Const)->toSInt(); }
    int msbConst() const { return lsbConst() + widthConst() - 1; }
    VNumRange& declRange() VL_MT_STABLE { return m_declRange; }
    const VNumRange& declRange() const VL_MT_STABLE { return m_declRange; }
    void declRange(const VNumRange& flag) { m_declRange = flag; }
    int declElWidth() const { return m_declElWidth; }
    void declElWidth(int flag) { m_declElWidth = flag; }
};
class AstShiftL final : public AstNodeBiop {
public:
    AstShiftL(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
        : ASTGEN_SUPER_ShiftL(fl, lhsp, rhsp) {
        if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstShiftL;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opShiftL(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f<< %r)"; }
    string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvshl %l %r)"; }
    string emitSimpleOperator() override {
        return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<";
    }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return false; }
};
class AstShiftLOvr final : public AstNodeBiop {
    // Like ShiftL but checks for an over shift and returns zeros
    // TODO: All Verilog shifts should start as these and later get demoted to AstShiftL
public:
    AstShiftLOvr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
        : ASTGEN_SUPER_ShiftLOvr(fl, lhsp, rhsp) {
        if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstShiftLOvr;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opShiftL(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f<< %r)"; }
    string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return false; }
};
class AstShiftR final : public AstNodeBiop {
public:
    AstShiftR(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
        : ASTGEN_SUPER_ShiftR(fl, lhsp, rhsp) {
        if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstShiftR;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opShiftR(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f>> %r)"; }
    string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvlshr %l %r)"; }
    string emitSimpleOperator() override {
        return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>";
    }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    // LHS size might be > output size, so don't want to force size
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstShiftROvr final : public AstNodeBiop {
    // Like ShiftR but checks for an over shift and returns zeros
    // TODO: All Verilog shifts should start as these and later get demoted to AstShiftR
public:
    AstShiftROvr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
        : ASTGEN_SUPER_ShiftROvr(fl, lhsp, rhsp) {
        if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstShiftROvr;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opShiftR(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f>> %r)"; }
    string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    // LHS size might be > output size, so don't want to force size
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstShiftRS final : public AstNodeBiop {
    // Shift right with sign extension, >>> operator
    // Output data type's width determines which bit is used for sign extension
public:
    AstShiftRS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
        : ASTGEN_SUPER_ShiftRS(fl, lhsp, rhsp) {
        // Important that widthMin be correct, as opExtend requires it after V3Expand
        if (setwidth) dtypeSetLogicSized(setwidth, VSigning::SIGNED);
    }
    ASTGEN_MEMBERS_AstShiftRS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
    }
    string emitVerilog() override { return "%k(%l %f>>> %r)"; }
    string emitSMT() const override { return "(bvashr %l %r)"; }
    string emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool signedFlavor() const override { return true; }
};
class AstShiftRSOvr final : public AstNodeBiop {
    // Shift right with sign extension, >>> operator, checks for an over shift and returns zeros
    // Output data type's width determines which bit is used for sign extension
    // TODO: All Verilog shifts should start as these and later get demoted to AstShiftRSOvr
public:
    AstShiftRSOvr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
        : ASTGEN_SUPER_ShiftRSOvr(fl, lhsp, rhsp) {
        // Important that widthMin be correct, as opExtend requires it after V3Expand
        if (setwidth) dtypeSetLogicSized(setwidth, VSigning::SIGNED);
    }
    ASTGEN_MEMBERS_AstShiftRSOvr;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
    }
    string emitVerilog() override { return "%k(%l %f>>> %r)"; }
    string emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool signedFlavor() const override { return true; }
};
class AstSub final : public AstNodeBiop {
public:
    AstSub(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Sub(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstSub;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opSub(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f- %r)"; }
    string emitC() override { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvsub %l %r)"; }
    string emitSimpleOperator() override { return "-"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
};
class AstSubD final : public AstNodeBiop {
public:
    AstSubD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_SubD(fl, lhsp, rhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstSubD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opSubD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f- %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "-"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstURandomRange final : public AstNodeBiop {
    // $urandom_range
public:
    AstURandomRange(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_URandomRange(fl, lhsp, rhsp) {
        dtypeSetUInt32();  // Says IEEE
    }
    ASTGEN_MEMBERS_AstURandomRange;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%f$urandom_range(%l, %r)"; }
    string emitC() override { return "VL_URANDOM_RANGE_%nq(%li, %ri)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_PLI; }
};

// === AstNodeBiCom ===
class AstEq final : public AstNodeBiCom {
public:
    AstEq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Eq(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstEq;
    // Return AstEq/AstEqD
    static AstNodeBiop* newTyped(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp);
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opEq(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f== %r)"; }
    string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(__Vbv (= %l %r))"; }
    string emitSimpleOperator() override { return "=="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstEqCase final : public AstNodeBiCom {
public:
    AstEqCase(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_EqCase(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstEqCase;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opCaseEq(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f=== %r)"; }
    string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return "=="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstEqD final : public AstNodeBiCom {
public:
    AstEqD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_EqD(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstEqD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opEqD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f== %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "=="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstEqN final : public AstNodeBiCom {
public:
    AstEqN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_EqN(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstEqN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opEqN(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f== %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "=="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
    bool stringFlavor() const override { return true; }
};
class AstEqT final : public AstNodeBiCom {
    // Equal (==) for data types
public:
    AstEqT(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_EqT(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstEqT;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%k(%l %f== %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "=="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
};
class AstLogEq final : public AstNodeBiCom {
public:
    AstLogEq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LogEq(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLogEq;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLogEq(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f<-> %r)"; }
    string emitC() override { return "VL_LOGEQ_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvxnor %l %r)"; }
    string emitSimpleOperator() override { return "<->"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstNeq final : public AstNodeBiCom {
public:
    AstNeq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Neq(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstNeq;
    static AstNodeBiop* newTyped(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp);
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opNeq(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f!= %r)"; }
    string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return "!="; }
    string emitSMT() const override { return "(__Vbv (not (= %l %r)))"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstNeqCase final : public AstNodeBiCom {
public:
    AstNeqCase(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_NeqCase(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstNeqCase;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opCaseNeq(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f!== %r)"; }
    string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return "!="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
};
class AstNeqD final : public AstNodeBiCom {
public:
    AstNeqD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_NeqD(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstNeqD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opNeqD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f!= %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "!="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstNeqN final : public AstNodeBiCom {
public:
    AstNeqN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_NeqN(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstNeqN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opNeqN(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f!= %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "!="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
    bool stringFlavor() const override { return true; }
};
class AstNeqT final : public AstNodeBiCom {
    // Not-equal (!=) for data types
public:
    AstNeqT(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_NeqT(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstNeqT;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%k(%l %f!= %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "!="; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_STR; }
};

// === AstNodeBiComAsv ===
class AstAdd final : public AstNodeBiComAsv {
public:
    AstAdd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Add(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstAdd;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opAdd(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f+ %r)"; }
    string emitC() override { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvadd %l %r)"; }
    string emitSimpleOperator() override { return "+"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
};
class AstAddD final : public AstNodeBiComAsv {
public:
    AstAddD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_AddD(fl, lhsp, rhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstAddD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opAddD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f+ %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "+"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstAnd final : public AstNodeBiComAsv {
public:
    AstAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_And(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstAnd;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opAnd(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f& %r)"; }
    string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvand %l %r)"; }
    string emitSimpleOperator() override { return "&"; }
    bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    const char* widthMismatch() const override VL_MT_STABLE;
};
class AstMul final : public AstNodeBiComAsv {
public:
    AstMul(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Mul(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstMul;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opMul(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f* %r)"; }
    string emitC() override { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvmul %l %r)"; }
    string emitSimpleOperator() override { return "*"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; }
};
class AstMulD final : public AstNodeBiComAsv {
public:
    AstMulD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_MulD(fl, lhsp, rhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstMulD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opMulD(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f* %r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "*"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstMulS final : public AstNodeBiComAsv {
public:
    AstMulS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_MulS(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstMulS;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opMulS(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f* %r)"; }
    string emitC() override { return "VL_MULS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    string emitSimpleOperator() override { return ""; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return true; }
    bool sizeMattersRhs() const override { return true; }
    int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; }
    bool signedFlavor() const override { return true; }
};
class AstOr final : public AstNodeBiComAsv {
public:
    AstOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Or(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstOr;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opOr(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f| %r)"; }
    string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvor %l %r)"; }
    string emitSimpleOperator() override { return "|"; }
    bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    const char* widthMismatch() const override VL_MT_STABLE;
};
class AstXor final : public AstNodeBiComAsv {
public:
    AstXor(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Xor(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstXor;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opXor(lhs, rhs);
    }
    string emitVerilog() override { return "%k(%l %f^ %r)"; }
    string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
    string emitSMT() const override { return "(bvxor %l %r)"; }
    string emitSimpleOperator() override { return "^"; }
    bool cleanOut() const override { return false; }  // Lclean && Rclean
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    const char* widthMismatch() const override VL_MT_STABLE;
};

// === AstNodeDistBiop ===
class AstDistChiSquare final : public AstNodeDistBiop {
public:
    AstDistChiSquare(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_DistChiSquare(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstDistChiSquare;
    string emitVerilog() override { return "%f$dist_chi_square(%l, %r)"; }
    string emitC() override { return "VL_DIST_CHI_SQUARE(%li, %ri)"; }
};
class AstDistExponential final : public AstNodeDistBiop {
public:
    AstDistExponential(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_DistExponential(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstDistExponential;
    string emitVerilog() override { return "%f$dist_exponential(%l, %r)"; }
    string emitC() override { return "VL_DIST_EXPONENTIAL(%li, %ri)"; }
};
class AstDistPoisson final : public AstNodeDistBiop {
public:
    AstDistPoisson(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_DistPoisson(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstDistPoisson;
    string emitVerilog() override { return "%f$dist_poisson(%l, %r)"; }
    string emitC() override { return "VL_DIST_POISSON(%li, %ri)"; }
};
class AstDistT final : public AstNodeDistBiop {
public:
    AstDistT(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_DistT(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstDistT;
    string emitVerilog() override { return "%f$dist_t(%l, %r)"; }
    string emitC() override { return "VL_DIST_T(%li, %ri)"; }
};

// === AstNodeSel ===
class AstArraySel final : public AstNodeSel {
    void init(const AstNode* fromp) {
        if (fromp && VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) {
            // Strip off array to find what array references
            dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep());
        }
    }

public:
    AstArraySel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp)
        : ASTGEN_SUPER_ArraySel(fl, fromp, bitp) {
        init(fromp);
    }
    AstArraySel(FileLine* fl, AstNodeExpr* fromp, int bit)
        : ASTGEN_SUPER_ArraySel(fl, fromp, new AstConst(fl, bit)) {  // Need () constructor
        init(fromp);
    }
    ASTGEN_MEMBERS_AstArraySel;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%k(%l%f[%r])"; }
    string emitC() override { return "%li%k[%ri]"; }
    string emitSMT() const override { return "(select %l %r)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isGateOptimizable() const override { return true; }  // esp for V3Const::ifSameAssign
    bool isPredictOptimizable() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    // Special operators
    // Return base var (or const) nodep dereferences
    static AstNode* baseFromp(AstNode* nodep, bool overMembers);
};
class AstAssocSel final : public AstNodeSel {
    void init(const AstNode* fromp) {
        if (fromp && VN_IS(fromp->dtypep()->skipRefp(), AssocArrayDType)) {
            // Strip off array to find what array references
            dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), AssocArrayDType)->subDTypep());
        }
    }

public:
    AstAssocSel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp)
        : ASTGEN_SUPER_AssocSel(fl, fromp, bitp) {
        init(fromp);
    }
    ASTGEN_MEMBERS_AstAssocSel;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%k(%l%f[%r])"; }
    string emitC() override { return "%li%k[%ri]"; }
    string emitSMT() const override { return "(select %l %r)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isGateOptimizable() const override { return false; }  // AssocSel creates on miss
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // AssocSel creates on miss
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
};
class AstWildcardSel final : public AstNodeSel {
    void init(const AstNode* fromp) {
        if (fromp && VN_IS(fromp->dtypep()->skipRefp(), WildcardArrayDType)) {
            // Strip off array to find what array references
            dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), WildcardArrayDType)->subDTypep());
        }
    }

public:
    AstWildcardSel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp)
        : ASTGEN_SUPER_WildcardSel(fl, fromp, bitp) {
        init(fromp);
    }
    ASTGEN_MEMBERS_AstWildcardSel;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%k(%l%f[%r])"; }
    string emitC() override { return "%li%k[%ri]"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool isGateOptimizable() const override { return true; }  // esp for V3Const::ifSameAssign
    bool isPredictOptimizable() const override { return false; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
};
class AstWordSel final : public AstNodeSel {
    // Select a single word from a multi-word wide value
public:
    AstWordSel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp)
        : ASTGEN_SUPER_WordSel(fl, fromp, bitp) {
        dtypeSetUInt32();  // Always used on WData arrays so returns edata size
    }
    ASTGEN_MEMBERS_AstWordSel;
    void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
    string emitVerilog() override { return "%k(%l%f[%r])"; }
    string emitC() override {
        return "%li[%ri]";
    }  // Not %k, as usually it's a small constant rhsp
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};

// === AstNodeStream ===
class AstStreamL final : public AstNodeStream {
    // Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
public:
    AstStreamL(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_StreamL(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstStreamL;
    string emitVerilog() override { return "%f{ << %r %k{%l} }"; }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opStreamL(lhs, rhs);
    }
    string emitC() override { return "VL_STREAML_%nq%lq%rq(%lw, %P, %li, %ri)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 2; }
};
class AstStreamR final : public AstNodeStream {
    // Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
public:
    AstStreamR(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_StreamR(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstStreamR;
    string emitVerilog() override { return "%f{ >> %r %k{%l} }"; }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opAssign(lhs);
    }
    string emitC() override { return isWide() ? "VL_ASSIGN_W(%nw, %P, %li)" : "%li"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 2; }
};

// === AstNodeSystemBiopD ===
class AstAtan2D final : public AstNodeSystemBiopD {
public:
    AstAtan2D(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Atan2D(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstAtan2D;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.setDouble(std::atan2(lhs.toDouble(), rhs.toDouble()));
    }
    string emitVerilog() override { return "%f$atan2(%l,%r)"; }
    string emitC() override { return "atan2(%li,%ri)"; }
};
class AstHypotD final : public AstNodeSystemBiopD {
public:
    AstHypotD(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_HypotD(fl, lhsp, rhsp) {}
    ASTGEN_MEMBERS_AstHypotD;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.setDouble(std::hypot(lhs.toDouble(), rhs.toDouble()));
    }
    string emitVerilog() override { return "%f$hypot(%l,%r)"; }
    string emitC() override { return "hypot(%li,%ri)"; }
};

// === AstNodeCCall ===
class AstCCall final : public AstNodeCCall {
    // C++ function call
    VSelfPointerText m_selfPointer
        = VSelfPointerText{VSelfPointerText::Empty()};  // Output code object
                                                        // pointer (e.g.: 'this')
public:
    AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
        : ASTGEN_SUPER_CCall(fl, funcp, argsp) {
        UASSERT_OBJ(funcp, this, "CCall created calling null function");
    }
    ASTGEN_MEMBERS_AstCCall;

    const VSelfPointerText& selfPointer() const { return m_selfPointer; }
    void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
    string selfPointerProtect(bool useSelfForThis) const {
        return selfPointer().protect(useSelfForThis, protect());
    }
};
class AstCMethodCall final : public AstNodeCCall {
    // C++ method call
    // @astgen op1 := fromp : AstNodeExpr
public:
    AstCMethodCall(FileLine* fl, AstNodeExpr* fromp, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
        : ASTGEN_SUPER_CMethodCall(fl, funcp, argsp) {
        this->fromp(fromp);
        UASSERT_OBJ(funcp, this, "CMethodCall created calling null function");
    }
    ASTGEN_MEMBERS_AstCMethodCall;
};
class AstCNew final : public AstNodeCCall {
    // C++ new() call
public:
    AstCNew(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
        : ASTGEN_SUPER_CNew(fl, funcp, argsp) {}
    ASTGEN_MEMBERS_AstCNew;
};

// === AstNodeFTaskRef ===
class AstFuncRef final : public AstNodeFTaskRef {
    // A reference to a function
    bool m_superReference = false;  // Called with super reference
public:
    inline AstFuncRef(FileLine* fl, AstFunc* taskp, AstNodeExpr* pinsp);
    AstFuncRef(FileLine* fl, const string& name, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_FuncRef(fl, name, pinsp) {}
    ASTGEN_MEMBERS_AstFuncRef;
    bool superReference() const { return m_superReference; }
    void superReference(bool flag) { m_superReference = flag; }
};
class AstMethodCall final : public AstNodeFTaskRef {
    // A reference to a member task (or function)
    // Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
    // @astgen op2 := fromp : AstNodeExpr
    //
public:
    AstMethodCall(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name,
                  AstNodeExpr* pinsp)
        : ASTGEN_SUPER_MethodCall(fl, name, pinsp) {
        this->fromp(fromp);
        dtypep(nullptr);  // V3Width will resolve
    }
    AstMethodCall(FileLine* fl, AstNodeExpr* fromp, const string& name, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_MethodCall(fl, name, pinsp) {
        this->fromp(fromp);
    }
    ASTGEN_MEMBERS_AstMethodCall;
};
class AstNew final : public AstNodeFTaskRef {
    // New as constructor
    // Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
    bool m_isImplicit = false;  // Implicitly generated from extends args
    bool m_isScoped = false;  // Had :: scope when parsed
public:
    AstNew(FileLine* fl, AstNodeExpr* pinsp, bool isScoped = false)
        : ASTGEN_SUPER_New(fl, "new", pinsp)
        , m_isScoped{isScoped} {}
    ASTGEN_MEMBERS_AstNew;
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool isImplicit() const { return m_isImplicit; }
    void isImplicit(bool flag) { m_isImplicit = flag; }
    bool isScoped() const { return m_isScoped; }
    void isScoped(bool flag) { m_isScoped = flag; }
    bool isPure() override { return false; }
};
class AstTaskRef final : public AstNodeFTaskRef {
    // A reference to a task
    bool m_superReference = false;  // Called with super reference
public:
    inline AstTaskRef(FileLine* fl, AstTask* taskp, AstNodeExpr* pinsp);
    AstTaskRef(FileLine* fl, const string& name, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_TaskRef(fl, name, pinsp) {
        dtypeSetVoid();
    }
    ASTGEN_MEMBERS_AstTaskRef;
    bool superReference() const { return m_superReference; }
    void superReference(bool flag) { m_superReference = flag; }
};

// === AstNodePreSel ===
class AstSelBit final : public AstNodePreSel {
    // Single bit range extraction, perhaps with non-constant selection or array selection
    // Gets replaced during link with AstArraySel or AstSel
    // @astgen alias op2 := bitp
private:
    VAccess m_access;  // Left hand side assignment
public:
    AstSelBit(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp)
        : ASTGEN_SUPER_SelBit(fl, fromp, bitp, nullptr) {
        UASSERT_OBJ(!v3Global.assertDTypesResolved(), this,
                    "not coded to create after dtypes resolved");
    }
    ASTGEN_MEMBERS_AstSelBit;
    VAccess access() const { return m_access; }
    void access(const VAccess& flag) { m_access = flag; }
};
class AstSelExtract final : public AstNodePreSel {
    // Range extraction, gets replaced with AstSel
    // @astgen alias op2 := leftp
    // @astgen alias op3 := rightp
public:
    AstSelExtract(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* msbp, AstNodeExpr* lsbp)
        : ASTGEN_SUPER_SelExtract(fl, fromp, msbp, lsbp) {}
    ASTGEN_MEMBERS_AstSelExtract;
};
class AstSelMinus final : public AstNodePreSel {
    // -: range extraction, perhaps with non-constant selection
    // Gets replaced during link with AstSel
    // @astgen alias op2 := bitp
    // @astgen alias op3 := widthp
public:
    AstSelMinus(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp, AstNodeExpr* widthp)
        : ASTGEN_SUPER_SelMinus(fl, fromp, bitp, widthp) {}
    ASTGEN_MEMBERS_AstSelMinus;
};
class AstSelPlus final : public AstNodePreSel {
    // +: range extraction, perhaps with non-constant selection
    // Gets replaced during link with AstSel
    // @astgen alias op2 := bitp
    // @astgen alias op3 := widthp
public:
    AstSelPlus(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp, AstNodeExpr* widthp)
        : ASTGEN_SUPER_SelPlus(fl, fromp, bitp, widthp) {}
    ASTGEN_MEMBERS_AstSelPlus;
};

// === AstNodeQuadop ===
class AstCountBits final : public AstNodeQuadop {
    // Number of bits set in vector
public:
    AstCountBits(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ctrl1p)
        : ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl1p->cloneTreePure(false),
                                 ctrl1p->cloneTreePure(false)) {}
    AstCountBits(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ctrl1p, AstNodeExpr* ctrl2p)
        : ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl2p, ctrl2p->cloneTreePure(false)) {}
    AstCountBits(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ctrl1p, AstNodeExpr* ctrl2p,
                 AstNodeExpr* ctrl3p)
        : ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl2p, ctrl3p) {}
    ASTGEN_MEMBERS_AstCountBits;
    void numberOperate(V3Number& out, const V3Number& expr, const V3Number& ctrl1,
                       const V3Number& ctrl2, const V3Number& ctrl3) override {
        out.opCountBits(expr, ctrl1, ctrl2, ctrl3);
    }
    string emitVerilog() override { return "%f$countbits(%l, %r, %f, %o)"; }
    string emitC() override { return ""; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool cleanThs() const override { return true; }
    bool cleanFhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sizeMattersThs() const override { return false; }
    bool sizeMattersFhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return widthInstrs() * 16; }
};

// === AstNodeTermop ===
class AstInferredDisable final : public AstNodeTermop {
public:
    explicit AstInferredDisable(FileLine* fl)
        : ASTGEN_SUPER_InferredDisable(fl) {
        dtypeSetLogicSized(1, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstInferredDisable;
    string emitVerilog() override { return "%f$inferred_disable"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool isSystemFunc() const override { return true; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstTime final : public AstNodeTermop {
    VTimescale m_timeunit;  // Parent module time unit
public:
    AstTime(FileLine* fl, const VTimescale& timeunit)
        : ASTGEN_SUPER_Time(fl)
        , m_timeunit{timeunit} {
        dtypeSetUInt64();
    }
    ASTGEN_MEMBERS_AstTime;
    string emitVerilog() override { return "%f$time"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_TIME; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    VTimescale timeunit() const { return m_timeunit; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
class AstTimeD final : public AstNodeTermop {
    VTimescale m_timeunit;  // Parent module time unit
public:
    AstTimeD(FileLine* fl, const VTimescale& timeunit)
        : ASTGEN_SUPER_TimeD(fl)
        , m_timeunit{timeunit} {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstTimeD;
    string emitVerilog() override { return "%f$realtime"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isSystemFunc() const override { return true; }
    int instrCount() const override { return INSTR_COUNT_TIME; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    VTimescale timeunit() const { return m_timeunit; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};

// === AstNodeTriop ===

class AstCond final : public AstNodeTriop {
    // @astgen alias op1 := condp
    // @astgen alias op2 := thenp
    // @astgen alias op3 := elsep
public:
    AstCond(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep);
    ASTGEN_MEMBERS_AstCond;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        out.opAssign(lhs.isNeqZero() ? rhs : ths);
    }
    string emitVerilog() override { return "%k(%l %f? %r %k: %t)"; }
    string emitC() override { return "VL_COND_%nq%lq%rq%tq(%nw, %P, %li, %ri, %ti)"; }
    string emitSMT() const override { return "(ite (__Vbool %l) %r %t)"; }
    bool cleanOut() const override { return false; }  // clean if e1 & e2 clean
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return false; }
    bool cleanThs() const override { return false; }  // Propagates up
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sizeMattersThs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_BRANCH; }
};
class AstPostAdd final : public AstNodeTriop {
    // Post-increment/add
    // Children: lhsp: AstConst (1) as currently support only ++ not +=
    // Children: rhsp: tree with AstVarRef that is value to read before operation
    // Children: thsp: tree with AstVarRef LValue that is stored after operation
public:
    AstPostAdd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : ASTGEN_SUPER_PostAdd(fl, lhsp, rhsp, thsp) {}
    ASTGEN_MEMBERS_AstPostAdd;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        V3ERROR_NA;  // Need to modify lhs
    }
    string emitVerilog() override { return "%k(%r++)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
    bool cleanLhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanRhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanThs() const override { V3ERROR_NA_RETURN(false); }
    bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); }
};
class AstPostSub final : public AstNodeTriop {
    // Post-decrement/subtract
    // Children: lhsp: AstConst (1) as currently support only -- not -=
    // Children: rhsp: tree with AstVarRef that is value to read before operation
    // Children: thsp: tree with AstVarRef LValue that is stored after operation
public:
    AstPostSub(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : ASTGEN_SUPER_PostSub(fl, lhsp, rhsp, thsp) {}
    ASTGEN_MEMBERS_AstPostSub;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        V3ERROR_NA;  // Need to modify lhs
    }
    string emitVerilog() override { return "%k(%r--)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
    bool cleanLhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanRhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanThs() const override { V3ERROR_NA_RETURN(false); }
    bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); }
};
class AstPreAdd final : public AstNodeTriop {
    // Pre-increment/add
    // Children: lhsp: AstConst (1) as currently support only ++ not +=
    // Children: rhsp: tree with AstVarRef that is value to read before operation
    // Children: thsp: tree with AstVarRef LValue that is stored after operation
public:
    AstPreAdd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : ASTGEN_SUPER_PreAdd(fl, lhsp, rhsp, thsp) {}
    ASTGEN_MEMBERS_AstPreAdd;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        V3ERROR_NA;  // Need to modify lhs
    }
    string emitVerilog() override { return "%k(++%r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
    bool cleanLhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanRhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanThs() const override { V3ERROR_NA_RETURN(false); }
    bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); }
};
class AstPreSub final : public AstNodeTriop {
    // Pre-decrement/subtract
    // Children: lhsp: AstConst (1) as currently support only -- not -=
    // Children: rhsp: tree with AstVarRef that is value to read before operation
    // Children: thsp: tree with AstVarRef LValue that is stored after operation
public:
    AstPreSub(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : ASTGEN_SUPER_PreSub(fl, lhsp, rhsp, thsp) {}
    ASTGEN_MEMBERS_AstPreSub;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        V3ERROR_NA;  // Need to modify lhs
    }
    string emitVerilog() override { return "%k(--%r)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
    bool cleanLhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanRhs() const override { V3ERROR_NA_RETURN(false); }
    bool cleanThs() const override { V3ERROR_NA_RETURN(false); }
    bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); }
    bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); }
};
class AstPutcN final : public AstNodeTriop {
    // Verilog string.putc()
public:
    AstPutcN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* ths)
        : ASTGEN_SUPER_PutcN(fl, lhsp, rhsp, ths) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstPutcN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        out.opPutcN(lhs, rhs, ths);
    }
    string name() const override VL_MT_STABLE { return "putc"; }
    string emitVerilog() override { return "%k(%l.putc(%r,%t))"; }
    string emitC() override { return "VL_PUTC_N(%li,%ri,%ti)"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool cleanThs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sizeMattersThs() const override { return false; }
};
class AstSliceSel final : public AstNodeTriop {
    // Multiple array element extraction
    // @astgen alias op1 := fromp
    VNumRange m_declRange;  // Range of the 'from' array if isRanged() is set, else invalid
public:
    AstSliceSel(FileLine* fl, AstNodeExpr* fromp, const VNumRange& declRange)
        : ASTGEN_SUPER_SliceSel(fl, fromp,
                                new AstConst(fl, declRange.lo()),  // Need () constructor
                                new AstConst(fl, declRange.elements()))  // Need () constructor
        , m_declRange{declRange} {}
    ASTGEN_MEMBERS_AstSliceSel;
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    void numberOperate(V3Number& out, const V3Number& from, const V3Number& lo,
                       const V3Number& width) override {
        V3ERROR_NA;
    }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }  // Removed before EmitC
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool cleanRhs() const override { return true; }
    bool cleanThs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sizeMattersThs() const override { return false; }
    bool sameNode(const AstNode*) const override { return true; }
    int instrCount() const override { return 10; }  // Removed before matters
    // For widthConst()/loConst etc, see declRange().elements() and other VNumRange methods
    VNumRange& declRange() VL_MT_STABLE { return m_declRange; }
    const VNumRange& declRange() const VL_MT_STABLE { return m_declRange; }
};
class AstSubstrN final : public AstNodeTriop {
    // Verilog string.substr()
public:
    AstSubstrN(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* ths)
        : ASTGEN_SUPER_SubstrN(fl, lhsp, rhsp, ths) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstSubstrN;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override {
        out.opSubstrN(lhs, rhs, ths);
    }
    string name() const override VL_MT_STABLE { return "substr"; }
    string emitVerilog() override { return "%k(%l.substr(%r,%t))"; }
    string emitC() override { return "VL_SUBSTR_N(%li,%ri,%ti)"; }
    string emitSimpleOperator() override { return ""; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool cleanRhs() const override { return true; }
    bool cleanThs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sizeMattersThs() const override { return false; }
};

// === AstNodeDistTriop ===
class AstDistErlang final : public AstNodeDistTriop {
public:
    AstDistErlang(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : ASTGEN_SUPER_DistErlang(fl, lhsp, rhsp, thsp) {}
    ASTGEN_MEMBERS_AstDistErlang;
    string emitVerilog() override { return "%f$dist_erlang(%l, %r, %t)"; }
    string emitC() override { return "VL_DIST_ERLANG(%li, %ri, %ti)"; }
};
class AstDistNormal final : public AstNodeDistTriop {
public:
    AstDistNormal(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : ASTGEN_SUPER_DistNormal(fl, lhsp, rhsp, thsp) {}
    ASTGEN_MEMBERS_AstDistNormal;
    string emitVerilog() override { return "%f$dist_normal(%l, %r, %t)"; }
    string emitC() override { return "VL_DIST_NORMAL(%li, %ri, %ti)"; }
};
class AstDistUniform final : public AstNodeDistTriop {
public:
    AstDistUniform(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
        : ASTGEN_SUPER_DistUniform(fl, lhsp, rhsp, thsp) {}
    ASTGEN_MEMBERS_AstDistUniform;
    string emitVerilog() override { return "%f$dist_uniform(%l, %r, %t)"; }
    string emitC() override { return "VL_DIST_UNIFORM(%li, %ri, %ti)"; }
};

// === AstNodeUniop ===
class AstAtoN final : public AstNodeUniop {
    // string.atoi(), atobin(), atohex(), atooct(), atoireal()
public:
    enum FmtType { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 };

private:
    const FmtType m_fmt;  // Operation type
public:
    AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmt)
        : ASTGEN_SUPER_AtoN(fl, lhsp)
        , m_fmt{fmt} {
        fmt == ATOREAL ? dtypeSetDouble() : dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstAtoN;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAtoN(lhs, m_fmt); }
    string name() const override VL_MT_STABLE {
        switch (m_fmt) {
        case ATOI: return "atoi";
        case ATOHEX: return "atohex";
        case ATOOCT: return "atooct";
        case ATOBIN: return "atobin";
        case ATOREAL: return "atoreal";
        }
        V3ERROR_NA_RETURN("");
    }
    string emitVerilog() override { return "%l." + name() + "()"; }
    string emitC() override {
        switch (m_fmt) {
        case ATOI: return "VL_ATOI_N(%li, 10)";
        case ATOHEX: return "VL_ATOI_N(%li, 16)";
        case ATOOCT: return "VL_ATOI_N(%li, 8)";
        case ATOBIN: return "VL_ATOI_N(%li, 2)";
        case ATOREAL: return "std::atof(%li.c_str())";
        }
        V3ERROR_NA_RETURN("");
    }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    FmtType format() const { return m_fmt; }
};
class AstBitsToRealD final : public AstNodeUniop {
public:
    AstBitsToRealD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_BitsToRealD(fl, lhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstBitsToRealD;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opBitsToRealD(lhs); }
    string emitVerilog() override { return "%f$bitstoreal(%l)"; }
    string emitC() override { return "VL_CVT_D_Q(%li)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }  // Eliminated before matters
    bool sizeMattersLhs() const override { return false; }  // Eliminated before matters
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool isSystemFunc() const override { return true; }
};
class AstCAwait final : public AstNodeUniop {
    // Emit C++'s co_await expression
    // @astgen alias op1 := exprp
    //
    // @astgen ptr := m_sentreep : Optional[AstSenTree]  // Sentree related to this await
public:
    AstCAwait(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep = nullptr)
        : ASTGEN_SUPER_CAwait(fl, exprp)
        , m_sentreep{sentreep} {}
    ASTGEN_MEMBERS_AstCAwait;
    bool isTimingControl() const override { return true; }
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    AstSenTree* sentreep() const { return m_sentreep; }
    void clearSentreep() { m_sentreep = nullptr; }
    void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstCCast final : public AstNodeUniop {
    // Cast to C-based data type
    int m_size;

public:
    AstCCast(FileLine* fl, AstNodeExpr* lhsp, int setwidth, int minwidth = -1)
        : ASTGEN_SUPER_CCast(fl, lhsp) {
        m_size = setwidth;
        if (setwidth) {
            if (minwidth == -1) minwidth = setwidth;
            dtypeSetLogicUnsized(setwidth, minwidth, VSigning::UNSIGNED);
        }
    }
    // cppcheck-suppress constParameterPointer
    // cppcheck-suppress constParameterCallback
    AstCCast(FileLine* fl, AstNodeExpr* lhsp, AstNode* typeFromp)
        : ASTGEN_SUPER_CCast(fl, lhsp) {
        dtypeFrom(typeFromp);
        m_size = width();
    }
    ASTGEN_MEMBERS_AstCCast;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
    string emitVerilog() override { return "%f$_CAST(%l)"; }
    string emitC() override { return "VL_CAST_%nq%lq(%nw,%lw, %P, %li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }  // Special cased in V3Cast
    bool sameNode(const AstNode* samep) const override {
        return size() == VN_DBG_AS(samep, CCast)->size();
    }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    //
    int size() const { return m_size; }
};
class AstCLog2 final : public AstNodeUniop {
public:
    AstCLog2(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_CLog2(fl, lhsp) {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstCLog2;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opCLog2(lhs); }
    string emitVerilog() override { return "%f$clog2(%l)"; }
    string emitC() override { return "VL_CLOG2_%lq(%lW, %P, %li)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 16; }
    bool isSystemFunc() const override { return true; }
};
class AstCastWrap final : public AstNodeUniop {
    // A cast which has been expanded and the LHSP does all the lifting
    // This remains until V3Width final commit pass to suppress ENUMVALUE warnings
public:
    AstCastWrap(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_CastWrap(fl, lhsp) {}
    ASTGEN_MEMBERS_AstCastWrap;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
    string emitVerilog() override { return "(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return 0; }
};
class AstCountOnes final : public AstNodeUniop {
    // Number of bits set in vector
public:
    AstCountOnes(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_CountOnes(fl, lhsp) {}
    ASTGEN_MEMBERS_AstCountOnes;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opCountOnes(lhs); }
    string emitVerilog() override { return "%f$countones(%l)"; }
    string emitC() override { return "VL_COUNTONES_%lq(%lW, %P, %li)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 16; }
    bool isSystemFunc() const override { return true; }
};
class AstCvtPackString final : public AstNodeUniop {
    // Convert to Verilator Packed String (aka verilog "string")
public:
    AstCvtPackString(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_CvtPackString(fl, lhsp) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstCvtPackString;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstExtend final : public AstNodeUniop {
    // Expand a value into a wider entity by 0 extension.  Width is implied from nodep->width()
public:
    AstExtend(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_Extend(fl, lhsp) {}
    AstExtend(FileLine* fl, AstNodeExpr* lhsp, int width)
        : ASTGEN_SUPER_Extend(fl, lhsp) {
        dtypeSetLogicSized(width, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstExtend;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
    string emitVerilog() override { return "%l"; }
    string emitC() override { return "VL_EXTEND_%nq%lq(%nw,%lw, %P, %li)"; }
    string emitSMT() const override {
        return "((_ zero_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
    }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override {
        return false;  // Because the EXTEND operator self-casts
    }
    int instrCount() const override { return 0; }
};
class AstExtendS final : public AstNodeUniop {
    // Expand a value into a wider entity by sign extension.  Width is implied from nodep->width()
public:
    AstExtendS(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_ExtendS(fl, lhsp) {}
    AstExtendS(FileLine* fl, AstNodeExpr* lhsp, int width)
        // Important that widthMin be correct, as opExtend requires it after V3Expand
        : ASTGEN_SUPER_ExtendS(fl, lhsp) {
        dtypeSetLogicSized(width, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstExtendS;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.opExtendS(lhs, lhsp()->widthMinV());
    }
    string emitVerilog() override { return "%l"; }
    string emitC() override { return "VL_EXTENDS_%nq%lq(%nw,%lw, %P, %li)"; }
    string emitSMT() const override {
        return "((_ sign_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
    }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override {
        return false;  // Because the EXTEND operator self-casts
    }
    int instrCount() const override { return 0; }
    bool signedFlavor() const override { return true; }
};
class AstFEof final : public AstNodeUniop {
public:
    AstFEof(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_FEof(fl, lhsp) {}
    ASTGEN_MEMBERS_AstFEof;
    void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
    string emitVerilog() override { return "%f$feof(%l)"; }
    string emitC() override { return "(%li ? feof(VL_CVT_I_FP(%li)) : true)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 16; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: $display has 'visual' ordering
    bool isSystemFunc() const override { return true; }
    AstNode* filep() const { return lhsp(); }
};
class AstFGetC final : public AstNodeUniop {
public:
    AstFGetC(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_FGetC(fl, lhsp) {}
    ASTGEN_MEMBERS_AstFGetC;
    void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
    string emitVerilog() override { return "%f$fgetc(%l)"; }
    // Non-existent filehandle returns EOF
    string emitC() override { return "(%li ? fgetc(VL_CVT_I_FP(%li)) : -1)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 64; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() override { return false; }  // SPECIAL: $display has 'visual' ordering
    AstNode* filep() const { return lhsp(); }
    bool isSystemFunc() const override { return true; }
};
class AstISToRD final : public AstNodeUniop {
    // $itor where lhs is signed
public:
    AstISToRD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_ISToRD(fl, lhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstISToRD;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opISToRD(lhs); }
    string emitVerilog() override { return "%f$itor($signed(%l))"; }
    string emitC() override { return "VL_ISTOR_D_%lq(%lw, %li)"; }
    bool emitCheckMaxWords() override { return true; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool isSystemFunc() const override { return true; }
};
class AstIToRD final : public AstNodeUniop {
    // $itor where lhs is unsigned
public:
    AstIToRD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_IToRD(fl, lhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstIToRD;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opIToRD(lhs); }
    string emitVerilog() override { return "%f$itor(%l)"; }
    string emitC() override { return "VL_ITOR_D_%lq(%lw, %li)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool isSystemFunc() const override { return true; }
};
class AstIsUnbounded final : public AstNodeUniop {
    // True if is unbounded ($)
public:
    AstIsUnbounded(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_IsUnbounded(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstIsUnbounded;
    void numberOperate(V3Number& out, const V3Number&) override {
        // Any constant isn't unbounded
        out.setZero();
    }
    string emitVerilog() override { return "%f$isunbounded(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
};
class AstIsUnknown final : public AstNodeUniop {
    // True if any unknown bits
public:
    AstIsUnknown(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_IsUnknown(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstIsUnknown;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opIsUnknown(lhs); }
    string emitVerilog() override { return "%f$isunknown(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    bool isSystemFunc() const override { return true; }
};
class AstLenN final : public AstNodeUniop {
    // Length of a string
public:
    AstLenN(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_LenN(fl, lhsp) {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstLenN;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLenN(lhs); }
    string emitVerilog() override { return "%f(%l)"; }
    string emitC() override { return "VL_LEN_IN(%li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstLogNot final : public AstNodeUniop {
public:
    AstLogNot(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_LogNot(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLogNot;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLogNot(lhs); }
    string emitVerilog() override { return "%f(! %l)"; }
    string emitC() override { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; }
    string emitSMT() const override { return "(__Vbv (not (__Vbool %l)))"; }
    string emitSimpleOperator() override { return "!"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstNToI final : public AstNodeUniop {
    // String to any-size integral
public:
    AstNToI(FileLine* fl, AstNodeExpr* lhsp, AstNodeDType* dtypep = nullptr)
        : ASTGEN_SUPER_NToI(fl, lhsp) {
        this->dtypep(dtypep);
    }
    ASTGEN_MEMBERS_AstNToI;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNToI(lhs); }
    string emitVerilog() override { return "'(%l)"; }
    string emitC() override { return "VL_NTOI_%nq(%nw, %P, %li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstNegate final : public AstNodeUniop {
public:
    AstNegate(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_Negate(fl, lhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstNegate;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNegate(lhs); }
    string emitVerilog() override { return "%f(- %l)"; }
    string emitC() override { return "VL_NEGATE_%lq(%lW, %P, %li)"; }
    string emitSMT() const override { return "(bvneg %l)"; }
    string emitSimpleOperator() override { return "-"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return true; }
};
class AstNegateD final : public AstNodeUniop {
public:
    AstNegateD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_NegateD(fl, lhsp) {
        dtypeSetDouble();
    }
    ASTGEN_MEMBERS_AstNegateD;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNegateD(lhs); }
    string emitVerilog() override { return "%f(- %l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { return "-"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool doubleFlavor() const override { return true; }
};
class AstNot final : public AstNodeUniop {
public:
    AstNot(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_Not(fl, lhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstNot;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNot(lhs); }
    string emitVerilog() override { return "%f(~ %l)"; }
    string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
    string emitSMT() const override { return "(bvnot %l)"; }
    string emitSimpleOperator() override { return "~"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return true; }
    const char* widthMismatch() const override VL_MT_STABLE;
};
class AstNullCheck final : public AstNodeUniop {
    // Return LHS after checking that LHS is non-null
public:
    AstNullCheck(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_NullCheck(fl, lhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstNullCheck;
    void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
    int instrCount() const override { return 1; }  // Rarely executes
    string emitVerilog() override { return "%l"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); }
};
class AstOneHot final : public AstNodeUniop {
    // True if only single bit set in vector
public:
    AstOneHot(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_OneHot(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstOneHot;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opOneHot(lhs); }
    string emitVerilog() override { return "%f$onehot(%l)"; }
    string emitC() override { return "VL_ONEHOT_%lq(%lW, %P, %li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 4; }
    bool isSystemFunc() const override { return true; }
};
class AstOneHot0 final : public AstNodeUniop {
    // True if only single bit, or no bits set in vector
public:
    AstOneHot0(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_OneHot0(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstOneHot0;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opOneHot0(lhs); }
    string emitVerilog() override { return "%f$onehot0(%l)"; }
    string emitC() override { return "VL_ONEHOT0_%lq(%lW, %P, %li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return widthInstrs() * 3; }
    bool isSystemFunc() const override { return true; }
};
class AstRToIRoundS final : public AstNodeUniop {
    // Convert real to integer, with arbitrary sized output (not just "integer" format)
public:
    AstRToIRoundS(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_RToIRoundS(fl, lhsp) {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstRToIRoundS;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opRToIRoundS(lhs); }
    string emitVerilog() override { return "%f$rtoi_rounded(%l)"; }
    string emitC() override {
        return isWide() ? "VL_RTOIROUND_%nq_D(%nw, %P, %li)" : "VL_RTOIROUND_%nq_D(%li)";
    }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool isSystemFunc() const override { return true; }
};
class AstRToIS final : public AstNodeUniop {
    // $rtoi(lhs)
public:
    AstRToIS(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_RToIS(fl, lhsp) {
        dtypeSetSigned32();
    }
    ASTGEN_MEMBERS_AstRToIS;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opRToIS(lhs); }
    string emitVerilog() override { return "%f$rtoi(%l)"; }
    string emitC() override { return "VL_RTOI_I_D(%li)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }  // Eliminated before matters
    bool sizeMattersLhs() const override { return false; }  // Eliminated before matters
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool isSystemFunc() const override { return true; }
};
class AstRealToBits final : public AstNodeUniop {
public:
    AstRealToBits(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_RealToBits(fl, lhsp) {
        dtypeSetUInt64();
    }
    ASTGEN_MEMBERS_AstRealToBits;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opRealToBits(lhs); }
    string emitVerilog() override { return "%f$realtobits(%l)"; }
    string emitC() override { return "VL_CVT_Q_D(%li)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }  // Eliminated before matters
    bool sizeMattersLhs() const override { return false; }  // Eliminated before matters
    int instrCount() const override { return INSTR_COUNT_DBL; }
    bool isSystemFunc() const override { return true; }
};
class AstRedAnd final : public AstNodeUniop {
public:
    AstRedAnd(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_RedAnd(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstRedAnd;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opRedAnd(lhs); }
    string emitVerilog() override { return "%f(& %l)"; }
    string emitC() override { return "VL_REDAND_%nq%lq(%lw, %P, %li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstRedOr final : public AstNodeUniop {
public:
    AstRedOr(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_RedOr(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstRedOr;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opRedOr(lhs); }
    string emitVerilog() override { return "%f(| %l)"; }
    string emitC() override { return "VL_REDOR_%lq(%lW, %P, %li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstRedXor final : public AstNodeUniop {
public:
    AstRedXor(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_RedXor(fl, lhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstRedXor;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opRedXor(lhs); }
    string emitVerilog() override { return "%f(^ %l)"; }
    string emitC() override { return "VL_REDXOR_%lq(%lW, %P, %li)"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override {
        const int w = lhsp()->width();
        return (w != 1 && w != 2 && w != 4 && w != 8 && w != 16);
    }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return 1 + V3Number::log2b(width()); }
};
class AstResizeLValue final : public AstNodeUniop {
    // Resize a LValue into a wider/narrower entity at function argument boundry
    // Width and signness is implied from nodep->width()
public:
    AstResizeLValue(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_ResizeLValue(fl, lhsp) {}
    ASTGEN_MEMBERS_AstResizeLValue;
    void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
    string emitVerilog() override { return "%l"; }
    string emitC() final override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    int instrCount() const override { return 0; }
};
class AstSigned final : public AstNodeUniop {
    // $signed(lhs)
public:
    AstSigned(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_Signed(fl, lhsp) {
        UASSERT_OBJ(!v3Global.assertDTypesResolved(), this,
                    "not coded to create after dtypes resolved");
    }
    ASTGEN_MEMBERS_AstSigned;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.opAssign(lhs);
        out.isSigned(false);
    }
    string emitVerilog() override { return "%f$signed(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }  // Eliminated before matters
    bool sizeMattersLhs() const override { return true; }  // Eliminated before matters
    int instrCount() const override { return 0; }
    bool isSystemFunc() const override { return true; }
};
class AstTimeImport final : public AstNodeUniop {
    // Take a constant that represents a time and needs conversion based on time units
    VTimescale m_timeunit;  // Parent module time unit
public:
    AstTimeImport(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_TimeImport(fl, lhsp) {}
    ASTGEN_MEMBERS_AstTimeImport;
    void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
    string emitVerilog() override { return "%l"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return false; }
    void dump(std::ostream& str = std::cout) const override;
    void dumpJson(std::ostream& str = std::cout) const override;
    VTimescale timeunit() const { return m_timeunit; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
class AstToLowerN final : public AstNodeUniop {
    // string.tolower()
public:
    AstToLowerN(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_ToLowerN(fl, lhsp) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstToLowerN;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opToLowerN(lhs); }
    string emitVerilog() override { return "%l.tolower()"; }
    string emitC() override { return "VL_TOLOWER_NN(%li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstToStringN final : public AstNodeUniop {
public:
    AstToStringN(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_ToStringN(fl, lhsp) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstToStringN;
    void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
    string emitVerilog() override { return "$sformatf(\"%p\", %l)"; }
    string emitC() override { return isWide() ? "VL_TO_STRING_W(%nw, %li)" : "VL_TO_STRING(%li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstToUpperN final : public AstNodeUniop {
    // string.toupper()
public:
    AstToUpperN(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_ToUpperN(fl, lhsp) {
        dtypeSetString();
    }
    ASTGEN_MEMBERS_AstToUpperN;
    void numberOperate(V3Number& out, const V3Number& lhs) override { out.opToUpperN(lhs); }
    string emitVerilog() override { return "%l.toupper()"; }
    string emitC() override { return "VL_TOUPPER_NN(%li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
};
class AstUnsigned final : public AstNodeUniop {
    // $unsigned(lhs)
public:
    AstUnsigned(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_Unsigned(fl, lhsp) {
        UASSERT_OBJ(!v3Global.assertDTypesResolved(), this,
                    "not coded to create after dtypes resolved");
    }
    ASTGEN_MEMBERS_AstUnsigned;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.opAssign(lhs);
        out.isSigned(false);
    }
    string emitVerilog() override { return "%f$unsigned(%l)"; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }  // Eliminated before matters
    bool sizeMattersLhs() const override { return true; }  // Eliminated before matters
    int instrCount() const override { return 0; }
    bool isSystemFunc() const override { return true; }
};

// === AstNodeSystemUniopD ===
class AstAcosD final : public AstNodeSystemUniopD {
public:
    AstAcosD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_AcosD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstAcosD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::acos(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$acos(%l)"; }
    string emitC() override { return "acos(%li)"; }
};
class AstAcoshD final : public AstNodeSystemUniopD {
public:
    AstAcoshD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_AcoshD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstAcoshD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::acosh(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$acosh(%l)"; }
    string emitC() override { return "acosh(%li)"; }
};
class AstAsinD final : public AstNodeSystemUniopD {
public:
    AstAsinD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_AsinD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstAsinD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::asin(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$asin(%l)"; }
    string emitC() override { return "asin(%li)"; }
};
class AstAsinhD final : public AstNodeSystemUniopD {
public:
    AstAsinhD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_AsinhD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstAsinhD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::asinh(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$asinh(%l)"; }
    string emitC() override { return "asinh(%li)"; }
};
class AstAtanD final : public AstNodeSystemUniopD {
public:
    AstAtanD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_AtanD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstAtanD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::atan(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$atan(%l)"; }
    string emitC() override { return "atan(%li)"; }
};
class AstAtanhD final : public AstNodeSystemUniopD {
public:
    AstAtanhD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_AtanhD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstAtanhD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::atanh(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$atanh(%l)"; }
    string emitC() override { return "atanh(%li)"; }
};
class AstCeilD final : public AstNodeSystemUniopD {
public:
    AstCeilD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_CeilD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstCeilD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::ceil(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$ceil(%l)"; }
    string emitC() override { return "ceil(%li)"; }
};
class AstCosD final : public AstNodeSystemUniopD {
public:
    AstCosD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_CosD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstCosD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::cos(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$cos(%l)"; }
    string emitC() override { return "cos(%li)"; }
};
class AstCoshD final : public AstNodeSystemUniopD {
public:
    AstCoshD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_CoshD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstCoshD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::cosh(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$cosh(%l)"; }
    string emitC() override { return "cosh(%li)"; }
};
class AstExpD final : public AstNodeSystemUniopD {
public:
    AstExpD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_ExpD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstExpD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::exp(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$exp(%l)"; }
    string emitC() override { return "exp(%li)"; }
};
class AstFloorD final : public AstNodeSystemUniopD {
public:
    AstFloorD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_FloorD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstFloorD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::floor(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$floor(%l)"; }
    string emitC() override { return "floor(%li)"; }
};
class AstLog10D final : public AstNodeSystemUniopD {
public:
    AstLog10D(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_Log10D(fl, lhsp) {}
    ASTGEN_MEMBERS_AstLog10D;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::log10(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$log10(%l)"; }
    string emitC() override { return "log10(%li)"; }
};
class AstLogD final : public AstNodeSystemUniopD {
public:
    AstLogD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_LogD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstLogD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::log(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$ln(%l)"; }
    string emitC() override { return "log(%li)"; }
};
class AstSinD final : public AstNodeSystemUniopD {
public:
    AstSinD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_SinD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstSinD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::sin(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$sin(%l)"; }
    string emitC() override { return "sin(%li)"; }
};
class AstSinhD final : public AstNodeSystemUniopD {
public:
    AstSinhD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_SinhD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstSinhD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::sinh(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$sinh(%l)"; }
    string emitC() override { return "sinh(%li)"; }
};
class AstSqrtD final : public AstNodeSystemUniopD {
public:
    AstSqrtD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_SqrtD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstSqrtD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::sqrt(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$sqrt(%l)"; }
    string emitC() override { return "sqrt(%li)"; }
};
class AstTanD final : public AstNodeSystemUniopD {
public:
    AstTanD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_TanD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstTanD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::tan(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$tan(%l)"; }
    string emitC() override { return "tan(%li)"; }
};
class AstTanhD final : public AstNodeSystemUniopD {
public:
    AstTanhD(FileLine* fl, AstNodeExpr* lhsp)
        : ASTGEN_SUPER_TanhD(fl, lhsp) {}
    ASTGEN_MEMBERS_AstTanhD;
    void numberOperate(V3Number& out, const V3Number& lhs) override {
        out.setDouble(std::tanh(lhs.toDouble()));
    }
    string emitVerilog() override { return "%f$tanh(%l)"; }
    string emitC() override { return "tanh(%li)"; }
};

// === AstNodeVarRef ===
class AstVarRef final : public AstNodeVarRef {
    // A reference to a variable (lvalue or rvalue)
    std::string m_name;  // Name of varref, though name() favors varp()->namep()
                         // if non-null pointer (e.g.: 'this')
public:
    // This form only allowed post-link because output/wire compression may
    // lead to deletion of AstVar's
    inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
    inline AstVarRef(FileLine* fl, AstNodeModule* classOrPackagep, AstVar* varp,
                     const VAccess& access);
    // This form only allowed post-link (see above)
    inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
    ASTGEN_MEMBERS_AstVarRef;
    std::string nameThis() const { return m_name; }
    inline string name() const override;  // * = Var name
    void name(const string& name) override { m_name = name; }
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    const char* broken() const override;
    bool sameNode(const AstNode* samep) const override;
    inline bool sameNode(const AstVarRef* samep) const;
    inline bool sameNoLvalue(const AstVarRef* samep) const;
    int instrCount() const override;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
};
class AstVarXRef final : public AstNodeVarRef {
    // A VarRef to something in another module before AstScope.
    // Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope
    string m_name;
    string m_dotted;  // Dotted part of scope the name()'ed reference is under or ""
    string m_inlinedDots;  // Dotted hierarchy flattened out
    bool m_containsGenBlock = false;  // Contains gen block reference
public:
    AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
        : ASTGEN_SUPER_VarXRef(fl, nullptr, access)
        , m_name{name}
        , m_dotted{dotted} {}
    inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
    ASTGEN_MEMBERS_AstVarXRef;
    string name() const override VL_MT_STABLE { return m_name; }  // * = Var name
    void name(const std::string& name) override { m_name = name; }  // * = Var name
    void dump(std::ostream& str) const override;
    void dumpJson(std::ostream& str) const override;
    string dotted() const { return m_dotted; }
    void dotted(const string& dotted) { m_dotted = dotted; }
    string inlinedDots() const { return m_inlinedDots; }
    void inlinedDots(const string& flag) { m_inlinedDots = flag; }
    bool containsGenBlock() const { return m_containsGenBlock; }
    void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    int instrCount() const override { return widthInstrs(); }
    bool sameNode(const AstNode* samep) const override {
        const AstVarXRef* asamep = VN_DBG_AS(samep, VarXRef);
        return (selfPointer() == asamep->selfPointer() && varp() == asamep->varp()
                && name() == asamep->name() && dotted() == asamep->dotted());
    }
};

#endif  // Guard
