// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: AstNode sub-types representing expressions
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 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;
    // TODO: The only AstNodeExpr without dtype is AstArg. Otherwise this could be final.
    bool hasDType() const override { 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 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();
};
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
    // Binary expression
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
protected:
    AstNodeBiop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : AstNodeExpr{t, fl} {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }

public:
    ASTGEN_MEMBERS_AstNodeBiop;
    // Clone single node, just get same type back.
    virtual AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0;
    // 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 same(const AstNode*) const override { return true; }
};
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; }
    int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        V3ERROR_NA;
        return nullptr;
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) 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; }
    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
    AstCFunc* m_funcp;
    string m_argTypes;

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 cloneRelink() override;
    const char* broken() const override;
    int instrCount() const override { return INSTR_COUNT_CALL; }
    bool same(const AstNode* samep) const override {
        const AstNodeCCall* const asamep = static_cast<const AstNodeCCall*>(samep);
        return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
    }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    bool isPure() const override;
    bool isOutputter() const override { return !isPure(); }
    AstCFunc* funcp() const { return m_funcp; }
    void funcp(AstCFunc* funcp) { m_funcp = funcp; }
    void argTypes(const string& str) { m_argTypes = str; }
    string argTypes() const { return m_argTypes; }

    string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
    string emitC() final override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const final override { return true; }
};
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]
    AstNodeFTask* m_taskp = nullptr;  // [AfterLink] Pointer to task referenced
    AstNodeModule* m_classOrPackagep = nullptr;  // Package hierarchy
    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)
protected:
    AstNodeFTaskRef(VNType t, FileLine* fl, AstNode* namep, AstNodeExpr* pinsp)
        : AstNodeExpr{t, fl} {
        this->namep(namep);
        this->addPinsp(pinsp);
    }
    AstNodeFTaskRef(VNType t, FileLine* fl, const string& name, AstNodeExpr* pinsp)
        : AstNodeExpr{t, fl}
        , m_name{name} {
        this->addPinsp(pinsp);
    }

public:
    ASTGEN_MEMBERS_AstNodeFTaskRef;
    const char* broken() const override;
    void cloneRelink() override;
    void dump(std::ostream& str = std::cout) const override;
    string name() const override { 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; }

    string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
    string emitC() final override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
};
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]
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 same(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); }
};
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
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 same(const AstNode*) const override { return true; }
};
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;
    // Know no children, and hot function, so skip iterator for speed
    // cppcheck-suppress functionConst
    void iterateChildren(VNVisitor& v) {}
    void dump(std::ostream& str) const override;
};
class AstNodeTriop VL_NOT_FINAL : public AstNodeExpr {
    // Ternary expression
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    // @astgen op3 := thsp : AstNodeExpr
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;
    // 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 same(const AstNode*) const override { return true; }
};
class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
    // @astgen alias op1 := condp
    // @astgen alias op2 := thenp
    // @astgen alias op3 := elsep
protected:
    AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
        : AstNodeTriop{t, fl, condp, thenp, elsep} {
        if (thenp) {
            dtypeFrom(thenp);
        } else if (elsep) {
            dtypeFrom(elsep);
        }
    }

public:
    ASTGEN_MEMBERS_AstNodeCond;
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
                       const V3Number& ths) override;
    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)"; }
    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; }
    virtual AstNodeExpr* cloneType(AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) = 0;
};
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; }
    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
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;
    // 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 same(const AstNode*) const override { return true; }
};
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; }
    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
    VAccess m_access;  // Left hand side assignment
    AstVar* m_varp;  // [AfterLink] Pointer to variable itself
    AstVarScope* m_varScopep = nullptr;  // Varscope for hierarchy
    AstNodeModule* m_classOrPackagep = nullptr;  // Package hierarchy
    string m_name;  // Name of variable
    string m_selfPointer;  // Output code object pointer (e.g.: 'this')

protected:
    AstNodeVarRef(VNType t, FileLine* fl, const string& name, const VAccess& access)
        : AstNodeExpr{t, fl}
        , m_access{access}
        , m_name{name} {
        varp(nullptr);
    }
    AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
        : AstNodeExpr{t, fl}
        , m_access{access}
        , m_name{name} {
        // May have varp==nullptr
        this->varp(varp);
    }

public:
    ASTGEN_MEMBERS_AstNodeVarRef;
    void dump(std::ostream& str) const override;
    const char* broken() const override;
    int instrCount() const override { return widthInstrs(); }
    void cloneRelink() override;
    string name() const override { return m_name; }  // * = Var name
    void name(const string& name) override { m_name = name; }
    VAccess access() const { return m_access; }
    void access(const VAccess& flag) { m_access = flag; }  // Avoid using this; Set in constructor
    AstVar* varp() const { return m_varp; }  // [After Link] Pointer to variable
    void varp(AstVar* varp) {
        m_varp = varp;
        dtypeFrom((AstNode*)varp);
    }
    AstVarScope* varScopep() const { return m_varScopep; }
    void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
    string selfPointer() const { return m_selfPointer; }
    void selfPointer(const string& value) { m_selfPointer = value; }
    string selfPointerProtect(bool useSelfForThis) const;
    AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
    void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
    // Know no children, and hot function, so skip iterator for speed
    // cppcheck-suppress functionConst
    void iterateChildren(VNVisitor& v) {}
};

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

// === AstNodeExpr ===
class AstAddrOfCFunc final : public AstNodeExpr {
    // Get address of CFunc
    AstCFunc* m_funcp;  // Pointer to function itself

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

public:
    ASTGEN_MEMBERS_AstAddrOfCFunc;
    void cloneRelink() override;
    const char* broken() 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 { return false; }
    string name() const override { 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] // Expr or DType
    // @astgen op2 := dimp : Optional[AstNodeExpr]
    VAttrType m_attrType;  // What sort of extraction
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;
    VAttrType attrType() const { return m_attrType; }
    void dump(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 {
    // @astgen op1 := exprsp : List[AstNode] // Expressions to print
    const bool m_cleanOut;
    bool m_pure;  // Pure optimizable
public:
    // Emit C textual expr function (like AstUCFunc)
    AstCExpr(FileLine* fl, AstNode* exprsp)
        : ASTGEN_SUPER_CExpr(fl)
        , m_cleanOut{true}
        , m_pure{false} {
        addExprsp(exprsp);
        dtypeFrom(exprsp);
    }
    inline AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool cleanOut = true);
    ASTGEN_MEMBERS_AstCExpr;
    bool isGateOptimizable() const override { return m_pure; }
    bool isPredictOptimizable() const override { return m_pure; }
    bool cleanOut() const override { return m_cleanOut; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool same(const AstNode* /*samep*/) const override { return true; }
    bool pure() const { return m_pure; }
    void pure(bool flag) { m_pure = flag; }
};
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
    string m_name;  // Name of method
    bool m_pure = false;  // Pure optimizable
public:
    AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name,
                   AstNodeExpr* pinsp = nullptr)
        : ASTGEN_SUPER_CMethodHard(fl)
        , m_name{name} {
        // TODO: this constructor is exactly the same as the other, bar the ignored tag argument
        this->fromp(fromp);
        this->addPinsp(pinsp);
        dtypep(nullptr);  // V3Width will resolve
    }
    AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, const string& name,
                   AstNodeExpr* pinsp = nullptr)
        : ASTGEN_SUPER_CMethodHard(fl)
        , m_name{name} {
        this->fromp(fromp);
        this->addPinsp(pinsp);
    }
    ASTGEN_MEMBERS_AstCMethodHard;
    string name() const override { return m_name; }  // * = Var name
    void name(const string& name) override { m_name = name; }
    bool same(const AstNode* samep) const override {
        const AstCMethodHard* asamep = static_cast<const AstCMethodHard*>(samep);
        return (m_name == asamep->m_name);
    }
    bool isPure() const override { return m_pure; }
    void pure(bool flag) { m_pure = flag; }
    int instrCount() const override;
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
};
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);
        this->childDTypep(dtp);
        dtypeFrom(dtp);
    }
    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 { 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} {
        this->addSelp(selp);
    }
    ASTGEN_MEMBERS_AstCellArrayRef;
    // ACCESSORS
    string name() const override { 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 : AstNodeExpr
private:
    string m_name;  // Cell name
public:
    AstCellRef(FileLine* fl, const string& name, AstNode* cellp, AstNodeExpr* exprp)
        : ASTGEN_SUPER_CellRef(fl)
        , m_name{name} {
        this->cellp(cellp);
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstCellRef;
    // ACCESSORS
    string name() const override { 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]
private:
    string m_name;
    // Node not NodeModule to appease some early parser usage
    AstNode* m_classOrPackageNodep;  // Package hierarchy
public:
    AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
                         AstPin* paramsp)
        : ASTGEN_SUPER_ClassOrPackageRef(fl)
        , m_name{name}
        , m_classOrPackageNodep{classOrPackageNodep} {
        this->addParamsp(paramsp);
    }
    ASTGEN_MEMBERS_AstClassOrPackageRef;
    // METHODS
    const char* broken() const override {
        BROKEN_RTN(m_classOrPackageNodep && !m_classOrPackageNodep->brokeExists());
        return nullptr;
    }
    void cloneRelink() override {
        if (m_classOrPackageNodep && m_classOrPackageNodep->clonep()) {
            m_classOrPackageNodep = m_classOrPackageNodep->clonep();
        }
    }
    bool same(const AstNode* samep) const override {
        return (m_classOrPackageNodep
                == static_cast<const AstClassOrPackageRef*>(samep)->m_classOrPackageNodep);
    }
    void dump(std::ostream& str = std::cout) const override;
    string name() const override { return m_name; }  // * = Var name
    AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
    void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
    AstNodeModule* classOrPackagep() const;
    AstPackage* packagep() const { return VN_CAST(classOrPackageNodep(), Package); }
    void classOrPackagep(AstNodeModule* nodep) { m_classOrPackageNodep = (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 same(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]
public:
    explicit AstConsDynArray(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr)
        : ASTGEN_SUPER_ConsDynArray(fl) {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstConsDynArray;
    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 same(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]
public:
    explicit AstConsQueue(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr)
        : ASTGEN_SUPER_ConsQueue(fl) {
        this->lhsp(lhsp);
        this->rhsp(rhsp);
    }
    ASTGEN_MEMBERS_AstConsQueue;
    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 same(const AstNode* /*samep*/) const override { return true; }
};
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 same(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.widthMin()),
                                 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) {
        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, sourcep) {
        initWithNumber();
    }
    class VerilogStringLiteral {};  // for creator type-overload selection
    AstConst(FileLine* fl, VerilogStringLiteral, const string& str)
        : ASTGEN_SUPER_Const(fl)
        , m_num(V3Number::VerilogStringLiteral{}, this, str) {
        initWithNumber();
    }
    AstConst(FileLine* fl, uint32_t num)
        : ASTGEN_SUPER_Const(fl)
        , m_num(this, 32, num) {
        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) {
        m_num.width(32, false);
        dtypeSetLogicUnsized(32, m_num.widthMin(), 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) {
        m_num.width(32, true);
        dtypeSetLogicUnsized(32, m_num.widthMin(), 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) {
        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) {
        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) {
        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(V3Number::String{}, this, 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) {
        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) {
        dtypeSetBit();
    }
    class Null {};
    AstConst(FileLine* fl, Null)
        : ASTGEN_SUPER_Const(fl)
        , m_num(V3Number::Null{}, this) {
        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(V3Number::OneStep{}, this) {
        dtypeSetLogicSized(64, VSigning::UNSIGNED);
        initWithNumber();
    }
    ASTGEN_MEMBERS_AstConst;
    string name() const override { 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 same(const AstNode* samep) const override {
        const AstConst* const sp = static_cast<const AstConst*>(samep);
        return num().isCaseEq(sp->num());
    }
    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 AstDot final : public AstNodeExpr {
    // A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
    // These are eliminated in the link stage
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    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;
    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 same(const AstNode* /*samep*/) const override { return true; }
    bool cleanOut() const override { return true; }
};
class AstEnumItemRef final : public AstNodeExpr {
    AstEnumItem* m_itemp;  // [AfterLink] Pointer to item
    AstNodeModule* m_classOrPackagep;  // Package hierarchy
public:
    AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
        : ASTGEN_SUPER_EnumItemRef(fl)
        , m_itemp{itemp}
        , m_classOrPackagep{classOrPackagep} {
        dtypeFrom(m_itemp);
    }
    ASTGEN_MEMBERS_AstEnumItemRef;
    void dump(std::ostream& str) const override;
    string name() const override { return itemp()->name(); }
    int instrCount() const override { return 0; }
    const char* broken() const override;
    void cloneRelink() override {
        if (m_itemp->clonep()) m_itemp = m_itemp->clonep();
    }
    bool same(const AstNode* samep) const override {
        const AstEnumItemRef* const sp = static_cast<const AstEnumItemRef*>(samep);
        return itemp() == sp->itemp();
    }
    AstEnumItem* itemp() const { return m_itemp; }
    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
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 false; }
    bool same(const AstNode*) const override { return true; }
};
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 isPure() const override { return false; }  // SPECIAL: $display has 'visual' ordering
    bool same(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() const override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() const override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool same(const AstNode* /*samep*/) 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() const override { return false; }
    bool isOutputter() const override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return false; }
    bool same(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 { 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() const override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() const override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool same(const AstNode* samep) const override {
        return text() == static_cast<const AstFScanF*>(samep)->text();
    }
    string text() const { return m_text; }  // * = Text to display
    void text(const string& text) { m_text = text; }
};
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() const override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() const override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool same(const AstNode* /*samep*/) 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() const override { return false; }
    bool isOutputter() const override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return false; }
    bool same(const AstNode* /*samep*/) 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)
        : ASTGEN_SUPER_Fell(fl) {
        this->exprp(exprp);
    }
    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 same(const AstNode* /*samep*/) 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 |-> |=>
    // @astgen op1 := lhsp : AstNodeExpr
    // @astgen op2 := rhsp : AstNodeExpr
    // @astgen op3 := sentreep : Optional[AstSenTree]
public:
    AstImplication(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Implication(fl) {
        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 same(const AstNode* /*samep*/) const override { return true; }
};
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
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;
    const char* broken() const override;
    void cloneRelink() override;
    bool same(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 == static_cast<const AstInitArray*>(samep)->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);
        this->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 same(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 { return m_name; }  // * = Var name
    void name(const string& name) override { m_name = name; }
    bool index() const { return m_index; }
};
class AstMemberSel final : public AstNodeExpr {
    // @astgen op1 := fromp : AstNodeExpr
    // Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
    string m_name;
    AstVar* m_varp = nullptr;  // Post link, variable within class that is target of selection
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, AstNodeDType* dtp)
        : ASTGEN_SUPER_MemberSel(fl)
        , m_name{dtp->name()} {
        this->fromp(fromp);
        dtypep(dtp);
    }
    ASTGEN_MEMBERS_AstMemberSel;
    void cloneRelink() override;
    const char* broken() const override;
    void dump(std::ostream& str) const override;
    string name() const override { return m_name; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool same(const AstNode* samep) const override { return true; }  // dtype comparison does it
    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 same(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 same(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
};
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]

    VParseRefExp m_expect;  // Type we think it should resolve to
    string m_name;

public:
    AstParseRef(FileLine* fl, VParseRefExp expect, const string& name, AstNode* lhsp = nullptr,
                AstNodeFTaskRef* ftaskrefp = nullptr)
        : ASTGEN_SUPER_ParseRef(fl)
        , m_expect{expect}
        , m_name{name} {
        this->lhsp(lhsp);
        this->ftaskrefp(ftaskrefp);
    }
    ASTGEN_MEMBERS_AstParseRef;
    void dump(std::ostream& str) const override;
    string name() const override { return m_name; }  // * = Var name
    bool same(const AstNode* samep) const override {
        const AstParseRef* const asamep = static_cast<const AstParseRef*>(samep);
        return (expect() == asamep->expect() && m_name == asamep->m_name);
    }
    void name(const string& name) override { m_name = name; }
    VParseRefExp expect() const { return m_expect; }
    void expect(VParseRefExp exp) { m_expect = exp; }

    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)
        : ASTGEN_SUPER_Past(fl) {
        this->exprp(exprp);
        this->ticksp(ticksp);
    }
    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 same(const AstNode* /*samep*/) const override { return true; }
};
class AstPatMember final : public AstNodeExpr {
    // Verilog '{a} or '{a{b}}
    // Parents: AstPattern
    // Children: expression, AstPattern, replication count
    // 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
    bool m_default = false;

public:
    AstPatMember(FileLine* fl, AstNodeExpr* lhssp, AstNode* keyp, AstNodeExpr* repp)
        : ASTGEN_SUPER_PatMember(fl) {
        this->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;
    bool isDefault() const { return m_default; }
    void isDefault(bool flag) { m_default = 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 { 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 = false;  // $urandom vs $random
    const bool m_reset = false;  // Random reset, versus always random
public:
    class Reset {};
    AstRand(FileLine* fl, Reset, AstNodeDType* dtp, bool reset)
        : ASTGEN_SUPER_Rand(fl)
        , m_reset{reset} {
        dtypep(dtp);
    }
    AstRand(FileLine* fl, AstNode* seedp, bool urandom)
        : ASTGEN_SUPER_Rand(fl)
        , m_urandom{urandom} {
        this->seedp(seedp);
    }
    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 {
        return m_reset ? "VL_RAND_RESET_%nq(%nw, %P)"
               : seedp()
                   // cppcheck-has-bug-suppress knownConditionTrueFalse
                   ? (urandom() ? "VL_URANDOM_SEEDED_%nq%lq(%li)"  //
                                : "VL_RANDOM_SEEDED_%nq%lq(%li)")
                   : (isWide() ? "VL_RANDOM_%nq(%nw, %P)"  //
                               : "VL_RANDOM_%nq()");
    }
    bool cleanOut() const override { return false; }
    bool isGateOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_PLI; }
    bool same(const AstNode* /*samep*/) const override { return true; }
    bool combinable(const AstRand* samep) const {
        return !seedp() && !samep->seedp() && reset() == samep->reset()
               && urandom() == samep->urandom();
    }
    bool reset() const { return m_reset; }
    bool urandom() const { return m_urandom; }
};
class AstRose final : public AstNodeExpr {
    // Verilog $rose
    // @astgen op1 := exprp : AstNodeExpr
    // @astgen op2 := sentreep : Optional[AstSenTree]
public:
    AstRose(FileLine* fl, AstNodeExpr* exprp)
        : ASTGEN_SUPER_Rose(fl) {
        this->exprp(exprp);
    }
    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 same(const AstNode* /*samep*/) const override { return true; }
};
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 { return m_text; }
    int instrCount() const override { return INSTR_COUNT_PLI; }
    bool same(const AstNode* samep) const override {
        return text() == static_cast<const AstSFormatF*>(samep)->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; }
    void hasFormat(bool flag) { m_hasFormat = flag; }
    bool hasFormat() const { return m_hasFormat; }
    char missingArgChar() const { return m_missingArgChar; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
    VTimescale timeunit() const { return m_timeunit; }

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

public:
    AstSScanF(FileLine* fl, const string& text, AstNode* fromp, AstNode* exprsp)
        : ASTGEN_SUPER_SScanF(fl)
        , m_text{text} {
        this->addExprsp(exprsp);
        this->fromp(fromp);
    }
    ASTGEN_MEMBERS_AstSScanF;
    string name() const override { 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() const override { return false; }  // SPECIAL: has 'visual' ordering
    bool isOutputter() const override { return true; }  // SPECIAL: makes output
    bool cleanOut() const override { return false; }
    bool same(const AstNode* samep) const override {
        return text() == static_cast<const AstSScanF*>(samep)->text();
    }
    string text() const { return m_text; }  // * = Text to display
    void text(const string& text) { m_text = text; }
};
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 same(const AstNode* /*samep*/) const override { return true; }
};
class AstScopeName final : public AstNodeExpr {
    // For display %m and DPI context imports
    // Parents:  DISPLAY
    // @astgen op1 := scopeAttrp : List[AstText]
    // @astgen op2 := scopeEntrp : List[AstText]
    bool m_dpiExport = false;  // Is for dpiExport
    const bool m_forFormat = false;  // Is for a format %m
    string scopeNameFormatter(AstText* scopeTextp) const;
    string scopePrettyNameFormatter(AstText* scopeTextp) const;

public:
    class ForFormat {};
    AstScopeName(FileLine* fl, bool forFormat)
        : ASTGEN_SUPER_ScopeName(fl)
        , m_forFormat{forFormat} {
        dtypeSetUInt64();
    }
    ASTGEN_MEMBERS_AstScopeName;
    bool same(const AstNode* samep) const override {
        return (m_dpiExport == static_cast<const AstScopeName*>(samep)->m_dpiExport
                && m_forFormat == static_cast<const AstScopeName*>(samep)->m_forFormat);
    }
    string emitVerilog() override { return ""; }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return true; }
    void dump(std::ostream& str = std::cout) const override;
    string scopeSymName() const {  // Name for __Vscope variable including children
        return scopeNameFormatter(scopeAttrp());
    }
    string scopeDpiName() const {  // Name for DPI import scope
        return scopeNameFormatter(scopeEntrp());
    }
    string scopePrettySymName() const {  // Name for __Vscope variable including children
        return scopePrettyNameFormatter(scopeAttrp());
    }
    string scopePrettyDpiName() const {  // Name for __Vscope variable including children
        return scopePrettyNameFormatter(scopeEntrp());
    }
    bool dpiExport() const { return m_dpiExport; }
    void dpiExport(bool flag) { m_dpiExport = flag; }
    bool forFormat() const { return m_forFormat; }
};
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);
        this->addElementsp(elementsp);
    }
    ASTGEN_MEMBERS_AstSelLoopVars;
    bool same(const AstNode* /*samep*/) const override { return true; }
    bool maybePointedTo() const override { return false; }

    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
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 same(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 same(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)
        : ASTGEN_SUPER_Stable(fl) {
        this->exprp(exprp);
    }
    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 same(const AstNode* /*samep*/) 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() const override { return false; }
    bool isOutputter() const override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return true; }
    bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstStructSel final : public AstNodeExpr {
    // Unpacked struct member access
    // Parents: math|stmt
    // Children: varref, math
    // @astgen op1 := fromp : AstNodeExpr
private:
    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 { return m_name; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool cleanOut() const override { return false; }
    bool same(const AstNode* samep) const override {
        const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
        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) {
        this->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() const override { return false; }  // Though deleted before opt
    bool isOutputter() const 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() const override { return false; }
    bool isOutputter() const override { return true; }
    bool isUnlikely() const override { return true; }
    bool cleanOut() const override { return true; }
    bool same(const AstNode* /*samep*/) 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; }
    bool cleanOut() const override { return true; }
    bool same(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);
    }
    ASTGEN_MEMBERS_AstThisRef;
    string emitC() override { return "this"; }
    string emitVerilog() override { return "this"; }
    bool same(const AstNode* /*samep*/) const override { return true; }
    bool cleanOut() const override { return true; }
    AstNodeDType* getChildDTypep() const override { return childDTypep(); }
    AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : 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; }
    int instrCount() const override { return widthInstrs(); }
    bool same(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; }
    int instrCount() const override { return widthInstrs(); }
    bool same(const AstNode* /*samep*/) const override { return true; }
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
    VTimescale timeunit() const { return m_timeunit; }
};
class AstUCFunc final : public AstNodeExpr {
    // User's $c function
    // @astgen op1 := exprsp : List[AstNode] // Expressions to print (some are AstText)
public:
    AstUCFunc(FileLine* fl, AstNode* exprsp)
        : ASTGEN_SUPER_UCFunc(fl) {
        this->addExprsp(exprsp);
    }
    ASTGEN_MEMBERS_AstUCFunc;
    bool cleanOut() const override { return false; }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override { V3ERROR_NA_RETURN(""); }
    bool isPure() const override { return false; }  // SPECIAL: User may order w/other sigs
    bool isOutputter() const override { return true; }
    bool isGateOptimizable() const override { return false; }
    bool isSubstOptimizable() const override { return false; }
    bool isPredictOptimizable() const override { return false; }
    int instrCount() const override { return INSTR_COUNT_PLI; }
    bool same(const AstNode* /*samep*/) const override { return true; }
};
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() const override { return !outp(); }
    bool cleanOut() const override { return true; }
    bool same(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 : AstNodeExpr
public:
    AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
            AstNodeExpr* exprp)
        : ASTGEN_SUPER_With(fl) {
        this->indexArgRefp(indexArgRefp);
        this->valueArgRefp(valueArgRefp);
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstWith;
    bool same(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 : AstNode
    // @astgen op2 := exprp : Optional[AstNodeExpr]
public:
    AstWithParse(FileLine* fl, AstNode* funcrefp, AstNodeExpr* exprp)
        : ASTGEN_SUPER_WithParse(fl) {
        this->funcrefp(funcrefp);
        this->exprp(exprp);
    }
    ASTGEN_MEMBERS_AstWithParse;
    bool same(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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstBufIf1{fileline(), lhsp, rhsp};
    }
    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;
    }
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstCastDynamic{fileline(), lhsp, rhsp};
    }
    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; }
    int instrCount() const override { return widthInstrs() * 20; }
    bool isPure() const override { return true; }
};
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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstCompareNN{fileline(), lhsp, rhsp, m_ignoreCase};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opCompareNN(lhs, rhs, m_ignoreCase);
    }
    string name() const override { 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstConcat{fileline(), lhsp, rhsp};
    }
    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)"; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstConcatN{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstDiv{fileline(), lhsp, rhsp};
    }
    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)"; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstDivD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstDivS{fileline(), lhsp, rhsp};
    }
    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)"; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstEqWild{fileline(), lhsp, rhsp};
    }
    // 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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstFGetS{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) 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; }
    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& out, const V3Number& lhs, const V3Number& rhs) override {
        V3ERROR_NA;
    }
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstFUngetC{fileline(), lhsp, rhsp};
    }
    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; }
    int instrCount() const override { return widthInstrs() * 64; }
    bool isPure() const 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGetcN{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opGetcN(lhs, rhs);
    }
    string name() const override { 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGetcRefN{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGt{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGtD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGtN{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGtS{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGte{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGteD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGteN{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstGteS{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLogAnd{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLogIf{fileline(), lhsp, rhsp};
    }
    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 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 {
    // LOGOR with optional side effects
    // Side effects currently used in some V3Width code
    // TBD if this concept is generally adopted for side-effect tracking
    // versus V3Const tracking it itself
    bool m_sideEffect = false;  // Has side effect, relies on short-circuiting
public:
    AstLogOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LogOr(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLogOr;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLogOr{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        out.opLogOr(lhs, rhs);
    }
    bool same(const AstNode* samep) const override {
        const AstLogOr* const sp = static_cast<const AstLogOr*>(samep);
        return m_sideEffect == sp->m_sideEffect;
    }
    void dump(std::ostream& str = std::cout) const override;
    string emitVerilog() override { return "%k(%l %f|| %r)"; }
    string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %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; }
    int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
    bool isPure() const override { return !m_sideEffect; }
    void sideEffect(bool flag) { m_sideEffect = flag; }
    bool sideEffect() const { return m_sideEffect; }
};
class AstLt final : public AstNodeBiop {
public:
    AstLt(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Lt(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLt;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLt{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLtD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLtN{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLtS{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLte{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLteD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLteN{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLteS{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstModDiv{fileline(), lhsp, rhsp};
    }
    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)"; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstModDivS{fileline(), lhsp, rhsp};
    }
    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)"; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstNeqWild{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstPow{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstPowD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstPowSS{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstPowSU{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstPowUS{fileline(), lhsp, rhsp};
    }
    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)) {
                dtypeSetLogicSized(lhsp->width() * constp->toUInt(), VSigning::UNSIGNED);
            }
        }
    }
    AstReplicate(FileLine* fl, AstNodeExpr* lhsp, uint32_t repCount)
        : AstReplicate{fl, lhsp, new AstConst{fl, repCount}} {}
    ASTGEN_MEMBERS_AstReplicate;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstReplicate{fileline(), lhsp, rhsp};
    }
    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)"; }
    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();
    }
    AstReplicateN(FileLine* fl, AstNodeExpr* lhsp, uint32_t repCount)
        : AstReplicateN{fl, lhsp, new AstConst{fl, repCount}} {}
    ASTGEN_MEMBERS_AstReplicateN;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstReplicateN{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstShiftL{fileline(), lhsp, rhsp};
    }
    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 (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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstShiftR{fileline(), lhsp, rhsp};
    }
    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 (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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstShiftRS{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstSub{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstSubD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstURandomRange{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) 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; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstEq{fileline(), lhsp, rhsp};
    }
    // 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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstEqCase{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstEqD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstEqN{fileline(), lhsp, rhsp};
    }
    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 AstLogEq final : public AstNodeBiCom {
public:
    AstLogEq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_LogEq(fl, lhsp, rhsp) {
        dtypeSetBit();
    }
    ASTGEN_MEMBERS_AstLogEq;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstLogEq{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstNeq{fileline(), lhsp, 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 "!="; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstNeqCase{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstNeqD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstNeqN{fileline(), lhsp, rhsp};
    }
    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; }
};

// === 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstAdd{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstAddD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstAnd{fileline(), lhsp, rhsp};
    }
    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 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; }
};
class AstMul final : public AstNodeBiComAsv {
public:
    AstMul(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Mul(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstMul;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstMul{fileline(), lhsp, rhsp};
    }
    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 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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstMulD{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstMulS{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstOr{fileline(), lhsp, rhsp};
    }
    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 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; }
};
class AstXor final : public AstNodeBiComAsv {
public:
    AstXor(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
        : ASTGEN_SUPER_Xor(fl, lhsp, rhsp) {
        dtypeFrom(lhsp);
    }
    ASTGEN_MEMBERS_AstXor;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstXor{fileline(), lhsp, rhsp};
    }
    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 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; }
};

// === 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(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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstArraySel{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
        V3ERROR_NA; /* How can from be a const? */
    }
    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 true; }
    bool same(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(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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstAssocSel{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) 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 same(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
};
class AstWildcardSel final : public AstNodeSel {
    void init(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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstWildcardSel{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) 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 same(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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstWordSel{fileline(), lhsp, rhsp};
    }
    void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit) 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 same(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;
    AstNodeStream* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstStreamL{fileline(), lhsp, rhsp};
    }
    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 true; }
    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;
    AstNodeStream* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstStreamR{fileline(), lhsp, rhsp};
    }
    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 true; }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstAtan2D{fileline(), lhsp, rhsp};
    }
    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;
    AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
        return new AstHypotD{fileline(), lhsp, rhsp};
    }
    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
    string m_selfPointer;  // Output code object pointer (e.g.: 'this')

public:
    AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
        : ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
    ASTGEN_MEMBERS_AstCCall;

    string selfPointer() const { return m_selfPointer; }
    void selfPointer(const string& value) { m_selfPointer = value; }
    string selfPointerProtect(bool useSelfForThis) const;
};
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);
    }
    ASTGEN_MEMBERS_AstCMethodCall;
    const char* broken() const override {
        BROKEN_BASE_RTN(AstNodeCCall::broken());
        BROKEN_RTN(!fromp());
        return nullptr;
    }
};
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
public:
    AstFuncRef(FileLine* fl, AstParseRef* namep, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_FuncRef(fl, (AstNode*)namep, pinsp) {}
    AstFuncRef(FileLine* fl, const string& name, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_FuncRef(fl, name, pinsp) {}
    ASTGEN_MEMBERS_AstFuncRef;
};
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;
    const char* broken() const override {
        BROKEN_BASE_RTN(AstNodeFTaskRef::broken());
        BROKEN_RTN(!fromp());
        return nullptr;
    }
    void dump(std::ostream& str) const override;
};
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
public:
    AstNew(FileLine* fl, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_New(fl, "new", pinsp) {}
    ASTGEN_MEMBERS_AstNew;
    bool same(const AstNode* /*samep*/) const override { return true; }
    int instrCount() const override { return widthInstrs(); }
};
class AstTaskRef final : public AstNodeFTaskRef {
    // A reference to a task
public:
    AstTaskRef(FileLine* fl, AstParseRef* namep, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_TaskRef(fl, (AstNode*)namep, pinsp) {
        dtypeSetVoid();
    }
    AstTaskRef(FileLine* fl, const string& name, AstNodeExpr* pinsp)
        : ASTGEN_SUPER_TaskRef(fl, name, pinsp) {
        dtypeSetVoid();
    }
    ASTGEN_MEMBERS_AstTaskRef;
};

// === 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
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;
};
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 := widtph
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 := widtph
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->cloneTree(false),
                                 ctrl1p->cloneTree(false)) {}
    AstCountBits(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ctrl1p, AstNodeExpr* ctrl2p)
        : ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl2p, ctrl2p->cloneTree(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; }
    int instrCount() const override { return widthInstrs() * 16; }
};

// === AstNodeTermop ===
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; }
    int instrCount() const override { return INSTR_COUNT_TIME; }
    bool same(const AstNode* /*samep*/) const override { return true; }
    void dump(std::ostream& str = std::cout) const override;
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
    VTimescale timeunit() const { return m_timeunit; }
};
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; }
    int instrCount() const override { return INSTR_COUNT_TIME; }
    bool same(const AstNode* /*samep*/) const override { return true; }
    void dump(std::ostream& str = std::cout) const override;
    void timeunit(const VTimescale& flag) { m_timeunit = flag; }
    VTimescale timeunit() const { return m_timeunit; }
};

// === AstNodeTriop ===
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 { 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 true; }
    bool sizeMattersRhs() const override { return true; }
    bool sizeMattersThs() const override { 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 { 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 true; }
    bool sizeMattersRhs() const override { return true; }
    bool sizeMattersThs() const override { 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 { 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 true; }
    bool sizeMattersRhs() const override { return true; }
    bool sizeMattersThs() const override { 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 { 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 true; }
    bool sizeMattersRhs() const override { return true; }
    bool sizeMattersThs() const override { 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 { 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 AstSel final : public AstNodeTriop {
    // Multiple bit range extraction
    // @astgen alias op1 := fromp
    // @astgen alias op2 := lsbp
    // @astgen alias op3 := widthp
    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
public:
    AstSel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* lsbp, AstNodeExpr* widthp)
        : ASTGEN_SUPER_Sel(fl, fromp, lsbp, widthp)
        , m_declElWidth{1} {
        if (VN_IS(widthp, Const)) {
            dtypeSetLogicSized(VN_AS(widthp, Const)->toUInt(), VSigning::UNSIGNED);
        }
    }
    AstSel(FileLine* fl, AstNodeExpr* fromp, int lsb, int bitwidth)
        : ASTGEN_SUPER_Sel(fl, fromp, new AstConst(fl, lsb),  // Need () constructor
                           new AstConst(fl, bitwidth))  // Need () constructor
        , m_declElWidth{1} {
        dtypeSetLogicSized(bitwidth, VSigning::UNSIGNED);
    }
    ASTGEN_MEMBERS_AstSel;
    void dump(std::ostream& str) const override;
    void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit,
                       const V3Number& width) override {
        out.opSel(from, bit.toUInt() + width.toUInt() - 1, bit.toUInt());
    }
    string emitVerilog() override { V3ERROR_NA_RETURN(""); }
    string emitC() override {
        return widthp()->isOne() ? "VL_BITSEL_%nq%lq%rq%tq(%lw, %P, %li, %ri)"
               : isWide()        ? "VL_SEL_%nq%lq%rq%tq(%nw,%lw, %P, %li, %ri, %ti)"
                                 : "VL_SEL_%nq%lq%rq%tq(%lw, %P, %li, %ri, %ti)";
    }
    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 sizeMattersLhs() const override { return false; }
    bool sizeMattersRhs() const override { return false; }
    bool sizeMattersThs() const override { return false; }
    bool same(const AstNode*) const override { return true; }
    int instrCount() const override { return widthInstrs() * (VN_CAST(lsbp(), Const) ? 3 : 10); }
    int widthConst() const { return VN_AS(widthp(), Const)->toSInt(); }
    int lsbConst() const { return VN_AS(lsbp(), Const)->toSInt(); }
    int msbConst() const { return lsbConst() + widthConst() - 1; }
    VNumRange& declRange() { return m_declRange; }
    const VNumRange& declRange() const { 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 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 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 same(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() { return m_declRange; }
    const VNumRange& declRange() const { return m_declRange; }
    void declRange(const VNumRange& flag) { m_declRange = flag; }
};
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 { 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; }
};

// === AstNodeCond ===
class AstCond final : public AstNodeCond {
    // Conditional ?: expression
public:
    AstCond(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
        : ASTGEN_SUPER_Cond(fl, condp, thenp, elsep) {}
    ASTGEN_MEMBERS_AstCond;
    AstNodeExpr* cloneType(AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) override {
        return new AstCond{fileline(), condp, thenp, elsep};
    }
};
class AstCondBound final : public AstNodeCond {
    // Conditional ?: expression, specially made for safety checking of array bounds
public:
    AstCondBound(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
        : ASTGEN_SUPER_CondBound(fl, condp, thenp, elsep) {}
    ASTGEN_MEMBERS_AstCondBound;
    AstNodeExpr* cloneType(AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) override {
        return new AstCondBound{fileline(), condp, thenp, elsep};
    }
};

// === 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 {
        switch (m_fmt) {
        case ATOI: return "atoi";
        case ATOHEX: return "atohex";
        case ATOOCT: return "atooct";
        case ATOBIN: return "atobin";
        case ATOREAL: return "atoreal";
        default: V3ERROR_NA;
        }
    }
    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())";
        default: V3ERROR_NA;
        }
    }
    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; }
};
class AstCAwait final : public AstNodeUniop {
    // Emit C++'s co_await expression
    // @astgen alias op1 := exprp
    AstSenTree* m_sensesp;  // Sentree related to this await
public:
    AstCAwait(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sensesp = nullptr)
        : ASTGEN_SUPER_CAwait(fl, exprp)
        , m_sensesp{sensesp} {}
    ASTGEN_MEMBERS_AstCAwait;
    bool isTimingControl() const override { return true; }
    const char* broken() const override;
    void cloneRelink() override;
    void dump(std::ostream& str) const override;
    AstSenTree* sensesp() const { return m_sensesp; }
    void clearSensesp() { m_sensesp = 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);
        }
    }
    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 same(const AstNode* samep) const override {
        return size() == static_cast<const AstCCast*>(samep)->size();
    }
    void dump(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; }
};
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; }
};
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 { V3ERROR_NA; }
    string emitVerilog() override { return "%f$_CAST(%l)"; }
    string emitC() override { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; }
    bool cleanOut() const override { return true; }
    bool cleanLhs() const override { return true; }
    bool sizeMattersLhs() const override { return false; }
    bool same(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)"; }
    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)"; }
    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 isPure() const override { return false; }  // SPECIAL: $display has 'visual' ordering
    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 isPure() const override { return false; }  // SPECIAL: $display has 'visual' ordering
    AstNode* filep() const { return lhsp(); }
};
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; }
};
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; }
};
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; }
};
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; }
};
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 emitSimpleOperator() override { return "!"; }
    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 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 emitSimpleOperator() override { return "~"; }
    bool cleanOut() const override { return false; }
    bool cleanLhs() const override { return false; }
    bool sizeMattersLhs() const override { return true; }
};
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 same(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; }
};
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; }
};
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; }
};
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; }
};
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; }
};
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 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; }
};
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 timeunit(const VTimescale& flag) { m_timeunit = flag; }
    VTimescale timeunit() const { return m_timeunit; }
};
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 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; }
};

// === 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)
public:
    AstVarRef(FileLine* fl, const string& name, const VAccess& access)
        : ASTGEN_SUPER_VarRef(fl, name, nullptr, access) {}
    // 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);
    // This form only allowed post-link (see above)
    inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
    ASTGEN_MEMBERS_AstVarRef;
    void dump(std::ostream& str) const override;
    bool same(const AstNode* samep) const override;
    inline bool same(const AstVarRef* samep) const;
    inline bool sameNoLvalue(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_dotted;  // Dotted part of scope the name()'ed reference is under or ""
    string m_inlinedDots;  // Dotted hierarchy flattened out
public:
    AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
        : ASTGEN_SUPER_VarXRef(fl, name, nullptr, access)
        , m_dotted{dotted} {}
    inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
    ASTGEN_MEMBERS_AstVarXRef;
    void dump(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; }
    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 same(const AstNode* samep) const override {
        const AstVarXRef* asamep = static_cast<const AstVarXRef*>(samep);
        return (selfPointer() == asamep->selfPointer() && varp() == asamep->varp()
                && name() == asamep->name() && dotted() == asamep->dotted());
    }
};

#endif  // Guard
